Files
xahaud/include/beast/http/parser_v1.hpp
Vinnie Falco d8dea963fa Squashed 'src/beast/' changes from 1b9a714..6d5547a
6d5547a Set version to 1.0.0-b34
6fab138 Fix and tidy up CMake build scripts:
ccefa54 Set version to 1.0.0-b33
32afe41 Set internal state correctly when writing frames:
fe3e20b Add write_frames unit test
578dcd0 Add decorator unit test
aaa3733 Use fwrite return value in file_body
df66165 Require Visual Studio 2015 Update 3 or later
b8e5a21 Set version to 1.0.0-b32
ffb1758 Update CMake scripts for finding packages:
b893749 Remove http Writer suspend and resume feature (API Change):
27864fb Add io_service completion invariants tests
eba05a7 Set version to 1.0.0-b31
484bcef Fix badge markdown in README.md
5663bea Add missing dynabuf_readstream member
0d7a551 Tidy up build settings
0fd4030 Move the handler, don't copy it

git-subtree-dir: src/beast
git-subtree-split: 6d5547a32c50ec95832c4779311502555ab0ee1f
2017-04-20 13:40:52 -07:00

340 lines
8.6 KiB
C++

//
// 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_PARSER_V1_HPP
#define BEAST_HTTP_PARSER_V1_HPP
#include <beast/config.hpp>
#include <beast/http/concepts.hpp>
#include <beast/http/header_parser_v1.hpp>
#include <beast/http/message.hpp>
#include <beast/core/error.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <boost/assert.hpp>
#include <boost/optional.hpp>
#include <functional>
#include <string>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
/** Skip body option.
The options controls whether or not the parser expects to see a
HTTP body, regardless of the presence or absence of certain fields
such as Content-Length.
Depending on the request, some responses do not carry a body.
For example, a 200 response to a CONNECT request from a tunneling
proxy. In these cases, callers use the @ref skip_body option to
inform the parser that no body is expected. The parser will consider
the message complete after the header has been received.
Example:
@code
parser_v1<true, empty_body, fields> p;
p.set_option(skip_body{true});
@endcode
@note Objects of this type are passed to @ref parser_v1::set_option.
*/
struct skip_body
{
bool value;
explicit
skip_body(bool v)
: value(v)
{
}
};
/** A parser for producing HTTP/1 messages.
This class uses the basic HTTP/1 wire format parser to convert
a series of octets into a `message`.
@note A new instance of the parser is required for each message.
*/
template<bool isRequest, class Body, class Fields>
class parser_v1
: public basic_parser_v1<isRequest,
parser_v1<isRequest, Body, Fields>>
, private std::conditional<isRequest,
detail::request_parser_base,
detail::response_parser_base>::type
{
public:
/// The type of message this parser produces.
using message_type =
message<isRequest, Body, Fields>;
private:
using reader =
typename message_type::body_type::reader;
static_assert(is_Body<Body>::value,
"Body requirements not met");
static_assert(has_reader<Body>::value,
"Body has no reader");
static_assert(is_Reader<reader, message_type>::value,
"Reader requirements not met");
std::string field_;
std::string value_;
message_type m_;
boost::optional<reader> r_;
std::uint8_t skip_body_ = 0;
bool flush_ = false;
public:
/// Default constructor
parser_v1() = default;
/// Move constructor
parser_v1(parser_v1&&) = default;
/// Copy constructor (disallowed)
parser_v1(parser_v1 const&) = delete;
/// Move assignment (disallowed)
parser_v1& operator=(parser_v1&&) = delete;
/// Copy assignment (disallowed)
parser_v1& operator=(parser_v1 const&) = delete;
/** Construct the parser.
@param args Forwarded to the message constructor.
@note This function participates in overload resolution only
if the first argument is not a parser or fields parser.
*/
#if GENERATING_DOCS
template<class... Args>
explicit
parser_v1(Args&&... args);
#else
template<class Arg1, class... ArgN,
class = typename std::enable_if<
! std::is_same<typename
std::decay<Arg1>::type,
header_parser_v1<isRequest, Fields>>::value &&
! std::is_same<typename
std::decay<Arg1>::type, parser_v1>::value
>::type>
explicit
parser_v1(Arg1&& arg1, ArgN&&... argn)
: m_(std::forward<Arg1>(arg1),
std::forward<ArgN>(argn)...)
{
}
#endif
/** Construct the parser from a fields parser.
@param parser The fields parser to construct from.
@param args Forwarded to the message body constructor.
*/
template<class... Args>
explicit
parser_v1(header_parser_v1<isRequest, Fields>& parser,
Args&&... args)
: m_(parser.release(), std::forward<Args>(args)...)
{
static_cast<basic_parser_v1<
isRequest, parser_v1<
isRequest, Body, Fields>>&>(*this) = parser;
}
/// Set the skip body option.
void
set_option(skip_body const& o)
{
skip_body_ = o.value ? 1 : 0;
}
/** Returns the parsed message.
Only valid if @ref complete would return `true`.
*/
message_type const&
get() const
{
return m_;
}
/** Returns the parsed message.
Only valid if @ref complete would return `true`.
*/
message_type&
get()
{
return m_;
}
/** Returns ownership of the parsed message.
Ownership is transferred to the caller. Only
valid if @ref complete would return `true`.
Requires:
`message<isRequest, Body, Fields>` is @b MoveConstructible
*/
message_type
release()
{
static_assert(std::is_move_constructible<decltype(m_)>::value,
"MoveConstructible requirements not met");
return std::move(m_);
}
private:
friend class basic_parser_v1<isRequest, parser_v1>;
void flush()
{
if(! flush_)
return;
flush_ = false;
BOOST_ASSERT(! field_.empty());
m_.fields.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)
{
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&)
{
on_request_or_response(
std::integral_constant<bool, isRequest>{});
}
void on_response(error_code&)
{
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_header(std::uint64_t, error_code&)
{
flush();
m_.version = 10 * this->http_major() + this->http_minor();
}
body_what
on_body_what(std::uint64_t, error_code& ec)
{
if(skip_body_)
return body_what::skip;
r_.emplace(m_);
r_->init(ec);
return body_what::normal;
}
void on_body(boost::string_ref const& s, error_code& ec)
{
r_->write(s.data(), s.size(), ec);
}
void on_complete(error_code&)
{
}
};
/** Create a new parser from a fields parser.
Associates a Body type with a fields parser, and returns
a new parser which parses a complete message object
containing the original message fields and a new body
of the specified body type.
This function allows HTTP messages to be parsed in two stages.
First, the fields 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 fields parser to construct from. Ownership
of the message fields in the fields parser is transferred
as if by call to @ref header_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 @b Body type.
@par Example
@code
headers_parser<true, fields> ph;
...
auto p = with_body<string_body>(ph);
...
message<true, string_body, fields> m = p.release();
@endcode
*/
template<class Body,
bool isRequest, class Fields, class... Args>
parser_v1<isRequest, Body, Fields>
with_body(header_parser_v1<isRequest, Fields>& parser,
Args&&... args)
{
return parser_v1<isRequest, Body, Fields>(
parser, std::forward<Args>(args)...);
}
} // http
} // beast
#endif