forked from boostorg/beast
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
|
* Tidy up documentation
|
||||||
* Add basic_parser_v1::reset
|
* Add basic_parser_v1::reset
|
||||||
* Fix handling of body_what::pause in basic_parser_v1
|
* Fix handling of body_what::pause in basic_parser_v1
|
||||||
|
* Add headers_parser
|
||||||
|
|
||||||
API Changes:
|
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__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__empty_body">empty_body</link></member>
|
||||||
<member><link linkend="beast.ref.http__headers">headers</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">message</link></member>
|
||||||
<member><link linkend="beast.ref.http__message_headers">message_headers</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>
|
<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__prepare">prepare</link></member>
|
||||||
<member><link linkend="beast.ref.http__read">read</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__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>
|
<member><link linkend="beast.ref.http__write">write</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
||||||
|
@@ -226,6 +226,9 @@ template<bool isRequest, class Derived>
|
|||||||
class basic_parser_v1 : public detail::parser_base
|
class basic_parser_v1 : public detail::parser_base
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
template<bool, class>
|
||||||
|
friend class basic_parser_v1;
|
||||||
|
|
||||||
using self = basic_parser_v1;
|
using self = basic_parser_v1;
|
||||||
typedef void(self::*pmf_t)(error_code&, boost::string_ref const&);
|
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
|
bool upgrade_ : 1; // true if parser exited for upgrade
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Copy constructor.
|
|
||||||
basic_parser_v1(basic_parser_v1 const&) = default;
|
|
||||||
|
|
||||||
/// Copy assignment.
|
|
||||||
basic_parser_v1& operator=(basic_parser_v1 const&) = default;
|
|
||||||
|
|
||||||
/// Default constructor
|
/// Default constructor
|
||||||
basic_parser_v1();
|
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.
|
/** Set options on the parser.
|
||||||
|
|
||||||
@param args One or more parser options to set.
|
@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();
|
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>
|
template<bool isRequest, class Derived>
|
||||||
bool
|
bool
|
||||||
basic_parser_v1<isRequest, Derived>::
|
basic_parser_v1<isRequest, Derived>::
|
||||||
@@ -942,6 +991,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
|||||||
case body_what::pause:
|
case body_what::pause:
|
||||||
return used();
|
return used();
|
||||||
}
|
}
|
||||||
|
--p;
|
||||||
s_ = s_headers_done;
|
s_ = s_headers_done;
|
||||||
// fall through
|
// fall through
|
||||||
}
|
}
|
||||||
@@ -1198,6 +1248,7 @@ void
|
|||||||
basic_parser_v1<isRequest, Derived>::
|
basic_parser_v1<isRequest, Derived>::
|
||||||
reset()
|
reset()
|
||||||
{
|
{
|
||||||
|
cb_ = nullptr;
|
||||||
h_left_ = h_max_;
|
h_left_ = h_max_;
|
||||||
b_left_ = b_max_;
|
b_left_ = b_max_;
|
||||||
reset(std::integral_constant<bool, isRequest>{});
|
reset(std::integral_constant<bool, isRequest>{});
|
||||||
|
@@ -8,8 +8,8 @@
|
|||||||
#ifndef BEAST_HTTP_PARSER_V1_HPP
|
#ifndef BEAST_HTTP_PARSER_V1_HPP
|
||||||
#define 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/concepts.hpp>
|
||||||
|
#include <beast/http/headers_parser_v1.hpp>
|
||||||
#include <beast/http/message.hpp>
|
#include <beast/http/message.hpp>
|
||||||
#include <beast/core/error.hpp>
|
#include <beast/core/error.hpp>
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
@@ -22,21 +22,6 @@
|
|||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
struct parser_request
|
|
||||||
{
|
|
||||||
std::string method_;
|
|
||||||
std::string uri_;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct parser_response
|
|
||||||
{
|
|
||||||
std::string reason_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // detail
|
|
||||||
|
|
||||||
/** Skip body option.
|
/** Skip body option.
|
||||||
|
|
||||||
The options controls whether or not the parser expects to see a
|
The options controls whether or not the parser expects to see a
|
||||||
@@ -80,7 +65,8 @@ class parser_v1
|
|||||||
: public basic_parser_v1<isRequest,
|
: public basic_parser_v1<isRequest,
|
||||||
parser_v1<isRequest, Body, Headers>>
|
parser_v1<isRequest, Body, Headers>>
|
||||||
, private std::conditional<isRequest,
|
, private std::conditional<isRequest,
|
||||||
detail::parser_request, detail::parser_response>::type
|
detail::request_parser_base,
|
||||||
|
detail::response_parser_base>::type
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// The type of message this parser produces.
|
/// The type of message this parser produces.
|
||||||
@@ -123,7 +109,7 @@ public:
|
|||||||
|
|
||||||
/** Construct the parser.
|
/** Construct the parser.
|
||||||
|
|
||||||
@param args A list of arguments forwarded to the message constructor.
|
@param args Forwarded to the message constructor.
|
||||||
*/
|
*/
|
||||||
#if GENERATING_DOCS
|
#if GENERATING_DOCS
|
||||||
template<class... Args>
|
template<class... Args>
|
||||||
@@ -141,7 +127,23 @@ public:
|
|||||||
}
|
}
|
||||||
#endif
|
#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
|
void
|
||||||
set_option(skip_body const& o)
|
set_option(skip_body const& o)
|
||||||
{
|
{
|
||||||
@@ -150,7 +152,7 @@ public:
|
|||||||
|
|
||||||
/** Returns the parsed message.
|
/** Returns the parsed message.
|
||||||
|
|
||||||
Only valid if `complete()` would return `true`.
|
Only valid if @ref complete would return `true`.
|
||||||
*/
|
*/
|
||||||
message_type const&
|
message_type const&
|
||||||
get() const
|
get() const
|
||||||
@@ -160,7 +162,7 @@ public:
|
|||||||
|
|
||||||
/** Returns the parsed message.
|
/** Returns the parsed message.
|
||||||
|
|
||||||
Only valid if `complete()` would return `true`.
|
Only valid if @ref complete would return `true`.
|
||||||
*/
|
*/
|
||||||
message_type&
|
message_type&
|
||||||
get()
|
get()
|
||||||
@@ -168,13 +170,13 @@ public:
|
|||||||
return m_;
|
return m_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the parsed message.
|
/** Returns ownership of the parsed message.
|
||||||
|
|
||||||
Ownership is transferred to the caller.
|
Ownership is transferred to the caller. Only
|
||||||
Only valid if `complete()` would return `true`.
|
valid if @ref complete` would return `true`.
|
||||||
|
|
||||||
Requires:
|
Requires:
|
||||||
`message<isRequest, Body, Headers>` is MoveConstructible
|
`message<isRequest, Body, Headers>` is @b MoveConstructible
|
||||||
*/
|
*/
|
||||||
message_type
|
message_type
|
||||||
release()
|
release()
|
||||||
@@ -217,6 +219,30 @@ private:
|
|||||||
this->reason_.append(s.data(), s.size());
|
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&)
|
void on_field(boost::string_ref const& s, error_code&)
|
||||||
{
|
{
|
||||||
flush();
|
flush();
|
||||||
@@ -229,18 +255,6 @@ private:
|
|||||||
flush_ = true;
|
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
|
void
|
||||||
on_headers(std::uint64_t, error_code& ec)
|
on_headers(std::uint64_t, error_code& ec)
|
||||||
{
|
{
|
||||||
@@ -258,18 +272,6 @@ private:
|
|||||||
return body_what::normal;
|
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)
|
void on_body(boost::string_ref const& s, error_code& ec)
|
||||||
{
|
{
|
||||||
r_->write(s.data(), s.size(), 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
|
} // http
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
|
@@ -48,6 +48,7 @@ unit-test http-tests :
|
|||||||
http/concepts.cpp
|
http/concepts.cpp
|
||||||
http/empty_body.cpp
|
http/empty_body.cpp
|
||||||
http/headers.cpp
|
http/headers.cpp
|
||||||
|
http/headers_parser_v1.cpp
|
||||||
http/message.cpp
|
http/message.cpp
|
||||||
http/parse.cpp
|
http/parse.cpp
|
||||||
http/parse_error.cpp
|
http/parse_error.cpp
|
||||||
|
@@ -17,6 +17,7 @@ add_executable (http-tests
|
|||||||
concepts.cpp
|
concepts.cpp
|
||||||
empty_body.cpp
|
empty_body.cpp
|
||||||
headers.cpp
|
headers.cpp
|
||||||
|
headers_parser_v1.cpp
|
||||||
message.cpp
|
message.cpp
|
||||||
parse.cpp
|
parse.cpp
|
||||||
parse_error.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.
|
// Test that header file is self-contained.
|
||||||
#include <beast/http/parser_v1.hpp>
|
#include <beast/http/parser_v1.hpp>
|
||||||
|
|
||||||
|
#include <beast/core/streambuf.hpp>
|
||||||
#include <beast/http/headers.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/http/string_body.hpp>
|
||||||
|
#include <beast/test/string_stream.hpp>
|
||||||
|
#include <beast/test/yield_to.hpp>
|
||||||
#include <beast/unit_test/suite.hpp>
|
#include <beast/unit_test/suite.hpp>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
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:
|
public:
|
||||||
void testRegressions()
|
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
|
void run() override
|
||||||
{
|
{
|
||||||
using boost::asio::buffer;
|
using boost::asio::buffer;
|
||||||
@@ -104,6 +140,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
testRegressions();
|
testRegressions();
|
||||||
|
testWithBody();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user