basic_parser: apply header_limit_ to trailer headers

This commit is contained in:
Mohammad Nejati
2024-08-20 07:18:55 +00:00
committed by Mohammad Nejati
parent f181fbf6e4
commit a620d4175a
6 changed files with 86 additions and 138 deletions

View File

@ -110,7 +110,6 @@ class basic_parser
static unsigned constexpr flagContentLength = 1<< 10; static unsigned constexpr flagContentLength = 1<< 10;
static unsigned constexpr flagChunked = 1<< 11; static unsigned constexpr flagChunked = 1<< 11;
static unsigned constexpr flagUpgrade = 1<< 12; static unsigned constexpr flagUpgrade = 1<< 12;
static unsigned constexpr flagFinalChunk = 1<< 13;
static constexpr static constexpr
std::uint64_t std::uint64_t

View File

@ -44,6 +44,7 @@ struct basic_parser_base
chunk_header0, chunk_header0,
chunk_header, chunk_header,
chunk_body, chunk_body,
trailer_fields,
complete complete
}; };
@ -108,11 +109,6 @@ struct basic_parser_base
char const* it, char const* last, char const* it, char const* last,
error_code& ec); error_code& ec);
BOOST_BEAST_DECL
static
char const*
find_eom(char const* p, char const* last);
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
BOOST_BEAST_DECL BOOST_BEAST_DECL

View File

@ -201,40 +201,6 @@ parse_hex(char const*& it, std::uint64_t& v)
return true; 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* char const*

View File

@ -164,6 +164,20 @@ loop:
parse_chunk_header(p, n, ec); parse_chunk_header(p, n, ec);
if(ec) if(ec)
goto done; 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; break;
case state::chunk_body: case state::chunk_body:
@ -564,7 +578,7 @@ parse_body_to_eof(char const*& p,
template<bool isRequest> template<bool isRequest>
void void
basic_parser<isRequest>:: basic_parser<isRequest>::
parse_chunk_header(char const*& p0, parse_chunk_header(char const*& in,
std::size_t n, error_code& ec) std::size_t n, error_code& ec)
{ {
/* /*
@ -581,101 +595,52 @@ parse_chunk_header(char const*& p0,
chunk-ext-val = token / quoted-string chunk-ext-val = token / quoted-string
*/ */
auto p = p0; auto p = in;
auto const pend = p + n; 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;
BOOST_BEAST_ASSIGN_EC(ec, error::need_more); }
return; if(f_ & flagExpectCRLF)
} {
if(f_ & flagExpectCRLF) // Treat the last CRLF in a chunk as
{ // part of the next chunk, so p can
// Treat the last CRLF in a chunk as // be parsed in one call instead of two.
// part of the next chunk, so p can if(! parse_crlf(p))
// 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::bad_chunk); BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk);
return; return;
} }
if(size != 0) }
auto const eol = find_eol(p + skip_, pend, ec);
if(ec)
return;
if(! eol)
{
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);
std::uint64_t size;
if(! parse_hex(p, size))
{
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk);
return;
}
if (body_limit_.has_value())
{
if (size > *body_limit_)
{ {
if (body_limit_.has_value()) BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
{
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; return;
} }
*body_limit_ -= size;
f_ |= flagFinalChunk;
}
else
{
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);
}
auto eom = find_eom(p0 + skip_, pend);
if(! eom)
{
BOOST_ASSERT(n >= 3);
skip_ = n - 3;
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
return;
} }
auto const start = p; auto const start = p;
@ -688,20 +653,21 @@ parse_chunk_header(char const*& p0,
return; return;
} }
auto const ext = make_string(start, p); auto const ext = make_string(start, p);
this->on_chunk_header_impl(0, ext, ec); this->on_chunk_header_impl(size, ext, ec);
if(ec) if(ec)
return; return;
p = eol;
parse_fields(p, static_cast<std::size_t>(eom - p), ec);
if(ec)
return;
BOOST_ASSERT(p == eom);
p0 = eom;
this->on_finish_impl(ec); len_ = size;
if(ec) skip_ = 0;
in = eol;
f_ |= flagExpectCRLF;
if(size != 0)
{
state_ = state::chunk_body;
return; return;
state_ = state::complete; }
state_ = state::trailer_fields;
header_limit_ += 2; // for the final chunk's CRLF
} }
template<bool isRequest> template<bool isRequest>

View File

@ -859,6 +859,26 @@ public:
BEAST_EXPECT(! p.is_done()); BEAST_EXPECT(! p.is_done());
BEAST_EXPECTS(ec == error::header_limit, ec.message()); 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; multi_buffer b;
ostream(b) << ostream(b) <<

View File

@ -470,7 +470,8 @@ public:
ostream(b) << "0\r\n"; // needs an extra CRLF ostream(b) << "0\r\n"; // needs an extra CRLF
used = p.put(b.data(), ec); used = p.put(b.data(), ec);
BEAST_EXPECT(used == 0); BEAST_EXPECT(used == 3);
b.consume(used);
BEAST_EXPECT(ec == error::need_more); BEAST_EXPECT(ec == error::need_more);
ostream(b) << "\r"; ostream(b) << "\r";
@ -480,7 +481,7 @@ public:
ostream(b) << "\n"; ostream(b) << "\n";
used = p.put(b.data(), ec); used = p.put(b.data(), ec);
BEAST_EXPECT(used == 5); BEAST_EXPECT(used == 2);
BEAST_EXPECT(!ec); BEAST_EXPECT(!ec);
BEAST_EXPECT(p.is_done()); BEAST_EXPECT(p.is_done());
} }