Skip to content

Commit

Permalink
basic_parser: apply header_limit_ to trailer headers
Browse files Browse the repository at this point in the history
  • Loading branch information
ashtum committed Aug 21, 2024
1 parent f181fbf commit a620d41
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 135 deletions.
1 change: 0 additions & 1 deletion include/boost/beast/http/basic_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ class basic_parser
static unsigned constexpr flagContentLength = 1<< 10;
static unsigned constexpr flagChunked = 1<< 11;
static unsigned constexpr flagUpgrade = 1<< 12;
static unsigned constexpr flagFinalChunk = 1<< 13;

static constexpr
std::uint64_t
Expand Down
6 changes: 1 addition & 5 deletions include/boost/beast/http/detail/basic_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ struct basic_parser_base
chunk_header0,
chunk_header,
chunk_body,
trailer_fields,
complete
};

Expand Down Expand Up @@ -108,11 +109,6 @@ struct basic_parser_base
char const* it, char const* last,
error_code& ec);

BOOST_BEAST_DECL
static
char const*
find_eom(char const* p, char const* last);

//--------------------------------------------------------------------------

BOOST_BEAST_DECL
Expand Down
34 changes: 0 additions & 34 deletions include/boost/beast/http/detail/basic_parser.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -201,40 +201,6 @@ parse_hex(char const*& it, std::uint64_t& v)
return true;
}

char const*
basic_parser_base::
find_eom(char const* p, char const* last)
{
for(;;)
{
if(p + 4 > last)
return nullptr;
if(p[3] != '\n')
{
if(p[3] == '\r')
++p;
else
p += 4;
}
else if(p[2] != '\r')
{
p += 4;
}
else if(p[1] != '\n')
{
p += 2;
}
else if(p[0] != '\r')
{
p += 2;
}
else
{
return p + 4;
}
}
}

//--------------------------------------------------------------------------

char const*
Expand Down
152 changes: 59 additions & 93 deletions include/boost/beast/http/impl/basic_parser.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,20 @@ loop:
parse_chunk_header(p, n, ec);
if(ec)
goto done;
BOOST_ASSERT(! skip_);
if(state_ != state::trailer_fields)
break;
n = static_cast<std::size_t>(p1 - p);
BOOST_FALLTHROUGH;

case state::trailer_fields:
parse_fields(p, n, ec);
if(ec)
goto done;
this->on_finish_impl(ec);
if(ec)
goto done;
state_ = state::complete;
break;

case state::chunk_body:
Expand Down Expand Up @@ -564,7 +578,7 @@ parse_body_to_eof(char const*& p,
template<bool isRequest>
void
basic_parser<isRequest>::
parse_chunk_header(char const*& p0,
parse_chunk_header(char const*& in,
std::size_t n, error_code& ec)
{
/*
Expand All @@ -581,102 +595,53 @@ parse_chunk_header(char const*& p0,
chunk-ext-val = token / quoted-string
*/

auto p = p0;
auto p = in;
auto const pend = p + n;
char const* eol;

if(! (f_ & flagFinalChunk))
if(n < skip_ + 2)
{
if(n < skip_ + 2)
{
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
return;
}
if(f_ & flagExpectCRLF)
{
// Treat the last CRLF in a chunk as
// part of the next chunk, so p can
// be parsed in one call instead of two.
if(! parse_crlf(p))
{
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk);
return;
}
}
eol = find_eol(p0 + skip_, pend, ec);
if(ec)
return;
if(! eol)
{
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
skip_ = n - 1;
return;
}
skip_ = static_cast<
std::size_t>(eol - 2 - p0);

std::uint64_t size;
if(! parse_hex(p, size))
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
return;
}
if(f_ & flagExpectCRLF)
{
// Treat the last CRLF in a chunk as
// part of the next chunk, so p can
// be parsed in one call instead of two.
if(! parse_crlf(p))
{
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk);
return;
}
if(size != 0)
{
if (body_limit_.has_value())
{
if (size > *body_limit_)
{
BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
return;
}
*body_limit_ -= size;
}
auto const start = p;
parse_chunk_extensions(p, pend, ec);
if(ec)
return;
if(p != eol -2 )
{
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
return;
}
auto const ext = make_string(start, p);
this->on_chunk_header_impl(size, ext, ec);
if(ec)
return;
len_ = size;
skip_ = 2;
p0 = eol;
f_ |= flagExpectCRLF;
state_ = state::chunk_body;
return;
}

f_ |= flagFinalChunk;
}
else

auto const eol = find_eol(p + skip_, pend, ec);
if(ec)
return;
if(! eol)
{
BOOST_ASSERT(n >= 3);
if(f_ & flagExpectCRLF)
{
BOOST_ASSERT(n >= 5);
BOOST_VERIFY(parse_crlf(p));
}
std::uint64_t size;
BOOST_VERIFY(parse_hex(p, size));
eol = find_eol(p, pend, ec);
BOOST_ASSERT(! ec);
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
if(p != pend)
skip_ = pend - p - 1;
return;
}
skip_ = static_cast<std::size_t>(eol - p - 2);

auto eom = find_eom(p0 + skip_, pend);
if(! eom)
std::uint64_t size;
if(! parse_hex(p, size))
{
BOOST_ASSERT(n >= 3);
skip_ = n - 3;
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk);
return;
}
if (body_limit_.has_value())
{
if (size > *body_limit_)
{
BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
return;
}
*body_limit_ -= size;
}

