New HTTP interfaces (API Change):

fix #123
fix #154
fix #265

This completely replaces the HTTP parser used to read and
parse incoming messages. The new version is faster than
the old one, and written in fewer lines. A summary of
changes:

* parse and async_parse renamed to read and async_read

* basic_parser is optimized for linear buffers,
  use flat_streambuf for the best performance with these
  functions:

  - http::read
  - http::read_some
  - http::async_read
  - http::async_read_some

* The overloads of read() and async_read() which take
  just a header have been removed, since they would
  throw away important parse metadata.

* The derived class callbacks for basic_parser have
  been streamlined. All strings passed to callbacks
  are presented in their entirety, instead of being
  provided in pieces.

These changes allow use-cases that were previously
difficult or impossible, such as:

- Efficient relaying

- Late body type commitment

- Expect: 100-continue handling
This commit is contained in:
Vinnie Falco
2017-07-20 08:12:15 -07:00
parent 34830a1780
commit e8be3fd7d3
74 changed files with 7106 additions and 5972 deletions
+128 -11
View File
@@ -9,6 +9,7 @@
#include "message_fuzz.hpp"
#include <beast/http.hpp>
#include <beast/core/consuming_buffers.hpp>
#include <beast/core/streambuf.hpp>
#include <beast/core/to_string.hpp>
#include <beast/unit_test/suite.hpp>
@@ -64,9 +65,39 @@ public:
return v;
}
template<class ConstBufferSequence,
bool isRequest, bool isDirect, class Derived>
static
std::size_t
feed(ConstBufferSequence const& buffers,
basic_parser<isRequest, isDirect, Derived>& parser,
error_code& ec)
{
using boost::asio::buffer_size;
beast::consuming_buffers<
ConstBufferSequence> cb{buffers};
std::size_t used = 0;
for(;;)
{
auto const n =
parser.write(cb, ec);
if(ec)
return 0;
if(n == 0)
break;
cb.consume(n);
used += n;
if(parser.is_complete())
break;
if(buffer_size(cb) == 0)
break;
}
return used;
}
template<class Parser>
void
testParser(std::size_t repeat, corpus const& v)
testParser1(std::size_t repeat, corpus const& v)
{
while(repeat--)
for(auto const& sb : v)
@@ -79,6 +110,21 @@ public:
}
}
template<class Parser>
void
testParser2(std::size_t repeat, corpus const& v)
{
while(repeat--)
for(auto const& sb : v)
{
Parser p;
error_code ec;
feed(sb.data(), p, ec);
if(! BEAST_EXPECTS(! ec, ec.message()))
log << to_string(sb.data()) << std::endl;
}
}
template<class Function>
void
timedTest(std::size_t repeat, std::string const& name, Function&& f)
@@ -98,21 +144,92 @@ public:
}
template<bool isRequest>
struct null_parser : basic_parser_v1<isRequest, null_parser<isRequest>>
struct null_parser :
basic_parser<isRequest, true,
null_parser<isRequest>>
{
};
template<bool isRequest, class Body, class Fields>
struct bench_parser : basic_parser<
isRequest, false, bench_parser<isRequest, Body, Fields>>
{
using mutable_buffers_type =
boost::asio::mutable_buffers_1;
void
on_request(boost::string_ref const&,
boost::string_ref const&,
int, error_code&)
{
}
void
on_response(int,
boost::string_ref const&,
int, error_code&)
{
}
void
on_field(boost::string_ref const&,
boost::string_ref const&,
error_code&)
{
}
void
on_header(error_code& ec)
{
}
void
on_body(error_code& ec)
{
}
void
on_body(std::uint64_t content_length,
error_code& ec)
{
}
void
on_data(boost::string_ref const&,
error_code& ec)
{
}
void
on_chunk(std::uint64_t,
boost::string_ref const&,
error_code&)
{
}
void
on_body(boost::string_ref const&,
error_code&)
{
}
void
on_complete(error_code&)
{
}
};
void
testSpeed()
{
static std::size_t constexpr Trials = 3;
static std::size_t constexpr Repeat = 50;
static std::size_t constexpr Repeat = 500;
log << "sizeof(request parser) == " <<
sizeof(basic_parser_v1<true, null_parser<true>>) << '\n';
sizeof(null_parser<true>) << '\n';
log << "sizeof(response parser) == " <<
sizeof(basic_parser_v1<false, null_parser<true>>)<< '\n';
sizeof(null_parser<false>)<< '\n';
testcase << "Parser speed test, " <<
((Repeat * size_ + 512) / 1024) << "KB in " <<
@@ -121,20 +238,20 @@ public:
timedTest(Trials, "nodejs_parser",
[&]
{
testParser<nodejs_parser<
testParser1<nodejs_parser<
true, streambuf_body, fields>>(
Repeat, creq_);
testParser<nodejs_parser<
testParser1<nodejs_parser<
false, streambuf_body, fields>>(
Repeat, cres_);
});
timedTest(Trials, "http::basic_parser_v1",
timedTest(Trials, "http::basic_parser",
[&]
{
testParser<parser_v1<
true, streambuf_body, fields>>(
testParser2<bench_parser<
true, streambuf_body, fields> >(
Repeat, creq_);
testParser<parser_v1<
testParser2<bench_parser<
false, streambuf_body, fields>>(
Repeat, cres_);
});