mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 21:34:46 +02:00
Add headers_parser:
This allows just the HTTP headers to be parsed, and the choice of body to be deferred to a subsequent call to parse.
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
* Tidy up documentation
|
||||
* Add basic_parser_v1::reset
|
||||
* Fix handling of body_what::pause in basic_parser_v1
|
||||
* Add headers_parser
|
||||
|
||||
API Changes:
|
||||
|
||||
|
@@ -34,6 +34,7 @@
|
||||
<member><link linkend="beast.ref.http__basic_parser_v1">basic_parser_v1</link></member>
|
||||
<member><link linkend="beast.ref.http__empty_body">empty_body</link></member>
|
||||
<member><link linkend="beast.ref.http__headers">headers</link></member>
|
||||
<member><link linkend="beast.ref.http__headers_parser">headers_parser</link></member>
|
||||
<member><link linkend="beast.ref.http__message">message</link></member>
|
||||
<member><link linkend="beast.ref.http__message_headers">message_headers</link></member>
|
||||
<member><link linkend="beast.ref.http__parser_v1">parser_v1</link></member>
|
||||
@@ -65,6 +66,7 @@
|
||||
<member><link linkend="beast.ref.http__prepare">prepare</link></member>
|
||||
<member><link linkend="beast.ref.http__read">read</link></member>
|
||||
<member><link linkend="beast.ref.http__swap">swap</link></member>
|
||||
<member><link linkend="beast.ref.http__with_body">with_body</link></member>
|
||||
<member><link linkend="beast.ref.http__write">write</link></member>
|
||||
</simplelist>
|
||||
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
||||
|
@@ -226,6 +226,9 @@ template<bool isRequest, class Derived>
|
||||
class basic_parser_v1 : public detail::parser_base
|
||||
{
|
||||
private:
|
||||
template<bool, class>
|
||||
friend class basic_parser_v1;
|
||||
|
||||
using self = basic_parser_v1;
|
||||
typedef void(self::*pmf_t)(error_code&, boost::string_ref const&);
|
||||
|
||||
@@ -284,15 +287,19 @@ private:
|
||||
bool upgrade_ : 1; // true if parser exited for upgrade
|
||||
|
||||
public:
|
||||
/// Copy constructor.
|
||||
basic_parser_v1(basic_parser_v1 const&) = default;
|
||||
|
||||
/// Copy assignment.
|
||||
basic_parser_v1& operator=(basic_parser_v1 const&) = default;
|
||||
|
||||
/// Default constructor
|
||||
basic_parser_v1();
|
||||
|
||||
/// Copy constructor.
|
||||
template<class OtherDerived>
|
||||
basic_parser_v1(basic_parser_v1<
|
||||
isRequest, OtherDerived> const& other);
|
||||
|
||||
/// Copy assignment.
|
||||
template<class OtherDerived>
|
||||
basic_parser_v1& operator=(basic_parser_v1<
|
||||
isRequest, OtherDerived> const& other);
|
||||
|
||||
/** Set options on the parser.
|
||||
|
||||
@param args One or more parser options to set.
|
||||
|
234
include/beast/http/headers_parser_v1.hpp
Normal file
234
include/beast/http/headers_parser_v1.hpp
Normal file
@@ -0,0 +1,234 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 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_HEADERS_PARSER_V1_HPP
|
||||
#define BEAST_HTTP_HEADERS_PARSER_V1_HPP
|
||||
|
||||
#include <beast/http/basic_parser_v1.hpp>
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct request_parser_base
|
||||
{
|
||||
std::string method_;
|
||||
std::string uri_;
|
||||
};
|
||||
|
||||
struct response_parser_base
|
||||
{
|
||||
std::string reason_;
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
/** A parser for HTTP/1 request and response headers.
|
||||
|
||||
This class uses the HTTP/1 wire format parser to
|
||||
convert a series of octets into a @ref request_headers
|
||||
or @ref @response_headers.
|
||||
|
||||
@note A new instance of the parser is required for each message.
|
||||
*/
|
||||
template<bool isRequest, class Headers>
|
||||
class headers_parser_v1
|
||||
: public basic_parser_v1<isRequest,
|
||||
headers_parser_v1<isRequest, Headers>>
|
||||
, private std::conditional<isRequest,
|
||||
detail::request_parser_base,
|
||||
detail::response_parser_base>::type
|
||||
{
|
||||
public:
|
||||
/// The type of message this parser produces.
|
||||
using headers_type =
|
||||
message_headers<isRequest, Headers>;
|
||||
|
||||
private:
|
||||
// VFALCO Check Headers requirements?
|
||||
|
||||
std::string field_;
|
||||
std::string value_;
|
||||
headers_type h_;
|
||||
bool flush_ = false;
|
||||
|
||||
public:
|
||||
/// Default constructor
|
||||
headers_parser_v1() = default;
|
||||
|
||||
/// Move constructor
|
||||
headers_parser_v1(headers_parser_v1&&) = default;
|
||||
|
||||
/// Copy constructor (disallowed)
|
||||
headers_parser_v1(headers_parser_v1 const&) = delete;
|
||||
|
||||
/// Move assignment (disallowed)
|
||||
headers_parser_v1& operator=(headers_parser_v1&&) = delete;
|
||||
|
||||
/// Copy assignment (disallowed)
|
||||
headers_parser_v1& operator=(headers_parser_v1 const&) = delete;
|
||||
|
||||
/** Construct the parser.
|
||||
|
||||
@param args Forwarded to the message headers constructor.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class... Args>
|
||||
explicit
|
||||
headers_parser_v1(Args&&... args);
|
||||
#else
|
||||
template<class Arg1, class... ArgN,
|
||||
class = typename std::enable_if<! std::is_same<
|
||||
typename std::decay<Arg1>::type, headers_parser_v1>::value>>
|
||||
explicit
|
||||
headers_parser_v1(Arg1&& arg1, ArgN&&... argn)
|
||||
: h_(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Returns the parsed headers.
|
||||
|
||||
Only valid if @ref complete would return `true`.
|
||||
*/
|
||||
headers_type const&
|
||||
get() const
|
||||
{
|
||||
return h_;
|
||||
}
|
||||
|
||||
/** Returns the parsed headers.
|
||||
|
||||
Only valid if @ref complete would return `true`.
|
||||
*/
|
||||
headers_type&
|
||||
get()
|
||||
{
|
||||
return h_;
|
||||
}
|
||||
|
||||
/** Returns ownership of the parsed headers.
|
||||
|
||||
Ownership is transferred to the caller. Only
|
||||
valid if @ref complete would return `true`.
|
||||
|
||||
Requires:
|
||||
`message_headers<isRequest, Headers>` is @b MoveConstructible
|
||||
*/
|
||||
headers_type
|
||||
release()
|
||||
{
|
||||
static_assert(std::is_move_constructible<decltype(h_)>::value,
|
||||
"MoveConstructible requirements not met");
|
||||
return std::move(h_);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class basic_parser_v1<isRequest, headers_parser_v1>;
|
||||
|
||||
void flush()
|
||||
{
|
||||
if(! flush_)
|
||||
return;
|
||||
flush_ = false;
|
||||
BOOST_ASSERT(! field_.empty());
|
||||
h_.headers.insert(field_, value_);
|
||||
field_.clear();
|
||||
value_.clear();
|
||||
}
|
||||
|
||||
void on_start(error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void on_method(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->method_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_uri(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->uri_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_reason(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->reason_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_request_or_response(std::true_type)
|
||||
{
|
||||
h_.method = std::move(this->method_);
|
||||
h_.url = std::move(this->uri_);
|
||||
}
|
||||
|
||||
void on_request_or_response(std::false_type)
|
||||
{
|
||||
h_.status = this->status_code();
|
||||
h_.reason = std::move(this->reason_);
|
||||
}
|
||||
|
||||
void on_request(error_code& ec)
|
||||
{
|
||||
on_request_or_response(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_response(error_code& ec)
|
||||
{
|
||||
on_request_or_response(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_field(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
flush();
|
||||
field_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_value(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
value_.append(s.data(), s.size());
|
||||
flush_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
on_headers(std::uint64_t, error_code&)
|
||||
{
|
||||
flush();
|
||||
h_.version = 10 * this->http_major() + this->http_minor();
|
||||
}
|
||||
|
||||
body_what
|
||||
on_body_what(std::uint64_t, error_code&)
|
||||
{
|
||||
return body_what::pause;
|
||||
}
|
||||
|
||||
void on_body(boost::string_ref const&, error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void on_complete(error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
@@ -50,6 +50,55 @@ basic_parser_v1()
|
||||
init();
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
template<class OtherDerived>
|
||||
basic_parser_v1<isRequest, Derived>::
|
||||
basic_parser_v1(basic_parser_v1<
|
||||
isRequest, OtherDerived> const& other)
|
||||
: h_max_(other.h_max_)
|
||||
, h_left_(other.h_left_)
|
||||
, b_max_(other.b_max_)
|
||||
, b_left_(other.b_left_)
|
||||
, content_length_(other.content_length_)
|
||||
, cb_(nullptr)
|
||||
, s_(other.s_)
|
||||
, flags_(other.flags_)
|
||||
, fs_(other.fs_)
|
||||
, pos_(other.pos_)
|
||||
, http_major_(other.http_major_)
|
||||
, http_minor_(other.http_minor_)
|
||||
, status_code_(other.status_code_)
|
||||
, upgrade_(other.upgrade_)
|
||||
{
|
||||
BOOST_ASSERT(! other.cb_);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
template<class OtherDerived>
|
||||
auto
|
||||
basic_parser_v1<isRequest, Derived>::
|
||||
operator=(basic_parser_v1<
|
||||
isRequest, OtherDerived> const& other) ->
|
||||
basic_parser_v1&
|
||||
{
|
||||
BOOST_ASSERT(! other.cb_);
|
||||
h_max_ = other.h_max_;
|
||||
h_left_ = other.h_left_;
|
||||
b_max_ = other.b_max_;
|
||||
b_left_ = other.b_left_;
|
||||
content_length_ = other.content_length_;
|
||||
cb_ = nullptr;
|
||||
s_ = other.s_;
|
||||
flags_ = other.flags_;
|
||||
fs_ = other.fs_;
|
||||
pos_ = other.pos_;
|
||||
http_major_ = other.http_major_;
|
||||
http_minor_ = other.http_minor_;
|
||||
status_code_ = other.status_code_;
|
||||
upgrade_ = other.upgrade_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
bool
|
||||
basic_parser_v1<isRequest, Derived>::
|
||||
@@ -942,6 +991,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
case body_what::pause:
|
||||
return used();
|
||||
}
|
||||
--p;
|
||||
s_ = s_headers_done;
|
||||
// fall through
|
||||
}
|
||||
@@ -1198,6 +1248,7 @@ void
|
||||
basic_parser_v1<isRequest, Derived>::
|
||||
reset()
|
||||
{
|
||||
cb_ = nullptr;
|
||||
h_left_ = h_max_;
|
||||
b_left_ = b_max_;
|
||||
reset(std::integral_constant<bool, isRequest>{});
|
||||
|
@@ -8,8 +8,8 @@
|
||||
#ifndef BEAST_HTTP_PARSER_V1_HPP
|
||||
#define BEAST_HTTP_PARSER_V1_HPP
|
||||
|
||||
#include <beast/http/basic_parser_v1.hpp>
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/http/headers_parser_v1.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
@@ -22,21 +22,6 @@
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct parser_request
|
||||
{
|
||||
std::string method_;
|
||||
std::string uri_;
|
||||
};
|
||||
|
||||
struct parser_response
|
||||
{
|
||||
std::string reason_;
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
/** Skip body option.
|
||||
|
||||
The options controls whether or not the parser expects to see a
|
||||
@@ -80,7 +65,8 @@ class parser_v1
|
||||
: public basic_parser_v1<isRequest,
|
||||
parser_v1<isRequest, Body, Headers>>
|
||||
, private std::conditional<isRequest,
|
||||
detail::parser_request, detail::parser_response>::type
|
||||
detail::request_parser_base,
|
||||
detail::response_parser_base>::type
|
||||
{
|
||||
public:
|
||||
/// The type of message this parser produces.
|
||||
@@ -123,7 +109,7 @@ public:
|
||||
|
||||
/** Construct the parser.
|
||||
|
||||
@param args A list of arguments forwarded to the message constructor.
|
||||
@param args Forwarded to the message constructor.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class... Args>
|
||||
@@ -141,7 +127,23 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Set the expect body option.
|
||||
/** Construct the parser from a headers parser.
|
||||
|
||||
@param parser The headers parser to construct from.
|
||||
@param args Forwarded to the message body constructor.
|
||||
*/
|
||||
template<class... Args>
|
||||
explicit
|
||||
parser_v1(headers_parser_v1<isRequest, Headers>&& parser,
|
||||
Args&&... args)
|
||||
: m_(parser.release(), std::forward<Args>(args)...)
|
||||
{
|
||||
static_cast<basic_parser_v1<
|
||||
isRequest, parser_v1<
|
||||
isRequest, Body, Headers>>&>(*this) = parser;
|
||||
}
|
||||
|
||||
/// Set the skip body option.
|
||||
void
|
||||
set_option(skip_body const& o)
|
||||
{
|
||||
@@ -150,7 +152,7 @@ public:
|
||||
|
||||
/** Returns the parsed message.
|
||||
|
||||
Only valid if `complete()` would return `true`.
|
||||
Only valid if @ref complete would return `true`.
|
||||
*/
|
||||
message_type const&
|
||||
get() const
|
||||
@@ -160,7 +162,7 @@ public:
|
||||
|
||||
/** Returns the parsed message.
|
||||
|
||||
Only valid if `complete()` would return `true`.
|
||||
Only valid if @ref complete would return `true`.
|
||||
*/
|
||||
message_type&
|
||||
get()
|
||||
@@ -168,13 +170,13 @@ public:
|
||||
return m_;
|
||||
}
|
||||
|
||||
/** Returns the parsed message.
|
||||
/** Returns ownership of the parsed message.
|
||||
|
||||
Ownership is transferred to the caller.
|
||||
Only valid if `complete()` would return `true`.
|
||||
Ownership is transferred to the caller. Only
|
||||
valid if @ref complete` would return `true`.
|
||||
|
||||
Requires:
|
||||
`message<isRequest, Body, Headers>` is MoveConstructible
|
||||
`message<isRequest, Body, Headers>` is @b MoveConstructible
|
||||
*/
|
||||
message_type
|
||||
release()
|
||||
@@ -217,6 +219,30 @@ private:
|
||||
this->reason_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_request_or_response(std::true_type)
|
||||
{
|
||||
m_.method = std::move(this->method_);
|
||||
m_.url = std::move(this->uri_);
|
||||
}
|
||||
|
||||
void on_request_or_response(std::false_type)
|
||||
{
|
||||
m_.status = this->status_code();
|
||||
m_.reason = std::move(this->reason_);
|
||||
}
|
||||
|
||||
void on_request(error_code& ec)
|
||||
{
|
||||
on_request_or_response(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_response(error_code& ec)
|
||||
{
|
||||
on_request_or_response(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_field(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
flush();
|
||||
@@ -229,18 +255,6 @@ private:
|
||||
flush_ = true;
|
||||
}
|
||||
|
||||
void set(std::true_type)
|
||||
{
|
||||
m_.method = std::move(this->method_);
|
||||
m_.url = std::move(this->uri_);
|
||||
}
|
||||
|
||||
void set(std::false_type)
|
||||
{
|
||||
m_.status = this->status_code();
|
||||
m_.reason = std::move(this->reason_);
|
||||
}
|
||||
|
||||
void
|
||||
on_headers(std::uint64_t, error_code& ec)
|
||||
{
|
||||
@@ -258,18 +272,6 @@ private:
|
||||
return body_what::normal;
|
||||
}
|
||||
|
||||
void on_request(error_code& ec)
|
||||
{
|
||||
set(std::integral_constant<
|
||||
bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_response(error_code& ec)
|
||||
{
|
||||
set(std::integral_constant<
|
||||
bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_body(boost::string_ref const& s, error_code& ec)
|
||||
{
|
||||
r_->write(s.data(), s.size(), ec);
|
||||
@@ -280,6 +282,46 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
/** Create a new parser from a headers parser.
|
||||
|
||||
Associates a Body type with a headers parser, and returns
|
||||
a new parser which parses a complete message object
|
||||
containing the original message headers and a new body
|
||||
of the specified body type.
|
||||
|
||||
This function allows HTTP messages to be parsed in two stages.
|
||||
First, the headers are parsed and control is returned. Then,
|
||||
the caller can choose at run-time, the type of Body to
|
||||
associate with the message. And finally, complete the parse
|
||||
in a second call.
|
||||
|
||||
@param parser The headers parser to construct from. Ownership
|
||||
of the message headers in the headers parser is transferred
|
||||
as if by call to @ref headers_parser_v1::release.
|
||||
|
||||
@param args Forwarded to the body constructor of the message
|
||||
in the new parser.
|
||||
|
||||
@return A parser for a message with the specified @ref Body type.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
headers_parser<true, headers> ph;
|
||||
...
|
||||
auto p = with_body<string_body>(std::move(ph));
|
||||
...
|
||||
message<true, string_body, headers> m = p.release();
|
||||
@endcode
|
||||
*/
|
||||
template<class Body, bool isRequest, class Headers, class... Args>
|
||||
parser_v1<isRequest, Body, Headers>
|
||||
with_body(headers_parser_v1<isRequest, Headers>& parser,
|
||||
Args&&... args)
|
||||
{
|
||||
return parser_v1<isRequest, Body, Headers>(
|
||||
std::move(parser), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
|
@@ -48,6 +48,7 @@ unit-test http-tests :
|
||||
http/concepts.cpp
|
||||
http/empty_body.cpp
|
||||
http/headers.cpp
|
||||
http/headers_parser_v1.cpp
|
||||
http/message.cpp
|
||||
http/parse.cpp
|
||||
http/parse_error.cpp
|
||||
|
@@ -17,6 +17,7 @@ add_executable (http-tests
|
||||
concepts.cpp
|
||||
empty_body.cpp
|
||||
headers.cpp
|
||||
headers_parser_v1.cpp
|
||||
message.cpp
|
||||
parse.cpp
|
||||
parse_error.cpp
|
||||
|
90
test/http/headers_parser_v1.cpp
Normal file
90
test/http/headers_parser_v1.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 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/headers_parser_v1.hpp>
|
||||
|
||||
#include <beast/http/headers.hpp>
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
class headers_parser_v1_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void testParser()
|
||||
{
|
||||
{
|
||||
error_code ec;
|
||||
headers_parser_v1<true, headers> 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;
|
||||
headers_parser_v1<true, headers> 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;
|
||||
headers_parser_v1<false, headers> 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;
|
||||
headers_parser_v1<false, headers> 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(headers_parser_v1,http,beast);
|
||||
|
||||
} // http
|
||||
} // beast
|
@@ -8,14 +8,21 @@
|
||||
// Test that header file is self-contained.
|
||||
#include <beast/http/parser_v1.hpp>
|
||||
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/http/headers.hpp>
|
||||
#include <beast/http/headers_parser_v1.hpp>
|
||||
#include <beast/http/parse.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/test/string_stream.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
|
||||
class parser_v1_test
|
||||
: public beast::unit_test::suite
|
||||
, public test::enable_yield_to
|
||||
{
|
||||
public:
|
||||
void testRegressions()
|
||||
@@ -46,6 +53,35 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void testWithBody()
|
||||
{
|
||||
test::string_stream ss{ios_,
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"User-Agent: test\r\n"
|
||||
"Content-Length: 1\r\n"
|
||||
"\r\n"
|
||||
"*"};
|
||||
streambuf rb;
|
||||
headers_parser_v1<true, headers> p0;
|
||||
parse(ss, rb, p0);
|
||||
request_headers<headers> const& reqh = p0.get();
|
||||
BEAST_EXPECT(reqh.method == "GET");
|
||||
BEAST_EXPECT(reqh.url == "/");
|
||||
BEAST_EXPECT(reqh.version == 11);
|
||||
BEAST_EXPECT(reqh.headers["User-Agent"] == "test");
|
||||
BEAST_EXPECT(reqh.headers["Content-Length"] == "1");
|
||||
parser_v1<true, string_body, headers> 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().headers["User-Agent"] == "test");
|
||||
BEAST_EXPECT(p.get().headers["Content-Length"] == "1");
|
||||
parse(ss, rb, p);
|
||||
request<string_body, headers> req = p.release();
|
||||
BEAST_EXPECT(req.body == "*");
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
@@ -104,6 +140,7 @@ public:
|
||||
}
|
||||
|
||||
testRegressions();
|
||||
testWithBody();
|
||||
}
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user