auto const start = p;
parse_chunk_extensions(p, pend, ec);
Expand All @@ -688,20 +653,21 @@ parse_chunk_header(char const*& p0,
return;
}
auto const ext = make_string(start, p);
this->on_chunk_header_impl(0, ext, ec);
if(ec)
return;
p = eol;
parse_fields(p, static_cast<std::size_t>(eom - p), ec);
this->on_chunk_header_impl(size, ext, ec);
if(ec)
return;
BOOST_ASSERT(p == eom);
p0 = eom;

this->on_finish_impl(ec);
if(ec)
len_ = size;
skip_ = 0;
in = eol;
f_ |= flagExpectCRLF;
if(size != 0)
{
state_ = state::chunk_body;
return;
state_ = state::complete;
}
state_ = state::trailer_fields;
header_limit_ += 2; // for the final chunk's CRLF
}

template<bool isRequest>
Expand Down
20 changes: 20 additions & 0 deletions test/beast/http/basic_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,26 @@ class basic_parser_test : public beast::unit_test::suite
BEAST_EXPECT(! p.is_done());
BEAST_EXPECTS(ec == error::header_limit, ec.message());
}
{
multi_buffer b;
ostream(b) <<
"POST / HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"0\r\n";
error_code ec;
test_parser<true> p;
p.header_limit(47);
p.eager(true);
b.consume(p.put(b.data(), ec));
BEAST_EXPECTS(ec == error::need_more, ec.message());
ostream(b) <<
"field: value\r\n"
"\r\n";
b.consume(p.put(b.data(), ec));
BEAST_EXPECT(! p.is_done());
BEAST_EXPECTS(ec == error::header_limit, ec.message());
}
{
multi_buffer b;
ostream(b) <<
Expand Down
5 changes: 3 additions & 2 deletions test/beast/http/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,8 @@ class parser_test

ostream(b) << "0\r\n"; // needs an extra CRLF
used = p.put(b.data(), ec);
BEAST_EXPECT(used == 0);
BEAST_EXPECT(used == 3);
b.consume(used);
BEAST_EXPECT(ec == error::need_more);

ostream(b) << "\r";
Expand All @@ -480,7 +481,7 @@ class parser_test

ostream(b) << "\n";
used = p.put(b.data(), ec);
BEAST_EXPECT(used == 5);
BEAST_EXPECT(used == 2);
BEAST_EXPECT(!ec);
BEAST_EXPECT(p.is_done());
}
Expand Down

0 comments on commit a620d41

Please sign in to comment.