Parser concept, fixes:

A new concept Parser is introduced with routines to read from a stream
into the parser. This solves a problem with the old read interface where
messages must be default constructible and move assignable.

Parser fixes:

* Fix detect invalid reason-phrase octets
* Fix write_eof to set the 'complete' state on success
* Fix consider parse complete if eof received on empty body

WebSocket:

* Increase coverage
This commit is contained in:
Vinnie Falco
2016-04-30 10:29:39 -04:00
parent 8921da91b8
commit 2a8de0fd6b
28 changed files with 1536 additions and 701 deletions

View File

@@ -8,6 +8,7 @@
// Test that header file is self-contained.
#include <beast/http/read.hpp>
#include <beast/http/headers.hpp>
#include <beast/http/streambuf_body.hpp>
#include <beast/test/fail_stream.hpp>
#include <beast/test/string_stream.hpp>
@@ -23,6 +24,227 @@ class read_test
, public test::enable_yield_to
{
public:
template<bool isRequest>
class fail_parser
: public basic_parser_v1<isRequest, fail_parser<isRequest>>
{
test::fail_counter& fc_;
public:
template<class... Args>
explicit
fail_parser(test::fail_counter& fc, Args&&... args)
: fc_(fc)
{
}
void on_start(error_code& ec)
{
fc_.fail(ec);
}
void on_method(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_uri(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_reason(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_request(error_code& ec)
{
fc_.fail(ec);
}
void on_response(error_code& ec)
{
fc_.fail(ec);
}
void on_field(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_value(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
int on_headers(error_code& ec)
{
fc_.fail(ec);
return 0;
}
void on_body(boost::string_ref const&, error_code& ec)
{
fc_.fail(ec);
}
void on_complete(error_code& ec)
{
fc_.fail(ec);
}
};
template<bool isRequest>
void failMatrix(const char* s, yield_context do_yield)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
static std::size_t constexpr limit = 100;
std::size_t n;
auto const len = strlen(s);
for(n = 0; n < limit; ++n)
{
streambuf sb;
sb.commit(buffer_copy(
sb.prepare(len), buffer(s, len)));
test::fail_counter fc(n);
test::fail_stream<
test::string_stream> fs{fc, ios_, ""};
fail_parser<isRequest> p(fc);
error_code ec;
parse(fs, sb, p, ec);
if(! ec)
break;
}
expect(n < limit);
for(n = 0; n < limit; ++n)
{
static std::size_t constexpr pre = 10;
streambuf sb;
sb.commit(buffer_copy(
sb.prepare(pre), buffer(s, pre)));
test::fail_counter fc(n);
test::fail_stream<test::string_stream> fs{
fc, ios_, std::string{s + pre, len - pre}};
fail_parser<isRequest> p(fc);
error_code ec;
parse(fs, sb, p, ec);
if(! ec)
break;
}
expect(n < limit);
for(n = 0; n < limit; ++n)
{
streambuf sb;
sb.commit(buffer_copy(
sb.prepare(len), buffer(s, len)));
test::fail_counter fc(n);
test::fail_stream<
test::string_stream> fs{fc, ios_, ""};
fail_parser<isRequest> p(fc);
error_code ec;
async_parse(fs, sb, p, do_yield[ec]);
if(! ec)
break;
}
expect(n < limit);
for(n = 0; n < limit; ++n)
{
static std::size_t constexpr pre = 10;
streambuf sb;
sb.commit(buffer_copy(
sb.prepare(pre), buffer(s, pre)));
test::fail_counter fc(n);
test::fail_stream<test::string_stream> fs{
fc, ios_, std::string{s + pre, len - pre}};
fail_parser<isRequest> p(fc);
error_code ec;
async_parse(fs, sb, p, do_yield[ec]);
if(! ec)
break;
}
expect(n < limit);
}
void testThrow()
{
try
{
streambuf sb;
test::string_stream ss(ios_, "GET / X");
parser_v1<true, streambuf_body, headers> p;
parse(ss, sb, p);
fail();
}
catch(std::exception const&)
{
pass();
}
}
void testFailures(yield_context do_yield)
{
char const* req[] = {
"GET / HTTP/1.0\r\n"
"Host: localhost\r\n"
"User-Agent: test\r\n"
"Empty:\r\n"
"\r\n"
,
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
"User-Agent: test\r\n"
"Content-Length: 2\r\n"
"\r\n"
"**"
,
"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"
,
nullptr
};
char const* res[] = {
"HTTP/1.0 200 OK\r\n"
"Server: test\r\n"
"\r\n"
,
"HTTP/1.0 200 OK\r\n"
"Server: test\r\n"
"\r\n"
"***"
,
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 3\r\n"
"\r\n"
"***"
,
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"10\r\n"
"****************\r\n"
"0\r\n\r\n"
,
nullptr
};
for(std::size_t i = 0; req[i]; ++i)
failMatrix<true>(req[i], do_yield);
for(std::size_t i = 0; res[i]; ++i)
failMatrix<false>(res[i], do_yield);
}
void testRead(yield_context do_yield)
{
static std::size_t constexpr limit = 100;
@@ -30,7 +252,6 @@ public:
for(n = 0; n < limit; ++n)
{
streambuf sb;
test::fail_stream<test::string_stream> fs(n, ios_,
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
@@ -41,6 +262,7 @@ public:
request_v1<streambuf_body> m;
try
{
streambuf sb;
read(fs, sb, m);
break;
}
@@ -52,7 +274,6 @@ public:
for(n = 0; n < limit; ++n)
{
streambuf sb;
test::fail_stream<test::string_stream> fs(n, ios_,
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
@@ -62,6 +283,7 @@ public:
);
request_v1<streambuf_body> m;
error_code ec;
streambuf sb;
read(fs, sb, m, ec);
if(! ec)
break;
@@ -70,7 +292,6 @@ public:
for(n = 0; n < limit; ++n)
{
streambuf sb;
test::fail_stream<test::string_stream> fs(n, ios_,
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
@@ -80,6 +301,7 @@ public:
);
request_v1<streambuf_body> m;
error_code ec;
streambuf sb;
async_read(fs, sb, m, do_yield[ec]);
if(! ec)
break;
@@ -87,10 +309,38 @@ public:
expect(n < limit);
}
void testEof(yield_context do_yield)
{
{
streambuf sb;
test::string_stream ss(ios_, "");
parser_v1<true, streambuf_body, headers> p;
error_code ec;
parse(ss, sb, p, ec);
expect(ec == boost::asio::error::eof);
}
{
streambuf sb;
test::string_stream ss(ios_, "");
parser_v1<true, streambuf_body, headers> p;
error_code ec;
async_parse(ss, sb, p, do_yield[ec]);
expect(ec == boost::asio::error::eof);
}
}
void run() override
{
testThrow();
yield_to(std::bind(&read_test::testFailures,
this, std::placeholders::_1));
yield_to(std::bind(&read_test::testRead,
this, std::placeholders::_1));
yield_to(std::bind(&read_test::testEof,
this, std::placeholders::_1));
}
};