mirror of
https://github.com/boostorg/beast.git
synced 2025-08-02 06:15:24 +02:00
Parser body_limit is optional (API Change):
API Changes: The signature of basic_parser<>::body_limit(n) has changed. It now accepts an optional std::uint64_t. The caller may indicate that no body limit is required by calling body_limit(boost::none). The default limits remain in place in order to maintain 'safe by default' behaviour. closes #1897 closes #1965
This commit is contained in:
@@ -1,9 +1,15 @@
|
|||||||
|
* Parser body_limit is optional (API Change)
|
||||||
* Fix basic_stream expires_after (API Change)
|
* Fix basic_stream expires_after (API Change)
|
||||||
* Fix FILE namespace qualification
|
* Fix FILE namespace qualification
|
||||||
|
|
||||||
API Changes:
|
API Changes:
|
||||||
|
|
||||||
basic_stream::expires_after used to take a nanosecond duration type argument. This
|
* The signature of basic_parser<>::body_limit(n) has changed. It now accepts an
|
||||||
|
optional std::uint64_t. The caller may indicate that no body limit is required
|
||||||
|
by calling body_limit(boost::none). The default limits remain in place in order
|
||||||
|
to maintain 'safe by default' behaviour.
|
||||||
|
|
||||||
|
* basic_stream::expires_after used to take a nanosecond duration type argument. This
|
||||||
required users on systems where the steady_clock::duration_type was less accurate
|
required users on systems where the steady_clock::duration_type was less accurate
|
||||||
to explicity duration_cast when calling this function, making code non-portable.
|
to explicity duration_cast when calling this function, making code non-portable.
|
||||||
The duration type is now that of the embedded steady_clock.
|
The duration type is now that of the embedded steady_clock.
|
||||||
|
@@ -69,8 +69,9 @@ template<bool isRequest>
|
|||||||
class basic_parser
|
class basic_parser
|
||||||
: private detail::basic_parser_base
|
: private detail::basic_parser_base
|
||||||
{
|
{
|
||||||
std::uint64_t body_limit_ =
|
boost::optional<std::uint64_t>
|
||||||
default_body_limit(is_request{}); // max payload body
|
body_limit_ =
|
||||||
|
default_body_limit(is_request{}); // max payload body
|
||||||
std::uint64_t len_ = 0; // size of chunk or body
|
std::uint64_t len_ = 0; // size of chunk or body
|
||||||
std::uint64_t len0_ = 0; // content length if known
|
std::uint64_t len0_ = 0; // content length if known
|
||||||
std::unique_ptr<char[]> buf_; // temp storage
|
std::unique_ptr<char[]> buf_; // temp storage
|
||||||
@@ -289,10 +290,11 @@ public:
|
|||||||
|
|
||||||
The default limit is 1MB for requests and 8MB for responses.
|
The default limit is 1MB for requests and 8MB for responses.
|
||||||
|
|
||||||
@param v The payload body limit to set
|
@param v An optional integral value representing the body limit.
|
||||||
|
If this is equal to `boost::none`, then the body limit is disabled.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
body_limit(std::uint64_t v)
|
body_limit(boost::optional<std::uint64_t> v)
|
||||||
{
|
{
|
||||||
body_limit_ = v;
|
body_limit_ = v;
|
||||||
}
|
}
|
||||||
|
@@ -573,12 +573,15 @@ basic_parser<isRequest>::
|
|||||||
parse_body_to_eof(char const*& p,
|
parse_body_to_eof(char const*& p,
|
||||||
std::size_t n, error_code& ec)
|
std::size_t n, error_code& ec)
|
||||||
{
|
{
|
||||||
if(n > body_limit_)
|
if(body_limit_.has_value())
|
||||||
{
|
{
|
||||||
ec = error::body_limit;
|
if (n > *body_limit_)
|
||||||
return;
|
{
|
||||||
|
ec = error::body_limit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*body_limit_ -= n;
|
||||||
}
|
}
|
||||||
body_limit_ = body_limit_ - n;
|
|
||||||
ec = {};
|
ec = {};
|
||||||
n = this->on_body_impl(string_view{p, n}, ec);
|
n = this->on_body_impl(string_view{p, n}, ec);
|
||||||
p += n;
|
p += n;
|
||||||
@@ -648,12 +651,15 @@ parse_chunk_header(char const*& p0,
|
|||||||
}
|
}
|
||||||
if(size != 0)
|
if(size != 0)
|
||||||
{
|
{
|
||||||
if(size > body_limit_)
|
if (body_limit_.has_value())
|
||||||
{
|
{
|
||||||
ec = error::body_limit;
|
if (size > *body_limit_)
|
||||||
return;
|
{
|
||||||
|
ec = error::body_limit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*body_limit_ -= size;
|
||||||
}
|
}
|
||||||
body_limit_ -= size;
|
|
||||||
auto const start = p;
|
auto const start = p;
|
||||||
parse_chunk_extensions(p, pend, ec);
|
parse_chunk_extensions(p, pend, ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
#include <boost/beast/core/buffers_cat.hpp>
|
#include <boost/beast/core/buffers_cat.hpp>
|
||||||
#include <boost/beast/core/buffers_prefix.hpp>
|
#include <boost/beast/core/buffers_prefix.hpp>
|
||||||
#include <boost/beast/core/buffers_suffix.hpp>
|
#include <boost/beast/core/buffers_suffix.hpp>
|
||||||
|
#include <boost/beast/core/flat_buffer.hpp>
|
||||||
#include <boost/beast/core/multi_buffer.hpp>
|
#include <boost/beast/core/multi_buffer.hpp>
|
||||||
#include <boost/beast/core/ostream.hpp>
|
#include <boost/beast/core/ostream.hpp>
|
||||||
#include <boost/beast/http/parser.hpp>
|
#include <boost/beast/http/parser.hpp>
|
||||||
@@ -1378,6 +1379,127 @@ public:
|
|||||||
BEAST_EXPECT(p.is_done());
|
BEAST_EXPECT(p.is_done());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testChunkedOverflow()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
const std::string hdr =
|
||||||
|
"HTTP/1.1 200 OK" "\r\n"
|
||||||
|
"Server: test" "\r\n"
|
||||||
|
"Transfer-Encoding: chunked" "\r\n"
|
||||||
|
"\r\n";
|
||||||
|
const std::string chunk1 =
|
||||||
|
"10000000000000000" "\r\n"
|
||||||
|
"data...";
|
||||||
|
test_parser<false> p;
|
||||||
|
error_code ec;
|
||||||
|
p.put(net::buffer(hdr), ec);
|
||||||
|
BEAST_EXPECT(!ec);
|
||||||
|
BEAST_EXPECT(p.is_header_done());
|
||||||
|
auto bt = p.put(net::buffer(chunk1), ec);
|
||||||
|
BEAST_EXPECT(bt == 0);
|
||||||
|
BEAST_EXPECT(ec == error::bad_chunk);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string hdr =
|
||||||
|
"HTTP/1.1 200 OK" "\r\n"
|
||||||
|
"Server: test" "\r\n"
|
||||||
|
"Transfer-Encoding: chunked" "\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"1" "\r\n"
|
||||||
|
"x" "\r\n";
|
||||||
|
const std::string chunk2 =
|
||||||
|
"FFFFFFFFFFFFFFFF" "\r\n"
|
||||||
|
"data...";
|
||||||
|
test_parser<false> p;
|
||||||
|
p.eager(true);
|
||||||
|
error_code ec;
|
||||||
|
flat_buffer fb;
|
||||||
|
fb.commit(net::buffer_copy(fb.prepare(10000), net::buffer(hdr)));
|
||||||
|
fb.consume(p.put(fb.data(), ec));
|
||||||
|
BEAST_EXPECT(p.is_header_done());
|
||||||
|
BEAST_EXPECT(ec = error::need_more);
|
||||||
|
fb.commit(net::buffer_copy(fb.prepare(10000), net::buffer(chunk2)));
|
||||||
|
auto bt = p.put(fb.data(), ec);
|
||||||
|
BEAST_EXPECT(bt == 0);
|
||||||
|
BEAST_EXPECT(ec == error::body_limit);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const std::string hdr =
|
||||||
|
"HTTP/1.1 200 OK" "\r\n"
|
||||||
|
"Server: test" "\r\n"
|
||||||
|
"Transfer-Encoding: chunked" "\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"1" "\r\n"
|
||||||
|
"x" "\r\n";
|
||||||
|
const std::string chunk2 =
|
||||||
|
"FFFFFFFFFFFFFFFF" "\r\n"
|
||||||
|
"data...";
|
||||||
|
test_parser<false> p;
|
||||||
|
p.eager(true);
|
||||||
|
p.body_limit(boost::none);
|
||||||
|
error_code ec;
|
||||||
|
flat_buffer fb;
|
||||||
|
fb.commit(net::buffer_copy(fb.prepare(10000), net::buffer(hdr)));
|
||||||
|
fb.consume(p.put(fb.data(), ec));
|
||||||
|
BEAST_EXPECT(p.is_header_done());
|
||||||
|
BEAST_EXPECTS(ec = error::need_more, ec.message());
|
||||||
|
fb.commit(net::buffer_copy(fb.prepare(10000), net::buffer(chunk2)));
|
||||||
|
auto bt = p.put(fb.data(), ec);
|
||||||
|
BEAST_EXPECT(bt == 27);
|
||||||
|
BEAST_EXPECT(!ec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testChunkedBodySize()
|
||||||
|
{
|
||||||
|
string_view resp =
|
||||||
|
"HTTP/1.1 200 OK\r\n"
|
||||||
|
"Server: test\r\n"
|
||||||
|
"Transfer-Encoding: chunked\r\n"
|
||||||
|
"\r\n"
|
||||||
|
|
||||||
|
// chunk 1
|
||||||
|
"4\r\n"
|
||||||
|
"Wiki\r\n"
|
||||||
|
|
||||||
|
// chunk 2
|
||||||
|
"5\r\n"
|
||||||
|
"pedia\r\n"
|
||||||
|
|
||||||
|
// chunk 3
|
||||||
|
"E\r\n"
|
||||||
|
" in\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"chunks.\r\n"
|
||||||
|
|
||||||
|
// end
|
||||||
|
"0\r\n"
|
||||||
|
"\r\n";
|
||||||
|
|
||||||
|
{ // body limit not exceeded
|
||||||
|
test_parser<false> p;
|
||||||
|
p.eager(true);
|
||||||
|
p.body_limit(23);
|
||||||
|
error_code ec;
|
||||||
|
p.put(net::buffer(resp.data(), resp.size()), ec);
|
||||||
|
BEAST_EXPECT(!ec);
|
||||||
|
p.put_eof(ec);
|
||||||
|
BEAST_EXPECT(!ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // body limit exceeded
|
||||||
|
test_parser<false> p;
|
||||||
|
p.eager(true);
|
||||||
|
p.body_limit(22);
|
||||||
|
error_code ec;
|
||||||
|
p.put(net::buffer(resp.data(), resp.size()), ec);
|
||||||
|
BEAST_EXPECT(ec == error::body_limit);
|
||||||
|
p.put_eof(ec);
|
||||||
|
BEAST_EXPECT(ec == error::partial_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1404,6 +1526,8 @@ public:
|
|||||||
testRegression1();
|
testRegression1();
|
||||||
testIssue1211();
|
testIssue1211();
|
||||||
testIssue1267();
|
testIssue1267();
|
||||||
|
testChunkedOverflow();
|
||||||
|
testChunkedBodySize();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -18,7 +18,6 @@
|
|||||||
#include <boost/beast/core/flat_buffer.hpp>
|
#include <boost/beast/core/flat_buffer.hpp>
|
||||||
#include <boost/beast/core/multi_buffer.hpp>
|
#include <boost/beast/core/multi_buffer.hpp>
|
||||||
#include <boost/beast/core/ostream.hpp>
|
#include <boost/beast/core/ostream.hpp>
|
||||||
#include <boost/beast/http/read.hpp>
|
|
||||||
#include <boost/beast/http/string_body.hpp>
|
#include <boost/beast/http/string_body.hpp>
|
||||||
#include <boost/system/system_error.hpp>
|
#include <boost/system/system_error.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
Reference in New Issue
Block a user