Refactor HTTP serialization and parsing (API Change):

fix #404

Parsing and serialization interfaces have been fine tuned and unified.

For parsing these stream operations are defined:

* read
* read_header
* read_some
* async_read
* async_read_header
* async_read_some

For serialization these stream operations are defined:

* write
* write_header
* write_some
* async_write
* async_write_header
* async_write_some
This commit is contained in:
Vinnie Falco committed 2017-06-02 19:12:34 -07:00
1 parent 50cba32565
commit fe75a7ca2a
44 files changed
+3655 -3250

No files matched your search

+16
View File
@@ -8,6 +8,7 @@
// Test that header file is self-contained.
#include <beast/core/bind_handler.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <beast/unit_test/suite.hpp>
#include <functional>
@@ -16,6 +17,21 @@ namespace beast {
class bind_handler_test : public unit_test::suite
{
public:
struct handler
{
void
operator()() const;
};
#if 0
// This function should fail to compile
void
failStdBind()
{
std::bind(bind_handler(handler{}));
}
#endif
void
callback(int v)
{
+1
View File
@@ -40,6 +40,7 @@ add_executable (http-bench
${BEAST_INCLUDES}
${EXTRAS_INCLUDES}
nodejs_parser.hpp
message_fuzz.hpp
../../extras/beast/unit_test/main.cpp
nodejs_parser.cpp
parser_bench.cpp
+39 -35
View File
@@ -148,19 +148,18 @@ public:
return {s, N-1};
}
template<
bool isRequest, bool isDirect, class Derived>
template<bool isRequest, class Derived>
static
std::size_t
feed(boost::asio::const_buffer buffer,
basic_parser<isRequest, isDirect, Derived>& parser,
basic_parser<isRequest, Derived>& parser,
error_code& ec)
{
using boost::asio::const_buffers_1;
std::size_t used = 0;
for(;;)
{
auto const n = parser.write(
auto const n = parser.put(
const_buffers_1{buffer}, ec);
if(ec)
return 0;
@@ -168,7 +167,7 @@ public:
break;
buffer = buffer + n;
used += n;
if(parser.is_complete())
if(parser.is_done())
break;
if(buffer_size(buffer) == 0)
break;
@@ -177,11 +176,11 @@ public:
}
template<class ConstBufferSequence,
bool isRequest, bool isDirect, class Derived>
bool isRequest, class Derived>
static
std::size_t
feed(ConstBufferSequence const& buffers,
basic_parser<isRequest, isDirect, Derived>& parser,
basic_parser<isRequest, Derived>& parser,
error_code& ec)
{
using boost::asio::buffer_size;
@@ -191,14 +190,14 @@ public:
for(;;)
{
auto const n =
parser.write(cb, ec);
parser.put(cb, ec);
if(ec)
return 0;
if(n == 0)
break;
cb.consume(n);
used += n;
if(parser.is_complete())
if(parser.is_done())
break;
if(buffer_size(cb) == 0)
break;
@@ -206,12 +205,11 @@ public:
return used;
}
template<
bool isRequest, bool isDirect, class Derived>
template<bool isRequest, class Derived>
static
std::size_t
feed(boost::asio::const_buffers_1 buffers,
basic_parser<isRequest, isDirect, Derived>& parser,
basic_parser<isRequest, Derived>& parser,
error_code& ec)
{
return feed(*buffers.begin(), parser, ec);
@@ -225,7 +223,7 @@ public:
using boost::asio::buffer;
test_parser<isRequest> p;
if(skipBody)
p.skip_body();
p.skip(true);
error_code ec;
auto const n = feed(buffer(
s.data(), s.size()), p, ec);
@@ -233,8 +231,8 @@ public:
return;
if(! BEAST_EXPECT(n == s.size()))
return;
if(p.state() == parse_state::body_to_eof)
p.write_eof(ec);
if(p.need_eof())
p.put_eof(ec);
if(BEAST_EXPECTS(! ec, ec.message()))
pred(p);
}
@@ -257,18 +255,19 @@ public:
using boost::asio::buffer;
test_parser<isRequest> p;
if(skipBody)
p.skip_body();
p.skip(true);
error_code ec;
feed(buffer(
s.data(), s.size()), p, ec);
if(! ec && ev)
p.write_eof(ec);
p.put_eof(ec);
BEAST_EXPECTS(ec == ev, ec.message());
}
void
testFlatten()
{
#if 0
using boost::asio::buffer;
{
std::string const s =
@@ -289,7 +288,7 @@ public:
BEAST_EXPECTS(! ec, ec.message());
feed(buffer_cat(b1, b2), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
}
{
@@ -310,9 +309,10 @@ public:
ec = {};
feed(buffer_cat(b1, b2), p, ec);
BEAST_EXPECTS(! ec, ec.message());
p.write_eof(ec);
p.put_eof(ec);
}
}
#endif
}
// Check that all callbacks are invoked
@@ -331,7 +331,7 @@ public:
"*";
feed(buffer(s), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
BEAST_EXPECT(p.got_on_begin);
BEAST_EXPECT(p.got_on_field);
BEAST_EXPECT(p.got_on_header);
@@ -350,7 +350,7 @@ public:
"*";
feed(buffer(s), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
BEAST_EXPECT(p.got_on_begin);
BEAST_EXPECT(p.got_on_field);
BEAST_EXPECT(p.got_on_header);
@@ -747,6 +747,7 @@ public:
void testBody()
{
#if 0
using boost::asio::buffer;
good<true>(
"GET / HTTP/1.1\r\n"
@@ -774,7 +775,7 @@ public:
buf("67890")),
p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
// request without Content-Length or
@@ -787,7 +788,7 @@ public:
"\r\n"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
{
error_code ec;
@@ -797,7 +798,7 @@ public:
"\r\n"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
// response without Content-Length or
@@ -810,17 +811,17 @@ public:
"\r\n"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(! p.is_complete());
BEAST_EXPECT(! p.is_done());
BEAST_EXPECT(p.state() == parse_state::body_to_eof);
feed(buf(
"hello"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(! p.is_complete());
BEAST_EXPECT(! p.is_done());
BEAST_EXPECT(p.state() == parse_state::body_to_eof);
p.write_eof(ec);
p.put_eof(ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
// 304 "Not Modified" response does not require eof
@@ -832,7 +833,7 @@ public:
"\r\n"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
// Chunked response does not require eof
@@ -845,12 +846,12 @@ public:
"\r\n"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(! p.is_complete());
BEAST_EXPECT(! p.is_done());
feed(buf(
"0\r\n\r\n"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
// restart: 1.0 assumes Connection: close
@@ -862,7 +863,7 @@ public:
"\r\n"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
// restart: 1.1 assumes Connection: keep-alive
@@ -874,7 +875,7 @@ public:
"\r\n"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
bad<true>(
@@ -882,8 +883,10 @@ public:
"Content-Length: 1\r\n"
"\r\n",
error::partial_message);
#endif
}
#if 0
template<bool isRequest>
void
check_header(
@@ -898,6 +901,7 @@ public:
BEAST_EXPECT(! p.got_on_complete);
BEAST_EXPECT(p.state() != parse_state::header);
}
#endif
void
testSplit()
@@ -921,7 +925,7 @@ public:
BEAST_EXPECT(! p.got_on_chunk);
BEAST_EXPECT(! p.got_on_complete);
BEAST_EXPECT(p.state() != parse_state::header);
BEAST_EXPECT(! p.is_complete());
BEAST_EXPECT(! p.is_done());
BEAST_EXPECT(p.body.empty());
b.consume(n);
p.resume();
@@ -933,7 +937,7 @@ public:
BEAST_EXPECT(p.got_on_body);
BEAST_EXPECT(! p.got_on_chunk);
BEAST_EXPECT(p.got_on_complete);
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
BEAST_EXPECT(p.body == "*****");
#endif
}
+463 -504
View File
File diff suppressed because it is too large. Load diff
+10 -3
View File
@@ -22,6 +22,13 @@ class header_parser_test
, public test::enable_yield_to
{
public:
static
boost::asio::const_buffers_1
buf(string_view s)
{
return {s.data(), s.size()};
}
void
testParse()
{
@@ -34,7 +41,7 @@ public:
flat_buffer db{1024};
header_parser<true, fields> p;
read_some(is, db, p);
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_header_done());
}
{
test::string_istream is{ios_,
@@ -47,8 +54,8 @@ public:
flat_buffer db{1024};
header_parser<true, fields> p;
read_some(is, db, p);
BEAST_EXPECT(! p.is_complete());
BEAST_EXPECT(p.state() == parse_state::body);
BEAST_EXPECT(p.is_header_done());
BEAST_EXPECT(! p.is_done());
}
}
+220 -94
View File
@@ -14,10 +14,10 @@
#include <beast/test/string_istream.hpp>
#include <beast/test/string_ostream.hpp>
#include <beast/test/yield_to.hpp>
#include <beast/core/consuming_buffers.hpp>
#include <beast/core/flat_buffer.hpp>
#include <beast/core/multi_buffer.hpp>
#include <beast/http/header_parser.hpp>
#include <beast/http/read.hpp>
#include <beast/core/ostream.hpp>
#include <beast/http/read.hpp>
#include <beast/http/string_body.hpp>
#include <boost/system/system_error.hpp>
@@ -30,41 +30,117 @@ class message_parser_test
, public beast::test::enable_yield_to
{
public:
template<bool isRequest, class Pred>
void
testMatrix(std::string const& s, Pred const& pred)
template<bool isRequest>
using parser_type =
message_parser<isRequest, string_body, fields>;
static
boost::asio::const_buffers_1
buf(string_view s)
{
beast::test::string_istream ss{get_io_service(), s};
error_code ec;
#if 0
multi_buffer buffer;
#else
flat_buffer buffer{1024};
#endif
message<isRequest, string_body, fields> m;
read(ss, buffer, m, ec);
if(! BEAST_EXPECTS(! ec, ec.message()))
return;
pred(m);
return {s.data(), s.size()};
}
template<class ConstBufferSequence,
bool isRequest, class Derived>
static
void
put(ConstBufferSequence const& buffers,
basic_parser<isRequest, Derived>& p,
error_code& ec)
{
using boost::asio::buffer_size;
consuming_buffers<ConstBufferSequence> cb{buffers};
for(;;)
{
auto const used = p.put(cb, ec);
cb.consume(used);
if(ec)
return;
if(p.need_eof() &&
buffer_size(cb) == 0)
{
p.put_eof(ec);
if(ec)
return;
}
if(p.is_done())
break;
}
}
template<bool isRequest, class F>
void
doMatrix(string_view s0, F const& f)
{
using boost::asio::buffer;
// parse a single buffer
{
auto s = s0;
error_code ec;
parser_type<isRequest> p;
put(buffer(s.data(), s.size()), p, ec);
if(! BEAST_EXPECTS(! ec, ec.message()))
return;
f(p);
}
// parse two buffers
for(auto n = s0.size() - 1; n >= 1; --n)
{
auto s = s0;
error_code ec;
parser_type<isRequest> p;
p.eager(true);
auto used =
p.put(buffer(s.data(), n), ec);
s.remove_prefix(used);
if(ec == error::need_more)
ec = {};
if(! BEAST_EXPECTS(! ec, ec.message()))
continue;
BEAST_EXPECT(! p.is_done());
used = p.put(
buffer(s.data(), s.size()), ec);
s.remove_prefix(used);
if(! BEAST_EXPECTS(! ec, ec.message()))
continue;
BEAST_EXPECT(s.empty());
if(p.need_eof())
{
p.put_eof(ec);
if(! BEAST_EXPECTS(! ec, ec.message()))
continue;
}
if(BEAST_EXPECT(p.is_done()))
f(p);
}
}
void
testRead()
testParse()
{
testMatrix<false>(
doMatrix<false>(
"HTTP/1.0 200 OK\r\n"
"Server: test\r\n"
"\r\n"
"*******",
[&](message<false, string_body, fields> const& m)
"Hello, world!",
[&](parser_type<false> const& p)
{
BEAST_EXPECTS(m.body == "*******",
"body='" + m.body + "'");
auto const& m = p.get();
BEAST_EXPECT(! p.is_chunked());
BEAST_EXPECT(p.need_eof());
BEAST_EXPECT(p.content_length() == boost::none);
BEAST_EXPECT(m.version == 10);
BEAST_EXPECT(m.status == 200);
BEAST_EXPECT(m.reason() == "OK");
BEAST_EXPECT(m.fields["Server"] == "test");
BEAST_EXPECT(m.body == "Hello, world!");
}
);
testMatrix<false>(
"HTTP/1.0 200 OK\r\n"
doMatrix<false>(
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Expect: Expires, MD5-Fingerprint\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"5\r\n"
@@ -75,132 +151,137 @@ public:
"Expires: never\r\n"
"MD5-Fingerprint: -\r\n"
"\r\n",
[&](message<false, string_body, fields> const& m)
[&](parser_type<false> const& p)
{
auto const& m = p.get();
BEAST_EXPECT(! p.need_eof());
BEAST_EXPECT(p.is_chunked());
BEAST_EXPECT(p.content_length() == boost::none);
BEAST_EXPECT(m.version == 11);
BEAST_EXPECT(m.status == 200);
BEAST_EXPECT(m.reason() == "OK");
BEAST_EXPECT(m.fields["Server"] == "test");
BEAST_EXPECT(m.fields["Transfer-Encoding"] == "chunked");
BEAST_EXPECT(m.fields["Expires"] == "never");
BEAST_EXPECT(m.fields["MD5-Fingerprint"] == "-");
BEAST_EXPECT(m.body == "*****--");
}
);
testMatrix<false>(
doMatrix<false>(
"HTTP/1.0 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 5\r\n"
"\r\n"
"*****",
[&](message<false, string_body, fields> const& m)
[&](parser_type<false> const& p)
{
auto const& m = p.get();
BEAST_EXPECT(m.body == "*****");
}
);
testMatrix<true>(
doMatrix<true>(
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"\r\n",
[&](message<true, string_body, fields> const& m)
[&](parser_type<true> const& p)
{
auto const& m = p.get();
BEAST_EXPECT(m.method() == "GET");
BEAST_EXPECT(m.target() == "/");
BEAST_EXPECT(m.version == 11);
BEAST_EXPECT(! p.need_eof());
BEAST_EXPECT(! p.is_chunked());
BEAST_EXPECT(p.content_length() == boost::none);
}
);
testMatrix<true>(
doMatrix<true>(
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"X: \t x \t \r\n"
"\r\n",
[&](message<true, string_body, fields> const& m)
[&](parser_type<true> const& p)
{
auto const& m = p.get();
BEAST_EXPECT(m.fields["X"] == "x");
}
);
}
void
testParse()
{
using boost::asio::buffer;
// test eager(true)
{
error_code ec;
beast::test::string_istream is{
get_io_service(),
parser_type<true> p;
p.eager(true);
p.put(buf(
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*"};
flat_buffer b{1024};
message_parser<true, string_body, fields> p;
read(is, b, p, ec);
"*")
, ec);
auto const& m = p.get();
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(! ec);
BEAST_EXPECT(p.is_done());
BEAST_EXPECT(p.is_header_done());
BEAST_EXPECT(! p.need_eof());
BEAST_EXPECT(m.method() == "GET");
BEAST_EXPECT(m.target() == "/");
BEAST_EXPECT(m.version == 11);
BEAST_EXPECT(m.fields["User-Agent"] == "test");
BEAST_EXPECT(m.body == "*");
}
#if 0
{
// test partial parsing of final chunk
// parse through the chunk body
beast::test::string_istream is{
get_io_service(), ""};
multi_buffer b;
b <<
error_code ec;
flat_buffer b;
parser_type<true> p;
p.eager(true);
ostream(b) <<
"PUT / HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"1\r\n"
"*";
error_code ec;
message_parser<true, string_body, fields> p;
read(is, b, p, ec);
BEAST_EXPECT(b.size() == 0);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(!p.is_complete());
auto used = p.put(b.data(), ec);
b.consume(used);
BEAST_EXPECT(! ec);
BEAST_EXPECT(! p.is_done());
BEAST_EXPECT(p.get().body == "*");
b << "\r\n0;d;e=3;f=\"4\"\r\n"
ostream(b) <<
"\r\n"
"0;d;e=3;f=\"4\"\r\n"
"Expires: never\r\n"
"MD5-Fingerprint: -\r\n";
// incomplete parse, missing the final crlf
BEAST_EXPECT(p.write(b.data(), ec) == 0);
used = p.put(b.data(), ec);
b.consume(used);
BEAST_EXPECT(ec == error::need_more);
ec = {};
BEAST_EXPECT(! p.is_done());
ostream(b) <<
"\r\n"; // final crlf to end message
used = p.put(b.data(), ec);
b.consume(used);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(!p.is_complete());
b << "\r\n"; // final crlf to end message
BEAST_EXPECT(p.write(b.data(), ec) == b.size());
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
}
{
error_code ec;
message_parser<false, string_body, fields> p;
std::string const s =
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*";
p.write(buffer(s), ec);
auto const& m = p.get();
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(m.status == 200);
BEAST_EXPECT(m.reason() == "OK");
BEAST_EXPECT(m.version == 11);
BEAST_EXPECT(m.fields["Server"] == "test");
BEAST_EXPECT(m.body == "*");
BEAST_EXPECT(p.is_done());
}
// skip body
{
error_code ec;
message_parser<false, string_body, fields> p;
std::string const s =
"HTTP/1.1 200 Connection Established\r\n"
"Proxy-Agent: Zscaler/5.1\r\n"
"\r\n";
p.skip_body();
p.write(buffer(s), ec);
p.skip(true);
p.put(buf(
"HTTP/1.1 200 OK\r\n"
"Content-Length: 5\r\n"
"\r\n"
"*****")
, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
BEAST_EXPECT(p.is_header_done());
BEAST_EXPECT(p.content_length() &&
*p.content_length() == 5);
}
#endif
}
void
@@ -219,8 +300,8 @@ public:
read_some(ss, b, p0, ec);
b.consume(bytes_used);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p0.state() != parse_state::header);
BEAST_EXPECT(! p0.is_complete());
BEAST_EXPECT(p0.is_header_done());
BEAST_EXPECT(! p0.is_done());
message_parser<true,
string_body, fields> p1{std::move(p0)};
read(ss, b, p1, ec);
@@ -228,12 +309,57 @@ public:
BEAST_EXPECT(p1.get().body == "*****");
}
//--------------------------------------------------------------------------
template<class DynamicBuffer>
void
testNeedMore()
{
error_code ec;
std::size_t used;
{
DynamicBuffer b;
parser_type<true> p;
ostream(b) <<
"GET / HTTP/1.1\r\n";
used = p.put(b.data(), ec);
BEAST_EXPECTS(ec == error::need_more, ec.message());
b.consume(used);
ec = {};
ostream(b) <<
"User-Agent: test\r\n"
"\r\n";
used = p.put(b.data(), ec);
BEAST_EXPECTS(! ec, ec.message());
b.consume(used);
BEAST_EXPECT(p.is_done());
BEAST_EXPECT(p.is_header_done());
}
}
void
testGotSome()
{
error_code ec;
parser_type<true> p;
auto used = p.put(buf(""), ec);
BEAST_EXPECT(ec == error::need_more);
BEAST_EXPECT(! p.got_some());
BEAST_EXPECT(used == 0);
ec = {};
used = p.put(buf("G"), ec);
BEAST_EXPECT(ec == error::need_more);
BEAST_EXPECT(p.got_some());
BEAST_EXPECT(used == 0);
}
void
run() override
{
testRead();
testParse();
testExpect100Continue();
testNeedMore<flat_buffer>();
testNeedMore<multi_buffer>();
testGotSome();
}
};
+28 -39
View File
@@ -11,6 +11,7 @@
#include <beast/http.hpp>
#include <beast/core/consuming_buffers.hpp>
#include <beast/core/ostream.hpp>
#include <beast/core/flat_buffer.hpp>
#include <beast/core/multi_buffer.hpp>
#include <beast/unit_test/suite.hpp>
#include <boost/lexical_cast.hpp>
@@ -26,7 +27,8 @@ class parser_bench_test : public beast::unit_test::suite
public:
static std::size_t constexpr N = 2000;
using corpus = std::vector<multi_buffer>;
//using corpus = std::vector<multi_buffer>;
using corpus = std::vector<flat_buffer>;
corpus creq_;
corpus cres_;
@@ -41,22 +43,17 @@ public:
std::string>(buffers(bs));
}
parser_bench_test()
{
creq_ = build_corpus(N/2, std::true_type{});
cres_ = build_corpus(N/2, std::false_type{});
}
corpus
build_corpus(std::size_t n, std::true_type)
{
corpus v;
v.resize(N);
v.resize(n);
message_fuzz mg;
for(std::size_t i = 0; i < n; ++i)
{
mg.request(v[i]);
size_ += v[i].size();
BEAST_EXPECT(v[i].size() > 0);
}
return v;
}
@@ -65,22 +62,23 @@ public:
build_corpus(std::size_t n, std::false_type)
{
corpus v;
v.resize(N);
v.resize(n);
message_fuzz mg;
for(std::size_t i = 0; i < n; ++i)
{
mg.response(v[i]);
size_ += v[i].size();
BEAST_EXPECT(v[i].size() > 0);
}
return v;
}
template<class ConstBufferSequence,
bool isRequest, bool isDirect, class Derived>
bool isRequest, class Derived>
static
std::size_t
feed(ConstBufferSequence const& buffers,
basic_parser<isRequest, isDirect, Derived>& parser,
basic_parser<isRequest, Derived>& parser,
error_code& ec)
{
using boost::asio::buffer_size;
@@ -90,14 +88,14 @@ public:
for(;;)
{
auto const n =
parser.write(cb, ec);
parser.put(cb, ec);
if(ec)
return 0;
if(n == 0)
break;
cb.consume(n);
used += n;
if(parser.is_complete())
if(parser.is_done())
break;
if(buffer_size(cb) == 0)
break;
@@ -155,14 +153,13 @@ public:
template<bool isRequest>
struct null_parser :
basic_parser<isRequest, true,
null_parser<isRequest>>
basic_parser<isRequest, null_parser<isRequest>>
{
};
template<bool isRequest, class Body, class Fields>
struct bench_parser : basic_parser<
isRequest, false, bench_parser<isRequest, Body, Fields>>
isRequest, bench_parser<isRequest, Body, Fields>>
{
using mutable_buffers_type =
boost::asio::mutable_buffers_1;
@@ -194,13 +191,8 @@ public:
}
void
on_body(error_code& ec)
{
}
void
on_body(std::uint64_t content_length,
error_code& ec)
on_body(boost::optional<std::uint64_t> const&,
error_code&)
{
}
@@ -217,12 +209,6 @@ public:
{
}
void
on_body(string_view const&,
error_code&)
{
}
void
on_complete(error_code&)
{
@@ -235,6 +221,9 @@ public:
static std::size_t constexpr Trials = 3;
static std::size_t constexpr Repeat = 500;
creq_ = build_corpus(N/2, std::true_type{});
cres_ = build_corpus(N/2, std::false_type{});
log << "sizeof(request parser) == " <<
sizeof(null_parser<true>) << '\n';
@@ -245,16 +234,6 @@ public:
((Repeat * size_ + 512) / 1024) << "KB in " <<
(Repeat * (creq_.size() + cres_.size())) << " messages";
timedTest(Trials, "nodejs_parser",
[&]
{
testParser1<nodejs_parser<
true, dynamic_body, fields>>(
Repeat, creq_);
testParser1<nodejs_parser<
false, dynamic_body, fields>>(
Repeat, cres_);
});
timedTest(Trials, "http::basic_parser",
[&]
{
@@ -265,6 +244,16 @@ public:
false, dynamic_body, fields>>(
Repeat, cres_);
});
timedTest(Trials, "nodejs_parser",
[&]
{
testParser1<nodejs_parser<
true, dynamic_body, fields>>(
Repeat, creq_);
testParser1<nodejs_parser<
false, dynamic_body, fields>>(
Repeat, cres_);
});
pass();
}
+51 -1
View File
@@ -10,11 +10,14 @@
#include "test_parser.hpp"
#include <beast/core/ostream.hpp>
#include <beast/core/static_buffer.hpp>
#include <beast/http/fields.hpp>
#include <beast/http/dynamic_body.hpp>
#include <beast/http/header_parser.hpp>
#include <beast/http/string_body.hpp>
#include <beast/test/fail_stream.hpp>
#include <beast/test/pipe_stream.hpp>
#include <beast/test/string_istream.hpp>
#include <beast/test/yield_to.hpp>
#include <beast/unit_test/suite.hpp>
@@ -108,7 +111,7 @@ public:
{
multi_buffer b;
test::string_istream ss(ios_, "GET / X");
message_parser<true, dynamic_body, fields> p;
request_parser<dynamic_body> p;
read(ss, b, p);
fail();
}
@@ -118,6 +121,52 @@ public:
}
}
void
testBufferOverflow()
{
{
test::pipe p{ios_};
ostream(p.server.buffer) <<
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
"User-Agent: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"10\r\n"
"****************\r\n"
"0\r\n\r\n";
static_buffer_n<1024> b;
request<string_body> req;
try
{
read(p.server, b, req);
pass();
}
catch(std::exception const& e)
{
fail(e.what(), __FILE__, __LINE__);
}
}
{
test::pipe p{ios_};
ostream(p.server.buffer) <<
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
"User-Agent: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"10\r\n"
"****************\r\n"
"0\r\n\r\n";
error_code ec;
static_buffer_n<10> b;
request<string_body> req;
read(p.server, b, req, ec);
BEAST_EXPECTS(ec == error::buffer_overflow,
ec.message());
}
}
void testFailures(yield_context do_yield)
{
char const* req[] = {
@@ -318,6 +367,7 @@ public:
run() override
{
testThrow();
testBufferOverflow();
yield_to([&](yield_context yield){
testFailures(yield); });
+12 -21
View File
@@ -16,8 +16,7 @@ namespace http {
template<bool isRequest>
class test_parser
: public basic_parser<isRequest, false,
test_parser<isRequest>>
: public basic_parser<isRequest, test_parser<isRequest>>
{
test::fail_counter* fc_ = nullptr;
@@ -36,8 +35,6 @@ public:
bool got_on_header = false;
bool got_on_body = false;
bool got_content_length = false;
bool got_on_prepare = false;
bool got_on_commit = false;
bool got_on_chunk = false;
bool got_on_complete = false;
@@ -50,10 +47,9 @@ public:
}
void
on_request(
string_view const& method_,
string_view const& path_,
int version_, error_code& ec)
on_request(string_view const& method_,
string_view const& path_,
int version_, error_code& ec)
{
method = std::string(
method_.data(), method_.size());
@@ -98,19 +94,13 @@ public:
}
void
on_body(error_code& ec)
on_body(boost::optional<
std::uint64_t> const& content_length_,
error_code& ec)
{
got_on_body = true;
if(fc_)
fc_->fail(ec);
}
void
on_body(std::uint64_t content_length,
error_code& ec)
{
got_on_body = true;
got_content_length = true;
got_content_length =
static_cast<bool>(content_length_);
if(fc_)
fc_->fail(ec);
}
@@ -120,12 +110,13 @@ public:
error_code& ec)
{
body.append(s.data(), s.size());
if(fc_)
fc_->fail(ec);
}
void
on_chunk(std::uint64_t,
string_view const&,
error_code& ec)
string_view const&, error_code& ec)
{
got_on_chunk = true;
if(fc_)
+3
View File
@@ -9,6 +9,7 @@
#include <beast/http/type_traits.hpp>
#include <beast/http/empty_body.hpp>
#include <string>
namespace beast {
namespace http {
@@ -17,5 +18,7 @@ BOOST_STATIC_ASSERT(! is_body_reader<int>::value);
BOOST_STATIC_ASSERT(is_body_reader<empty_body>::value);
BOOST_STATIC_ASSERT(! is_body_writer<std::string>::value);
} // http
} // beast
+4 -92
View File
@@ -382,7 +382,7 @@ public:
m.body = "*****";
error_code ec;
write(fs, m, ec);
if(ec == boost::asio::error::eof)
if(ec == error::end_of_stream)
{
BEAST_EXPECT(fs.next_layer().str ==
"GET / HTTP/1.0\r\n"
@@ -415,7 +415,7 @@ public:
m.body = "*****";
error_code ec;
async_write(fs, m, do_yield[ec]);
if(ec == boost::asio::error::eof)
if(ec == error::end_of_stream)
{
BEAST_EXPECT(fs.next_layer().str ==
"GET / HTTP/1.0\r\n"
@@ -559,7 +559,7 @@ public:
test::string_ostream ss(ios_);
error_code ec;
write(ss, m, ec);
BEAST_EXPECT(ec == boost::asio::error::eof);
BEAST_EXPECT(ec == error::end_of_stream);
BEAST_EXPECT(ss.str ==
"GET / HTTP/1.0\r\n"
"User-Agent: test\r\n"
@@ -596,7 +596,7 @@ public:
test::string_ostream ss(ios_);
error_code ec;
write(ss, m, ec);
BEAST_EXPECT(ec == boost::asio::error::eof);
BEAST_EXPECT(ec == error::end_of_stream);
BEAST_EXPECT(ss.str ==
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
@@ -949,93 +949,6 @@ public:
}
}
/** Execute a child process and return the output as an HTTP response.
@param input A stream to read the child process output from.
@param output A stream to write the HTTP response to.
*/
template<class SyncReadStream, class SyncWriteStream>
void
cgi_process(SyncReadStream& input, SyncWriteStream& output, error_code& ec)
{
multi_buffer b;
message<false, buffer_body<true,
typename multi_buffer::const_buffers_type>, fields> m;
m.status = 200;
m.version = 11;
m.fields.insert("Server", "cgi-process");
m.fields.insert("Transfer-Encoding", "chunked");
m.body.first = boost::none;
m.body.second = true;
auto sr = make_serializer(m);
// send the header first, so the
// other end gets it right away
for(;;)
{
write_some(output, sr, ec);
if(ec == error::need_more)
{
ec = {};
break;
}
if(ec)
return;
}
// send the body
for(;;)
{
// read from input
auto bytes_transferred =
input.read_some(b.prepare(1024), ec);
if(ec == boost::asio::error::eof)
{
BOOST_ASSERT(bytes_transferred == 0);
ec = {};
m.body = {boost::none, false};
}
else
{
if(ec)
return;
b.commit(bytes_transferred);
m.body = {b.data(), true};
}
// write to output
for(;;)
{
write_some(output, sr, ec);
if(ec == error::need_more)
{
ec = {};
break;
}
if(ec)
return;
if(sr.is_done())
goto is_done;
}
b.consume(b.size());
}
is_done:
;
}
void
testCgiRelay()
{
error_code ec;
std::string const body = "Hello, world!\n";
test::string_ostream so{get_io_service(), 3};
test::string_istream si{get_io_service(), body, 6};
cgi_process(si, so, ec);
BEAST_EXPECT(equal_body<false>(so.str, body));
}
void run() override
{
yield_to([&](yield_context yield){
@@ -1046,7 +959,6 @@ public:
test_std_ostream();
testOstream();
testIoService();
testCgiRelay();
yield_to(
[&](yield_context yield)
{