forked from boostorg/beast
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:
+5
-6
@@ -49,15 +49,14 @@ unit-test http-tests :
|
||||
../extras/beast/unit_test/main.cpp
|
||||
http/basic_dynabuf_body.cpp
|
||||
http/basic_fields.cpp
|
||||
http/basic_parser_v1.cpp
|
||||
http/basic_parser.cpp
|
||||
http/concepts.cpp
|
||||
http/empty_body.cpp
|
||||
http/design.cpp
|
||||
http/error.cpp
|
||||
http/fields.cpp
|
||||
http/header_parser_v1.cpp
|
||||
http/header_parser.cpp
|
||||
http/message.cpp
|
||||
http/parse.cpp
|
||||
http/parse_error.cpp
|
||||
http/parser_v1.cpp
|
||||
http/message_parser.cpp
|
||||
http/read.cpp
|
||||
http/reason.cpp
|
||||
http/rfc7230.cpp
|
||||
|
||||
@@ -8,19 +8,18 @@ add_executable (http-tests
|
||||
${BEAST_INCLUDES}
|
||||
${EXTRAS_INCLUDES}
|
||||
message_fuzz.hpp
|
||||
fail_parser.hpp
|
||||
test_parser.hpp
|
||||
../../extras/beast/unit_test/main.cpp
|
||||
basic_dynabuf_body.cpp
|
||||
basic_fields.cpp
|
||||
basic_parser_v1.cpp
|
||||
basic_parser.cpp
|
||||
concepts.cpp
|
||||
empty_body.cpp
|
||||
design.cpp
|
||||
error.cpp
|
||||
fields.cpp
|
||||
header_parser_v1.cpp
|
||||
header_parser.cpp
|
||||
message.cpp
|
||||
parse.cpp
|
||||
parse_error.cpp
|
||||
parser_v1.cpp
|
||||
message_parser.cpp
|
||||
read.cpp
|
||||
reason.cpp
|
||||
rfc7230.cpp
|
||||
|
||||
@@ -0,0 +1,960 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <beast/http/basic_parser.hpp>
|
||||
|
||||
#include "test_parser.hpp"
|
||||
|
||||
#include <beast/core/buffer_cat.hpp>
|
||||
#include <beast/core/consuming_buffers.hpp>
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
class basic_parser_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
enum parse_flag
|
||||
{
|
||||
chunked = 1,
|
||||
connection_keep_alive = 2,
|
||||
connection_close = 4,
|
||||
connection_upgrade = 8,
|
||||
upgrade = 16,
|
||||
};
|
||||
|
||||
class expect_version
|
||||
{
|
||||
suite& s_;
|
||||
int version_;
|
||||
|
||||
public:
|
||||
expect_version(suite& s, int version)
|
||||
: s_(s)
|
||||
, version_(version)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Parser>
|
||||
void
|
||||
operator()(Parser const& p) const
|
||||
{
|
||||
s_.BEAST_EXPECT(p.version == version_);
|
||||
}
|
||||
};
|
||||
|
||||
class expect_status
|
||||
{
|
||||
suite& s_;
|
||||
int status_;
|
||||
|
||||
public:
|
||||
expect_status(suite& s, int status)
|
||||
: s_(s)
|
||||
, status_(status)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Parser>
|
||||
void
|
||||
operator()(Parser const& p) const
|
||||
{
|
||||
s_.BEAST_EXPECT(p.status == status_);
|
||||
}
|
||||
};
|
||||
|
||||
class expect_flags
|
||||
{
|
||||
suite& s_;
|
||||
unsigned flags_;
|
||||
|
||||
public:
|
||||
expect_flags(suite& s, unsigned flags)
|
||||
: s_(s)
|
||||
, flags_(flags)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Parser>
|
||||
void
|
||||
operator()(Parser const& p) const
|
||||
{
|
||||
if(flags_ & parse_flag::chunked)
|
||||
s_.BEAST_EXPECT(p.is_chunked());
|
||||
if(flags_ & parse_flag::connection_keep_alive)
|
||||
s_.BEAST_EXPECT(p.is_keep_alive());
|
||||
if(flags_ & parse_flag::connection_close)
|
||||
s_.BEAST_EXPECT(! p.is_keep_alive());
|
||||
if(flags_ & parse_flag::upgrade)
|
||||
s_.BEAST_EXPECT(! p.is_upgrade());
|
||||
}
|
||||
};
|
||||
|
||||
class expect_keepalive
|
||||
{
|
||||
suite& s_;
|
||||
bool v_;
|
||||
|
||||
public:
|
||||
expect_keepalive(suite& s, bool v)
|
||||
: s_(s)
|
||||
, v_(v)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Parser>
|
||||
void
|
||||
operator()(Parser const& p) const
|
||||
{
|
||||
s_.BEAST_EXPECT(p.is_keep_alive() == v_);
|
||||
}
|
||||
};
|
||||
|
||||
class expect_body
|
||||
{
|
||||
suite& s_;
|
||||
std::string const& body_;
|
||||
|
||||
public:
|
||||
expect_body(expect_body&&) = default;
|
||||
|
||||
expect_body(suite& s, std::string const& v)
|
||||
: s_(s)
|
||||
, body_(v)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Parser>
|
||||
void
|
||||
operator()(Parser const& p) const
|
||||
{
|
||||
s_.BEAST_EXPECT(p.body == body_);
|
||||
}
|
||||
};
|
||||
|
||||
template<std::size_t N>
|
||||
static
|
||||
boost::asio::const_buffers_1
|
||||
buf(char const (&s)[N])
|
||||
{
|
||||
return {s, N-1};
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
static
|
||||
std::size_t
|
||||
feed(boost::asio::const_buffer buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::const_buffers_1;
|
||||
std::size_t used = 0;
|
||||
for(;;)
|
||||
{
|
||||
auto const n = parser.write(
|
||||
const_buffers_1{buffer}, ec);
|
||||
if(ec)
|
||||
return 0;
|
||||
if(n == 0)
|
||||
break;
|
||||
buffer = buffer + n;
|
||||
used += n;
|
||||
if(parser.is_complete())
|
||||
break;
|
||||
if(buffer_size(buffer) == 0)
|
||||
break;
|
||||
}
|
||||
return used;
|
||||
}
|
||||
|
||||
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;
|
||||
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<
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
static
|
||||
std::size_t
|
||||
feed(boost::asio::const_buffers_1 buffers,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
return feed(*buffers.begin(), parser, ec);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Pred>
|
||||
void
|
||||
good(boost::string_ref const& s,
|
||||
Pred const& pred, bool skipBody = false)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
test_parser<isRequest> p;
|
||||
if(skipBody)
|
||||
p.skip_body();
|
||||
error_code ec;
|
||||
auto const n = feed(buffer(
|
||||
s.data(), s.size()), p, ec);
|
||||
if(! BEAST_EXPECTS(! ec, ec.message()))
|
||||
return;
|
||||
if(! BEAST_EXPECT(n == s.size()))
|
||||
return;
|
||||
if(p.state() == parse_state::body_to_eof)
|
||||
p.write_eof(ec);
|
||||
if(BEAST_EXPECTS(! ec, ec.message()))
|
||||
pred(p);
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
good(boost::string_ref const& s)
|
||||
{
|
||||
good<isRequest>(s,
|
||||
[](test_parser<isRequest> const&)
|
||||
{
|
||||
});
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
bad(boost::string_ref const& s,
|
||||
error_code const& ev, bool skipBody = false)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
test_parser<isRequest> p;
|
||||
if(skipBody)
|
||||
p.skip_body();
|
||||
error_code ec;
|
||||
feed(buffer(
|
||||
s.data(), s.size()), p, ec);
|
||||
if(! ec && ev)
|
||||
p.write_eof(ec);
|
||||
BEAST_EXPECTS(ec == ev, ec.message());
|
||||
}
|
||||
|
||||
void
|
||||
testFlatten()
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
{
|
||||
std::string const s =
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Content-Length: 1\r\n"
|
||||
"\r\n"
|
||||
"*";
|
||||
for(std::size_t i = 1;
|
||||
i < s.size() - 1; ++i)
|
||||
{
|
||||
auto const b1 =
|
||||
buffer(s.data(), i);
|
||||
auto const b2 = buffer(
|
||||
s.data() + i, s.size() - i);
|
||||
test_parser<true> p;
|
||||
error_code ec;
|
||||
feed(b1, p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
feed(buffer_cat(b1, b2), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
}
|
||||
}
|
||||
{
|
||||
std::string const s =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"\r\n";
|
||||
for(std::size_t i = 1;
|
||||
i < s.size() - 1; ++i)
|
||||
{
|
||||
auto const b1 =
|
||||
buffer(s.data(), i);
|
||||
auto const b2 = buffer(
|
||||
s.data() + i, s.size() - i);
|
||||
test_parser<false> p;
|
||||
error_code ec;
|
||||
feed(b1, p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
ec = {};
|
||||
feed(buffer_cat(b1, b2), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
p.write_eof(ec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all callbacks are invoked
|
||||
void
|
||||
testCallbacks()
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
{
|
||||
test_parser<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"
|
||||
"*";
|
||||
feed(buffer(s), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(p.got_on_begin);
|
||||
BEAST_EXPECT(p.got_on_field);
|
||||
BEAST_EXPECT(p.got_on_header);
|
||||
BEAST_EXPECT(p.got_on_body);
|
||||
BEAST_EXPECT(! p.got_on_chunk);
|
||||
BEAST_EXPECT(p.got_on_complete);
|
||||
}
|
||||
{
|
||||
test_parser<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"
|
||||
"*";
|
||||
feed(buffer(s), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(p.got_on_begin);
|
||||
BEAST_EXPECT(p.got_on_field);
|
||||
BEAST_EXPECT(p.got_on_header);
|
||||
BEAST_EXPECT(p.got_on_body);
|
||||
BEAST_EXPECT(! p.got_on_chunk);
|
||||
BEAST_EXPECT(p.got_on_complete);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testRequestLine()
|
||||
{
|
||||
good<true>("GET /x HTTP/1.0\r\n\r\n");
|
||||
good<true>("!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz / HTTP/1.0\r\n\r\n");
|
||||
good<true>("GET / HTTP/1.0\r\n\r\n", expect_version{*this, 10});
|
||||
good<true>("G / HTTP/1.1\r\n\r\n", expect_version{*this, 11});
|
||||
// VFALCO TODO various forms of good request-target (uri)
|
||||
good<true>("GET / HTTP/0.1\r\n\r\n", expect_version{*this, 1});
|
||||
good<true>("GET / HTTP/2.3\r\n\r\n", expect_version{*this, 23});
|
||||
good<true>("GET / HTTP/4.5\r\n\r\n", expect_version{*this, 45});
|
||||
good<true>("GET / HTTP/6.7\r\n\r\n", expect_version{*this, 67});
|
||||
good<true>("GET / HTTP/8.9\r\n\r\n", expect_version{*this, 89});
|
||||
|
||||
bad<true>("\tGET / HTTP/1.0\r\n" "\r\n", error::bad_method);
|
||||
bad<true>("GET\x01 / HTTP/1.0\r\n" "\r\n", error::bad_method);
|
||||
bad<true>("GET / HTTP/1.0\r\n" "\r\n", error::bad_path);
|
||||
bad<true>("GET \x01 HTTP/1.0\r\n" "\r\n", error::bad_path);
|
||||
bad<true>("GET /\x01 HTTP/1.0\r\n" "\r\n", error::bad_path);
|
||||
// VFALCO TODO various forms of bad request-target (uri)
|
||||
bad<true>("GET / HTTP/1.0\r\n" "\r\n", error::bad_version);
|
||||
bad<true>("GET / _TTP/1.0\r\n" "\r\n", error::bad_version);
|
||||
bad<true>("GET / H_TP/1.0\r\n" "\r\n", error::bad_version);
|
||||
bad<true>("GET / HT_P/1.0\r\n" "\r\n", error::bad_version);
|
||||
bad<true>("GET / HTT_/1.0\r\n" "\r\n", error::bad_version);
|
||||
bad<true>("GET / HTTP_1.0\r\n" "\r\n", error::bad_version);
|
||||
bad<true>("GET / HTTP/01.2\r\n" "\r\n", error::bad_version);
|
||||
bad<true>("GET / HTTP/3.45\r\n" "\r\n", error::bad_version);
|
||||
bad<true>("GET / HTTP/67.89\r\n" "\r\n", error::bad_version);
|
||||
bad<true>("GET / HTTP/x.0\r\n" "\r\n", error::bad_version);
|
||||
bad<true>("GET / HTTP/1.x\r\n" "\r\n", error::bad_version);
|
||||
bad<true>("GET / HTTP/1.0 \r\n" "\r\n", error::bad_version);
|
||||
bad<true>("GET / HTTP/1_0\r\n" "\r\n", error::bad_version);
|
||||
bad<true>("GET / HTTP/1.0\n\r\n" "\r\n", error::bad_version);
|
||||
bad<true>("GET / HTTP/1.0\n\r\r\n" "\r\n", error::bad_line_ending);
|
||||
bad<true>("GET / HTTP/1.0\r\r\n" "\r\n", error::bad_line_ending);
|
||||
}
|
||||
|
||||
void
|
||||
testStatusLine()
|
||||
{
|
||||
good<false>("HTTP/0.1 200 OK\r\n" "\r\n", expect_version{*this, 1});
|
||||
good<false>("HTTP/2.3 200 OK\r\n" "\r\n", expect_version{*this, 23});
|
||||
good<false>("HTTP/4.5 200 OK\r\n" "\r\n", expect_version{*this, 45});
|
||||
good<false>("HTTP/6.7 200 OK\r\n" "\r\n", expect_version{*this, 67});
|
||||
good<false>("HTTP/8.9 200 OK\r\n" "\r\n", expect_version{*this, 89});
|
||||
good<false>("HTTP/1.0 000 OK\r\n" "\r\n", expect_status{*this, 0});
|
||||
good<false>("HTTP/1.1 012 OK\r\n" "\r\n", expect_status{*this, 12});
|
||||
good<false>("HTTP/1.0 345 OK\r\n" "\r\n", expect_status{*this, 345});
|
||||
good<false>("HTTP/1.0 678 OK\r\n" "\r\n", expect_status{*this, 678});
|
||||
good<false>("HTTP/1.0 999 OK\r\n" "\r\n", expect_status{*this, 999});
|
||||
good<false>("HTTP/1.0 200 \tX\r\n" "\r\n", expect_version{*this, 10});
|
||||
good<false>("HTTP/1.1 200 X\r\n" "\r\n", expect_version{*this, 11});
|
||||
good<false>("HTTP/1.0 200 \r\n" "\r\n");
|
||||
good<false>("HTTP/1.1 200 X \r\n" "\r\n");
|
||||
good<false>("HTTP/1.1 200 X\t\r\n" "\r\n");
|
||||
good<false>("HTTP/1.1 200 \x80\x81...\xfe\xff\r\n\r\n");
|
||||
good<false>("HTTP/1.1 200 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\r\n\r\n");
|
||||
|
||||
bad<false>("\rHTTP/1.0 200 OK\r\n" "\r\n", error::bad_line_ending);
|
||||
bad<false>("\nHTTP/1.0 200 OK\r\n" "\r\n", error::bad_version);
|
||||
bad<false>(" HTTP/1.0 200 OK\r\n" "\r\n", error::bad_version);
|
||||
bad<false>("_TTP/1.0 200 OK\r\n" "\r\n", error::bad_version);
|
||||
bad<false>("H_TP/1.0 200 OK\r\n" "\r\n", error::bad_version);
|
||||
bad<false>("HT_P/1.0 200 OK\r\n" "\r\n", error::bad_version);
|
||||
bad<false>("HTT_/1.0 200 OK\r\n" "\r\n", error::bad_version);
|
||||
bad<false>("HTTP_1.0 200 OK\r\n" "\r\n", error::bad_version);
|
||||
bad<false>("HTTP/01.2 200 OK\r\n" "\r\n", error::bad_version);
|
||||
bad<false>("HTTP/3.45 200 OK\r\n" "\r\n", error::bad_version);
|
||||
bad<false>("HTTP/67.89 200 OK\r\n" "\r\n", error::bad_version);
|
||||
bad<false>("HTTP/x.0 200 OK\r\n" "\r\n", error::bad_version);
|
||||
bad<false>("HTTP/1.x 200 OK\r\n" "\r\n", error::bad_version);
|
||||
bad<false>("HTTP/1_0 200 OK\r\n" "\r\n", error::bad_version);
|
||||
bad<false>("HTTP/1.0 200 OK\r\n" "\r\n", error::bad_status);
|
||||
bad<false>("HTTP/1.0 0 OK\r\n" "\r\n", error::bad_status);
|
||||
bad<false>("HTTP/1.0 12 OK\r\n" "\r\n", error::bad_status);
|
||||
bad<false>("HTTP/1.0 3456 OK\r\n" "\r\n", error::bad_status);
|
||||
bad<false>("HTTP/1.0 200\r\n" "\r\n", error::bad_status);
|
||||
bad<false>("HTTP/1.0 200 \n\r\n" "\r\n", error::bad_reason);
|
||||
bad<false>("HTTP/1.0 200 \x01\r\n" "\r\n", error::bad_reason);
|
||||
bad<false>("HTTP/1.0 200 \x7f\r\n" "\r\n", error::bad_reason);
|
||||
bad<false>("HTTP/1.0 200 OK\n\r\n" "\r\n", error::bad_reason);
|
||||
bad<false>("HTTP/1.0 200 OK\r\r\n" "\r\n", error::bad_line_ending);
|
||||
}
|
||||
|
||||
void
|
||||
testFields()
|
||||
{
|
||||
auto const m =
|
||||
[](std::string const& s)
|
||||
{
|
||||
return "GET / HTTP/1.1\r\n" + s + "\r\n";
|
||||
};
|
||||
good<true>(m("f:\r\n"));
|
||||
good<true>(m("f: \r\n"));
|
||||
good<true>(m("f:\t\r\n"));
|
||||
good<true>(m("f: \t\r\n"));
|
||||
good<true>(m("f: v\r\n"));
|
||||
good<true>(m("f:\tv\r\n"));
|
||||
good<true>(m("f:\tv \r\n"));
|
||||
good<true>(m("f:\tv\t\r\n"));
|
||||
good<true>(m("f:\tv\t \r\n"));
|
||||
good<true>(m("f:\r\n \r\n"));
|
||||
good<true>(m("f:v\r\n"));
|
||||
good<true>(m("f: v\r\n u\r\n"));
|
||||
good<true>(m("!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz: v\r\n"));
|
||||
good<true>(m("f: !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x80\x81...\xfe\xff\r\n"));
|
||||
|
||||
bad<true>(m(" f: v\r\n"), error::bad_field);
|
||||
bad<true>(m("\tf: v\r\n"), error::bad_field);
|
||||
bad<true>(m("f : v\r\n"), error::bad_field);
|
||||
bad<true>(m("f\t: v\r\n"), error::bad_field);
|
||||
bad<true>(m("f: \n\r\n"), error::bad_value);
|
||||
bad<true>(m("f: v\r \r\n"), error::bad_line_ending);
|
||||
bad<true>(m("f: \r v\r\n"), error::bad_line_ending);
|
||||
bad<true>("GET / HTTP/1.1\r\n\r \n\r\n\r\n",error::bad_line_ending);
|
||||
}
|
||||
|
||||
void
|
||||
testConnectionField()
|
||||
{
|
||||
auto const m =
|
||||
[](std::string const& s)
|
||||
{
|
||||
return "GET / HTTP/1.1\r\n" + s + "\r\n";
|
||||
};
|
||||
auto const cn =
|
||||
[](std::string const& s)
|
||||
{
|
||||
return "GET / HTTP/1.1\r\nConnection: " + s + "\r\n";
|
||||
};
|
||||
#if 0
|
||||
auto const keepalive =
|
||||
[&](bool v)
|
||||
{
|
||||
//return keepalive_f{*this, v};
|
||||
return true;
|
||||
};
|
||||
#endif
|
||||
|
||||
good<true>(cn("close\r\n"), expect_flags{*this, parse_flag::connection_close});
|
||||
good<true>(cn(",close\r\n"), expect_flags{*this, parse_flag::connection_close});
|
||||
good<true>(cn(" close\r\n"), expect_flags{*this, parse_flag::connection_close});
|
||||
good<true>(cn("\tclose\r\n"), expect_flags{*this, parse_flag::connection_close});
|
||||
good<true>(cn("close,\r\n"), expect_flags{*this, parse_flag::connection_close});
|
||||
good<true>(cn("close\t\r\n"), expect_flags{*this, parse_flag::connection_close});
|
||||
good<true>(cn("close\r\n"), expect_flags{*this, parse_flag::connection_close});
|
||||
good<true>(cn(" ,\t,,close,, ,\t,,\r\n"), expect_flags{*this, parse_flag::connection_close});
|
||||
good<true>(cn("\r\n close\r\n"), expect_flags{*this, parse_flag::connection_close});
|
||||
good<true>(cn("close\r\n \r\n"), expect_flags{*this, parse_flag::connection_close});
|
||||
good<true>(cn("any,close\r\n"), expect_flags{*this, parse_flag::connection_close});
|
||||
good<true>(cn("close,any\r\n"), expect_flags{*this, parse_flag::connection_close});
|
||||
good<true>(cn("any\r\n ,close\r\n"), expect_flags{*this, parse_flag::connection_close});
|
||||
good<true>(cn("close\r\n ,any\r\n"), expect_flags{*this, parse_flag::connection_close});
|
||||
good<true>(cn("close,close\r\n"), expect_flags{*this, parse_flag::connection_close}); // weird but allowed
|
||||
|
||||
good<true>(cn("keep-alive\r\n"), expect_flags{*this, parse_flag::connection_keep_alive});
|
||||
good<true>(cn("keep-alive \r\n"), expect_flags{*this, parse_flag::connection_keep_alive});
|
||||
good<true>(cn("keep-alive\t \r\n"), expect_flags{*this, parse_flag::connection_keep_alive});
|
||||
good<true>(cn("keep-alive\t ,x\r\n"), expect_flags{*this, parse_flag::connection_keep_alive});
|
||||
good<true>(cn("\r\n keep-alive \t\r\n"), expect_flags{*this, parse_flag::connection_keep_alive});
|
||||
good<true>(cn("keep-alive \r\n \t \r\n"), expect_flags{*this, parse_flag::connection_keep_alive});
|
||||
good<true>(cn("keep-alive\r\n \r\n"), expect_flags{*this, parse_flag::connection_keep_alive});
|
||||
|
||||
good<true>(cn("upgrade\r\n"), expect_flags{*this, parse_flag::connection_upgrade});
|
||||
good<true>(cn("upgrade \r\n"), expect_flags{*this, parse_flag::connection_upgrade});
|
||||
good<true>(cn("upgrade\t \r\n"), expect_flags{*this, parse_flag::connection_upgrade});
|
||||
good<true>(cn("upgrade\t ,x\r\n"), expect_flags{*this, parse_flag::connection_upgrade});
|
||||
good<true>(cn("\r\n upgrade \t\r\n"), expect_flags{*this, parse_flag::connection_upgrade});
|
||||
good<true>(cn("upgrade \r\n \t \r\n"), expect_flags{*this, parse_flag::connection_upgrade});
|
||||
good<true>(cn("upgrade\r\n \r\n"), expect_flags{*this, parse_flag::connection_upgrade});
|
||||
|
||||
// VFALCO What's up with these?
|
||||
//good<true>(cn("close,keep-alive\r\n"), expect_flags{*this, parse_flag::connection_close | parse_flag::connection_keep_alive});
|
||||
good<true>(cn("upgrade,keep-alive\r\n"), expect_flags{*this, parse_flag::connection_upgrade | parse_flag::connection_keep_alive});
|
||||
good<true>(cn("upgrade,\r\n keep-alive\r\n"), expect_flags{*this, parse_flag::connection_upgrade | parse_flag::connection_keep_alive});
|
||||
//good<true>(cn("close,keep-alive,upgrade\r\n"), expect_flags{*this, parse_flag::connection_close | parse_flag::connection_keep_alive | parse_flag::connection_upgrade});
|
||||
|
||||
good<true>("GET / HTTP/1.1\r\n\r\n", expect_keepalive(*this, true));
|
||||
good<true>("GET / HTTP/1.0\r\n\r\n", expect_keepalive(*this, false));
|
||||
good<true>("GET / HTTP/1.0\r\n"
|
||||
"Connection: keep-alive\r\n\r\n", expect_keepalive(*this, true));
|
||||
good<true>("GET / HTTP/1.1\r\n"
|
||||
"Connection: close\r\n\r\n", expect_keepalive(*this, false));
|
||||
|
||||
good<true>(cn("x\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("x,y\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("x ,y\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("x\t,y\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("keep\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn(",keep\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn(" keep\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("\tnone\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("keep,\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("keep\t\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("keep\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn(" ,\t,,keep,, ,\t,,\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("\r\n keep\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("keep\r\n \r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("closet\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn(",closet\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn(" closet\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("\tcloset\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("closet,\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("closet\t\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("closet\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn(" ,\t,,closet,, ,\t,,\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("\r\n closet\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("closet\r\n \r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("clog\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("key\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("uptown\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("keeper\r\n \r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("keep-alively\r\n \r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("up\r\n \r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("upgrader\r\n \r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("none\r\n"), expect_flags{*this, 0});
|
||||
good<true>(cn("\r\n none\r\n"), expect_flags{*this, 0});
|
||||
|
||||
good<true>(m("ConnectioX: close\r\n"), expect_flags{*this, 0});
|
||||
good<true>(m("Condor: close\r\n"), expect_flags{*this, 0});
|
||||
good<true>(m("Connect: close\r\n"), expect_flags{*this, 0});
|
||||
good<true>(m("Connections: close\r\n"), expect_flags{*this, 0});
|
||||
|
||||
good<true>(m("Proxy-Connection: close\r\n"), expect_flags{*this, parse_flag::connection_close});
|
||||
good<true>(m("Proxy-Connection: keep-alive\r\n"), expect_flags{*this, parse_flag::connection_keep_alive});
|
||||
good<true>(m("Proxy-Connection: upgrade\r\n"), expect_flags{*this, parse_flag::connection_upgrade});
|
||||
good<true>(m("Proxy-ConnectioX: none\r\n"), expect_flags{*this, 0});
|
||||
good<true>(m("Proxy-Connections: 1\r\n"), expect_flags{*this, 0});
|
||||
good<true>(m("Proxy-Connotes: see-also\r\n"), expect_flags{*this, 0});
|
||||
|
||||
bad<true>(cn("[\r\n"), error::bad_value);
|
||||
bad<true>(cn("close[\r\n"), error::bad_value);
|
||||
bad<true>(cn("close [\r\n"), error::bad_value);
|
||||
bad<true>(cn("close, upgrade [\r\n"), error::bad_value);
|
||||
bad<true>(cn("upgrade[]\r\n"), error::bad_value);
|
||||
bad<true>(cn("keep\r\n -alive\r\n"), error::bad_value);
|
||||
bad<true>(cn("keep-alive[\r\n"), error::bad_value);
|
||||
bad<true>(cn("keep-alive []\r\n"), error::bad_value);
|
||||
bad<true>(cn("no[ne]\r\n"), error::bad_value);
|
||||
}
|
||||
|
||||
void
|
||||
testContentLengthField()
|
||||
{
|
||||
auto const length =
|
||||
[&](std::string const& s, std::uint64_t v)
|
||||
{
|
||||
good<true>(s,
|
||||
[&](test_parser<true> const& p)
|
||||
{
|
||||
BEAST_EXPECT(p.content_length());
|
||||
BEAST_EXPECT(p.content_length() && *p.content_length() == v);
|
||||
}, true);
|
||||
};
|
||||
auto const c =
|
||||
[](std::string const& s)
|
||||
{
|
||||
return "GET / HTTP/1.1\r\nContent-Length: " + s + "\r\n";
|
||||
};
|
||||
auto const m =
|
||||
[](std::string const& s)
|
||||
{
|
||||
return "GET / HTTP/1.1\r\n" + s + "\r\n";
|
||||
};
|
||||
|
||||
length(c("0\r\n"), 0);
|
||||
length(c("00\r\n"), 0);
|
||||
length(c("1\r\n"), 1);
|
||||
length(c("01\r\n"), 1);
|
||||
length(c("9\r\n"), 9);
|
||||
length(c("123456789\r\n"), 123456789);
|
||||
length(c("42 \r\n"), 42);
|
||||
length(c("42\t\r\n"), 42);
|
||||
length(c("42 \t \r\n"), 42);
|
||||
|
||||
// VFALCO Investigate this failure
|
||||
//length(c("42\r\n \t \r\n"), 42);
|
||||
|
||||
good<true>(m("Content-LengtX: 0\r\n"), expect_flags{*this, 0});
|
||||
good<true>(m("Content-Lengths: many\r\n"), expect_flags{*this, 0});
|
||||
good<true>(m("Content: full\r\n"), expect_flags{*this, 0});
|
||||
|
||||
bad<true>(c("\r\n"), error::bad_content_length);
|
||||
bad<true>(c("18446744073709551616\r\n"), error::bad_content_length);
|
||||
bad<true>(c("0 0\r\n"), error::bad_content_length);
|
||||
bad<true>(c("0 1\r\n"), error::bad_content_length);
|
||||
bad<true>(c(",\r\n"), error::bad_content_length);
|
||||
bad<true>(c("0,\r\n"), error::bad_content_length);
|
||||
bad<true>(m(
|
||||
"Content-Length: 0\r\nContent-Length: 0\r\n"), error::bad_content_length);
|
||||
}
|
||||
|
||||
void
|
||||
testTransferEncodingField()
|
||||
{
|
||||
auto const m =
|
||||
[](std::string const& s)
|
||||
{
|
||||
return "GET / HTTP/1.1\r\n" + s + "\r\n";
|
||||
};
|
||||
auto const ce =
|
||||
[](std::string const& s)
|
||||
{
|
||||
return "GET / HTTP/1.1\r\nTransfer-Encoding: " + s + "\r\n0\r\n\r\n";
|
||||
};
|
||||
auto const te =
|
||||
[](std::string const& s)
|
||||
{
|
||||
return "GET / HTTP/1.1\r\nTransfer-Encoding: " + s + "\r\n";
|
||||
};
|
||||
good<true>(ce("chunked\r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce("chunked \r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce("chunked\t\r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce("chunked \t\r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce(" chunked\r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce("\tchunked\r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce("chunked,\r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce("chunked ,\r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce("chunked, \r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce(",chunked\r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce(", chunked\r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce(" ,chunked\r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce("chunked\r\n \r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce("\r\n chunked\r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce(",\r\n chunked\r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce("\r\n ,chunked\r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce(",\r\n chunked\r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce("gzip, chunked\r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce("gzip, chunked \r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
good<true>(ce("gzip, \r\n chunked\r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
|
||||
// Technically invalid but beyond the parser's scope to detect
|
||||
// VFALCO Look into this
|
||||
//good<true>(ce("custom;key=\",chunked\r\n"), expect_flags{*this, parse_flag::chunked});
|
||||
|
||||
good<true>(te("gzip\r\n"), expect_flags{*this, 0});
|
||||
good<true>(te("chunked, gzip\r\n"), expect_flags{*this, 0});
|
||||
good<true>(te("chunked\r\n , gzip\r\n"), expect_flags{*this, 0});
|
||||
good<true>(te("chunked,\r\n gzip\r\n"), expect_flags{*this, 0});
|
||||
good<true>(te("chunked,\r\n ,gzip\r\n"), expect_flags{*this, 0});
|
||||
good<true>(te("bigchunked\r\n"), expect_flags{*this, 0});
|
||||
good<true>(te("chunk\r\n ked\r\n"), expect_flags{*this, 0});
|
||||
good<true>(te("bar\r\n ley chunked\r\n"), expect_flags{*this, 0});
|
||||
good<true>(te("barley\r\n chunked\r\n"), expect_flags{*this, 0});
|
||||
|
||||
good<true>(m("Transfer-EncodinX: none\r\n"), expect_flags{*this, 0});
|
||||
good<true>(m("Transfer-Encodings: 2\r\n"), expect_flags{*this, 0});
|
||||
good<true>(m("Transfer-Encoded: false\r\n"), expect_flags{*this, 0});
|
||||
|
||||
bad<false>(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Length: 1\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n", error::bad_transfer_encoding, true);
|
||||
}
|
||||
|
||||
void
|
||||
testUpgradeField()
|
||||
{
|
||||
auto const m =
|
||||
[](std::string const& s)
|
||||
{
|
||||
return "GET / HTTP/1.1\r\n" + s + "\r\n";
|
||||
};
|
||||
good<true>(m("Upgrade:\r\n"), expect_flags{*this, parse_flag::upgrade});
|
||||
good<true>(m("Upgrade: \r\n"), expect_flags{*this, parse_flag::upgrade});
|
||||
good<true>(m("Upgrade: yes\r\n"), expect_flags{*this, parse_flag::upgrade});
|
||||
|
||||
good<true>(m("Up: yes\r\n"), expect_flags{*this, 0});
|
||||
good<true>(m("UpgradX: none\r\n"), expect_flags{*this, 0});
|
||||
good<true>(m("Upgrades: 2\r\n"), expect_flags{*this, 0});
|
||||
good<true>(m("Upsample: 4x\r\n"), expect_flags{*this, 0});
|
||||
|
||||
good<true>(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Connection: upgrade\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"\r\n",
|
||||
[&](test_parser<true> const& p)
|
||||
{
|
||||
BEAST_EXPECT(p.is_upgrade());
|
||||
});
|
||||
}
|
||||
|
||||
void testBody()
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
good<true>(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Content-Length: 1\r\n"
|
||||
"\r\n"
|
||||
"1",
|
||||
expect_body(*this, "1"));
|
||||
|
||||
good<false>(
|
||||
"HTTP/1.0 200 OK\r\n"
|
||||
"\r\n"
|
||||
"hello",
|
||||
expect_body(*this, "hello"));
|
||||
|
||||
// write the body in 3 pieces
|
||||
{
|
||||
error_code ec;
|
||||
test_parser<true> p;
|
||||
feed(buffer_cat(
|
||||
buf("GET / HTTP/1.1\r\n"
|
||||
"Content-Length: 10\r\n"
|
||||
"\r\n"),
|
||||
buf("12"),
|
||||
buf("345"),
|
||||
buf("67890")),
|
||||
p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
}
|
||||
|
||||
// request without Content-Length or
|
||||
// Transfer-Encoding: chunked has no body.
|
||||
{
|
||||
error_code ec;
|
||||
test_parser<true> p;
|
||||
feed(buf(
|
||||
"GET / HTTP/1.0\r\n"
|
||||
"\r\n"
|
||||
), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
}
|
||||
{
|
||||
error_code ec;
|
||||
test_parser<true> p;
|
||||
feed(buf(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"\r\n"
|
||||
), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
}
|
||||
|
||||
// response without Content-Length or
|
||||
// Transfer-Encoding: chunked requires eof.
|
||||
{
|
||||
error_code ec;
|
||||
test_parser<false> p;
|
||||
feed(buf(
|
||||
"HTTP/1.0 200 OK\r\n"
|
||||
"\r\n"
|
||||
), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(! p.is_complete());
|
||||
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.state() == parse_state::body_to_eof);
|
||||
p.write_eof(ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
}
|
||||
|
||||
// 304 "Not Modified" response does not require eof
|
||||
{
|
||||
error_code ec;
|
||||
test_parser<false> p;
|
||||
feed(buf(
|
||||
"HTTP/1.0 304 Not Modified\r\n"
|
||||
"\r\n"
|
||||
), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
}
|
||||
|
||||
// Chunked response does not require eof
|
||||
{
|
||||
error_code ec;
|
||||
test_parser<false> p;
|
||||
feed(buf(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n"
|
||||
), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(! p.is_complete());
|
||||
feed(buf(
|
||||
"0\r\n\r\n"
|
||||
), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
}
|
||||
|
||||
// restart: 1.0 assumes Connection: close
|
||||
{
|
||||
error_code ec;
|
||||
test_parser<true> p;
|
||||
feed(buf(
|
||||
"GET / HTTP/1.0\r\n"
|
||||
"\r\n"
|
||||
), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
}
|
||||
|
||||
// restart: 1.1 assumes Connection: keep-alive
|
||||
{
|
||||
error_code ec;
|
||||
test_parser<true> p;
|
||||
feed(buf(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"\r\n"
|
||||
), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
}
|
||||
|
||||
bad<true>(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Content-Length: 1\r\n"
|
||||
"\r\n",
|
||||
error::partial_message);
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
check_header(
|
||||
test_parser<isRequest> const& p)
|
||||
{
|
||||
BEAST_EXPECT(p.got_on_begin);
|
||||
BEAST_EXPECT(p.got_on_field);
|
||||
BEAST_EXPECT(p.got_on_header);
|
||||
BEAST_EXPECT(! p.got_on_body);
|
||||
BEAST_EXPECT(! p.got_on_chunk);
|
||||
BEAST_EXPECT(! p.got_on_end_body);
|
||||
BEAST_EXPECT(! p.got_on_complete);
|
||||
BEAST_EXPECT(p.state() != parse_state::header);
|
||||
}
|
||||
|
||||
void
|
||||
testSplit()
|
||||
{
|
||||
#if 0
|
||||
streambuf sb;
|
||||
sb <<
|
||||
"POST / HTTP/1.1\r\n"
|
||||
"Content-Length: 5\r\n"
|
||||
"\r\n"
|
||||
"*****";
|
||||
error_code ec;
|
||||
test_parser<true> p;
|
||||
p.pause();
|
||||
auto n = feed(sb.data(), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.got_on_begin);
|
||||
BEAST_EXPECT(p.got_on_field);
|
||||
BEAST_EXPECT(p.got_on_header);
|
||||
BEAST_EXPECT(! p.got_on_body);
|
||||
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.body.empty());
|
||||
sb.consume(n);
|
||||
p.resume();
|
||||
n = feed(sb.data(), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.got_on_begin);
|
||||
BEAST_EXPECT(p.got_on_field);
|
||||
BEAST_EXPECT(p.got_on_header);
|
||||
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.body == "*****");
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testFlatten();
|
||||
testCallbacks();
|
||||
testRequestLine();
|
||||
testStatusLine();
|
||||
testFields();
|
||||
testConnectionField();
|
||||
testContentLengthField();
|
||||
testTransferEncodingField();
|
||||
testUpgradeField();
|
||||
testBody();
|
||||
testSplit();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(basic_parser,http,beast);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,534 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#include <beast/core/flat_streambuf.hpp>
|
||||
#include <beast/core/prepare_buffers.hpp>
|
||||
#include <beast/http/chunk_encode.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/core/detail/clamp.hpp>
|
||||
#include <beast/test/string_istream.hpp>
|
||||
#include <beast/test/string_ostream.hpp>
|
||||
#include <beast/test/yield_to.hpp>
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
#include <boost/asio/read.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
class design_test
|
||||
: public beast::unit_test::suite
|
||||
, public beast::test::enable_yield_to
|
||||
{
|
||||
public:
|
||||
//--------------------------------------------------------------------------
|
||||
/*
|
||||
Read a message with a direct Reader Body.
|
||||
*/
|
||||
struct direct_body
|
||||
{
|
||||
using value_type = std::string;
|
||||
|
||||
class reader
|
||||
{
|
||||
value_type& body_;
|
||||
std::size_t len_ = 0;
|
||||
|
||||
public:
|
||||
static bool constexpr is_direct = true;
|
||||
|
||||
using mutable_buffers_type =
|
||||
boost::asio::mutable_buffers_1;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(message<isRequest, direct_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(std::uint64_t content_length)
|
||||
{
|
||||
if(content_length >
|
||||
(std::numeric_limits<std::size_t>::max)())
|
||||
throw std::length_error(
|
||||
"Content-Length max exceeded");
|
||||
body_.reserve(static_cast<
|
||||
std::size_t>(content_length));
|
||||
}
|
||||
|
||||
mutable_buffers_type
|
||||
prepare(std::size_t n)
|
||||
{
|
||||
body_.resize(len_ + n);
|
||||
return {&body_[len_], n};
|
||||
}
|
||||
|
||||
void
|
||||
commit(std::size_t n)
|
||||
{
|
||||
if(body_.size() > len_ + n)
|
||||
body_.resize(len_ + n);
|
||||
len_ = body_.size();
|
||||
}
|
||||
|
||||
void
|
||||
finish()
|
||||
{
|
||||
body_.resize(len_);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
void
|
||||
testDirectBody()
|
||||
{
|
||||
// Content-Length
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Content-Length: 1\r\n"
|
||||
"\r\n"
|
||||
"*"
|
||||
};
|
||||
message<true, direct_body, fields> m;
|
||||
flat_streambuf sb{1024};
|
||||
read(is, sb, m);
|
||||
BEAST_EXPECT(m.body == "*");
|
||||
}
|
||||
|
||||
// end of file
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"\r\n" // 19 byte header
|
||||
"*"
|
||||
};
|
||||
message<false, direct_body, fields> m;
|
||||
flat_streambuf sb{20};
|
||||
read(is, sb, m);
|
||||
BEAST_EXPECT(m.body == "*");
|
||||
}
|
||||
|
||||
// chunked
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n"
|
||||
"1\r\n"
|
||||
"*\r\n"
|
||||
"0\r\n\r\n"
|
||||
};
|
||||
message<true, direct_body, fields> m;
|
||||
flat_streambuf sb{100};
|
||||
read(is, sb, m);
|
||||
BEAST_EXPECT(m.body == "*");
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/*
|
||||
Read a message with an indirect Reader Body.
|
||||
*/
|
||||
struct indirect_body
|
||||
{
|
||||
using value_type = std::string;
|
||||
|
||||
class reader
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
static bool constexpr is_direct = false;
|
||||
|
||||
using mutable_buffers_type =
|
||||
boost::asio::null_buffers;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(message<isRequest, indirect_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(std::uint64_t content_length,
|
||||
error_code& ec)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
write(boost::string_ref const& s,
|
||||
error_code& ec)
|
||||
{
|
||||
body_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
void
|
||||
testIndirectBody()
|
||||
{
|
||||
// Content-Length
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Content-Length: 1\r\n"
|
||||
"\r\n"
|
||||
"*"
|
||||
};
|
||||
message<true, indirect_body, fields> m;
|
||||
flat_streambuf sb{1024};
|
||||
read(is, sb, m);
|
||||
BEAST_EXPECT(m.body == "*");
|
||||
}
|
||||
|
||||
// end of file
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"\r\n" // 19 byte header
|
||||
"*"
|
||||
};
|
||||
message<false, indirect_body, fields> m;
|
||||
flat_streambuf sb{20};
|
||||
read(is, sb, m);
|
||||
BEAST_EXPECT(m.body == "*");
|
||||
}
|
||||
|
||||
|
||||
// chunked
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n"
|
||||
"1\r\n"
|
||||
"*\r\n"
|
||||
"0\r\n\r\n"
|
||||
};
|
||||
message<true, indirect_body, fields> m;
|
||||
flat_streambuf sb{1024};
|
||||
read(is, sb, m);
|
||||
BEAST_EXPECT(m.body == "*");
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/*
|
||||
Read a message header and manually read the body.
|
||||
*/
|
||||
void
|
||||
testManualBody()
|
||||
{
|
||||
// Content-Length
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Content-Length: 5\r\n"
|
||||
"\r\n" // 37 byte header
|
||||
"*****"
|
||||
};
|
||||
header_parser<true, fields> p;
|
||||
flat_streambuf sb{38};
|
||||
auto const bytes_used =
|
||||
read_some(is, sb, p);
|
||||
sb.consume(bytes_used);
|
||||
BEAST_EXPECT(p.size() == 5);
|
||||
BEAST_EXPECT(sb.size() < 5);
|
||||
sb.commit(boost::asio::read(
|
||||
is, sb.prepare(5 - sb.size())));
|
||||
BEAST_EXPECT(sb.size() == 5);
|
||||
}
|
||||
|
||||
// end of file
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"\r\n" // 19 byte header
|
||||
"*****"
|
||||
};
|
||||
header_parser<false, fields> p;
|
||||
flat_streambuf sb{20};
|
||||
auto const bytes_used =
|
||||
read_some(is, sb, p);
|
||||
sb.consume(bytes_used);
|
||||
BEAST_EXPECT(p.state() ==
|
||||
parse_state::body_to_eof);
|
||||
BEAST_EXPECT(sb.size() < 5);
|
||||
sb.commit(boost::asio::read(
|
||||
is, sb.prepare(5 - sb.size())));
|
||||
BEAST_EXPECT(sb.size() == 5);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/*
|
||||
Read a header, check for Expect: 100-continue,
|
||||
then conditionally read the body.
|
||||
*/
|
||||
void
|
||||
testExpect100Continue()
|
||||
{
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Expect: 100-continue\r\n"
|
||||
"Content-Length: 5\r\n"
|
||||
"\r\n"
|
||||
"*****"
|
||||
};
|
||||
|
||||
header_parser<true, fields> p;
|
||||
flat_streambuf sb{128};
|
||||
auto const bytes_used =
|
||||
read_some(is, sb, p);
|
||||
sb.consume(bytes_used);
|
||||
BEAST_EXPECT(p.got_header());
|
||||
BEAST_EXPECT(
|
||||
p.get().fields["Expect"] ==
|
||||
"100-continue");
|
||||
message_parser<
|
||||
true, string_body, fields> p1{
|
||||
std::move(p)};
|
||||
read(is, sb, p1);
|
||||
BEAST_EXPECT(
|
||||
p1.get().body == "*****");
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/*
|
||||
Efficiently relay a message from one stream to another
|
||||
*/
|
||||
template<
|
||||
bool isRequest,
|
||||
class SyncWriteStream,
|
||||
class DynamicBuffer,
|
||||
class SyncReadStream>
|
||||
void
|
||||
relay(
|
||||
SyncWriteStream& out,
|
||||
DynamicBuffer& sb,
|
||||
SyncReadStream& in)
|
||||
{
|
||||
flat_streambuf buffer{4096}; // 4K limit
|
||||
header_parser<isRequest, fields> parser;
|
||||
error_code ec;
|
||||
do
|
||||
{
|
||||
auto const state0 = parser.state();
|
||||
auto const bytes_used =
|
||||
read_some(in, buffer, parser, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
switch(state0)
|
||||
{
|
||||
case parse_state::header:
|
||||
{
|
||||
BEAST_EXPECT(parser.got_header());
|
||||
write(out, parser.get());
|
||||
break;
|
||||
}
|
||||
|
||||
case parse_state::chunk_header:
|
||||
{
|
||||
// inspect parser.chunk_extension() here
|
||||
if(parser.is_complete())
|
||||
boost::asio::write(out,
|
||||
chunk_encode_final());
|
||||
break;
|
||||
}
|
||||
|
||||
case parse_state::body:
|
||||
case parse_state::body_to_eof:
|
||||
case parse_state::chunk_body:
|
||||
{
|
||||
if(! parser.is_complete())
|
||||
{
|
||||
auto const body = parser.body();
|
||||
boost::asio::write(out, chunk_encode(
|
||||
false, boost::asio::buffer(
|
||||
body.data(), body.size())));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case parse_state::complete:
|
||||
break;
|
||||
}
|
||||
buffer.consume(bytes_used);
|
||||
}
|
||||
while(! parser.is_complete());
|
||||
}
|
||||
|
||||
void
|
||||
testRelay()
|
||||
{
|
||||
// Content-Length
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Content-Length: 5\r\n"
|
||||
"\r\n" // 37 byte header
|
||||
"*****",
|
||||
3 // max_read
|
||||
};
|
||||
test::string_ostream os{ios_};
|
||||
flat_streambuf sb{16};
|
||||
relay<true>(os, sb, is);
|
||||
}
|
||||
|
||||
// end of file
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"\r\n" // 19 byte header
|
||||
"*****",
|
||||
3 // max_read
|
||||
};
|
||||
test::string_ostream os{ios_};
|
||||
flat_streambuf sb{16};
|
||||
relay<false>(os, sb, is);
|
||||
}
|
||||
|
||||
// chunked
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n"
|
||||
"5;x;y=1;z=\"-\"\r\n*****\r\n"
|
||||
"3\r\n---\r\n"
|
||||
"1\r\n+\r\n"
|
||||
"0\r\n\r\n",
|
||||
2 // max_read
|
||||
};
|
||||
test::string_ostream os{ios_};
|
||||
flat_streambuf sb{16};
|
||||
relay<true>(os, sb, is);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
/*
|
||||
Read the request header, then read the request body content using
|
||||
a fixed-size buffer, i.e. read the body in chunks of 4k for instance.
|
||||
The end of the body should be indicated somehow and chunk-encoding
|
||||
should be decoded by beast.
|
||||
*/
|
||||
template<bool isRequest,
|
||||
class SyncReadStream, class BodyCallback>
|
||||
void
|
||||
doFixedRead(SyncReadStream& stream, BodyCallback const& cb)
|
||||
{
|
||||
flat_streambuf buffer{4096}; // 4K limit
|
||||
header_parser<isRequest, fields> parser;
|
||||
std::size_t bytes_used;
|
||||
bytes_used = read_some(stream, buffer, parser);
|
||||
BEAST_EXPECT(parser.got_header());
|
||||
buffer.consume(bytes_used);
|
||||
do
|
||||
{
|
||||
bytes_used =
|
||||
read_some(stream, buffer, parser);
|
||||
if(! parser.body().empty())
|
||||
cb(parser.body());
|
||||
buffer.consume(bytes_used);
|
||||
}
|
||||
while(! parser.is_complete());
|
||||
}
|
||||
|
||||
struct bodyHandler
|
||||
{
|
||||
void
|
||||
operator()(boost::string_ref const& body) const
|
||||
{
|
||||
// called for each piece of the body,
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
testFixedRead()
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
|
||||
// Content-Length
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Content-Length: 1\r\n"
|
||||
"\r\n"
|
||||
"*"
|
||||
};
|
||||
doFixedRead<true>(is, bodyHandler{});
|
||||
}
|
||||
|
||||
// end of file
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"\r\n" // 19 byte header
|
||||
"*****"
|
||||
};
|
||||
doFixedRead<false>(is, bodyHandler{});
|
||||
}
|
||||
|
||||
// chunked
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n"
|
||||
"5;x;y=1;z=\"-\"\r\n*****\r\n"
|
||||
"3\r\n---\r\n"
|
||||
"1\r\n+\r\n"
|
||||
"0\r\n\r\n",
|
||||
2 // max_read
|
||||
};
|
||||
doFixedRead<true>(is, bodyHandler{});
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
testDirectBody();
|
||||
testIndirectBody();
|
||||
testManualBody();
|
||||
testExpect100Continue();
|
||||
testRelay();
|
||||
testFixedRead();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(design,http,beast);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
@@ -1,9 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <beast/http/empty_body.hpp>
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <beast/http/error.hpp>
|
||||
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
class error_test : public unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
check(char const* name, error ev)
|
||||
{
|
||||
auto const ec = make_error_code(ev);
|
||||
BEAST_EXPECT(std::string(ec.category().name()) == name);
|
||||
BEAST_EXPECT(! ec.message().empty());
|
||||
BEAST_EXPECT(std::addressof(ec.category()) ==
|
||||
std::addressof(detail::get_http_error_category()));
|
||||
BEAST_EXPECT(detail::get_http_error_category().equivalent(
|
||||
static_cast<std::underlying_type<error>::type>(ev),
|
||||
ec.category().default_error_condition(
|
||||
static_cast<std::underlying_type<error>::type>(ev))));
|
||||
BEAST_EXPECT(detail::get_http_error_category().equivalent(
|
||||
ec, static_cast<std::underlying_type<error>::type>(ev)));
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
check("http", error::end_of_stream);
|
||||
check("http", error::partial_message);
|
||||
check("http", error::buffer_overflow);
|
||||
check("http", error::bad_line_ending);
|
||||
check("http", error::bad_method);
|
||||
check("http", error::bad_path);
|
||||
check("http", error::bad_version);
|
||||
check("http", error::bad_status);
|
||||
check("http", error::bad_reason);
|
||||
check("http", error::bad_field);
|
||||
check("http", error::bad_value);
|
||||
check("http", error::bad_content_length);
|
||||
check("http", error::bad_transfer_encoding);
|
||||
check("http", error::bad_chunk);
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(error,http,beast);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
@@ -1,120 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_TEST_FAIL_PARSER_HPP
|
||||
#define BEAST_HTTP_TEST_FAIL_PARSER_HPP
|
||||
|
||||
#include <beast/http/basic_parser_v1.hpp>
|
||||
#include <beast/test/fail_counter.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest>
|
||||
class fail_parser
|
||||
: public basic_parser_v1<isRequest, fail_parser<isRequest>>
|
||||
{
|
||||
test::fail_counter& fc_;
|
||||
std::uint64_t content_length_ = no_content_length;
|
||||
body_what body_rv_ = body_what::normal;
|
||||
|
||||
public:
|
||||
std::string body;
|
||||
|
||||
template<class... Args>
|
||||
explicit
|
||||
fail_parser(test::fail_counter& fc, Args&&... args)
|
||||
: fc_(fc)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_body_rv(body_what rv)
|
||||
{
|
||||
body_rv_ = rv;
|
||||
}
|
||||
|
||||
// valid on successful parse
|
||||
std::uint64_t
|
||||
content_length() const
|
||||
{
|
||||
return content_length_;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
on_header(std::uint64_t content_length, error_code& ec)
|
||||
{
|
||||
if(fc_.fail(ec))
|
||||
return;
|
||||
}
|
||||
|
||||
body_what
|
||||
on_body_what(std::uint64_t content_length, error_code& ec)
|
||||
{
|
||||
if(fc_.fail(ec))
|
||||
return body_what::normal;
|
||||
content_length_ = content_length;
|
||||
return body_rv_;
|
||||
}
|
||||
|
||||
void on_body(boost::string_ref const& s, error_code& ec)
|
||||
{
|
||||
if(fc_.fail(ec))
|
||||
return;
|
||||
body.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_complete(error_code& ec)
|
||||
{
|
||||
fc_.fail(ec);
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <beast/http/header_parser.hpp>
|
||||
|
||||
#include <beast/core/flat_streambuf.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
#include <beast/test/string_istream.hpp>
|
||||
#include <beast/test/yield_to.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
class header_parser_test
|
||||
: public beast::unit_test::suite
|
||||
, public test::enable_yield_to
|
||||
{
|
||||
public:
|
||||
void
|
||||
testParse()
|
||||
{
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"User-Agent: test\r\n"
|
||||
"\r\n"
|
||||
};
|
||||
flat_streambuf db{1024};
|
||||
header_parser<true, fields> p;
|
||||
read_some(is, db, p);
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
}
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
"POST / HTTP/1.1\r\n"
|
||||
"User-Agent: test\r\n"
|
||||
"Content-Length: 1\r\n"
|
||||
"\r\n"
|
||||
"*"
|
||||
};
|
||||
flat_streambuf db{1024};
|
||||
header_parser<true, fields> p;
|
||||
read_some(is, db, p);
|
||||
BEAST_EXPECT(! p.is_complete());
|
||||
BEAST_EXPECT(p.state() == parse_state::body);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testParse();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(header_parser,http,beast);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <beast/http/header_parser_v1.hpp>
|
||||
|
||||
#include <beast/http/fields.hpp>
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
class header_parser_v1_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void testParser()
|
||||
{
|
||||
{
|
||||
error_code ec;
|
||||
header_parser_v1<true, fields> p;
|
||||
BEAST_EXPECT(! p.complete());
|
||||
auto const n = p.write(boost::asio::buffer(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"User-Agent: test\r\n"
|
||||
"\r\n"
|
||||
), ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.complete());
|
||||
BEAST_EXPECT(n == 36);
|
||||
}
|
||||
{
|
||||
error_code ec;
|
||||
header_parser_v1<true, fields> p;
|
||||
BEAST_EXPECT(! p.complete());
|
||||
auto const n = p.write(boost::asio::buffer(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"User-Agent: test\r\n"
|
||||
"Content-Length: 5\r\n"
|
||||
"\r\n"
|
||||
"*****"
|
||||
), ec);
|
||||
BEAST_EXPECT(n == 55);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.complete());
|
||||
}
|
||||
{
|
||||
error_code ec;
|
||||
header_parser_v1<false, fields> p;
|
||||
BEAST_EXPECT(! p.complete());
|
||||
auto const n = p.write(boost::asio::buffer(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Server: test\r\n"
|
||||
"\r\n"
|
||||
), ec);
|
||||
BEAST_EXPECT(n == 33);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.complete());
|
||||
}
|
||||
{
|
||||
error_code ec;
|
||||
header_parser_v1<false, fields> p;
|
||||
BEAST_EXPECT(! p.complete());
|
||||
auto const n = p.write(boost::asio::buffer(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Server: test\r\n"
|
||||
"Content-Length: 5\r\n"
|
||||
"\r\n"
|
||||
"*****"
|
||||
), ec);
|
||||
BEAST_EXPECT(n == 52);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.complete());
|
||||
}
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
testParser();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(header_parser_v1,http,beast);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
@@ -8,7 +8,7 @@
|
||||
// Test that header file is self-contained.
|
||||
#include <beast/http/message.hpp>
|
||||
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/http/fields.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
@@ -195,7 +195,7 @@ public:
|
||||
void testFreeFunctions()
|
||||
{
|
||||
{
|
||||
request<empty_body> m;
|
||||
request<string_body> m;
|
||||
m.method = "GET";
|
||||
m.url = "/";
|
||||
m.version = 11;
|
||||
@@ -213,7 +213,7 @@ public:
|
||||
|
||||
void testPrepare()
|
||||
{
|
||||
request<empty_body> m;
|
||||
request<string_body> m;
|
||||
m.version = 10;
|
||||
BEAST_EXPECT(! is_upgrade(m));
|
||||
m.fields.insert("Transfer-Encoding", "chunked");
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#define BEAST_HTTP_TEST_MESSAGE_FUZZ_HPP
|
||||
|
||||
#include <beast/core/write_dynabuf.hpp>
|
||||
#include <beast/http/detail/basic_parser_v1.hpp>
|
||||
#include <beast/http/detail/rfc7230.hpp>
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <beast/http/message_parser.hpp>
|
||||
|
||||
#include "test_parser.hpp"
|
||||
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
#include <beast/test/string_istream.hpp>
|
||||
#include <beast/test/string_ostream.hpp>
|
||||
#include <beast/test/yield_to.hpp>
|
||||
#include <beast/core/flat_streambuf.hpp>
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/http/header_parser.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <boost/system/system_error.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
class message_parser_test
|
||||
: public beast::unit_test::suite
|
||||
, public beast::test::enable_yield_to
|
||||
{
|
||||
public:
|
||||
template<bool isRequest, class Pred>
|
||||
void
|
||||
testMatrix(std::string const& s, Pred const& pred)
|
||||
{
|
||||
beast::test::string_istream ss{get_io_service(), s};
|
||||
error_code ec;
|
||||
#if 0
|
||||
streambuf dynabuf;
|
||||
#else
|
||||
flat_streambuf dynabuf{1024};
|
||||
#endif
|
||||
message<isRequest, string_body, fields> m;
|
||||
read(ss, dynabuf, m, ec);
|
||||
if(! BEAST_EXPECTS(! ec, ec.message()))
|
||||
return;
|
||||
pred(m);
|
||||
}
|
||||
|
||||
void
|
||||
testRead()
|
||||
{
|
||||
testMatrix<false>(
|
||||
"HTTP/1.0 200 OK\r\n"
|
||||
"Server: test\r\n"
|
||||
"\r\n"
|
||||
"*******",
|
||||
[&](message<false, string_body, fields> const& m)
|
||||
{
|
||||
BEAST_EXPECTS(m.body == "*******",
|
||||
"body='" + m.body + "'");
|
||||
}
|
||||
);
|
||||
testMatrix<false>(
|
||||
"HTTP/1.0 200 OK\r\n"
|
||||
"Server: test\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n"
|
||||
"5\r\n"
|
||||
"*****\r\n"
|
||||
"2;a;b=1;c=\"2\"\r\n"
|
||||
"--\r\n"
|
||||
"0;d;e=3;f=\"4\"\r\n"
|
||||
"Expires: never\r\n"
|
||||
"MD5-Fingerprint: -\r\n"
|
||||
"\r\n",
|
||||
[&](message<false, string_body, fields> const& m)
|
||||
{
|
||||
BEAST_EXPECT(m.body == "*****--");
|
||||
}
|
||||
);
|
||||
testMatrix<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)
|
||||
{
|
||||
BEAST_EXPECT(m.body == "*****");
|
||||
}
|
||||
);
|
||||
testMatrix<true>(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"User-Agent: test\r\n"
|
||||
"\r\n",
|
||||
[&](message<true, string_body, fields> const& m)
|
||||
{
|
||||
}
|
||||
);
|
||||
testMatrix<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)
|
||||
{
|
||||
BEAST_EXPECT(m.fields["X"] == "x");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
testParse()
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
{
|
||||
error_code ec;
|
||||
beast::test::string_istream is{
|
||||
get_io_service(),
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"User-Agent: test\r\n"
|
||||
"Content-Length: 1\r\n"
|
||||
"\r\n"
|
||||
"*"};
|
||||
flat_streambuf sb{1024};
|
||||
message_parser<true, string_body, fields> p;
|
||||
read(is, sb, p, ec);
|
||||
auto const& m = p.get();
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(m.method == "GET");
|
||||
BEAST_EXPECT(m.url == "/");
|
||||
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(), ""};
|
||||
streambuf sb;
|
||||
sb <<
|
||||
"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, sb, p, ec);
|
||||
BEAST_EXPECT(sb.size() == 0);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(!p.is_complete());
|
||||
BEAST_EXPECT(p.get().body == "*");
|
||||
sb << "\r\n0;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(sb.data(), ec) == 0);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(!p.is_complete());
|
||||
sb << "\r\n"; // final crlf to end message
|
||||
BEAST_EXPECT(p.write(sb.data(), ec) == sb.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 == "*");
|
||||
}
|
||||
// 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);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
testExpect100Continue()
|
||||
{
|
||||
test::string_istream ss{ios_,
|
||||
"POST / HTTP/1.1\r\n"
|
||||
"Expect: 100-continue\r\n"
|
||||
"Content-Length: 5\r\n"
|
||||
"\r\n"
|
||||
"*****"};
|
||||
streambuf sb;
|
||||
error_code ec;
|
||||
header_parser<true, fields> p0;
|
||||
auto const bytes_used =
|
||||
read_some(ss, sb, p0, ec);
|
||||
sb.consume(bytes_used);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p0.state() != parse_state::header);
|
||||
BEAST_EXPECT(! p0.is_complete());
|
||||
message_parser<true,
|
||||
string_body, fields> p1{std::move(p0)};
|
||||
read(ss, sb, p1, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p1.get().body == "*****");
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testRead();
|
||||
testParse();
|
||||
testExpect100Continue();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(message_parser,http,beast);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
@@ -740,11 +740,6 @@ template<bool isRequest, class Body, class Fields>
|
||||
class nodejs_parser
|
||||
: public nodejs_basic_parser<nodejs_parser<isRequest, Body, Fields>>
|
||||
{
|
||||
using message_type =
|
||||
message<isRequest, Body, Fields>;
|
||||
|
||||
message_type m_;
|
||||
typename message_type::body_type::reader r_;
|
||||
bool started_ = false;
|
||||
|
||||
public:
|
||||
@@ -752,7 +747,6 @@ public:
|
||||
|
||||
nodejs_parser()
|
||||
: http::nodejs_basic_parser<nodejs_parser>(isRequest)
|
||||
, r_(m_)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -763,12 +757,6 @@ public:
|
||||
return started_;
|
||||
}
|
||||
|
||||
message_type
|
||||
release()
|
||||
{
|
||||
return std::move(m_);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class http::nodejs_basic_parser<nodejs_parser>;
|
||||
|
||||
@@ -781,7 +769,6 @@ private:
|
||||
void
|
||||
on_field(std::string const& field, std::string const& value)
|
||||
{
|
||||
m_.fields.insert(field, value);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -798,9 +785,6 @@ private:
|
||||
int major, int minor, bool /*keep_alive*/, bool /*upgrade*/,
|
||||
std::true_type)
|
||||
{
|
||||
m_.method = detail::method_to_string(method);
|
||||
m_.url = url;
|
||||
m_.version = major * 10 + minor;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -819,7 +803,7 @@ private:
|
||||
return on_request(method, url,
|
||||
major, minor, keep_alive, upgrade,
|
||||
std::integral_constant<
|
||||
bool, message_type::is_request>{});
|
||||
bool, isRequest>{});
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -828,10 +812,6 @@ private:
|
||||
std::true_type)
|
||||
{
|
||||
beast::detail::ignore_unused(keep_alive, upgrade);
|
||||
m_.status = status;
|
||||
m_.reason = reason;
|
||||
m_.version = major * 10 + minor;
|
||||
// VFALCO TODO return expect_body_
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -848,14 +828,13 @@ private:
|
||||
{
|
||||
return on_response(
|
||||
status, reason, major, minor, keep_alive, upgrade,
|
||||
std::integral_constant<bool, ! message_type::is_request>{});
|
||||
std::integral_constant<bool, ! isRequest>{});
|
||||
}
|
||||
|
||||
void
|
||||
on_body(void const* data,
|
||||
std::size_t size, error_code& ec)
|
||||
{
|
||||
r_.write(data, size, ec);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <beast/http/parse.hpp>
|
||||
@@ -1,60 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <beast/http/parse_error.hpp>
|
||||
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
class parse_error_test : public unit_test::suite
|
||||
{
|
||||
public:
|
||||
void check(char const* name, parse_error ev)
|
||||
{
|
||||
auto const ec = make_error_code(ev);
|
||||
BEAST_EXPECT(std::string{ec.category().name()} == name);
|
||||
BEAST_EXPECT(! ec.message().empty());
|
||||
BEAST_EXPECT(std::addressof(ec.category()) ==
|
||||
std::addressof(detail::get_parse_error_category()));
|
||||
BEAST_EXPECT(detail::get_parse_error_category().equivalent(
|
||||
static_cast<std::underlying_type<parse_error>::type>(ev),
|
||||
ec.category().default_error_condition(
|
||||
static_cast<std::underlying_type<parse_error>::type>(ev))));
|
||||
BEAST_EXPECT(detail::get_parse_error_category().equivalent(
|
||||
ec, static_cast<std::underlying_type<parse_error>::type>(ev)));
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
check("http", parse_error::connection_closed);
|
||||
check("http", parse_error::bad_method);
|
||||
check("http", parse_error::bad_uri);
|
||||
check("http", parse_error::bad_version);
|
||||
check("http", parse_error::bad_crlf);
|
||||
check("http", parse_error::bad_status);
|
||||
check("http", parse_error::bad_reason);
|
||||
check("http", parse_error::bad_field);
|
||||
check("http", parse_error::bad_value);
|
||||
check("http", parse_error::bad_content_length);
|
||||
check("http", parse_error::illegal_content_length);
|
||||
check("http", parse_error::invalid_chunk_size);
|
||||
check("http", parse_error::invalid_ext_name);
|
||||
check("http", parse_error::invalid_ext_val);
|
||||
check("http", parse_error::header_too_big);
|
||||
check("http", parse_error::body_too_big);
|
||||
check("http", parse_error::short_read);
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(parse_error,http,beast);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
+128
-11
@@ -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_);
|
||||
});
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <beast/http/parser_v1.hpp>
|
||||
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/http/fields.hpp>
|
||||
#include <beast/http/header_parser_v1.hpp>
|
||||
#include <beast/http/parse.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/test/string_istream.hpp>
|
||||
#include <beast/test/yield_to.hpp>
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
class parser_v1_test
|
||||
: public beast::unit_test::suite
|
||||
, public test::enable_yield_to
|
||||
{
|
||||
public:
|
||||
void
|
||||
testParse()
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
{
|
||||
error_code ec;
|
||||
parser_v1<true, string_body,
|
||||
basic_fields<std::allocator<char>>> p;
|
||||
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);
|
||||
BEAST_EXPECT(! ec);
|
||||
BEAST_EXPECT(p.complete());
|
||||
auto m = p.release();
|
||||
BEAST_EXPECT(m.method == "GET");
|
||||
BEAST_EXPECT(m.url == "/");
|
||||
BEAST_EXPECT(m.version == 11);
|
||||
BEAST_EXPECT(m.fields["User-Agent"] == "test");
|
||||
BEAST_EXPECT(m.body == "*");
|
||||
}
|
||||
{
|
||||
error_code ec;
|
||||
parser_v1<false, string_body,
|
||||
basic_fields<std::allocator<char>>> 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);
|
||||
BEAST_EXPECT(! ec);
|
||||
BEAST_EXPECT(p.complete());
|
||||
auto m = p.release();
|
||||
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 == "*");
|
||||
}
|
||||
// skip body
|
||||
{
|
||||
error_code ec;
|
||||
parser_v1<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.set_option(skip_body{true});
|
||||
p.write(buffer(s), ec);
|
||||
BEAST_EXPECT(! ec);
|
||||
BEAST_EXPECT(p.complete());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testWithBody()
|
||||
{
|
||||
std::string const raw =
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"User-Agent: test\r\n"
|
||||
"Content-Length: 1\r\n"
|
||||
"\r\n"
|
||||
"*";
|
||||
test::string_istream ss{
|
||||
ios_, raw, raw.size() - 1};
|
||||
|
||||
streambuf rb;
|
||||
header_parser_v1<true, fields> p0;
|
||||
parse(ss, rb, p0);
|
||||
request_header const& reqh = p0.get();
|
||||
BEAST_EXPECT(reqh.method == "GET");
|
||||
BEAST_EXPECT(reqh.url == "/");
|
||||
BEAST_EXPECT(reqh.version == 11);
|
||||
BEAST_EXPECT(reqh.fields["User-Agent"] == "test");
|
||||
BEAST_EXPECT(reqh.fields["Content-Length"] == "1");
|
||||
parser_v1<true, string_body, fields> p =
|
||||
with_body<string_body>(p0);
|
||||
BEAST_EXPECT(p.get().method == "GET");
|
||||
BEAST_EXPECT(p.get().url == "/");
|
||||
BEAST_EXPECT(p.get().version == 11);
|
||||
BEAST_EXPECT(p.get().fields["User-Agent"] == "test");
|
||||
BEAST_EXPECT(p.get().fields["Content-Length"] == "1");
|
||||
parse(ss, rb, p);
|
||||
request<string_body, fields> req = p.release();
|
||||
BEAST_EXPECT(req.body == "*");
|
||||
}
|
||||
|
||||
void
|
||||
testRegressions()
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
|
||||
// consecutive empty header values
|
||||
{
|
||||
error_code ec;
|
||||
parser_v1<true, string_body, fields> p;
|
||||
std::string const s =
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"X1:\r\n"
|
||||
"X2:\r\n"
|
||||
"X3:x\r\n"
|
||||
"\r\n";
|
||||
p.write(buffer(s), ec);
|
||||
if(! BEAST_EXPECTS(! ec, ec.message()))
|
||||
return;
|
||||
BEAST_EXPECT(p.complete());
|
||||
auto const msg = p.release();
|
||||
BEAST_EXPECT(msg.fields.exists("X1"));
|
||||
BEAST_EXPECT(msg.fields["X1"] == "");
|
||||
BEAST_EXPECT(msg.fields.exists("X2"));
|
||||
BEAST_EXPECT(msg.fields["X2"] == "");
|
||||
BEAST_EXPECT(msg.fields.exists("X3"));
|
||||
BEAST_EXPECT(msg.fields["X3"] == "x");
|
||||
}
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
testParse();
|
||||
testWithBody();
|
||||
testRegressions();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(parser_v1,http,beast);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
+23
-133
@@ -8,10 +8,12 @@
|
||||
// Test that header file is self-contained.
|
||||
#include <beast/http/read.hpp>
|
||||
|
||||
#include "fail_parser.hpp"
|
||||
#include "test_parser.hpp"
|
||||
|
||||
#include <beast/http/fields.hpp>
|
||||
#include <beast/http/header_parser.hpp>
|
||||
#include <beast/http/streambuf_body.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/test/fail_stream.hpp>
|
||||
#include <beast/test/string_istream.hpp>
|
||||
#include <beast/test/yield_to.hpp>
|
||||
@@ -27,62 +29,9 @@ class read_test
|
||||
, public test::enable_yield_to
|
||||
{
|
||||
public:
|
||||
struct fail_body
|
||||
{
|
||||
class reader;
|
||||
|
||||
class value_type
|
||||
{
|
||||
friend class reader;
|
||||
|
||||
std::string s_;
|
||||
test::fail_counter& fc_;
|
||||
|
||||
public:
|
||||
explicit
|
||||
value_type(test::fail_counter& fc)
|
||||
: fc_(fc)
|
||||
{
|
||||
}
|
||||
|
||||
value_type&
|
||||
operator=(std::string s)
|
||||
{
|
||||
s_ = std::move(s);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class reader
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Allocator>
|
||||
explicit
|
||||
reader(message<isRequest, fail_body, Allocator>& msg) noexcept
|
||||
: body_(msg.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec) noexcept
|
||||
{
|
||||
body_.fc_.fail(ec);
|
||||
}
|
||||
|
||||
void
|
||||
write(void const* data,
|
||||
std::size_t size, error_code& ec) noexcept
|
||||
{
|
||||
if(body_.fc_.fail(ec))
|
||||
return;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template<bool isRequest>
|
||||
void failMatrix(char const* s, yield_context do_yield)
|
||||
void
|
||||
failMatrix(char const* s, yield_context do_yield)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
@@ -97,9 +46,9 @@ public:
|
||||
test::fail_counter fc(n);
|
||||
test::fail_stream<
|
||||
test::string_istream> fs{fc, ios_, ""};
|
||||
fail_parser<isRequest> p(fc);
|
||||
test_parser<isRequest> p(fc);
|
||||
error_code ec;
|
||||
parse(fs, sb, p, ec);
|
||||
read(fs, sb, p, ec);
|
||||
if(! ec)
|
||||
break;
|
||||
}
|
||||
@@ -113,9 +62,9 @@ public:
|
||||
test::fail_counter fc(n);
|
||||
test::fail_stream<test::string_istream> fs{
|
||||
fc, ios_, std::string{s + pre, len - pre}};
|
||||
fail_parser<isRequest> p(fc);
|
||||
test_parser<isRequest> p(fc);
|
||||
error_code ec;
|
||||
parse(fs, sb, p, ec);
|
||||
read(fs, sb, p, ec);
|
||||
if(! ec)
|
||||
break;
|
||||
}
|
||||
@@ -128,9 +77,9 @@ public:
|
||||
test::fail_counter fc(n);
|
||||
test::fail_stream<
|
||||
test::string_istream> fs{fc, ios_, ""};
|
||||
fail_parser<isRequest> p(fc);
|
||||
test_parser<isRequest> p(fc);
|
||||
error_code ec;
|
||||
async_parse(fs, sb, p, do_yield[ec]);
|
||||
async_read(fs, sb, p, do_yield[ec]);
|
||||
if(! ec)
|
||||
break;
|
||||
}
|
||||
@@ -144,23 +93,9 @@ public:
|
||||
test::fail_counter fc(n);
|
||||
test::fail_stream<test::string_istream> fs{
|
||||
fc, ios_, std::string{s + pre, len - pre}};
|
||||
fail_parser<isRequest> p(fc);
|
||||
test_parser<isRequest> p(fc);
|
||||
error_code ec;
|
||||
async_parse(fs, sb, p, do_yield[ec]);
|
||||
if(! ec)
|
||||
break;
|
||||
}
|
||||
BEAST_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::string_istream ss{ios_, s};
|
||||
parser_v1<isRequest, fail_body, fields> p{fc};
|
||||
error_code ec;
|
||||
parse(ss, sb, p, ec);
|
||||
async_read(fs, sb, p, do_yield[ec]);
|
||||
if(! ec)
|
||||
break;
|
||||
}
|
||||
@@ -173,8 +108,8 @@ public:
|
||||
{
|
||||
streambuf sb;
|
||||
test::string_istream ss(ios_, "GET / X");
|
||||
parser_v1<true, streambuf_body, fields> p;
|
||||
parse(ss, sb, p);
|
||||
message_parser<true, streambuf_body, fields> p;
|
||||
read(ss, sb, p);
|
||||
fail();
|
||||
}
|
||||
catch(std::exception const&)
|
||||
@@ -245,52 +180,6 @@ public:
|
||||
failMatrix<false>(res[i], do_yield);
|
||||
}
|
||||
|
||||
void testReadHeaders(yield_context do_yield)
|
||||
{
|
||||
static std::size_t constexpr limit = 100;
|
||||
std::size_t n;
|
||||
|
||||
for(n = 0; n < limit; ++n)
|
||||
{
|
||||
test::fail_stream<test::string_istream> fs{n, ios_,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost\r\n"
|
||||
"User-Agent: test\r\n"
|
||||
"Content-Length: 5\r\n"
|
||||
"\r\n"
|
||||
};
|
||||
request_header m;
|
||||
try
|
||||
{
|
||||
streambuf sb;
|
||||
read(fs, sb, m);
|
||||
break;
|
||||
}
|
||||
catch(std::exception const&)
|
||||
{
|
||||
}
|
||||
}
|
||||
BEAST_EXPECT(n < limit);
|
||||
|
||||
for(n = 0; n < limit; ++n)
|
||||
{
|
||||
test::fail_stream<test::string_istream> fs(n, ios_,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost\r\n"
|
||||
"User-Agent: test\r\n"
|
||||
"Content-Length: 0\r\n"
|
||||
"\r\n"
|
||||
);
|
||||
request_header m;
|
||||
error_code ec;
|
||||
streambuf sb;
|
||||
async_read(fs, sb, m, do_yield[ec]);
|
||||
if(! ec)
|
||||
break;
|
||||
}
|
||||
BEAST_EXPECT(n < limit);
|
||||
}
|
||||
|
||||
void testRead(yield_context do_yield)
|
||||
{
|
||||
static std::size_t constexpr limit = 100;
|
||||
@@ -355,22 +244,23 @@ public:
|
||||
BEAST_EXPECT(n < limit);
|
||||
}
|
||||
|
||||
void testEof(yield_context do_yield)
|
||||
void
|
||||
testEof(yield_context do_yield)
|
||||
{
|
||||
{
|
||||
streambuf sb;
|
||||
test::string_istream ss(ios_, "");
|
||||
parser_v1<true, streambuf_body, fields> p;
|
||||
message_parser<true, streambuf_body, fields> p;
|
||||
error_code ec;
|
||||
parse(ss, sb, p, ec);
|
||||
read(ss, sb, p, ec);
|
||||
BEAST_EXPECT(ec == boost::asio::error::eof);
|
||||
}
|
||||
{
|
||||
streambuf sb;
|
||||
test::string_istream ss(ios_, "");
|
||||
parser_v1<true, streambuf_body, fields> p;
|
||||
message_parser<true, streambuf_body, fields> p;
|
||||
error_code ec;
|
||||
async_parse(ss, sb, p, do_yield[ec]);
|
||||
async_read(ss, sb, p, do_yield[ec]);
|
||||
BEAST_EXPECT(ec == boost::asio::error::eof);
|
||||
}
|
||||
}
|
||||
@@ -424,12 +314,12 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void run() override
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testThrow();
|
||||
|
||||
yield_to(&read_test::testFailures, this);
|
||||
yield_to(&read_test::testReadHeaders, this);
|
||||
yield_to(&read_test::testRead, this);
|
||||
yield_to(&read_test::testEof, this);
|
||||
|
||||
|
||||
+109
-5
@@ -13,9 +13,10 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <beast/core/detail/empty_base_optimization.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace test {
|
||||
|
||||
class rfc7230_test : public beast::unit_test::suite
|
||||
{
|
||||
@@ -135,9 +136,9 @@ public:
|
||||
BEAST_EXPECTS(got == good, fmt(got));
|
||||
};
|
||||
/*
|
||||
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
|
||||
ext = token param-list
|
||||
param-list = *( OWS ";" OWS param )
|
||||
ext-basic_parsed_list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
|
||||
ext = token param-basic_parsed_list
|
||||
param-basic_parsed_list = *( OWS ";" OWS param )
|
||||
param = token OWS "=" OWS ( token / quoted-string )
|
||||
*/
|
||||
cs(",", "");
|
||||
@@ -237,17 +238,120 @@ public:
|
||||
cs("x y", "x");
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
static
|
||||
std::vector<std::string>
|
||||
to_vector(boost::string_ref const& in)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
detail::basic_parsed_list<Policy> list{in};
|
||||
for(auto const& s :
|
||||
detail::basic_parsed_list<Policy>{in})
|
||||
v.emplace_back(s.data(), s.size());
|
||||
return v;
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
void
|
||||
validate(boost::string_ref const& in,
|
||||
std::vector<std::string> const& v)
|
||||
{
|
||||
BEAST_EXPECT(to_vector<Policy>(in) == v);
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
void
|
||||
good(boost::string_ref const& in)
|
||||
{
|
||||
BEAST_EXPECT(validate_list(
|
||||
detail::basic_parsed_list<Policy>{in}));
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
void
|
||||
good(boost::string_ref const& in,
|
||||
std::vector<std::string> const& v)
|
||||
{
|
||||
BEAST_EXPECT(validate_list(
|
||||
detail::basic_parsed_list<Policy>{in}));
|
||||
validate<Policy>(in, v);
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
void
|
||||
bad(boost::string_ref const& in)
|
||||
{
|
||||
BEAST_EXPECT(! validate_list(
|
||||
detail::basic_parsed_list<Policy>{in}));
|
||||
}
|
||||
|
||||
void
|
||||
testOptTokenList()
|
||||
{
|
||||
/*
|
||||
#token = [ ( "," / token ) *( OWS "," [ OWS token ] ) ]
|
||||
*/
|
||||
using type = detail::opt_token_list_policy;
|
||||
|
||||
good<type>("", {});
|
||||
good<type>(" ", {});
|
||||
good<type>("\t", {});
|
||||
good<type>(" \t", {});
|
||||
good<type>(",", {});
|
||||
good<type>(",,", {});
|
||||
good<type>(", ,", {});
|
||||
good<type>(",\t,", {});
|
||||
good<type>(", \t,", {});
|
||||
good<type>(", \t, ", {});
|
||||
good<type>(", \t,\t", {});
|
||||
good<type>(", \t, \t", {});
|
||||
|
||||
good<type>("x", {"x"});
|
||||
good<type>(" x", {"x"});
|
||||
good<type>("x,,", {"x"});
|
||||
good<type>("x, ,", {"x"});
|
||||
good<type>("x,, ", {"x"});
|
||||
good<type>("x,,,", {"x"});
|
||||
|
||||
good<type>("x,y", {"x","y"});
|
||||
good<type>("x ,y", {"x","y"});
|
||||
good<type>("x\t,y", {"x","y"});
|
||||
good<type>("x \t,y", {"x","y"});
|
||||
good<type>(" x,y", {"x","y"});
|
||||
good<type>(" x,y ", {"x","y"});
|
||||
good<type>(",x,y", {"x","y"});
|
||||
good<type>("x,y,", {"x","y"});
|
||||
good<type>(",,x,y", {"x","y"});
|
||||
good<type>(",x,,y", {"x","y"});
|
||||
good<type>(",x,y,", {"x","y"});
|
||||
good<type>("x ,, y", {"x","y"});
|
||||
good<type>("x , ,y", {"x","y"});
|
||||
|
||||
good<type>("x,y,z", {"x","y","z"});
|
||||
|
||||
bad<type>("(");
|
||||
bad<type>("x(");
|
||||
bad<type>("(x");
|
||||
bad<type>(",(");
|
||||
bad<type>("(,");
|
||||
bad<type>("x,(");
|
||||
bad<type>("(,x");
|
||||
bad<type>("x y");
|
||||
}
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
testOptTokenList();
|
||||
#if 0
|
||||
testParamList();
|
||||
testExtList();
|
||||
testTokenList();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(rfc7230,http,beast);
|
||||
|
||||
} // test
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#include <beast/core/to_string.hpp>
|
||||
#include <beast/http/fields.hpp>
|
||||
#include <beast/http/parser_v1.hpp>
|
||||
#include <beast/http/message_parser.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
#include <beast/test/string_istream.hpp>
|
||||
@@ -34,11 +34,12 @@ public:
|
||||
"\r\n"
|
||||
"xyz";
|
||||
test::string_istream ss(ios_, s);
|
||||
parser_v1<false, streambuf_body, fields> p;
|
||||
message_parser<false, streambuf_body, fields> p;
|
||||
streambuf sb;
|
||||
parse(ss, sb, p);
|
||||
BEAST_EXPECT(to_string(p.get().body.data()) == "xyz");
|
||||
BEAST_EXPECT(boost::lexical_cast<std::string>(p.get()) == s);
|
||||
read(ss, sb, p);
|
||||
auto const& m = p.get();
|
||||
BEAST_EXPECT(to_string(m.body.data()) == "xyz");
|
||||
BEAST_EXPECT(boost::lexical_cast<std::string>(m) == s);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_TEST_PARSER_HPP
|
||||
#define BEAST_HTTP_TEST_PARSER_HPP
|
||||
|
||||
#include <beast/http/basic_parser.hpp>
|
||||
#include <beast/test/fail_counter.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest>
|
||||
class test_parser
|
||||
: public basic_parser<isRequest, false,
|
||||
test_parser<isRequest>>
|
||||
{
|
||||
test::fail_counter* fc_ = nullptr;
|
||||
|
||||
public:
|
||||
using mutable_buffers_type =
|
||||
boost::asio::mutable_buffers_1;
|
||||
|
||||
int status = 0;
|
||||
int version = 0;
|
||||
std::string method;
|
||||
std::string path;
|
||||
std::string reason;
|
||||
std::string body;
|
||||
bool got_on_begin = false;
|
||||
bool got_on_field = false;
|
||||
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;
|
||||
|
||||
test_parser() = default;
|
||||
|
||||
explicit
|
||||
test_parser(test::fail_counter& fc)
|
||||
: fc_(&fc)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_request(
|
||||
boost::string_ref const& method_,
|
||||
boost::string_ref const& path_,
|
||||
int version_, error_code& ec)
|
||||
{
|
||||
method = std::string(
|
||||
method_.data(), method_.size());
|
||||
path = std::string(
|
||||
path_.data(), path_.size());
|
||||
version = version_;
|
||||
got_on_begin = true;
|
||||
if(fc_)
|
||||
fc_->fail(ec);
|
||||
}
|
||||
|
||||
void
|
||||
on_response(int status_,
|
||||
boost::string_ref const& reason_,
|
||||
int version_, error_code& ec)
|
||||
{
|
||||
status = status_;
|
||||
reason = std::string(
|
||||
reason_.data(), reason_.size());
|
||||
version = version_;
|
||||
got_on_begin = true;
|
||||
if(fc_)
|
||||
fc_->fail(ec);
|
||||
}
|
||||
|
||||
void
|
||||
on_field(boost::string_ref const&,
|
||||
boost::string_ref const&,
|
||||
error_code& ec)
|
||||
{
|
||||
got_on_field = true;
|
||||
if(fc_)
|
||||
fc_->fail(ec);
|
||||
}
|
||||
|
||||
void
|
||||
on_header(error_code& ec)
|
||||
{
|
||||
got_on_header = true;
|
||||
if(fc_)
|
||||
fc_->fail(ec);
|
||||
}
|
||||
|
||||
void
|
||||
on_body(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;
|
||||
if(fc_)
|
||||
fc_->fail(ec);
|
||||
}
|
||||
|
||||
void
|
||||
on_data(boost::string_ref const& s,
|
||||
error_code& ec)
|
||||
{
|
||||
body.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void
|
||||
on_chunk(std::uint64_t,
|
||||
boost::string_ref const&,
|
||||
error_code& ec)
|
||||
{
|
||||
got_on_chunk = true;
|
||||
if(fc_)
|
||||
fc_->fail(ec);
|
||||
}
|
||||
|
||||
void
|
||||
on_complete(error_code& ec)
|
||||
{
|
||||
got_on_complete = true;
|
||||
if(fc_)
|
||||
fc_->fail(ec);
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
+1
-2
@@ -10,7 +10,6 @@
|
||||
|
||||
#include <beast/http/fields.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
@@ -510,7 +509,7 @@ public:
|
||||
}
|
||||
// upgrade HTTP/1.1
|
||||
{
|
||||
message<true, empty_body, fields> m;
|
||||
message<true, string_body, fields> m;
|
||||
m.method = "GET";
|
||||
m.url = "/";
|
||||
m.version = 11;
|
||||
|
||||
@@ -174,7 +174,7 @@ public:
|
||||
for(n = 0; n < limit; ++n)
|
||||
{
|
||||
// valid
|
||||
http::request<http::empty_body> req;
|
||||
http::request_header req;
|
||||
req.method = "GET";
|
||||
req.url = "/";
|
||||
req.version = 11;
|
||||
|
||||
Reference in New Issue
Block a user