Files
boost_beast/include/beast/http/basic_parser.hpp
T

765 lines
20 KiB
C++
Raw Normal View History

2016-04-19 20:29:16 -04:00
//
// 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)
//
2014-08-26 09:25:19 -07:00
2016-04-19 20:29:16 -04:00
#ifndef BEAST_HTTP_BASIC_PARSER_HPP
#define BEAST_HTTP_BASIC_PARSER_HPP
2014-08-26 09:25:19 -07:00
2016-04-25 05:27:34 -04:00
#include <beast/http/message.hpp>
#include <beast/http/parse_error.hpp>
#include <beast/http/rfc7230.hpp>
#include <beast/http/detail/basic_parser.hpp>
2016-04-19 20:29:16 -04:00
#include <beast/type_check.hpp>
#include <boost/asio/buffer.hpp>
2016-04-25 05:27:34 -04:00
#include <array>
#include <cassert>
#include <climits>
2014-08-26 09:25:19 -07:00
#include <cstdint>
2016-04-19 20:29:16 -04:00
#include <type_traits>
2014-08-26 09:25:19 -07:00
namespace beast {
namespace http {
2016-04-25 05:27:34 -04:00
namespace parse_flag {
enum values
{
chunked = 1 << 0,
connection_keep_alive = 1 << 1,
connection_close = 1 << 2,
connection_upgrade = 1 << 3,
trailing = 1 << 4,
upgrade = 1 << 5,
skipbody = 1 << 6,
contentlength = 1 << 7
};
} // parse_flag
2016-03-11 06:40:37 -05:00
/** Parser for producing HTTP requests and responses.
2016-04-25 05:27:34 -04:00
During parsing, callbacks will be made to the derived class
if those members are present (detected through SFINAE). The
signatures which can be present in the derived class are:<br>
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
@li `void on_method(boost::string_ref const&, error_code& ec)`
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
Called for each piece of the Request-Method
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
@li `void on_uri(boost::string_ref const&, error_code& ec)`
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
Called for each piece of the Request-URI
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
@li `void on_reason(boost::string_ref const&, error_code& ec)`
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
Called for each piece of the reason-phrase
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
@li `void on_request(error_code& ec)`
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
Called after the entire Request-Line has been parsed successfully.
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
@li `void on_response(error_code& ec)`
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
Called after the entire Response-Line has been parsed successfully.
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
@li `void on_field(boost::string_ref const&, error_code& ec)`
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
Called for each piece of the current header field.
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
@li `void on_value(boost::string_ref const&, error_code& ec)`
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
Called for each piece of the current header value.
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
@li `int on_headers(error_code& ec)`
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
Called when all the headers have been parsed successfully.
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
@li `void on_body(boost::string_ref const&, error_code& ec)`
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
Called for each piece of the body. If the headers indicated
chunked encoding, the chunk encoding is removed from the
buffer before being passed to the callback.
@li `void on_complete(error_code& ec)`
Called when the entire message has been parsed successfully.
At this point, basic_parser::complete() returns `true`, and
the parser is ready to parse another message if keep_alive()
would return `true`.
The return value of `on_headers` is special, it controls whether
or not the parser should expect a body. These are the return values:
@li *0* The parser should expect a body
@li *1* The parser should skip the body. For example, this is
used when sending a response to a HEAD request.
@li *2* The parser should skip ths body, this is an
upgrade to a different protocol.
2016-03-11 06:40:37 -05:00
The parser uses traits to determine if the callback is possible.
2016-04-25 05:27:34 -04:00
If the Derived type omits one or more callbacks, they are simply
skipped with no compilation error. The default behavior of on_body
when the derived class does not provide the member, is to specify that
the body should not be skipped.
If a callback sets an error, parsing stops at the current octet
and the error is returned to the caller.
2016-03-11 06:40:37 -05:00
*/
2016-04-25 05:27:34 -04:00
template<bool isRequest, class Derived>
2014-08-26 09:25:19 -07:00
class basic_parser
{
2016-04-25 05:27:34 -04:00
private:
using self = basic_parser;
typedef void(self::*pmf_t)(error_code&, boost::string_ref const&);
static std::uint64_t constexpr no_content_length =
std::numeric_limits<std::uint64_t>::max();
enum state : std::uint8_t
{
s_closed = 1,
s_req_start,
s_req_method_start,
s_req_method,
s_req_space_before_url,
s_req_url_start,
s_req_url,
s_req_http_start,
s_req_http_H,
s_req_http_HT,
s_req_http_HTT,
s_req_http_HTTP,
s_req_major_start,
s_req_major,
s_req_minor_start,
s_req_minor,
s_req_line_end,
s_res_start,
s_res_H,
s_res_HT,
s_res_HTT,
s_res_HTTP,
s_res_major_start,
s_res_major,
s_res_minor_start,
s_res_minor,
s_res_status_code_start,
s_res_status_code,
s_res_status_start,
s_res_status,
s_res_line_almost_done,
s_res_line_done,
s_header_field_start,
s_header_field,
s_header_value_start,
s_header_value_discard_lWs0,
s_header_value_discard_ws0,
s_header_value_almost_done0,
s_header_value_text_start,
s_header_value_discard_lWs,
s_header_value_discard_ws,
s_header_value_text,
s_header_value_almost_done,
s_headers_almost_done,
s_headers_done,
s_chunk_size_start,
s_chunk_size,
s_chunk_parameters,
s_chunk_size_almost_done,
// states below do not count towards
// the limit on the size of the message
s_body_identity0,
s_body_identity,
s_body_identity_eof0,
s_body_identity_eof,
s_chunk_data_start,
s_chunk_data,
s_chunk_data_almost_done,
s_chunk_data_done,
s_complete,
s_restart
};
enum field_state : std::uint8_t
{
h_general = 0,
h_C,
h_CO,
h_CON,
h_matching_connection,
h_matching_proxy_connection,
h_matching_content_length,
h_matching_transfer_encoding,
h_matching_upgrade,
h_connection,
h_content_length,
h_transfer_encoding,
h_upgrade,
h_matching_transfer_encoding_chunked,
h_matching_connection_token_start,
h_matching_connection_keep_alive,
h_matching_connection_close,
h_matching_connection_upgrade,
h_matching_connection_token,
h_transfer_encoding_chunked,
h_connection_keep_alive,
h_connection_close,
h_connection_upgrade,
};
std::uint64_t content_length_;
std::uint64_t nread_;
pmf_t cb_;
state s_ : 8;
unsigned flags_ : 8;
unsigned fs_ : 8;
unsigned pos_ : 8; // position in field state
unsigned http_major_ : 16;
unsigned http_minor_ : 16;
unsigned status_code_ : 16;
bool upgrade_ : 1; // true if parser exited for upgrade
2014-08-26 09:25:19 -07:00
public:
2016-04-25 05:27:34 -04:00
/// Copy constructor.
basic_parser(basic_parser const&) = default;
2014-08-26 09:25:19 -07:00
2016-04-25 05:27:34 -04:00
/// Copy assignment.
basic_parser& operator=(basic_parser const&) = default;
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
/// Constructor
basic_parser()
{
init(std::integral_constant<bool, isRequest>{});
}
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
/// Returns internal flags associated with the parser.
unsigned
flags() const
{
return flags_;
}
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
/** Returns `true` if the message end is indicated by eof.
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
This function returns true if the semantics of the message require
that the end of the message is signaled by an end of file. For
example, if the message is a HTTP/1.0 message and the Content-Length
is unspecified, the end of the message is indicated by an end of file.
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
@return `true` if write_eof must be used to indicate the message end.
2014-08-26 09:25:19 -07:00
*/
bool
2016-04-25 05:27:34 -04:00
needs_eof() const
2014-08-26 09:25:19 -07:00
{
2016-04-25 05:27:34 -04:00
return needs_eof(
std::integral_constant<bool, isRequest>{});
}
/** Returns the major HTTP version number.
Examples:
* Returns 1 for HTTP/1.1
* Returns 1 for HTTP/1.0
@return The HTTP major version number.
*/
unsigned
http_major() const
{
return http_major_;
}
/** Returns the minor HTTP version number.
Examples:
* Returns 1 for HTTP/1.1
* Returns 0 for HTTP/1.0
@return The HTTP minor version number.
*/
unsigned
http_minor() const
{
return http_minor_;
}
/** Returns `true` if the message is an upgrade message.
A value of `true` indicates that the parser has successfully
completed parsing a HTTP upgrade message.
@return `true` if the message is an upgrade message.
*/
bool
upgrade() const
{
return upgrade_;
}
/** Returns the numeric HTTP Status-Code of a response.
@return The Status-Code.
*/
unsigned
status_code() const
{
return status_code_;
}
/** Returns `true` if the connection should be kept open.
@note This function is only valid to call when the parser
is complete.
*/
bool
keep_alive() const;
/** Returns `true` if the parse has completed succesfully.
When the parse has completed successfully, and the semantics
of the parsed message indicate that the connection is still
active, a subsequent call to `write` will begin parsing a
new message.
@return `true` If the parsing has completed successfully.
*/
bool
complete() const
{
return s_ == s_restart;
2014-08-26 09:25:19 -07:00
}
/** Write a sequence of buffers to the parser.
2014-08-26 09:25:19 -07:00
2016-04-25 05:27:34 -04:00
@param buffers An object meeting the requirements of
ConstBufferSequence that represents the input sequence.
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
@param ec Set to the error, if any error occurred.
2016-03-11 06:40:37 -05:00
@return The number of bytes consumed in the input sequence.
2014-08-26 09:25:19 -07:00
*/
template<class ConstBufferSequence,
class = typename std::enable_if<
! std::is_convertible<ConstBufferSequence,
boost::asio::const_buffer>::value>::type
>
2016-03-11 06:40:37 -05:00
std::size_t
2016-04-25 05:27:34 -04:00
write(ConstBufferSequence const& buffers, error_code& ec);
2016-03-11 06:40:37 -05:00
/** Write a single buffer of data to the parser.
2016-03-11 06:40:37 -05:00
@param buffer The buffer to write.
2016-03-11 06:40:37 -05:00
@param ec Set to the error, if any error occurred.
@return The number of bytes consumed in the buffer.
2016-03-11 06:40:37 -05:00
*/
std::size_t
write(boost::asio::const_buffer const& buffer, error_code& ec);
2014-08-26 09:25:19 -07:00
/** Called to indicate the end of file.
2016-03-11 06:40:37 -05:00
2014-08-26 09:25:19 -07:00
HTTP needs to know where the end of the stream is. For example,
sometimes servers send responses without Content-Length and
expect the client to consume input (for the body) until EOF.
Callbacks and errors will still be processed as usual.
2016-03-11 06:40:37 -05:00
2014-08-26 09:25:19 -07:00
@note This is typically called when a socket read returns eof.
2016-03-11 06:40:37 -05:00
@throws boost::system::system_error Thrown on failure.
2014-08-26 09:25:19 -07:00
*/
void
2016-03-11 06:40:37 -05:00
write_eof(error_code& ec);
2014-08-26 09:25:19 -07:00
private:
2016-03-11 06:40:37 -05:00
Derived&
impl()
{
return *static_cast<Derived*>(this);
}
2014-08-26 09:25:19 -07:00
2016-03-11 06:40:37 -05:00
void
2016-04-25 05:27:34 -04:00
init(std::true_type)
2016-03-11 06:40:37 -05:00
{
2016-04-25 05:27:34 -04:00
s_ = s_req_start;
2016-03-11 06:40:37 -05:00
}
void
2016-04-25 05:27:34 -04:00
init(std::false_type)
2016-03-11 06:40:37 -05:00
{
2016-04-25 05:27:34 -04:00
s_ = s_res_start;
2016-03-11 06:40:37 -05:00
}
bool
2016-04-25 05:27:34 -04:00
needs_eof(std::true_type) const;
bool
needs_eof(std::false_type) const;
2016-04-27 07:02:51 -04:00
template<class C>
class has_on_method_t
{
template<class T, class R =
decltype(std::declval<T>().on_method(
std::declval<boost::string_ref const&>(),
std::declval<error_code&>()),
std::true_type{})>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_method =
std::integral_constant<bool, has_on_method_t<C>::value>;
template<class C>
class has_on_uri_t
{
template<class T, class R =
decltype(std::declval<T>().on_uri(
std::declval<boost::string_ref const&>(),
std::declval<error_code&>()),
std::true_type{})>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_uri =
std::integral_constant<bool, has_on_uri_t<C>::value>;
template<class C>
class has_on_reason_t
{
template<class T, class R =
decltype(std::declval<T>().on_reason(
std::declval<boost::string_ref const&>(),
std::declval<error_code&>()),
std::true_type{})>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_reason =
std::integral_constant<bool, has_on_reason_t<C>::value>;
template<class C>
class has_on_request_t
{
template<class T, class R =
decltype(std::declval<T>().on_request(
std::declval<error_code&>()),
std::true_type{})>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_request =
std::integral_constant<bool, has_on_request_t<C>::value>;
template<class C>
class has_on_response_t
{
template<class T, class R =
decltype(std::declval<T>().on_response(
std::declval<error_code&>()),
std::true_type{})>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_response =
std::integral_constant<bool, has_on_response_t<C>::value>;
template<class C>
class has_on_field_t
{
template<class T, class R =
decltype(std::declval<T>().on_uri(
std::declval<boost::string_ref const&>(),
std::declval<error_code&>()),
std::true_type{})>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_field =
std::integral_constant<bool, has_on_field_t<C>::value>;
template<class C>
class has_on_value_t
{
template<class T, class R =
decltype(std::declval<T>().on_uri(
std::declval<boost::string_ref const&>(),
std::declval<error_code&>()),
std::true_type{})>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_value =
std::integral_constant<bool, has_on_value_t<C>::value>;
template<class C>
class has_on_headers_t
{
template<class T, class R = std::is_same<int,
decltype(std::declval<T>().on_headers(
std::declval<error_code&>()))>>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_headers =
std::integral_constant<bool, has_on_headers_t<C>::value>;
template<class C>
class has_on_body_t
{
template<class T, class R =
decltype(std::declval<T>().on_body(
std::declval<boost::string_ref const&>(),
std::declval<error_code&>()),
std::true_type{})>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_body =
std::integral_constant<bool, has_on_body_t<C>::value>;
template<class C>
class has_on_complete_t
{
template<class T, class R =
decltype(std::declval<T>().on_complete(
std::declval<error_code&>()),
std::true_type{})>
static R check(int);
template <class>
static std::false_type check(...);
using type = decltype(check<C>(0));
public:
static bool const value = type::value;
};
template<class C>
using has_on_complete =
std::integral_constant<bool, has_on_complete_t<C>::value>;
2016-04-25 05:27:34 -04:00
void call_on_method(error_code& ec,
boost::string_ref const& s, std::true_type)
2016-03-11 06:40:37 -05:00
{
2016-04-25 05:27:34 -04:00
impl().on_method(s, ec);
2016-03-11 06:40:37 -05:00
}
2016-04-25 05:27:34 -04:00
void call_on_method(error_code&,
boost::string_ref const&, std::false_type)
2016-03-11 06:40:37 -05:00
{
}
2016-04-25 05:27:34 -04:00
void call_on_method(error_code& ec,
boost::string_ref const& s)
2016-03-11 06:40:37 -05:00
{
2016-04-25 05:27:34 -04:00
call_on_method(ec, s, std::integral_constant<bool,
2016-04-27 07:02:51 -04:00
isRequest && has_on_method<Derived>::value>{});
2016-03-11 06:40:37 -05:00
}
2016-04-25 05:27:34 -04:00
void call_on_uri(error_code& ec,
boost::string_ref const& s, std::true_type)
{
impl().on_uri(s, ec);
}
void call_on_uri(error_code&,
boost::string_ref const&, std::false_type)
2016-03-11 06:40:37 -05:00
{
}
2016-04-25 05:27:34 -04:00
void call_on_uri(error_code& ec, boost::string_ref const& s)
2016-03-11 06:40:37 -05:00
{
2016-04-25 05:27:34 -04:00
call_on_uri(ec, s, std::integral_constant<bool,
2016-04-27 07:02:51 -04:00
isRequest && has_on_uri<Derived>::value>{});
2016-04-25 05:27:34 -04:00
}
2016-03-11 06:40:37 -05:00
2016-04-25 05:27:34 -04:00
void call_on_reason(error_code& ec,
boost::string_ref const& s, std::true_type)
{
impl().on_reason(s, ec);
}
void call_on_reason(error_code&,
boost::string_ref const&, std::false_type)
{
}
void call_on_reason(error_code& ec, boost::string_ref const& s)
{
call_on_reason(ec, s, std::integral_constant<bool,
2016-04-27 07:02:51 -04:00
! isRequest && has_on_reason<Derived>::value>{});
2016-04-25 05:27:34 -04:00
}
void call_on_request(error_code& ec, std::true_type)
{
impl().on_request(ec);
}
void call_on_request(error_code&, std::false_type)
{
}
void call_on_request(error_code& ec)
{
2016-04-27 07:02:51 -04:00
call_on_request(ec, std::integral_constant<bool,
isRequest && has_on_request<Derived>::value>{});
2016-04-25 05:27:34 -04:00
}
void call_on_response(error_code& ec, std::true_type)
{
impl().on_response(ec);
}
void call_on_response(error_code&, std::false_type)
{
}
void call_on_response(error_code& ec)
{
2016-04-27 07:02:51 -04:00
call_on_response(ec, std::integral_constant<bool,
! isRequest && has_on_response<Derived>::value>{});
2016-04-25 05:27:34 -04:00
}
void call_on_field(error_code& ec,
boost::string_ref const& s, std::true_type)
{
impl().on_field(s, ec);
}
void call_on_field(error_code&,
boost::string_ref const&, std::false_type)
{
}
void call_on_field(error_code& ec, boost::string_ref const& s)
{
2016-04-27 07:02:51 -04:00
call_on_field(ec, s, has_on_field<Derived>{});
2016-04-25 05:27:34 -04:00
}
void call_on_value(error_code& ec,
boost::string_ref const& s, std::true_type)
{
impl().on_value(s, ec);
}
void call_on_value(error_code&,
boost::string_ref const&, std::false_type)
{
}
void call_on_value(error_code& ec, boost::string_ref const& s)
{
2016-04-27 07:02:51 -04:00
call_on_value(ec, s, has_on_value<Derived>{});
2016-04-25 05:27:34 -04:00
}
int call_on_headers(error_code& ec, std::true_type)
{
return impl().on_headers(ec);
}
int call_on_headers(error_code& ec, std::false_type)
{
return 0;
}
int call_on_headers(error_code& ec)
{
2016-04-27 07:02:51 -04:00
return call_on_headers(ec, has_on_headers<Derived>{});
2016-04-25 05:27:34 -04:00
}
void call_on_body(error_code& ec,
boost::string_ref const& s, std::true_type)
{
impl().on_body(s, ec);
}
void call_on_body(error_code&,
boost::string_ref const&, std::false_type)
{
}
void call_on_body(error_code& ec, boost::string_ref const& s)
{
2016-04-27 07:02:51 -04:00
call_on_body(ec, s, has_on_body<Derived>{});
2016-04-25 05:27:34 -04:00
}
void call_on_complete(error_code& ec, std::true_type)
{
impl().on_complete(ec);
}
void call_on_complete(error_code&, std::false_type)
{
}
void call_on_complete(error_code& ec)
{
2016-04-27 07:02:51 -04:00
call_on_complete(ec, has_on_complete<Derived>{});
2016-04-25 05:27:34 -04:00
}
2014-08-26 09:25:19 -07:00
};
} // http
} // beast
2016-03-11 06:40:37 -05:00
#include <beast/http/impl/basic_parser.ipp>
2014-08-26 09:25:19 -07:00
#endif