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 committed 2016-05-12 19:20:57 -04:00
1 parent 8921da91b8
commit 2a8de0fd6b
28 files changed
+1536 -701

No files matched your search

+20 -1
View File
@@ -30,6 +30,14 @@ public:
h.insert(std::to_string(i), i);
}
template<class U, class V>
static
void
self_assign(U& u, V&& v)
{
u = std::forward<V>(v);
}
void testHeaders()
{
bh h1;
@@ -47,12 +55,23 @@ public:
bh h3(std::move(h1));
expect(h3.size() == 2);
expect(h1.size() == 0);
h2 = std::move(h2);
self_assign(h3, std::move(h3));
expect(h3.size() == 2);
expect(h2.erase("Not-Present") == 0);
}
void testRFC2616()
{
bh h;
h.insert("a", "x");
h.insert("a", "y");
expect(h["a"] == "x,y");
}
void run() override
{
testHeaders();
testRFC2616();
}
};
+198 -228
View File
@@ -49,6 +49,7 @@ public:
cb_req_checker, cb_res_checker>::type
{
bool start = false;
bool field = false;
bool value = false;
bool headers = false;
@@ -58,6 +59,10 @@ public:
private:
friend class basic_parser_v1<isRequest, cb_checker<isRequest>>;
void on_start(error_code&)
{
this->start = true;
}
void on_method(boost::string_ref const&, error_code&)
{
this->method = true;
@@ -101,68 +106,6 @@ public:
}
};
template<bool isRequest>
struct cb_fail
: public basic_parser_v1<isRequest, cb_fail<isRequest>>
{
std::size_t n_;
void fail(error_code& ec)
{
if(n_ > 0)
--n_;
if(! n_)
ec = boost::system::errc::make_error_code(
boost::system::errc::invalid_argument);
}
private:
friend class basic_parser_v1<isRequest, cb_checker<isRequest>>;
void on_method(boost::string_ref const&, error_code& ec)
{
fail(ec);
}
void on_uri(boost::string_ref const&, error_code& ec)
{
fail(ec);
}
void on_reason(boost::string_ref const&, error_code& ec)
{
fail(ec);
}
void on_request(error_code& ec)
{
fail(ec);
}
void on_response(error_code& ec)
{
fail(ec);
}
void on_field(boost::string_ref const&, error_code& ec)
{
fail(ec);
}
void on_value(boost::string_ref const&, error_code& ec)
{
fail(ec);
}
int on_headers(error_code& ec)
{
fail(ec);
return 0;
}
void on_body(boost::string_ref const&, error_code& ec)
{
fail(ec);
}
void on_complete(error_code& ec)
{
fail(ec);
}
};
//--------------------------------------------------------------------------
static
@@ -238,99 +181,6 @@ public:
}
};
void
testFail()
{
using boost::asio::buffer;
{
std::string const s =
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*";
static std::size_t constexpr limit = 100;
std::size_t n = 1;
for(; n < limit; ++n)
{
error_code ec;
basic_parser_v1<true, cb_fail<true>> p;
p.write(buffer(s), ec);
if(! ec)
break;
}
expect(n < limit);
}
{
std::string const s =
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*";
static std::size_t constexpr limit = 100;
std::size_t n = 1;
for(; n < limit; ++n)
{
error_code ec;
basic_parser_v1<false, cb_fail<false>> p;
p.write(buffer(s), ec);
if(! ec)
break;
}
expect(n < limit);
}
}
void
testCallbacks()
{
using boost::asio::buffer;
{
cb_checker<true> p;
error_code ec;
std::string const s =
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*";
p.write(buffer(s), ec);
if( expect(! ec))
{
expect(p.method);
expect(p.uri);
expect(p.request);
expect(p.field);
expect(p.value);
expect(p.headers);
expect(p.body);
expect(p.complete);
}
}
{
cb_checker<false> p;
error_code ec;
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);
if( expect(! ec))
{
expect(p.reason);
expect(p.response);
expect(p.field);
expect(p.value);
expect(p.headers);
expect(p.body);
expect(p.complete);
}
}
}
// Parse the entire input buffer as a valid message,
// then parse in two pieces of all possible lengths.
//
@@ -339,13 +189,22 @@ public:
parse(boost::string_ref const& m, F&& f)
{
using boost::asio::buffer;
for(;;)
{
error_code ec;
Parser p;
p.write(buffer(m.data(), m.size()), ec);
if(! expect(! ec, ec.message()))
break;
if(p.needs_eof())
{
p.write_eof(ec);
if(! expect(! ec, ec.message()))
break;
}
if(expect(p.complete()))
if(expect(! ec, ec.message()))
f(p);
f(p);
break;
}
for(std::size_t i = 1; i < m.size() - 1; ++i)
{
@@ -354,18 +213,21 @@ public:
p.write(buffer(&m[0], i), ec);
if(! expect(! ec, ec.message()))
continue;
if(p.complete())
{
f(p);
}
else
if(! p.complete())
{
p.write(buffer(&m[i], m.size() - i), ec);
if(! expect(! ec, ec.message()))
continue;
expect(p.complete());
f(p);
}
if(! p.complete() && p.needs_eof())
{
p.write_eof(ec);
if(! expect(! ec, ec.message()))
continue;
}
if(! expect(p.complete()))
continue;
f(p);
}
}
@@ -403,8 +265,6 @@ public:
}
}
//--------------------------------------------------------------------------
// Parse a valid message with expected version
//
template<bool isRequest>
@@ -432,24 +292,89 @@ public:
});
}
//--------------------------------------------------------------------------
// Check all callbacks invoked
void
testCallbacks()
{
using boost::asio::buffer;
{
cb_checker<true> p;
error_code ec;
std::string const s =
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*";
p.write(buffer(s), ec);
if(expect(! ec))
{
expect(p.start);
expect(p.method);
expect(p.uri);
expect(p.request);
expect(p.field);
expect(p.value);
expect(p.headers);
expect(p.body);
expect(p.complete);
}
}
{
cb_checker<false> p;
error_code ec;
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);
if(expect(! ec))
{
expect(p.start);
expect(p.reason);
expect(p.response);
expect(p.field);
expect(p.value);
expect(p.headers);
expect(p.body);
expect(p.complete);
}
}
}
void
testVersion()
{
version<true>("GET / HTTP/0.0\r\n\r\n", 0, 0);
version<true>("GET / HTTP/0.1\r\n\r\n", 0, 1);
version<true>("GET / HTTP/0.9\r\n\r\n", 0, 9);
version<true>("GET / HTTP/1.0\r\n\r\n", 1, 0);
version<true>("GET / HTTP/1.1\r\n\r\n", 1, 1);
version<true>("GET / HTTP/9.9\r\n\r\n", 9, 9);
version<true>("GET / HTTP/999.999\r\n\r\n", 999, 999);
version <true>("GET / HTTP/0.0\r\n\r\n", 0, 0);
version <true>("GET / HTTP/0.1\r\n\r\n", 0, 1);
version <true>("GET / HTTP/0.9\r\n\r\n", 0, 9);
version <true>("GET / HTTP/1.0\r\n\r\n", 1, 0);
version <true>("GET / HTTP/1.1\r\n\r\n", 1, 1);
version <true>("GET / HTTP/9.9\r\n\r\n", 9, 9);
version <true>("GET / HTTP/999.999\r\n\r\n", 999, 999);
parse_ev<true>("GET / HTTP/1000.0\r\n\r\n", parse_error::bad_version);
parse_ev<true>("GET / HTTP/0.1000\r\n\r\n", parse_error::bad_version);
parse_ev<true>("GET / HTTP/99999999999999999999.0\r\n\r\n", parse_error::bad_version);
parse_ev<true>("GET / HTTP/0.99999999999999999999\r\n\r\n", parse_error::bad_version);
version <false>("HTTP/0.0 200 OK\r\n\r\n", 0, 0);
version <false>("HTTP/0.1 200 OK\r\n\r\n", 0, 1);
version <false>("HTTP/0.9 200 OK\r\n\r\n", 0, 9);
version <false>("HTTP/1.0 200 OK\r\n\r\n", 1, 0);
version <false>("HTTP/1.1 200 OK\r\n\r\n", 1, 1);
version <false>("HTTP/9.9 200 OK\r\n\r\n", 9, 9);
version <false>("HTTP/999.999 200 OK\r\n\r\n", 999, 999);
parse_ev<false>("HTTP/1000.0 200 OK\r\n\r\n", parse_error::bad_version);
parse_ev<false>("HTTP/0.1000 200 OK\r\n\r\n", parse_error::bad_version);
parse_ev<false>("HTTP/99999999999999999999.0 200 OK\r\n\r\n", parse_error::bad_version);
parse_ev<false>("HTTP/0.99999999999999999999 200 OK\r\n\r\n", parse_error::bad_version);
}
void
testConnection(std::string const& token,
void testConnection(std::string const& token,
std::uint8_t flag)
{
checkf("GET / HTTP/1.1\r\nConnection:" + token + "\r\n\r\n", flag);
@@ -472,8 +397,7 @@ public:
checkf("GET / HTTP/1.1\r\nConnection: X," + token + "\t\r\n\r\n", flag);
}
void
testContentLength()
void testContentLength()
{
std::size_t const length = 0;
std::string const length_s =
@@ -493,8 +417,7 @@ public:
checkf("GET / HTTP/1.1\r\nContent-Length:\t\r\n" "\t"+ length_s + "\r\n\r\n", parse_flag::contentlength);
}
void
testTransferEncoding()
void testTransferEncoding()
{
checkf("GET / HTTP/1.1\r\nTransfer-Encoding:chunked\r\n\r\n0\r\n\r\n", parse_flag::chunked);
checkf("GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\n", parse_flag::chunked);
@@ -510,17 +433,15 @@ public:
checkf("GET / HTTP/1.1\r\nTransfer-Encoding:\t\r\n" "\tchunked\r\n\r\n0\r\n\r\n", parse_flag::chunked );
}
void
testFlags()
void testFlags()
{
testConnection("keep-alive",
parse_flag::connection_keep_alive);
testConnection("keep-alive", parse_flag::connection_keep_alive);
testConnection("close", parse_flag::connection_close);
testConnection("upgrade", parse_flag::connection_upgrade);
testConnection("close",
parse_flag::connection_close);
testConnection("upgrade",
parse_flag::connection_upgrade);
checkf("GET / HTTP/1.1\r\nConnection: close, win\r\n\r\n", parse_flag::connection_close);
checkf("GET / HTTP/1.1\r\nConnection: keep-alive, win\r\n\r\n", parse_flag::connection_keep_alive);
checkf("GET / HTTP/1.1\r\nConnection: upgrade, win\r\n\r\n", parse_flag::connection_upgrade);
testContentLength();
@@ -537,11 +458,46 @@ public:
"GET / HTTP/1.1\r\n"
"Transfer-Encoding:chunked\r\n"
"Content-Length: 0\r\n"
"Proxy-Connection: close\r\n"
"\r\n", parse_error::illegal_content_length);
}
void
testUpgrade()
void testHeaders()
{
parse<null_parser<true>>(
"GET / HTTP/1.0\r\n"
"Conniving: yes\r\n"
"Content-Lengthening: yes\r\n"
"Transfer-Encoding: deflate\r\n"
"Connection: sweep\r\n"
"\r\n",
[](null_parser<true> const&)
{
});
parse_ev<true>(
"GET / HTTP/1.0\r\n"
"Content-Length: 1\r\n"
"Content-Length: 2\r\n"
"\r\n",
parse_error::bad_content_length);
parse_ev<true>(
"GET / HTTP/1.0\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"fffffffffffffffff\r\n"
"0\r\n\r\n",
parse_error::bad_content_length);
parse_ev<true>("GET / HTTP/1.0\r\nContent-Length: 1e9\r\n\r\n",
parse_error::bad_content_length);
parse_ev<true>("GET / HTTP/1.0\r\nContent-Length: 99999999999999999999999\r\n\r\n",
parse_error::bad_content_length);
}
void testUpgrade()
{
using boost::asio::buffer;
null_parser<true> p;
@@ -576,11 +532,10 @@ public:
void testInvalidMatrix()
{
using boost::asio::buffer;
static std::size_t constexpr limit = 200;
std::string s;
std::size_t n;
for(n = 0; n < limit; ++n)
for(n = 0;; ++n)
{
// Create a request and set one octet to an invalid char
s =
@@ -590,61 +545,69 @@ public:
"Content-Length: 00\r\n"
"\r\n";
auto const len = s.size();
if(n >= s.size())
if(n < len)
{
pass();
break;
s[n] = 0;
for(std::size_t m = 1; m < len - 1; ++m)
{
null_parser<true> p;
error_code ec;
p.write(buffer(s.data(), m), ec);
if(ec)
{
pass();
continue;
}
p.write(buffer(s.data() + m, len - m), ec);
expect(ec);
}
}
s[n] = 0;
for(std::size_t m = 1; m < len - 1; ++m)
else
{
null_parser<true> p;
error_code ec;
p.write(buffer(s.data(), m), ec);
if(ec)
{
pass();
continue;
}
p.write(buffer(s.data() + m, len - m), ec);
expect(ec);
p.write(buffer(s.data(), s.size()), ec);
expect(! ec, ec.message());
break;
}
}
expect(n < limit);
for(n = 0; n < limit; ++n)
for(n = 0;; ++n)
{
// Create a response and set one octet to an invalid char
s =
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Transer-Encoding: chunked\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"10\r\n"
"****************\r\n"
"0\r\n\r\n";
auto const len = s.size();
if(n >= s.size())
if(n < len)
{
pass();
s[n] = 0;
for(std::size_t m = 1; m < len - 1; ++m)
{
null_parser<false> p;
error_code ec;
p.write(buffer(s.data(), m), ec);
if(ec)
{
pass();
continue;
}
p.write(buffer(s.data() + m, len - m), ec);
expect(ec);
}
}
else
{
null_parser<false> p;
error_code ec;
p.write(buffer(s.data(), s.size()), ec);
expect(! ec, ec.message());
break;
}
s[n] = 0;
for(std::size_t m = 1; m < len - 1; ++m)
{
null_parser<true> p;
error_code ec;
p.write(buffer(s.data(), m), ec);
if(ec)
{
pass();
continue;
}
p.write(buffer(s.data() + m, len - m), ec);
expect(ec);
}
}
expect(n < limit);
}
void
@@ -754,24 +717,30 @@ public:
expect(p.body == body);
};
};
parse<test_parser<true>>(
"GET / HTTP/1.1\r\nContent-Length: 1\r\n\r\n123", match("1"));
parse<test_parser<true>>(
"GET / HTTP/1.1\r\nContent-Length: 3\r\n\r\n123", match("123"));
parse<test_parser<true>>(
"GET / HTTP/1.1\r\nContent-Length: 0\r\n\r\n", match(""));
parse<test_parser<true>>(
"GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"
"1\r\n"
"a\r\n"
"0\r\n"
"\r\n", match("a"));
parse<test_parser<true>>(
"GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"
"2\r\n"
"ab\r\n"
"0\r\n"
"\r\n", match("ab"));
parse<test_parser<true>>(
"GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"
"2\r\n"
@@ -780,6 +749,7 @@ public:
"c\r\n"
"0\r\n"
"\r\n", match("abc"));
parse<test_parser<true>>(
"GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"
"10\r\n"
@@ -790,10 +760,10 @@ public:
void run() override
{
testFail();
testCallbacks();
testVersion();
testFlags();
testHeaders();
testUpgrade();
testBad();
testInvalidMatrix();
+22 -149
View File
@@ -9,126 +9,34 @@
#include <beast/http/message_v1.hpp>
#include <beast/unit_test/suite.hpp>
#include <beast/unit_test/thread.hpp>
#include <beast/core/placeholders.hpp>
#include <beast/core/streambuf.hpp>
#include <beast/http.hpp>
#include <boost/asio.hpp>
#include <beast/http/empty_body.hpp>
namespace beast {
namespace http {
class sync_echo_http_server
{
public:
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
private:
unit_test::suite& suite_;
boost::asio::io_service ios_;
socket_type sock_;
boost::asio::ip::tcp::acceptor acceptor_;
unit_test::thread thread_;
public:
sync_echo_http_server(
endpoint_type ep, unit_test::suite& suite)
: suite_(suite)
, sock_(ios_)
, acceptor_(ios_)
{
error_code ec;
acceptor_.open(ep.protocol(), ec);
maybe_throw(ec, "open");
acceptor_.bind(ep, ec);
maybe_throw(ec, "bind");
acceptor_.listen(
boost::asio::socket_base::max_connections, ec);
maybe_throw(ec, "listen");
acceptor_.async_accept(sock_,
std::bind(&sync_echo_http_server::on_accept, this,
beast::asio::placeholders::error));
thread_ = unit_test::thread(suite_,
[&]
{
ios_.run();
});
}
~sync_echo_http_server()
{
error_code ec;
ios_.dispatch(
[&]{ acceptor_.close(ec); });
thread_.join();
}
private:
void
fail(error_code ec, std::string what)
{
suite_.log <<
what << ": " << ec.message();
}
void
maybe_throw(error_code ec, std::string what)
{
if(ec &&
ec != boost::asio::error::operation_aborted)
{
fail(ec, what);
throw ec;
}
}
void
on_accept(error_code ec)
{
if(ec == boost::asio::error::operation_aborted)
return;
maybe_throw(ec, "accept");
std::thread{&sync_echo_http_server::do_client, this,
std::move(sock_), boost::asio::io_service::work{
sock_.get_io_service()}}.detach();
acceptor_.async_accept(sock_,
std::bind(&sync_echo_http_server::on_accept, this,
beast::asio::placeholders::error));
}
void
do_client(socket_type sock, boost::asio::io_service::work)
{
error_code ec;
streambuf rb;
for(;;)
{
request_v1<string_body> req;
read(sock, rb, req, ec);
if(ec)
break;
response_v1<string_body> resp;
resp.status = 100;
resp.reason = "OK";
resp.version = req.version;
resp.body = "Completed successfully.";
write(sock, resp, ec);
if(ec)
break;
}
}
};
class message_v1_test : public beast::unit_test::suite
{
public:
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
void testFreeFunctions()
{
{
request_v1<empty_body> m;
m.method = "GET";
m.url = "/";
m.version = 11;
m.headers.insert("Upgrade", "test");
expect(! is_upgrade(m));
void testFunctions()
prepare(m, connection::upgrade);
expect(is_upgrade(m));
expect(m.headers["Connection"] == "upgrade");
m.version = 10;
expect(! is_upgrade(m));
}
}
void testPrepare()
{
request_v1<empty_body> m;
m.version = 10;
@@ -164,45 +72,10 @@ public:
}
}
void
syncEcho(endpoint_type ep)
{
boost::asio::io_service ios;
socket_type sock(ios);
sock.connect(ep);
streambuf rb;
{
request_v1<string_body> req;
req.method = "GET";
req.url = "/";
req.version = 11;
req.body = "Beast.HTTP";
req.headers.replace("Host",
ep.address().to_string() + ":" +
std::to_string(ep.port()));
write(sock, req);
}
{
response_v1<string_body> m;
read(sock, rb, m);
}
}
void
testAsio()
{
endpoint_type ep{
address_type::from_string("127.0.0.1"), 6000};
sync_echo_http_server s(ep, *this);
syncEcho(ep);
}
void run() override
{
testFunctions();
testAsio();
pass();
testFreeFunctions();
testPrepare();
}
};
+253 -3
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));
}
};
+35
View File
@@ -7,3 +7,38 @@
// Test that header file is self-contained.
#include <beast/http/streambuf_body.hpp>
#include <beast/core/to_string.hpp>
#include <beast/http/headers.hpp>
#include <beast/http/parser_v1.hpp>
#include <beast/http/read.hpp>
#include <beast/test/string_stream.hpp>
#include <beast/unit_test/suite.hpp>
namespace beast {
namespace http {
class streambuf_body_test : public beast::unit_test::suite
{
boost::asio::io_service ios_;
public:
void run() override
{
test::string_stream ss(ios_,
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 3\r\n"
"\r\n"
"xyz");
parser_v1<false, streambuf_body, headers> p;
streambuf sb;
parse(ss, sb, p);
expect(to_string(p.get().body.data()) == "xyz");
}
};
BEAST_DEFINE_TESTSUITE(streambuf_body,http,beast);
} // http
} // beast