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:
Vinnie Falco
2016-11-20 07:32:41 -05:00
parent 34830a1780
commit e8be3fd7d3
74 changed files with 7106 additions and 5972 deletions

View File

@ -6,6 +6,10 @@
* Add flat_streambuf
* Rename to BEAST_DOXYGEN
API Changes:
* New HTTP interfaces
--------------------------------------------------------------------------------
1.0.0-b34

View File

@ -208,7 +208,7 @@ int main()
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
// Send HTTP request using beast
beast::http::request<beast::http::empty_body> req;
beast::http::request<beast::http::string_body> req;
req.method = "GET";
req.url = "/";
req.version = 11;

View File

@ -34,7 +34,7 @@ int main()
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
// Send HTTP request using beast
beast::http::request<beast::http::empty_body> req;
beast::http::request<beast::http::string_body> req;
req.method = "GET";
req.url = "/";
req.version = 11;

View File

@ -26,7 +26,6 @@ contents:
Algorithms
Write
Read
Parse
Examples
Send Request
Receive Response
@ -136,7 +135,7 @@ object:
[[HTTP Request] [HTTP Response]]
[[
```
request<empty_body> req;
request<string_body> req;
req.version = 11; // HTTP/1.1
req.method = "GET";
req.url = "/index.htm"
@ -227,15 +226,6 @@ The message [*`Body`] template parameter controls both the type of the data
member of the resulting message object, and the algorithms used during parsing
and serialization. Beast provides three very common [*`Body`] types:
* [link beast.ref.http__empty_body [*`empty_body`:]] An empty message body.
Used in GET requests where there is no message body. Example:
```
request<empty_body> req;
req.version = 11;
req.method = "GET";
req.url = "/index.html";
```
* [link beast.ref.http__string_body [*`string_body`:]] A body with a
`value_type` as `std::string`. Useful for quickly putting together a request
or response with simple text in the message body (such as an error message).
@ -303,7 +293,7 @@ operations performed). To send messages synchronously, use one of the
```
void send_request(boost::asio::ip::tcp::socket& sock)
{
request<empty_body> req;
request<string_body> req;
req.version = 11;
req.method = "GET";
req.url = "/index.html";
@ -322,7 +312,7 @@ An asynchronous interface is available:
```
void handle_write(boost::system::error_code);
...
request<empty_body> req;
request<string_body> req;
...
async_write(sock, req, std::bind(&handle_write, std::placeholders::_1));
```

View File

@ -107,7 +107,6 @@ provides implementations of the HTTP and WebSocket protocols.
[include types/DynamicBuffer.qbk]
[include types/Field.qbk]
[include types/FieldSequence.qbk]
[include types/Parser.qbk]
[include types/Reader.qbk]
[include types/Streams.qbk]
[include types/Writer.qbk]

View File

@ -31,13 +31,12 @@
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__basic_dynabuf_body">basic_dynabuf_body</link></member>
<member><link linkend="beast.ref.http__basic_fields">basic_fields</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__basic_parser">basic_parser</link></member>
<member><link linkend="beast.ref.http__fields">fields</link></member>
<member><link linkend="beast.ref.http__header">header</link></member>
<member><link linkend="beast.ref.http__header_parser_v1">header_parser_v1</link></member>
<member><link linkend="beast.ref.http__header_parser">header_parser</link></member>
<member><link linkend="beast.ref.http__message">message</link></member>
<member><link linkend="beast.ref.http__parser_v1">parser_v1</link></member>
<member><link linkend="beast.ref.http__message_parser">message_parser</link></member>
<member><link linkend="beast.ref.http__request">request</link></member>
<member><link linkend="beast.ref.http__request_header">request_header</link></member>
<member><link linkend="beast.ref.http__response">response</link></member>
@ -49,6 +48,7 @@
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__ext_list">ext_list</link></member>
<member><link linkend="beast.ref.http__opt_token_list">opt_token_list</link></member>
<member><link linkend="beast.ref.http__param_list">param_list</link></member>
<member><link linkend="beast.ref.http__token_list">token_list</link></member>
</simplelist>
@ -57,7 +57,7 @@
<bridgehead renderas="sect3">Functions</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__async_read">async_read</link></member>
<member><link linkend="beast.ref.http__async_parse">async_parse</link></member>
<member><link linkend="beast.ref.http__async_read_some">async_read_some</link></member>
<member><link linkend="beast.ref.http__async_write">async_write</link></member>
<member><link linkend="beast.ref.http__chunk_encode">chunk_encode</link></member>
<member><link linkend="beast.ref.http__chunk_encode_final">chunk_encode_final</link></member>
@ -65,17 +65,15 @@
<member><link linkend="beast.ref.http__is_keep_alive">is_keep_alive</link></member>
<member><link linkend="beast.ref.http__is_upgrade">is_upgrade</link></member>
<member><link linkend="beast.ref.http__operator_ls_">operator&lt;&lt;</link></member>
<member><link linkend="beast.ref.http__parse">parse</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_some">read_some</link></member>
<member><link linkend="beast.ref.http__reason_string">reason_string</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>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__is_Body">is_Body</link></member>
<member><link linkend="beast.ref.http__is_Parser">is_Parser</link></member>
<member><link linkend="beast.ref.http__is_Reader">is_Reader</link></member>
<member><link linkend="beast.ref.http__is_Writer">is_Writer</link></member>
<member><link linkend="beast.ref.http__has_reader">has_reader</link></member>
@ -83,26 +81,16 @@
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Options</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__header_max_size">header_max_size</link></member>
<member><link linkend="beast.ref.http__body_max_size">body_max_size</link></member>
<member><link linkend="beast.ref.http__skip_body">skip_body</link></member>
</simplelist>
<bridgehead renderas="sect3">Constants</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__body_what">body_what</link></member>
<member><link linkend="beast.ref.http__connection">connection</link></member>
<member><link linkend="beast.ref.http__no_content_length">no_content_length</link></member>
<member><link linkend="beast.ref.http__parse_error">parse_error</link></member>
<member><link linkend="beast.ref.http__parse_flag">parse_flag</link></member>
<member><link linkend="beast.ref.http__error">error</link></member>
</simplelist>
<bridgehead renderas="sect3">Concepts</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.Body">Body</link></member>
<member><link linkend="beast.ref.Field">Field</link></member>
<member><link linkend="beast.ref.FieldSequence">FieldSequence</link></member>
<member><link linkend="beast.ref.Parser">Parser</link></member>
<member><link linkend="beast.ref.Reader">Reader</link></member>
<member><link linkend="beast.ref.Writer">Writer</link></member>
</simplelist>

View File

@ -1,61 +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)
]
[section:Parser Parser requirements]
A [*Parser] is used to deserialize objects from
[link beast.ref.streams streams]. Objects of this type are used with
[link beast.ref.http__parse http::parse] and
[link beast.ref.http__async_parse http::async_parse]. The definition of
an object, and the predicate defining when the parse is complete, are
determined by the implementation.
In this table:
* `X` denotes a type meeting the requirements of [*Parser].
* `a` denotes a value of type `X`.
* `b` is a value meeting the requirements of __ConstBufferSequence__.
* `ec` is a value of type [link beast.ref.error_code `error_code&`].
[table Parser requirements
[[operation] [type] [semantics, pre/post-conditions]]
[
[`a.complete()`]
[`bool`]
[
Returns `true` when parsing is complete.
]
]
[
[`a.write(b, ec)`]
[`std::size_t`]
[
Sequentially parses the octets in the specified input buffer sequence
until an error occurs, the end of the buffer is reached, or parsing is
complete. Upon success, this function returns the number of bytes used
from the input. If an error occurs, `ec` is set to the error code and
parsing stops.
]
]
[
[`a.write_eof(ec)`]
[`void`]
[
Indicates to the parser that no more octets will be available.
Typically this function is called when the end of stream is reached.
For example, if a call to `boost::asio::ip::tcp::socket::read_some`
generates a `boost::asio::error::eof` error. Some objects, such as
certain HTTP/1 messages, determine the end of the message body by
an end of file marker or closing of the connection.
]
]
]
[endsect]

View File

@ -10,57 +10,198 @@
Parsers provided by the implementation will construct the corresponding
`reader` object during parsing. This customization point allows the
Body to determine the strategy for storing incoming message body data.
Readers come in two flavors, direct and indirect:
In this table:
Direct readers provide a buffer to callers, in which body data is placed.
This type of reader is used when the bytes corresponding to the body data
are stored without transformation. The parse algorithm performs stream or
socket reads directly into the reader-provided buffer, hence the name
"direct." This model avoids an unnecessary buffer copy. An example of
a [*Body] type with a direct reader is
[link beast.ref.http__string_body `string_body`].
Indirect readers are passed body data in a buffer managed by the parser
algorithm. This reader is appropriate when the body data is transformed
or not otherwised stored verbatim. Some examples of when an indirect
reader is appropriate:
* When bytes corresponding to the body are written to a file
as they are parsed.
* The content of the message is JSON, which is parsed as it is
being read in, and stored in a structured, hierarchical format.
In the tables below:
* `X` denotes a type meeting the requirements of [*`Reader`].
* `a` denotes a value of type `X`.
* `n` is a value convertible to `std::size_t`.
* `n` is a value convertible to `std::size_t` without loss of precision.
* `p` is a `void const*` to valid memory of at least `n` bytes.
* `v` is a value convertible to `std::uint64_t` without loss of precision.
* `s` is a value of type `boost::string_ref`.
* `ec` is a value of type [link beast.ref.error_code `error_code&`].
* `m` denotes a value of type `message&` where
`std::is_same<decltype(m.body), Body::value_type>::value == true`.
[table Reader requirements
[table Direct Reader requirements
[[operation] [type] [semantics, pre/post-conditions]]
[
[`X a(m);`]
[`X::is_direct`]
[`bool`]
[
This static constant must be set to `true` to indicate that
the reader is a direct reader.
]
]
[
[`X::mutable_buffers_type`]
[]
[
This member type must be present, and meet the requirements
of [*MutableBufferSequence]. It represents the type of
the writable buffers returned by the reader, in which
bytes representing the body are stored by the implementation.
]
]
[
[`X a{m};`]
[]
[
`a` is constructible from `m`. The lifetime of `m` is guaranteed
to end no earlier than after `a` is destroyed. The constructor
will be called after all headers have been stored in `m`, and
before any body data is deserialized. This function must be
`noexcept`.
just before parsing bytes corresponding to the body for messages
whose semantics indicate that a body is present with non-zero
length.
]
]
[
[`a.init(ec)`]
[`void`]
[`a.init()`]
[]
[
Called immediately after construction. If the function sets
an error code in `ec`, the parse is aborted and the error is
propagated to the caller. This function must be `noexcept`.
This function is called once before any bytes corresponding
to the body are presented to the reader, for messages whose
body is determined by the end-of-file marker on a stream,
or for messages where the chunked Transfer-Encoding is
specified.
]
]
[
[`a.write(p, n, ec)`]
[`void`]
[`a.init(v)`]
[]
[
Deserializes the input sequence into the body. If `ec` is set,
the deserialization is aborted and the error is propagated to
the caller. If the message headers specify a chunked transfer
encoding, the reader will receive the decoded version of the
body. This function must be `noexcept`.
This function is called once before any bytes corresponding
to the body are presented to the reader, for messages where
the Content-Length is specified. The value of `v` will be
set to the number of bytes indicated by the content length.
]
]
[
[`a.prepare(n)`]
[`mutable_buffers_type`]
[
The implementation calls this function to obtain a mutable
buffer sequence of up to `n` bytes in size in which to place
data corresponding to the body. The buffer returned must
be at least one byte in size, and may be smaller than `n`.
]
]
[
[`a.commit(n)`]
[]
[
The implementation calls this function to indicate to the
reader that `n` bytes of data have been successfully placed
into the buffer obtained through a prior call to `prepare`.
The value of `n` will be less than or equal to the size of
the buffer returned in the previous call to `prepare`.
]
]
[
[`a.finish()`]
[]
[
This function is called after all the bytes corresponding
to the body have been written to the buffers and committed.
]
]
]
[table Indirect Reader requirements
[[operation] [type] [semantics, pre/post-conditions]]
[
[`X::is_direct`]
[`bool`]
[
This static constant must be set to `false` to indicate that
the reader is an indirect reader.
]
]
[
[`X a{m};`]
[]
[
`a` is constructible from `m`. The lifetime of `m` is guaranteed
to end no earlier than after `a` is destroyed. The constructor
will be called after all headers have been stored in `m`, and
just before parsing bytes corresponding to the body for messages
whose semantics indicate that a body is present with non-zero
length.
]
]
[
[`a.init(ec)`]
[]
[
This function is called once before any bytes corresponding
to the body are presented to the reader, for messages whose
body is determined by the end-of-file market on a stream,
or for messages where the chunked Transfer-Encoding is
specified.
If `ec` is set before returning, parsing will stop
and the error will be returned to the caller.
]
]
[
[`a.init(v,ec)`]
[]
[
This function is called once before any bytes corresponding
to the body are presented to the reader, for messages where
the Content-Length is specified. The value of `v` will be
set to the number of bytes indicated by the content length.
If `ec` is set before returning, parsing will stop
and the error will be returned to the caller.
]
]
[
[`a.write(s,ec)`]
[]
[
The implementation calls this function with `s` containing
bytes corresponding to the body, after removing any present
chunked encoding transformation.
If `ec` is set before returning, parsing will stop
and the error will be returned to the caller.
]
]
[
[`a.finish(ec)`]
[]
[
This function is called after all the bytes corresponding
to the body have been written to the buffers and committed.
If `ec` is set before returning, parsing will stop
and the error will be returned to the caller.
]
]
]
[note
Definitions for required `Reader` member functions should be declared
inline so the generated code can become part of the implementation.

View File

@ -50,7 +50,7 @@ The WebSocket protocol is described fully in
The interface to Beast's WebSocket implementation is a single template
class [link beast.ref.websocket__stream `beast::websocket::stream`] which
wraps a "next layer" object. The next layer object must meet the requirements
of [link beast.ref.streams.SyncStream [*`SyncReadStream`]] if synchronous
of [link beast.ref.streams.SyncStream [*`SyncStream`]] if synchronous
operations are performed, or
[link beast.ref.streams.AsyncStream [*`AsyncStream`]] if asynchronous
operations are performed, or both. Arguments supplied during construction are

View File

@ -36,7 +36,7 @@ int main(int, char const*[])
ip::tcp::socket sock(ios);
connect(sock, it);
auto ep = sock.remote_endpoint();
request<empty_body> req;
request<string_body> req;
req.method = "GET";
req.url = "/";
req.version = 11;

View File

@ -22,7 +22,7 @@ int main()
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
// Send HTTP request using beast
beast::http::request<beast::http::empty_body> req;
beast::http::request<beast::http::string_body> req;
req.method = "GET";
req.url = "/";
req.version = 11;

View File

@ -34,7 +34,7 @@ int main()
stream.handshake(ssl::stream_base::client);
// Send HTTP request over SSL using Beast
beast::http::request<beast::http::empty_body> req;
beast::http::request<beast::http::string_body> req;
req.method = "GET";
req.url = "/";
req.version = 11;

View File

@ -10,17 +10,15 @@
#include <boost/range/algorithm/equal.hpp>
#include <boost/utility/string_ref.hpp>
#include <array>
#include <cstdint>
namespace beast {
namespace detail {
inline
char
tolower(char c)
tolower(signed char c)
{
static std::array<std::uint8_t, 256> constexpr tab = {{
static unsigned char constexpr tab[256] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
@ -37,8 +35,9 @@ tolower(char c)
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
}};
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
};
return static_cast<char>(
tab[static_cast<unsigned char>(c)]);
}
template<std::size_t N>

View File

@ -11,14 +11,14 @@
#include <beast/config.hpp>
#include <beast/http/basic_fields.hpp>
#include <beast/http/basic_parser_v1.hpp>
#include <beast/http/basic_parser.hpp>
#include <beast/http/chunk_encode.hpp>
#include <beast/http/empty_body.hpp>
#include <beast/http/error.hpp>
#include <beast/http/fields.hpp>
#include <beast/http/header_parser.hpp>
#include <beast/http/message.hpp>
#include <beast/http/parse.hpp>
#include <beast/http/parse_error.hpp>
#include <beast/http/parser_v1.hpp>
#include <beast/http/message_parser.hpp>
#include <beast/http/read.hpp>
#include <beast/http/read.hpp>
#include <beast/http/reason.hpp>
#include <beast/http/rfc7230.hpp>

View File

@ -33,30 +33,47 @@ private:
class reader
{
value_type& sb_;
value_type& body_;
public:
static bool constexpr is_direct = true;
using mutable_buffers_type =
typename DynamicBuffer::mutable_buffers_type;
template<bool isRequest, class Fields>
explicit
reader(message<isRequest,
basic_dynabuf_body, Fields>& m) noexcept
: sb_(m.body)
basic_dynabuf_body, Fields>& msg)
: body_(msg.body)
{
}
void
init(error_code&) noexcept
init()
{
}
void
write(void const* data,
std::size_t size, error_code&) noexcept
init(std::uint64_t content_length)
{
}
mutable_buffers_type
prepare(std::size_t n)
{
return body_.prepare(n);
}
void
commit(std::size_t n)
{
body_.commit(n);
}
void
finish()
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
sb_.commit(buffer_copy(
sb_.prepare(size), buffer(data, size)));
}
};

View File

@ -0,0 +1,643 @@
//
// 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_BASIC_PARSER_HPP
#define BEAST_HTTP_BASIC_PARSER_HPP
#include <beast/config.hpp>
#include <beast/core/error.hpp>
#include <beast/http/detail/basic_parser.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/optional.hpp>
#include <boost/assert.hpp>
#include <boost/utility/string_ref.hpp>
#include <memory>
#include <utility>
namespace beast {
namespace http {
/** Describes the parser's current state.
The state is expressed as the type of data that
@ref basic_parser is expecting to see in subsequently
provided octets.
*/
enum class parse_state
{
/// Expecting one or more header octets
header = 0,
/// Expecting one or more body octets
body = 1,
/// Expecting zero or more body octets followed by EOF
body_to_eof = 2,
/// Expecting additional chunk header octets
chunk_header = 3,
/// Expecting one or more chunk body octets
chunk_body = 4,
/** The parsing is complete.
The parse is considered complete when the full header
is received and either the full body is received, or
the semantics of the message indicate that no body
is expected. This includes the case where the caller
has indicated to the parser that no body is expected,
for example when receiving a response to a HEAD request.
*/
complete = 5
};
/** A parser for decoding HTTP/1 wire format messages.
This parser is designed to efficiently parse messages in the
HTTP/1 wire format. It allocates no memory when input is
presented as a single contiguous buffer, and uses minimal
state. It will handle chunked encoding and it understands
the semantics of the Connection, Content-Length, and Upgrade
fields.
The interface uses CRTP (Curiously Recurring Template Pattern).
To use this class, derive from @ref basic_parser. When bytes
are presented, the implementation will make a series of zero
or more calls to derived class members functions (referred to
as "callbacks" from here on) matching a specific signature.
Every callback must be provided by the derived class, or else
a compilation error will be generated. This exemplar shows
the signature and description of the callbacks required in
the derived class.
@par Derived Example
@code
template<bool isRequest>
struct derived
: basic_parser<isRequest, derived<isRequest>>
{
// The type used when providing a mutable
// buffer sequence in which to store body data.
//
using mutable_buffers_type = ...;
// When isRequest == true, called
// after the Request Line is received.
//
void
on_request(
boost::string_ref const& method,
boost::string_ref const& path,
int version,
error_code& ec);
// When isRequest == false, called
// after the Status Line is received.
//
void
on_response(
int status,
boost::string_ref const& reason,
int version,
error_code& ec);
// Called after receiving a field/value pair.
//
void
on_field(
boost::string_ref const& name,
boost::string_ref const& value,
error_code& ec);
// Called after the header is complete.
//
void
on_header(
error_code& ec);
// Called once before the body, if any, is started.
// This will only be called if the semantics of the
// message indicate that a body exists, including
// an indicated body of zero length.
//
void
on_body();
// Called zero or more times to provide body data.
//
// Only used if isDirect == false
//
void
on_data(
boost::string_ref const& s,
error_code& ec);
// Called zero or more times to retrieve a mutable
// buffer sequence in which to store body data.
//
// Only used if isDirect == true
//
mutable_buffers_type
on_prepare(
std::size_t n);
// Called after body data has been stored in the
// buffer returned by the previous call to on_prepare.
//
// Only used if isDirect == true
//
void
on_commit(
std::size_t n);
// If the Transfer-Encoding is specified, and the
// last item in the list of encodings is "chunked",
// called after receiving a chunk header or a final
// chunk.
//
void
on_chunk(
std::uint64_t length, // Length of this chunk
boost::string_ref const& ext, // The chunk extensions, if any
error_code& ec);
// Called once when the message is complete.
// This will be called even if there is no body.
//
void
on_complete(error_code& ec);
};
@endcode
If a callback sets the error code, the error will be propagated
to the caller of the parser. Behavior of parsing after an error
is returned is undefined.
When the parser state is positioned to read bytes belonging to
the body, calling @ref write or @ref write will implicitly
cause a buffer copy (because bytes are first transferred to the
dynamic buffer). To avoid this copy, the additional functions
@ref copy_body, @ref prepare_body, and @ref commit_body are
provided to allow the caller to read bytes directly into buffers
supplied by the parser.
The parser is optimized for the case where the input buffer
sequence consists of a single contiguous buffer. The
@ref beast::flat_streambuf class is provided, which guarantees
that the input sequence of the stream buffer will be represented
by exactly one contiguous buffer. To ensure the optimum performance
of the parser, use @ref beast::flat_streambuf with HTTP algorithms
such as @ref beast::http::read, @ref beast::http::read_some,
@ref beast::http::async_read, and @ref beast::http::async_read_some.
Alternatively, the caller may use custom techniques to ensure that
the structured portion of the HTTP message (header or chunk header)
is contained in a linear buffer.
@tparam isRequest A `bool` indicating whether the parser will be
presented with request or response message.
@tparam isDirect A `bool` indicating whether the parser interface
supports reading body data directly into parser-provided buffers.
@tparam Derived The derived class type. This is part of the
Curiously Recurring Template Pattern interface.
*/
template<bool isRequest, bool isDirect, class Derived>
class basic_parser
: private detail::basic_parser_base
{
template<bool OtherIsRequest,
bool OtherIsDirect, class OtherDerived>
friend class basic_parser;
// Message will be complete after reading header
static unsigned constexpr flagSkipBody = 1<< 0;
static unsigned constexpr flagOnBody = 1<< 1;
// The parser has read at least one byte
static unsigned constexpr flagGotSome = 1<< 2;
// Message semantics indicate a body is expected.
// cleared if flagSkipBody set
//
static unsigned constexpr flagHasBody = 1<< 3;
static unsigned constexpr flagHTTP11 = 1<< 4;
static unsigned constexpr flagNeedEOF = 1<< 5;
static unsigned constexpr flagExpectCRLF = 1<< 6;
static unsigned constexpr flagFinalChunk = 1<< 7;
static unsigned constexpr flagConnectionClose = 1<< 8;
static unsigned constexpr flagConnectionUpgrade = 1<< 9;
static unsigned constexpr flagConnectionKeepAlive = 1<< 10;
static unsigned constexpr flagContentLength = 1<< 11;
static unsigned constexpr flagChunked = 1<< 12;
static unsigned constexpr flagUpgrade = 1<< 13;
std::uint64_t len_; // size of chunk or body
std::unique_ptr<char[]> buf_;
std::size_t buf_len_ = 0;
std::size_t skip_ = 0; // search from here
std::size_t x_; // scratch variable
unsigned f_ = 0; // flags
parse_state state_ = parse_state::header;
boost::string_ref ext_;
boost::string_ref body_;
public:
/// Copy constructor (disallowed)
basic_parser(basic_parser const&) = delete;
/// Copy assignment (disallowed)
basic_parser& operator=(basic_parser const&) = delete;
/// Default constructor
basic_parser() = default;
/// `true` if this parser parses requests, `false` for responses.
static bool constexpr is_request = isRequest;
/// Destructor
~basic_parser() = default;
/** Move constructor
After the move, the only valid operation on the
moved-from object is destruction.
*/
template<bool OtherIsDirect, class OtherDerived>
basic_parser(basic_parser<
isRequest, OtherIsDirect, OtherDerived>&&);
/** Set the skip body option.
The option controls whether or not the parser expects to
see an 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 may use this function
inform the parser that no body is expected. The parser will
consider the message complete after the header has been received.
@note This function must called before any bytes are processed.
*/
void
skip_body();
/** Returns the current parser state.
The parser state indicates what octets the parser
expects to see next in the input stream.
*/
parse_state
state() const
{
return state_;
}
/// Returns `true` if the parser has received at least one byte of input.
bool
got_some() const
{
return (f_ & flagGotSome) != 0;
}
/// Returns `true` if the complete header has been parsed.
bool
got_header() const
{
return state_ != parse_state::header;
}
/** Returns `true` if a Content-Length is specified.
@note Only valid after parsing a complete header.
*/
bool
got_content_length() const
{
return (f_ & flagContentLength) != 0;
}
/** Returns `true` if the message is complete.
The message is complete after a full header is
parsed and one of the following is true:
@li @ref skip_body was called
@li The semantics of the message indicate there is no body.
@li The semantics of the message indicate a body is
expected, and the entire body was received.
*/
bool
is_complete() const
{
return state_ == parse_state::complete;
}
/** Returns `true` if the message is an upgrade message.
@note Only valid after parsing a complete header.
*/
bool
is_upgrade() const
{
return (f_ & flagConnectionUpgrade) != 0;
}
/** Returns `true` if keep-alive is specified
@note Only valid after parsing a complete header.
*/
bool
is_keep_alive() const;
/** Returns `true` if the chunked Transfer-Encoding is specified.
@note Only valid after parsing a complete header.
*/
bool
is_chunked() const
{
return (f_ & flagChunked) != 0;
}
/** Write part of a buffer sequence to the parser.
This function attempts to parse the HTTP message
stored in the caller provided buffers. Upon success,
a positive return value indicates that the parser
made forward progress, consuming that number of
bytes.
A return value of zero indicates that the parser
requires additional input. In this case the caller
should append additional bytes to the input buffer
sequence and call @ref write again.
@param buffers An object meeting the requirements of
@b ConstBufferSequence that represents the message.
@param ec Set to the error, if any occurred.
@return The number of bytes consumed in the buffer
sequence.
*/
template<class ConstBufferSequence>
std::size_t
write(ConstBufferSequence const& buffers, error_code& ec);
#if ! GENERATING_DOCS
std::size_t
write(boost::asio::const_buffers_1 const& buffer,
error_code& ec);
#endif
/** Inform the parser that the end of stream was reached.
In certain cases, 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.
This is typically called when a read from the
underlying stream object sets the error code to
`boost::asio::error::eof`.
@note Only valid after parsing a complete header.
@param ec Set to the error, if any occurred.
*/
void
write_eof(error_code& ec);
/** Returns the number of bytes remaining in the body or chunk.
If a Content-Length is specified and the parser state
is equal to @ref beast::http::parse_state::body, this will return
the number of bytes remaining in the body. If the
chunked Transfer-Encoding is indicated and the parser
state is equal to @ref beast::http::parse_state::chunk_body, this
will return the number of bytes remaining in the chunk.
Otherwise, the function behavior is undefined.
*/
std::uint64_t
size() const
{
BOOST_ASSERT(
state_ == parse_state::body ||
state_ == parse_state::chunk_body);
return len_;
}
/** Returns the body data parsed in the last call to @ref write.
This buffer is invalidated after any call to @ref write
or @ref write_eof.
@note If the last call to @ref write came from the input
area of a @b DynamicBuffer object, a call to the dynamic
buffer's `consume` function may invalidate this return
value.
*/
boost::string_ref const&
body() const
{
// This function not available when isDirect==true
static_assert(! isDirect, "");
return body_;
}
/** Returns the chunk extension parsed in the last call to @ref write.
This buffer is invalidated after any call to @ref write
or @ref write_eof.
@note If the last call to @ref write came from the input
area of a @b DynamicBuffer object, a call to the dynamic
buffer's `consume` function may invalidate this return
value.
*/
boost::string_ref const&
chunk_extension() const
{
// This function not available when isDirect==true
static_assert(! isDirect, "");
return ext_;
}
/** Returns the optional value of Content-Length if known.
@note The return value is undefined unless a complete
header has been parsed.
*/
boost::optional<std::uint64_t>
content_length() const
{
BOOST_ASSERT(got_header());
if(! (f_ & flagContentLength))
return boost::none;
return len_;
}
/** Copy leftover body data from the dynamic buffer.
@note This member function is only available when
`isDirect==true`.
@return The number of bytes processed from the dynamic
buffer. The caller should remove these bytes by calling
`consume` on the buffer.
*/
template<class DynamicBuffer>
std::size_t
copy_body(DynamicBuffer& dynabuf);
/** Returns a set of buffers for storing body data.
@note This member function is only available when
`isDirect==true`.
@param limit The maximum number of bytes in the
size of the returned buffer sequence. The actual size
of the buffer sequence may be lower than this number.
*/
template<class MutableBufferSequence>
void
prepare_body(boost::optional<
MutableBufferSequence>& buffers, std::size_t limit);
/** Commit body data.
@note This member function is only available when
`isDirect==true`.
*/
void
commit_body(std::size_t n);
/** Indicate that body octets have been consumed.
*/
void
consume(std::size_t n)
{
BOOST_ASSERT(n <= len_);
BOOST_ASSERT(
state_ == parse_state::body ||
state_ == parse_state::chunk_body);
len_ -= n;
if(len_ == 0)
{
if(state_ == parse_state::body)
state_ = parse_state::complete;
else
state_ = parse_state::chunk_header;
}
}
/** Consume all remaining body data.
This function instructs the parser to advance the
state past any expected body octets. Callers who
wish to read and process the body themselves will
call this function.
*/
void
consume_body(error_code& ec);
private:
inline
Derived&
impl()
{
return *static_cast<Derived*>(this);
}
template<class ConstBufferSequence>
boost::string_ref
maybe_flatten(
ConstBufferSequence const& buffers);
std::size_t
do_write(boost::asio::const_buffers_1 const& buffer,
error_code& ec, std::true_type);
std::size_t
do_write(boost::asio::const_buffers_1 const& buffer,
error_code& ec, std::false_type);
void
parse_startline(char const*& it,
int& version, int& status,
error_code& ec, std::true_type);
void
parse_startline(char const*& it,
int& version, int& status,
error_code& ec, std::false_type);
void
parse_fields(char const*& it,
char const* last, error_code& ec);
void
do_field(
boost::string_ref const& name,
boost::string_ref const& value,
error_code& ec);
std::size_t
parse_header(char const* p,
std::size_t n, error_code& ec);
void
do_header(int, std::true_type);
void
do_header(int status, std::false_type);
void
maybe_do_body_direct();
void
maybe_do_body_indirect(error_code& ec);
std::size_t
parse_chunk_header(char const* p,
std::size_t n, error_code& ec);
std::size_t
parse_body(char const* p,
std::size_t n, error_code& ec);
std::size_t
parse_body_to_eof(char const* p,
std::size_t n, error_code& ec);
std::size_t
parse_chunk_body(char const* p,
std::size_t n, error_code& ec);
void
do_complete(error_code& ec);
};
} // http
} // beast
#include <beast/http/impl/basic_parser.ipp>
#endif

View File

@ -1,856 +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_BASIC_PARSER_v1_HPP
#define BEAST_HTTP_BASIC_PARSER_v1_HPP
#include <beast/config.hpp>
#include <beast/http/message.hpp>
#include <beast/http/parse_error.hpp>
#include <beast/http/rfc7230.hpp>
#include <beast/http/detail/basic_parser_v1.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/assert.hpp>
#include <array>
#include <climits>
#include <cstdint>
#include <type_traits>
namespace beast {
namespace http {
/** Parse flags
The set of parser bit flags are returned by @ref basic_parser_v1::flags.
*/
enum parse_flag
{
chunked = 1,
connection_keep_alive = 2,
connection_close = 4,
connection_upgrade = 8,
trailing = 16,
upgrade = 32,
skipbody = 64,
contentlength = 128,
paused = 256
};
/** Body maximum size option.
Sets the maximum number of cumulative bytes allowed including
all body octets. Octets in chunk-encoded bodies are counted
after decoding. A value of zero indicates no limit on
the number of body octets.
The default body maximum size for requests is 4MB (four
megabytes or 4,194,304 bytes) and unlimited for responses.
@note Objects of this type are used with @ref basic_parser_v1::set_option.
*/
struct body_max_size
{
std::size_t value;
explicit
body_max_size(std::size_t v)
: value(v)
{
}
};
/** Header maximum size option.
Sets the maximum number of cumulative bytes allowed
including all header octets. A value of zero indicates
no limit on the number of header octets.
The default header maximum size is 16KB (16,384 bytes).
@note Objects of this type are used with @ref basic_parser_v1::set_option.
*/
struct header_max_size
{
std::size_t value;
explicit
header_max_size(std::size_t v)
: value(v)
{
}
};
/** A value indicating how the parser should treat the body.
This value is returned from the `on_header` callback in
the derived class. It controls what the parser does next
in terms of the message body.
*/
enum class body_what
{
/** The parser should expect a body, keep reading.
*/
normal,
/** Skip parsing of the body.
When returned by `on_header` this causes parsing to
complete and control to return to the caller. This
could be used when sending a response to a HEAD
request, for example.
*/
skip,
/** The message represents an UPGRADE request.
When returned by `on_body_prepare` this causes parsing
to complete and control to return to the caller.
*/
upgrade,
/** Suspend parsing before reading the body.
When returned by `on_body_prepare` this causes parsing
to pause. Control is returned to the caller, and the
parser state is preserved such that a subsequent call
to the parser will begin reading the message body.
This could be used by callers to inspect the HTTP
header before committing to read the body. For example,
to choose the body type based on the fields. Or to
respond to an Expect: 100-continue request.
*/
pause
};
/// The value returned when no content length is known or applicable.
static std::uint64_t constexpr no_content_length =
(std::numeric_limits<std::uint64_t>::max)();
/** A parser for decoding HTTP/1 wire format messages.
This parser is designed to efficiently parse messages in the
HTTP/1 wire format. It allocates no memory and uses minimal
state. It will handle chunked encoding and it understands the
semantics of the Connection and Content-Length header fields.
The interface uses CRTP (Curiously Recurring Template Pattern).
To use this class, derive from basic_parser. When bytes are
presented, the implementation will make a series of zero or
more calls to derived class members functions (referred to as
"callbacks" from here on) matching a specific signature.
Every callback must be provided by the derived class, or else
a compilation error will be generated. This exemplar shows
the signature and description of the callbacks required in
the derived class.
@code
template<bool isRequest>
struct exemplar : basic_parser_v1<isRequest, exemplar>
{
// Called when the first valid octet of a new message is received
//
void on_start(error_code&);
// Called for each piece of the Request-Method
//
void on_method(boost::string_ref const&, error_code&);
// Called for each piece of the Request-URI
//
void on_uri(boost::string_ref const&, error_code&);
// Called for each piece of the reason-phrase
//
void on_reason(boost::string_ref const&, error_code&);
// Called after the entire Request-Line has been parsed successfully.
//
void on_request(error_code&);
// Called after the entire Response-Line has been parsed successfully.
//
void on_response(error_code&);
// Called for each piece of the current header field.
//
void on_field(boost::string_ref const&, error_code&);
// Called for each piece of the current header value.
//
void on_value(boost::string_ref const&, error_code&)
// Called when the entire header has been parsed successfully.
//
void
on_header(std::uint64_t content_length, error_code&);
// Called after on_header, before the body is parsed
//
body_what
on_body_what(std::uint64_t content_length, error_code&);
// Called for each piece of the body.
//
// If the header indicates chunk encoding, the chunk
// encoding is removed from the buffer before being
// passed to the callback.
//
void on_body(boost::string_ref const&, error_code&);
// Called when the entire message has been parsed successfully.
// At this point, @ref complete returns `true`, and the parser
// is ready to parse another message if `keep_alive` would
// return `true`.
//
void on_complete(error_code&) {}
};
@endcode
The return value of `on_body_what` is special, it controls
whether or not the parser should expect a body. See @ref body_what
for choices of the return value.
If a callback sets an error, parsing stops at the current octet
and the error is returned to the caller. Callbacks must not throw
exceptions.
@tparam isRequest A `bool` indicating whether the parser will be
presented with request or response message.
@tparam Derived The derived class type. This is part of the
Curiously Recurring Template Pattern interface.
*/
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&);
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_length0,
h_content_length,
h_content_length_ows,
h_transfer_encoding,
h_upgrade,
h_matching_transfer_encoding_chunked,
h_matching_transfer_encoding_general,
h_matching_connection_keep_alive,
h_matching_connection_close,
h_matching_connection_upgrade,
h_transfer_encoding_chunked,
h_transfer_encoding_chunked_ows,
h_connection_keep_alive,
h_connection_keep_alive_ows,
h_connection_close,
h_connection_close_ows,
h_connection_upgrade,
h_connection_upgrade_ows,
h_connection_token,
h_connection_token_ows
};
std::size_t h_max_;
std::size_t h_left_;
std::size_t b_max_;
std::size_t b_left_;
std::uint64_t content_length_;
pmf_t cb_;
state s_ : 8;
unsigned fs_ : 8;
unsigned pos_ : 8; // position in field state
unsigned http_major_ : 16;
unsigned http_minor_ : 16;
unsigned status_code_ : 16;
unsigned flags_ : 9;
bool upgrade_ : 1; // true if parser exited for upgrade
public:
/// 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.
*/
#if BEAST_DOXYGEN
template<class... Args>
void
set_option(Args&&... args)
#else
template<class A1, class A2, class... An>
void
set_option(A1&& a1, A2&& a2, An&&... an)
#endif
{
set_option(std::forward<A1>(a1));
set_option(std::forward<A2>(a2),
std::forward<An>(an)...);
}
/// Set the header maximum size option
void
set_option(header_max_size const& o)
{
h_max_ = o.value;
h_left_ = h_max_;
}
/// Set the body maximum size option
void
set_option(body_max_size const& o)
{
b_max_ = o.value;
b_left_ = b_max_;
}
/// Returns internal flags associated with the parser.
unsigned
flags() const
{
return flags_;
}
/** Returns `true` if the message end is indicated by eof.
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.
@return `true` if write_eof must be used to indicate the message end.
*/
bool
needs_eof() const
{
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 ||
s_ == s_closed_complete ||
(flags_ & parse_flag::paused);
}
/** Write a sequence of buffers to the parser.
@param buffers An object meeting the requirements of
ConstBufferSequence that represents the input sequence.
@param ec Set to the error, if any error occurred.
@return The number of bytes consumed in the input sequence.
*/
template<class ConstBufferSequence>
#if BEAST_DOXYGEN
std::size_t
#else
typename std::enable_if<
! std::is_convertible<ConstBufferSequence,
boost::asio::const_buffer>::value,
std::size_t>::type
#endif
write(ConstBufferSequence const& buffers, error_code& ec);
/** Write a single buffer of data to the parser.
@param buffer The buffer to write.
@param ec Set to the error, if any error occurred.
@return The number of bytes consumed in the buffer.
*/
std::size_t
write(boost::asio::const_buffer const& buffer, error_code& ec);
/** Called to indicate the end of file.
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.
@note This is typically called when a socket read returns eof.
*/
void
write_eof(error_code& ec);
protected:
/** Reset the parsing state.
The state of the parser is reset to expect the beginning of
a new request or response. The old state is discarded.
*/
void
reset();
private:
Derived&
impl()
{
return *static_cast<Derived*>(this);
}
void
reset(std::true_type)
{
s_ = s_req_start;
}
void
reset(std::false_type)
{
s_ = s_res_start;
}
void
init(std::true_type)
{
// Request: 16KB max header, 4MB max body
h_max_ = 16 * 1024;
b_max_ = 4 * 1024 * 1024;
}
void
init(std::false_type)
{
// Response: 16KB max header, unlimited body
h_max_ = 16 * 1024;
b_max_ = 0;
}
void
init()
{
init(std::integral_constant<bool, isRequest>{});
reset();
}
bool
needs_eof(std::true_type) const;
bool
needs_eof(std::false_type) const;
template<class T, class = beast::detail::void_t<>>
struct check_on_start : std::false_type {};
template<class T>
struct check_on_start<T, beast::detail::void_t<decltype(
std::declval<T>().on_start(
std::declval<error_code&>())
)>> : std::true_type { };
template<class T, class = beast::detail::void_t<>>
struct check_on_method : std::false_type {};
template<class T>
struct check_on_method<T, beast::detail::void_t<decltype(
std::declval<T>().on_method(
std::declval<boost::string_ref>(),
std::declval<error_code&>())
)>> : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct check_on_uri : std::false_type {};
template<class T>
struct check_on_uri<T, beast::detail::void_t<decltype(
std::declval<T>().on_uri(
std::declval<boost::string_ref>(),
std::declval<error_code&>())
)>> : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct check_on_reason : std::false_type {};
template<class T>
struct check_on_reason<T, beast::detail::void_t<decltype(
std::declval<T>().on_reason(
std::declval<boost::string_ref>(),
std::declval<error_code&>())
)>> : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct check_on_request : std::false_type {};
template<class T>
struct check_on_request<T, beast::detail::void_t<decltype(
std::declval<T>().on_request(
std::declval<error_code&>())
)>> : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct check_on_response : std::false_type {};
template<class T>
struct check_on_response<T, beast::detail::void_t<decltype(
std::declval<T>().on_response(
std::declval<error_code&>())
)>> : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct check_on_field : std::false_type {};
template<class T>
struct check_on_field<T, beast::detail::void_t<decltype(
std::declval<T>().on_field(
std::declval<boost::string_ref>(),
std::declval<error_code&>())
)>> : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct check_on_value : std::false_type {};
template<class T>
struct check_on_value<T, beast::detail::void_t<decltype(
std::declval<T>().on_value(
std::declval<boost::string_ref>(),
std::declval<error_code&>())
)>> : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct check_on_headers : std::false_type {};
template<class T>
struct check_on_headers<T, beast::detail::void_t<decltype(
std::declval<T>().on_header(
std::declval<std::uint64_t>(),
std::declval<error_code&>())
)>> : std::true_type {};
// VFALCO Can we use std::is_detected? Is C++11 capable?
template<class C>
class check_on_body_what_t
{
template<class T, class R = std::is_convertible<decltype(
std::declval<T>().on_body_what(
std::declval<std::uint64_t>(),
std::declval<error_code&>())),
body_what>>
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 check_on_body_what =
std::integral_constant<bool, check_on_body_what_t<C>::value>;
template<class T, class = beast::detail::void_t<>>
struct check_on_body : std::false_type {};
template<class T>
struct check_on_body<T, beast::detail::void_t<decltype(
std::declval<T>().on_body(
std::declval<boost::string_ref>(),
std::declval<error_code&>())
)>> : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct check_on_complete : std::false_type {};
template<class T>
struct check_on_complete<T, beast::detail::void_t<decltype(
std::declval<T>().on_complete(
std::declval<error_code&>())
)>> : std::true_type {};
void call_on_start(error_code& ec)
{
static_assert(check_on_start<Derived>::value,
"on_start requirements not met");
impl().on_start(ec);
}
void call_on_method(error_code& ec,
boost::string_ref const& s, std::true_type)
{
static_assert(check_on_method<Derived>::value,
"on_method requirements not met");
if(h_max_ && s.size() > h_left_)
{
ec = parse_error::header_too_big;
return;
}
h_left_ -= s.size();
impl().on_method(s, ec);
}
void call_on_method(error_code&,
boost::string_ref const&, std::false_type)
{
}
void call_on_method(error_code& ec,
boost::string_ref const& s)
{
call_on_method(ec, s,
std::integral_constant<bool, isRequest>{});
}
void call_on_uri(error_code& ec,
boost::string_ref const& s, std::true_type)
{
static_assert(check_on_uri<Derived>::value,
"on_uri requirements not met");
if(h_max_ && s.size() > h_left_)
{
ec = parse_error::header_too_big;
return;
}
h_left_ -= s.size();
impl().on_uri(s, ec);
}
void call_on_uri(error_code&,
boost::string_ref const&, std::false_type)
{
}
void call_on_uri(error_code& ec,
boost::string_ref const& s)
{
call_on_uri(ec, s,
std::integral_constant<bool, isRequest>{});
}
void call_on_reason(error_code& ec,
boost::string_ref const& s, std::true_type)
{
static_assert(check_on_reason<Derived>::value,
"on_reason requirements not met");
if(h_max_ && s.size() > h_left_)
{
ec = parse_error::header_too_big;
return;
}
h_left_ -= s.size();
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, ! isRequest>{});
}
void call_on_request(error_code& ec, std::true_type)
{
static_assert(check_on_request<Derived>::value,
"on_request requirements not met");
impl().on_request(ec);
}
void call_on_request(error_code&, std::false_type)
{
}
void call_on_request(error_code& ec)
{
call_on_request(ec,
std::integral_constant<bool, isRequest>{});
}
void call_on_response(error_code& ec, std::true_type)
{
static_assert(check_on_response<Derived>::value,
"on_response requirements not met");
impl().on_response(ec);
}
void call_on_response(error_code&, std::false_type)
{
}
void call_on_response(error_code& ec)
{
call_on_response(ec,
std::integral_constant<bool, ! isRequest>{});
}
void call_on_field(error_code& ec,
boost::string_ref const& s)
{
static_assert(check_on_field<Derived>::value,
"on_field requirements not met");
if(h_max_ && s.size() > h_left_)
{
ec = parse_error::header_too_big;
return;
}
h_left_ -= s.size();
impl().on_field(s, ec);
}
void call_on_value(error_code& ec,
boost::string_ref const& s)
{
static_assert(check_on_value<Derived>::value,
"on_value requirements not met");
if(h_max_ && s.size() > h_left_)
{
ec = parse_error::header_too_big;
return;
}
h_left_ -= s.size();
impl().on_value(s, ec);
}
void
call_on_headers(error_code& ec)
{
static_assert(check_on_headers<Derived>::value,
"on_header requirements not met");
impl().on_header(content_length_, ec);
}
body_what
call_on_body_what(error_code& ec)
{
static_assert(check_on_body_what<Derived>::value,
"on_body_what requirements not met");
return impl().on_body_what(content_length_, ec);
}
void call_on_body(error_code& ec,
boost::string_ref const& s)
{
static_assert(check_on_body<Derived>::value,
"on_body requirements not met");
if(b_max_ && s.size() > b_left_)
{
ec = parse_error::body_too_big;
return;
}
b_left_ -= s.size();
impl().on_body(s, ec);
}
void call_on_complete(error_code& ec)
{
static_assert(check_on_complete<Derived>::value,
"on_complete requirements not met");
impl().on_complete(ec);
}
};
} // http
} // beast
#include <beast/http/impl/basic_parser_v1.ipp>
#endif

View File

@ -5,13 +5,15 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_TYPE_CHECK_HPP
#define BEAST_HTTP_TYPE_CHECK_HPP
#ifndef BEAST_HTTP_CONCEPTS_HPP
#define BEAST_HTTP_CONCEPTS_HPP
#include <beast/config.hpp>
#include <beast/core/error.hpp>
#include <beast/core/buffer_concepts.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/optional.hpp>
#include <type_traits>
#include <utility>
@ -86,45 +88,6 @@ public:
>;
};
template<class T>
class is_Parser
{
template<class U, class R =
std::is_convertible<decltype(
std::declval<U>().complete()),
bool>>
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R =
std::is_convertible<decltype(
std::declval<U>().write(
std::declval<boost::asio::const_buffers_1 const&>(),
std::declval<error_code&>())),
std::size_t>>
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
template<class U, class R = decltype(
std::declval<U>().write_eof(std::declval<error_code&>()),
std::true_type{})>
static R check3(int);
template<class>
static std::false_type check3(...);
using type3 = decltype(check3<T>(0));
public:
using type = std::integral_constant<bool,
type1::value &&
type2::value &&
type3::value
>;
};
} // detail
/// Determine if `T` meets the requirements of @b Body.
@ -183,19 +146,27 @@ template<class T, class M>
struct is_Reader : std::integral_constant<bool, ...> {};
#else
template<class T, class M, class = beast::detail::void_t<>>
struct is_Reader : std::false_type {};
struct is_Reader : std::true_type {};
template<class T, class M>
struct is_Reader<T, M, beast::detail::void_t<decltype(
std::declval<typename T::mutable_buffers_type>(),
std::declval<T>().init(
std::declval<error_code&>()),
std::declval<T>().write(
std::declval<void const*>(),
std::declval<std::size_t>(),
std::declval<error_code&>())
std::declval<boost::optional<std::uint64_t>>()),
std::declval<T>().prepare(
std::declval<std::size_t>()),
std::declval<T>().commit(
std::declval<std::size_t>()),
std::declval<T>().finish()
)> > : std::integral_constant<bool,
std::is_nothrow_constructible<T, M&>::value
>
is_MutableBufferSequence<
typename T::mutable_buffers_type>::value &&
std::is_convertible<decltype(
std::declval<T>().prepare(
std::declval<std::size_t>())),
typename T::mutable_buffers_type
>::value>
{
static_assert(std::is_same<
typename M::body_type::reader, T>::value,
@ -217,14 +188,6 @@ struct is_Writer : std::integral_constant<bool, ...> {};
using is_Writer = typename detail::is_Writer<T, M>::type;
#endif
/// Determine if `T` meets the requirements of @b Parser.
template<class T>
#if BEAST_DOXYGEN
struct is_Parser : std::integral_constant<bool, ...>{};
#else
using is_Parser = typename detail::is_Parser<T>::type;
#endif
} // http
} // beast

View File

@ -0,0 +1,194 @@
//
// 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_DETAIL_BASIC_PARSED_LIST_HPP
#define BEAST_HTTP_DETAIL_BASIC_PARSED_LIST_HPP
#include <beast/core/detail/empty_base_optimization.hpp>
#include <boost/utility/string_ref.hpp>
#include <cstddef>
#include <iterator>
namespace beast {
namespace http {
namespace detail {
/** A list parser which presents the sequence as a container.
*/
template<class Policy>
class basic_parsed_list
{
boost::string_ref s_;
public:
/// The type of policy this list uses for parsing.
using policy_type = Policy;
/// The type of each element in the list.
using value_type = typename Policy::value_type;
/// A constant iterator to a list element.
#if GENERATING_DOCS
using const_iterator = implementation_defined;
#else
class const_iterator;
#endif
class const_iterator
: private beast::detail::
empty_base_optimization<Policy>
{
basic_parsed_list const* list_ = nullptr;
char const* it_ = nullptr;
typename Policy::value_type v_;
bool error_ = false;
public:
using value_type =
typename Policy::value_type;
using reference = value_type const&;
using pointer = value_type const*;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::forward_iterator_tag;
const_iterator() = default;
bool
operator==(
const_iterator const& other) const
{
return
other.list_ == list_ &&
other.it_ == it_;
}
bool
operator!=(
const_iterator const& other) const
{
return ! (*this == other);
}
reference
operator*() const
{
return v_;
}
const_iterator&
operator++()
{
increment();
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
bool
error() const
{
return error_;
}
private:
friend class basic_parsed_list;
const_iterator(
basic_parsed_list const& list, bool at_end)
: list_(&list)
, it_(at_end ? nullptr :
list.s_.begin())
{
if(! at_end)
increment();
}
void
increment()
{
if(! this->member()(
v_, it_, list_->s_))
{
it_ = nullptr;
error_ = true;
}
}
};
/// Construct a list from a string
explicit
basic_parsed_list(boost::string_ref const& s)
: s_(s)
{
}
/// Return a const iterator to the beginning of the list
const_iterator begin() const;
/// Return a const iterator to the end of the list
const_iterator end() const;
/// Return a const iterator to the beginning of the list
const_iterator cbegin() const;
/// Return a const iterator to the end of the list
const_iterator cend() const;
};
template<class Policy>
inline
auto
basic_parsed_list<Policy>::
begin() const ->
const_iterator
{
return const_iterator{*this, false};
}
template<class Policy>
inline
auto
basic_parsed_list<Policy>::
end() const ->
const_iterator
{
return const_iterator{*this, true};
}
template<class Policy>
inline
auto
basic_parsed_list<Policy>::
cbegin() const ->
const_iterator
{
return const_iterator{*this, false};
}
template<class Policy>
inline
auto
basic_parsed_list<Policy>::
cend() const ->
const_iterator
{
return const_iterator{*this, true};
}
} // detail
} // http
} // beast
#endif

View File

@ -0,0 +1,446 @@
//
// 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_DETAIL_BASIC_PARSER_HPP
#define BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
#include <beast/core/detail/ci_char_traits.hpp>
#include <beast/http/error.hpp>
#include <beast/http/detail/rfc7230.hpp>
#include <boost/utility/string_ref.hpp>
#include <boost/version.hpp>
#include <cstddef>
#include <utility>
/*
Portions of this file based on code from picophttpparser,
copyright notice below.
https://github.com/h2o/picohttpparser
*/
/*
* Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
* Shigeo Mitsunari
*
* The software is licensed under either the MIT License (below) or the Perl
* license.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
namespace beast {
namespace http {
namespace detail {
#if __GNUC__ >= 3
# define BEAST_LIKELY(x) __builtin_expect(!!(x), 1)
# define BEAST_UNLIKELY(x) __builtin_expect(!!(x), 0)
#else
#define BEAST_LIKELY(x) (x)
#define BEAST_UNLIKELY(x) (x)
#endif
class basic_parser_base
{
protected:
static
bool
is_pathchar(char c)
{
// VFALCO This looks the same as the one below...
// TEXT = <any OCTET except CTLs, and excluding LWS>
static bool constexpr tab[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
};
return tab[static_cast<unsigned char>(c)];
}
static
bool
is_value_char(char c)
{
// any OCTET except CTLs and LWS
static bool constexpr tab[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
};
return tab[static_cast<unsigned char>(c)];
}
static
inline
bool
is_text(char c)
{
// VCHAR / SP / HT / obs-text
static bool constexpr tab[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
};
return tab[static_cast<unsigned char>(c)];
}
static
inline
bool
unhex(unsigned char& d, char c)
{
static signed char constexpr tab[256] = {
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 16
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 32
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 48
-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 64
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 80
-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 96
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 112
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 128
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 144
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 160
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 176
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 192
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 208
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 224
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 240
};
d = static_cast<unsigned char>(
tab[static_cast<unsigned char>(c)]);
return d != static_cast<unsigned char>(-1);
}
static
bool
is_digit(char c)
{
return static_cast<unsigned char>(c-'0') < 10;
}
static
bool
is_print(char c)
{
return static_cast<unsigned char>(c-33) < 94;
}
static
boost::string_ref
make_string(char const* first, char const* last)
{
return {first, static_cast<
std::size_t>(last - first)};
}
template<class = void>
static
bool
strieq(boost::string_ref const& s1,
boost::string_ref const& s2)
{
if(s1.size() != s2.size())
return false;
auto p1 = s1.data();
auto p2 = s2.data();
for(auto n = s1.size(); n--; ++p1, ++p2)
if(*p1 != tolower(*p2))
return false;
return true;
}
template<std::size_t N>
bool
strieq(const char (&s1)[N],
boost::string_ref const& s2)
{
return strieq({s1, N-1}, s2);
}
template<class Iter, class Unsigned>
static
bool
parse_dec(Iter it, Iter last, Unsigned& v)
{
if(! is_digit(*it))
return false;
v = *it - '0';
for(;;)
{
if(! is_digit(*++it))
break;
auto const d = *it - '0';
if(v > ((std::numeric_limits<
Unsigned>::max)() - 10) / 10)
return false;
v = 10 * v + d;
}
return it == last;
}
template<class Iter, class Unsigned>
bool
parse_hex(Iter& it, Unsigned& v)
{
unsigned char d;
if(! unhex(d, *it))
return false;
v = d;
for(;;)
{
if(! unhex(d, *++it))
break;
auto const v0 = v;
v = 16 * v + d;
if(v <= v0)
return false;
}
return true;
}
static
bool
parse_crlf(char const*& it)
{
if(*it != '\r')
return false;
if(*++it != '\n')
return false;
++it;
return true;
}
static
boost::string_ref
parse_method(char const*& it)
{
auto const first = it;
while(detail::is_tchar(*it))
++it;
return {first, static_cast<
boost::string_ref::size_type>(
it - first)};
}
static
boost::string_ref
parse_path(char const*& it)
{
auto const first = it;
while(is_pathchar(*it))
++it;
if(*it != ' ')
return {};
return {first, static_cast<
boost::string_ref::size_type>(
it - first)};
}
static
boost::string_ref
parse_name(char const*& it)
{
auto const first = it;
while(to_field_char(*it))
++it;
return {first, static_cast<
boost::string_ref::size_type>(
it - first)};
}
static
int
parse_version(char const*& it)
{
if(*it != 'H')
return -1;
if(*++it != 'T')
return -1;
if(*++it != 'T')
return -1;
if(*++it != 'P')
return -1;
if(*++it != '/')
return -1;
if(! is_digit(*++it))
return -1;
int v = 10 * (*it - '0');
if(*++it != '.')
return -1;
if(! is_digit(*++it))
return -1;
v += *it++ - '0';
return v;
}
static
int
parse_status(char const*& it)
{
int v;
if(! is_digit(*it))
return -1;
v = 100 * (*it - '0');
if(! is_digit(*++it))
return -1;
v += 10 * (*it - '0');
if(! is_digit(*++it))
return -1;
v += (*it++ - '0');
return v;
}
static
boost::string_ref
parse_reason(char const*& it)
{
auto const first = it;
while(*it != '\r')
{
if(! is_text(*it))
return {};
++it;
}
return {first, static_cast<
std::size_t>(it - first)};
}
// VFALCO Can SIMD help this?
static
char const*
find_eol(
char const* first, char const* last,
error_code& ec)
{
auto it = first;
for(;;)
{
if(it == last)
return nullptr;
if(*it == '\r')
{
if(++it == last)
return nullptr;
if(*it != '\n')
{
ec = error::bad_line_ending;
return nullptr;
}
return ++it;
}
// VFALCO Should we handle the legacy case
// for lines terminated with a single '\n'?
++it;
}
}
// VFALCO Can SIMD help this?
static
char const*
find_eom(
char const* first, char const* last,
error_code& ec)
{
auto it = first;
for(;;)
{
if(it == last)
return nullptr;
if(*it == '\r')
{
if(++it == last)
return nullptr;
if(*it != '\n')
{
ec = error::bad_line_ending;
return nullptr;
}
if(++it == last)
return nullptr;
if(*it != '\r')
{
++it;
continue;
}
if(++it == last)
return nullptr;
if(*it != '\n')
{
ec = error::bad_line_ending;
return nullptr;
}
return ++it;
}
// VFALCO Should we handle the legacy case
// for lines terminated with a single '\n'?
++it;
}
}
};
} // detail
} // http
} // beast
#endif

View File

@ -1,146 +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_DETAIL_BASIC_PARSER_V1_HPP
#define BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP
#include <cstdint>
namespace beast {
namespace http {
namespace detail {
template<class = void>
struct parser_str_t
{
static char constexpr close[6] = "close";
static char constexpr chunked[8] = "chunked";
static char constexpr keep_alive[11] = "keep-alive";
static char constexpr upgrade[8] = "upgrade";
static char constexpr connection[11] = "connection";
static char constexpr content_length[15] = "content-length";
static char constexpr proxy_connection[17] = "proxy-connection";
static char constexpr transfer_encoding[18] = "transfer-encoding";
};
template<class _>
char constexpr
parser_str_t<_>::close[6];
template<class _>
char constexpr
parser_str_t<_>::chunked[8];
template<class _>
char constexpr
parser_str_t<_>::keep_alive[11];
template<class _>
char constexpr
parser_str_t<_>::upgrade[8];
template<class _>
char constexpr
parser_str_t<_>::connection[11];
template<class _>
char constexpr
parser_str_t<_>::content_length[15];
template<class _>
char constexpr
parser_str_t<_>::proxy_connection[17];
template<class _>
char constexpr
parser_str_t<_>::transfer_encoding[18];
using parser_str = parser_str_t<>;
class parser_base
{
protected:
enum state : std::uint8_t
{
s_dead = 1,
s_req_start,
s_req_method0,
s_req_method,
s_req_url0,
s_req_url,
s_req_http,
s_req_http_H,
s_req_http_HT,
s_req_http_HTT,
s_req_http_HTTP,
s_req_major,
s_req_dot,
s_req_minor,
s_req_cr,
s_req_lf,
s_res_start,
s_res_H,
s_res_HT,
s_res_HTT,
s_res_HTTP,
s_res_major,
s_res_dot,
s_res_minor,
s_res_space_1,
s_res_status0,
s_res_status1,
s_res_status2,
s_res_space_2,
s_res_reason0,
s_res_reason,
s_res_line_lf,
s_res_line_done,
s_header_name0,
s_header_name,
s_header_value0_lf,
s_header_value0_almost_done,
s_header_value0,
s_header_value,
s_header_value_lf,
s_header_value_almost_done,
s_header_value_unfold,
s_headers_almost_done,
s_headers_done,
s_chunk_size0,
s_chunk_size,
s_chunk_ext_name0,
s_chunk_ext_name,
s_chunk_ext_val,
s_chunk_size_lf,
s_chunk_data0,
s_chunk_data,
s_chunk_data_cr,
s_chunk_data_lf,
s_body_pause,
s_body_identity0,
s_body_identity,
s_body_identity_eof0,
s_body_identity_eof,
s_complete,
s_restart,
s_closed_complete
};
};
} // detail
} // http
} // beast
#endif

View File

@ -46,7 +46,7 @@ is_alpha(char c)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240
};
static_assert(sizeof(tab) == 256, "");
return tab[static_cast<std::uint8_t>(c)];
return tab[static_cast<unsigned char>(c)];
}
inline
@ -73,7 +73,7 @@ is_text(char c)
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
};
static_assert(sizeof(tab) == 256, "");
return tab[static_cast<std::uint8_t>(c)];
return tab[static_cast<unsigned char>(c)];
}
inline
@ -105,7 +105,7 @@ is_tchar(char c)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240
};
static_assert(sizeof(tab) == 256, "");
return tab[static_cast<std::uint8_t>(c)];
return tab[static_cast<unsigned char>(c)];
}
inline
@ -134,7 +134,7 @@ is_qdchar(char c)
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
};
static_assert(sizeof(tab) == 256, "");
return tab[static_cast<std::uint8_t>(c)];
return tab[static_cast<unsigned char>(c)];
}
inline
@ -164,7 +164,7 @@ is_qpchar(char c)
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
};
static_assert(sizeof(tab) == 256, "");
return tab[static_cast<std::uint8_t>(c)];
return tab[static_cast<unsigned char>(c)];
}
// converts to lower case,
@ -200,7 +200,7 @@ to_field_char(char c)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static_assert(sizeof(tab) == 256, "");
return tab[static_cast<std::uint8_t>(c)];
return tab[static_cast<unsigned char>(c)];
}
// converts to lower case,
@ -230,9 +230,10 @@ to_value_char(char c)
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // 240
};
static_assert(sizeof(tab) == 256, "");
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
return static_cast<char>(tab[static_cast<unsigned char>(c)]);
}
// VFALCO TODO Make this return unsigned?
inline
std::int8_t
unhex(char c)
@ -256,22 +257,68 @@ unhex(char c)
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240
};
static_assert(sizeof(tab) == 256, "");
return tab[static_cast<std::uint8_t>(c)];
return tab[static_cast<unsigned char>(c)];
}
template<class FwdIt>
inline
void
skip_ows(FwdIt& it, FwdIt const& end)
{
while(it != end)
{
auto const c = *it;
if(c != ' ' && c != '\t')
if(*it != ' ' && *it != '\t')
break;
++it;
}
}
template<class RanIt>
inline
void
skip_ows_rev(
RanIt& it, RanIt const& first)
{
while(it != first)
{
auto const c = it[-1];
if(c != ' ' && c != '\t')
break;
--it;
}
}
// obs-fold = CRLF 1*( SP / HTAB )
// return `false` on parse error
//
template<class FwdIt>
inline
bool
skip_obs_fold(
FwdIt& it, FwdIt const& last)
{
for(;;)
{
if(*it != '\r')
return true;
if(++it == last)
return false;
if(*it != '\n')
return false;
if(++it == last)
return false;
if(*it != ' ' && *it != '\t')
return false;
for(;;)
{
if(++it == last)
return true;
if(*it != ' ' && *it != '\t')
return true;
}
}
}
template<class FwdIt>
void
skip_token(FwdIt& it, FwdIt const& last)
@ -403,6 +450,53 @@ increment()
}
}
/*
#token = [ ( "," / token ) *( OWS "," [ OWS token ] ) ]
*/
struct opt_token_list_policy
{
using value_type = boost::string_ref;
bool
operator()(value_type& v,
char const*& it, boost::string_ref const& s) const
{
v = {};
auto need_comma = it != s.begin();
for(;;)
{
detail::skip_ows(it, s.end());
if(it == s.end())
{
it = nullptr;
return true;
}
auto const c = *it;
if(detail::is_tchar(c))
{
if(need_comma)
return false;
auto const p0 = it;
for(;;)
{
++it;
if(it == s.end())
break;
if(! detail::is_tchar(*it))
break;
}
v = boost::string_ref{&*p0,
static_cast<std::size_t>(it - p0)};
return true;
}
if(c != ',')
return false;
need_comma = false;
++it;
}
}
};
} // detail
} // http
} // beast

View File

@ -0,0 +1,83 @@
//
// 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_ERROR_HPP
#define BEAST_HTTP_ERROR_HPP
#include <beast/config.hpp>
#include <beast/core/error.hpp>
namespace beast {
namespace http {
/// Error codes returned from HTTP parsing
enum class error
{
/** The end of the stream was reached.
This error is returned by @ref basic_parser::write_eof
when the end of stream is reached and there are no
unparsed bytes in the stream buffer.
*/
end_of_stream = 1,
/** The incoming message is incomplete.
This happens when the end of stream is reached
and some bytes have been received, but not the
entire message.
*/
partial_message,
/** Buffer maximum exceeded.
This error is returned when reading HTTP content
into a dynamic buffer, and the operation would
exceed the maximum size of the buffer.
*/
buffer_overflow,
/// The line ending was malformed
bad_line_ending,
/// The method is invalid.
bad_method,
/// The request-target is invalid.
bad_path,
/// The HTTP-version is invalid.
bad_version,
/// The status-code is invalid.
bad_status,
/// The reason-phrase is invalid.
bad_reason,
/// The field name is invalid.
bad_field,
/// The field value is invalid.
bad_value,
/// The Content-Length is invalid.
bad_content_length,
/// The Transfer-Encoding is invalid.
bad_transfer_encoding,
/// The chunk syntax is invalid.
bad_chunk
};
} // http
} // beast
#include <beast/http/impl/error.ipp>
#endif

View File

@ -0,0 +1,190 @@
//
// 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_HEADER_PARSER_HPP
#define BEAST_HTTP_HEADER_PARSER_HPP
#include <beast/config.hpp>
#include <beast/http/message.hpp>
#include <beast/http/basic_parser.hpp>
#include <array>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
/** A parser for producing HTTP/1 headers.
This class uses the basic HTTP/1 wire format parser to convert
a series of octets into a @ref header.
@note A new instance of the parser is required for each message.
@tparam isRequest Indicates whether a request or response
will be parsed.
@tparam Fields The type of container used to represent the fields.
*/
template<bool isRequest, class Fields>
class header_parser
: public basic_parser<isRequest, false,
header_parser<isRequest, Fields>>
{
header<isRequest, Fields> h_;
public:
using mutable_buffers_type =
boost::asio::null_buffers;
/// The type of @ref header this object produces.
using value_type = header<isRequest, Fields>;
/// Copy constructor.
header_parser(header_parser const&) = default;
/// Copy assignment.
header_parser& operator=(header_parser const&) = default;
/** Move constructor.
After the move, the only valid operation
on the moved-from object is destruction.
*/
header_parser(header_parser&&) = default;
/** Constructor
@param args If present, additional arguments to be
forwarded to the @ref beast::http::header constructor.
*/
template<class... Args>
explicit
header_parser(Args&&... args);
/** Returns the parsed header
Only valid if @ref got_header would return `true`.
*/
value_type const&
get() const
{
return h_;
}
/** Returns the parsed header.
Only valid if @ref got_header would return `true`.
*/
value_type&
get()
{
return h_;
}
/** Returns ownership of the parsed header.
Ownership is transferred to the caller. Only
valid if @ref got_header would return `true`.
Requires:
@ref value_type is @b MoveConstructible
*/
value_type
release()
{
static_assert(std::is_move_constructible<decltype(h_)>::value,
"MoveConstructible requirements not met");
return std::move(h_);
}
private:
friend class basic_parser<
isRequest, false, header_parser>;
void
on_request(
boost::string_ref const& method,
boost::string_ref const& path,
int version, error_code&)
{
h_.url = std::string{
path.data(), path.size()};
h_.method = std::string{
method.data(), method.size()};
h_.version = version;
}
void
on_response(int status,
boost::string_ref const& reason,
int version, error_code&)
{
h_.status = status;
h_.reason = std::string{
reason.data(), reason.size()};
h_.version = version;
}
void
on_field(boost::string_ref const& name,
boost::string_ref const& value,
error_code&)
{
h_.fields.insert(name, value);
}
void
on_header(error_code&)
{
}
void
on_body(error_code& ec)
{
}
void
on_body(std::uint64_t content_length,
error_code& ec)
{
}
void
on_data(boost::string_ref const& s,
error_code& ec)
{
}
void
on_commit(std::size_t n)
{
// Can't write body data with header-only parser!
BOOST_ASSERT(false);
throw std::logic_error{
"invalid member function call"};
}
void
on_chunk(std::uint64_t n,
boost::string_ref const& ext,
error_code& ec)
{
}
void
on_complete(error_code&)
{
}
};
} // http
} // beast
#include <beast/http/impl/header_parser.ipp>
#endif

View File

@ -1,233 +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_HEADERS_PARSER_V1_HPP
#define BEAST_HTTP_HEADERS_PARSER_V1_HPP
#include <beast/config.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 <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 a HTTP/1 request or response header.
This class uses the HTTP/1 wire format parser to
convert a series of octets into a request or
response @ref header.
@note A new instance of the parser is required for each message.
*/
template<bool isRequest, class Fields>
class header_parser_v1
: public basic_parser_v1<isRequest,
header_parser_v1<isRequest, Fields>>
, private std::conditional<isRequest,
detail::request_parser_base,
detail::response_parser_base>::type
{
public:
/// The type of the header this parser produces.
using header_type = header<isRequest, Fields>;
private:
// VFALCO Check Fields requirements?
std::string field_;
std::string value_;
header_type h_;
bool flush_ = false;
public:
/// Default constructor
header_parser_v1() = default;
/// Move constructor
header_parser_v1(header_parser_v1&&) = default;
/// Copy constructor (disallowed)
header_parser_v1(header_parser_v1 const&) = delete;
/// Move assignment (disallowed)
header_parser_v1& operator=(header_parser_v1&&) = delete;
/// Copy assignment (disallowed)
header_parser_v1& operator=(header_parser_v1 const&) = delete;
/** Construct the parser.
@param args Forwarded to the header constructor.
*/
#if BEAST_DOXYGEN
template<class... Args>
explicit
header_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>::value>>
explicit
header_parser_v1(Arg1&& arg1, ArgN&&... argn)
: h_(std::forward<Arg1>(arg1),
std::forward<ArgN>(argn)...)
{
}
#endif
/** Returns the parsed header
Only valid if @ref complete would return `true`.
*/
header_type const&
get() const
{
return h_;
}
/** Returns the parsed header.
Only valid if @ref complete would return `true`.
*/
header_type&
get()
{
return h_;
}
/** Returns ownership of the parsed header.
Ownership is transferred to the caller. Only
valid if @ref complete would return `true`.
Requires:
@ref header_type is @b MoveConstructible
*/
header_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, header_parser_v1>;
void flush()
{
if(! flush_)
return;
flush_ = false;
BOOST_ASSERT(! field_.empty());
h_.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)
{
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_header(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

View File

@ -0,0 +1,716 @@
//
// 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_IMPL_ASYNC_READ_IPP_HPP
#define BEAST_HTTP_IMPL_ASYNC_READ_IPP_HPP
#include <beast/http/concepts.hpp>
#include <beast/http/error.hpp>
#include <beast/http/message_parser.hpp>
#include <beast/http/read.hpp>
#include <beast/core/bind_handler.hpp>
#include <beast/core/handler_helpers.hpp>
#include <beast/core/handler_ptr.hpp>
#include <beast/core/stream_concepts.hpp>
#include <boost/assert.hpp>
#include <boost/optional.hpp>
namespace beast {
namespace http {
namespace detail {
template<class Stream, class DynamicBuffer,
bool isRequest, bool isDirect, class Derived,
class Handler>
class read_some_buffer_op
{
struct data
{
bool cont;
Stream& s;
DynamicBuffer& db;
basic_parser<isRequest, isDirect, Derived>& p;
boost::optional<typename
DynamicBuffer::mutable_buffers_type> mb;
boost::optional<typename
Derived::mutable_buffers_type> bb;
std::size_t bytes_used;
int state = 0;
data(Handler& handler, Stream& s_, DynamicBuffer& db_,
basic_parser<isRequest, isDirect, Derived>& p_)
: cont(beast_asio_helpers::
is_continuation(handler))
, s(s_)
, db(db_)
, p(p_)
{
}
};
handler_ptr<data, Handler> d_;
public:
read_some_buffer_op(read_some_buffer_op&&) = default;
read_some_buffer_op(read_some_buffer_op const&) = default;
template<class DeducedHandler, class... Args>
read_some_buffer_op(DeducedHandler&& h,
Stream& s, Args&&... args)
: d_(std::forward<DeducedHandler>(h),
s, std::forward<Args>(args)...)
{
(*this)(error_code{}, 0, false);
}
void
operator()(error_code ec,
std::size_t bytes_transferred, bool again = true);
friend
void*
asio_handler_allocate(std::size_t size,
read_some_buffer_op* op)
{
return beast_asio_helpers::
allocate(size, op->d_.handler());
}
friend
void
asio_handler_deallocate(
void* p, std::size_t size,
read_some_buffer_op* op)
{
return beast_asio_helpers::
deallocate(p, size, op->d_.handler());
}
friend
bool
asio_handler_is_continuation(
read_some_buffer_op* op)
{
return op->d_->cont;
}
template<class Function>
friend
void
asio_handler_invoke(Function&& f,
read_some_buffer_op* op)
{
return beast_asio_helpers::
invoke(f, op->d_.handler());
}
};
template<class Stream, class DynamicBuffer,
bool isRequest, bool isDirect, class Derived,
class Handler>
void
read_some_buffer_op<Stream, DynamicBuffer,
isRequest, isDirect, Derived, Handler>::
operator()(error_code ec,
std::size_t bytes_transferred, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
if(d.state == 99)
goto upcall;
for(;;)
{
switch(d.state)
{
case 0:
if(d.db.size() == 0)
{
d.state = 2;
break;
}
//[[fallthrough]]
case 1:
{
BOOST_ASSERT(d.db.size() > 0);
d.bytes_used =
d.p.write(d.db.data(), ec);
if(d.bytes_used > 0 || ec)
{
// call handler
if(d.state == 1)
goto upcall;
d.state = 99;
d.s.get_io_service().post(
bind_handler(std::move(*this), ec, 0));
return;
}
//[[fallthrough]]
}
case 2:
case 3:
{
auto const size =
read_size_helper(d.db, 65536);
BOOST_ASSERT(size > 0);
try
{
d.mb.emplace(d.db.prepare(size));
}
catch(std::length_error const&)
{
// call handler
if(d.state == 3)
goto upcall;
d.state = 99;
d.s.get_io_service().post(
bind_handler(std::move(*this),
error::buffer_overflow, 0));
return;
}
// read
d.state = 4;
d.s.async_read_some(*d.mb, std::move(*this));
return;
}
case 4:
if(ec == boost::asio::error::eof)
{
BOOST_ASSERT(bytes_transferred == 0);
d.bytes_used = 0;
if(! d.p.got_some())
goto upcall;
// caller sees EOF on next read.
ec = {};
d.p.write_eof(ec);
if(ec)
goto upcall;
BOOST_ASSERT(d.p.is_complete());
goto upcall;
}
else if(ec)
{
d.bytes_used = 0;
goto upcall;
}
BOOST_ASSERT(bytes_transferred > 0);
d.db.commit(bytes_transferred);
d.state = 1;
break;
}
}
upcall:
// can't pass any members of `d` otherwise UB
auto const bytes_used = d.bytes_used;
d_.invoke(ec, bytes_used);
}
//------------------------------------------------------------------------------
template<class Stream, class DynamicBuffer,
bool isRequest, class Derived, class Handler>
class read_some_body_op
{
struct data
{
bool cont;
Stream& s;
DynamicBuffer& db;
basic_parser<isRequest, true, Derived>& p;
boost::optional<typename
Derived::mutable_buffers_type> mb;
std::size_t bytes_used;
int state = 0;
data(Handler& handler, Stream& s_, DynamicBuffer& db_,
basic_parser<isRequest, true, Derived>& p_)
: cont(beast_asio_helpers::
is_continuation(handler))
, s(s_)
, db(db_)
, p(p_)
{
}
};
handler_ptr<data, Handler> d_;
public:
read_some_body_op(read_some_body_op&&) = default;
read_some_body_op(read_some_body_op const&) = default;
template<class DeducedHandler, class... Args>
read_some_body_op(DeducedHandler&& h,
Stream& s, Args&&... args)
: d_(std::forward<DeducedHandler>(h),
s, std::forward<Args>(args)...)
{
(*this)(error_code{}, 0, false);
}
void
operator()(error_code ec,
std::size_t bytes_transferred, bool again = true);
friend
void*
asio_handler_allocate(std::size_t size,
read_some_body_op* op)
{
return beast_asio_helpers::
allocate(size, op->d_.handler());
}
friend
void
asio_handler_deallocate(
void* p, std::size_t size,
read_some_body_op* op)
{
return beast_asio_helpers::
deallocate(p, size, op->d_.handler());
}
friend
bool
asio_handler_is_continuation(
read_some_body_op* op)
{
return op->d_->cont;
}
template<class Function>
friend
void
asio_handler_invoke(Function&& f,
read_some_body_op* op)
{
return beast_asio_helpers::
invoke(f, op->d_.handler());
}
};
template<class Stream, class DynamicBuffer,
bool isRequest, class Derived, class Handler>
void
read_some_body_op<Stream, DynamicBuffer,
isRequest, Derived, Handler>::
operator()(error_code ec,
std::size_t bytes_transferred, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
if(d.state == 99)
goto upcall;
for(;;)
{
switch(d.state)
{
case 0:
if(d.db.size() > 0)
{
d.bytes_used = d.p.copy_body(d.db);
// call handler
d.state = 99;
d.s.get_io_service().post(
bind_handler(std::move(*this),
ec, 0));
return;
}
d.p.prepare_body(d.mb, 65536);
// read
d.state = 1;
d.s.async_read_some(
*d.mb, std::move(*this));
return;
case 1:
d.bytes_used = 0;
if(ec == boost::asio::error::eof)
{
BOOST_ASSERT(bytes_transferred == 0);
// caller sees EOF on next read
ec = {};
d.p.write_eof(ec);
if(ec)
goto upcall;
BOOST_ASSERT(d.p.is_complete());
}
else if(! ec)
{
d.p.commit_body(bytes_transferred);
}
goto upcall;
}
}
upcall:
// can't pass any members of `d` otherwise UB
auto const bytes_used = d.bytes_used;
d_.invoke(ec, bytes_used);
}
//------------------------------------------------------------------------------
template<class Stream, class DynamicBuffer,
bool isRequest, bool isDirect, class Derived,
class Handler>
class parse_op
{
struct data
{
bool cont;
Stream& s;
DynamicBuffer& db;
basic_parser<isRequest, isDirect, Derived>& p;
data(Handler& handler, Stream& s_, DynamicBuffer& db_,
basic_parser<isRequest, isDirect, Derived>& p_)
: cont(beast_asio_helpers::
is_continuation(handler))
, s(s_)
, db(db_)
, p(p_)
{
}
};
handler_ptr<data, Handler> d_;
public:
parse_op(parse_op&&) = default;
parse_op(parse_op const&) = default;
template<class DeducedHandler, class... Args>
parse_op(DeducedHandler&& h,
Stream& s, Args&&... args)
: d_(std::forward<DeducedHandler>(h),
s, std::forward<Args>(args)...)
{
(*this)(error_code{}, 0, false);
}
void
operator()(error_code const& ec,
std::size_t bytes_used, bool again = true);
friend
void*
asio_handler_allocate(
std::size_t size, parse_op* op)
{
return beast_asio_helpers::
allocate(size, op->d_.handler());
}
friend
void
asio_handler_deallocate(
void* p, std::size_t size,
parse_op* op)
{
return beast_asio_helpers::
deallocate(p, size, op->d_.handler());
}
friend
bool
asio_handler_is_continuation(
parse_op* op)
{
return op->d_->cont;
}
template<class Function>
friend
void
asio_handler_invoke(
Function&& f, parse_op* op)
{
return beast_asio_helpers::
invoke(f, op->d_.handler());
}
};
template<class Stream, class DynamicBuffer,
bool isRequest, bool isDirect, class Derived,
class Handler>
void
parse_op<Stream, DynamicBuffer,
isRequest, isDirect, Derived, Handler>::
operator()(error_code const& ec,
std::size_t bytes_used, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
if(! ec)
{
d.db.consume(bytes_used);
if(! d.p.is_complete())
return async_read_some(
d.s, d.db, d.p, std::move(*this));
}
d_.invoke(ec);
}
//------------------------------------------------------------------------------
template<class Stream, class DynamicBuffer,
bool isRequest, class Body, class Fields,
class Handler>
class read_message_op
{
using parser_type =
message_parser<isRequest, Body, Fields>;
using message_type =
message<isRequest, Body, Fields>;
struct data
{
bool cont;
Stream& s;
DynamicBuffer& db;
message_type& m;
parser_type p;
bool started = false;
int state = 0;
data(Handler& handler, Stream& s_,
DynamicBuffer& sb_, message_type& m_)
: cont(beast_asio_helpers::
is_continuation(handler))
, s(s_)
, db(sb_)
, m(m_)
{
}
};
handler_ptr<data, Handler> d_;
public:
read_message_op(read_message_op&&) = default;
read_message_op(read_message_op const&) = default;
template<class DeducedHandler, class... Args>
read_message_op(DeducedHandler&& h, Stream& s, Args&&... args)
: d_(std::forward<DeducedHandler>(h),
s, std::forward<Args>(args)...)
{
(*this)(error_code{}, false);
}
void
operator()(error_code ec, bool again = true);
friend
void* asio_handler_allocate(
std::size_t size, read_message_op* op)
{
return beast_asio_helpers::
allocate(size, op->d_.handler());
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, read_message_op* op)
{
return beast_asio_helpers::
deallocate(p, size, op->d_.handler());
}
friend
bool asio_handler_is_continuation(read_message_op* op)
{
return op->d_->cont;
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, read_message_op* op)
{
return beast_asio_helpers::
invoke(f, op->d_.handler());
}
};
template<class Stream, class DynamicBuffer,
bool isRequest, class Body, class Fields,
class Handler>
void
read_message_op<Stream, DynamicBuffer, isRequest, Body, Fields, Handler>::
operator()(error_code ec, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
if(ec)
goto upcall;
switch(d.state)
{
case 0:
d.state = 1;
beast::http::async_read(
d.s, d.db, d.p, std::move(*this));
return;
case 1:
d.m = d.p.release();
goto upcall;
}
upcall:
d_.invoke(ec);
}
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, class Derived,
class ReadHandler>
typename async_completion<
ReadHandler, void(error_code, std::size_t)>::result_type
async_read_some(
AsyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, true, Derived>& parser,
ReadHandler&& handler)
{
beast::async_completion<ReadHandler,
void(error_code, std::size_t)> completion{handler};
switch(parser.state())
{
case parse_state::header:
case parse_state::chunk_header:
detail::read_some_buffer_op<AsyncReadStream,
DynamicBuffer, isRequest, true, Derived,
decltype(completion.handler)>{
completion.handler, stream, dynabuf, parser};
break;
default:
detail::read_some_body_op<AsyncReadStream,
DynamicBuffer, isRequest, Derived,
decltype(completion.handler)>{
completion.handler, stream, dynabuf, parser};
break;
}
return completion.result.get();
}
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, class Derived,
class ReadHandler>
inline
typename async_completion<
ReadHandler, void(error_code, std::size_t)>::result_type
async_read_some(
AsyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, false, Derived>& parser,
ReadHandler&& handler)
{
beast::async_completion<ReadHandler,
void(error_code, std::size_t)> completion{handler};
detail::read_some_buffer_op<AsyncReadStream,
DynamicBuffer, isRequest, false, Derived,
decltype(completion.handler)>{
completion.handler, stream, dynabuf, parser};
return completion.result.get();
}
} // detail
//------------------------------------------------------------------------------
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived,
class ReadHandler>
typename async_completion<
ReadHandler, void(error_code, std::size_t)>::result_type
async_read_some(
AsyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, isDirect, Derived>& parser,
ReadHandler&& handler)
{
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
BOOST_ASSERT(! parser.is_complete());
return detail::async_read_some(stream, dynabuf, parser,
std::forward<ReadHandler>(handler));
}
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived,
class ReadHandler>
typename async_completion<
ReadHandler, void(error_code)>::result_type
async_read(
AsyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, isDirect, Derived>& parser,
ReadHandler&& handler)
{
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
BOOST_ASSERT(! parser.is_complete());
beast::async_completion<ReadHandler,
void(error_code)> completion{handler};
detail::parse_op<AsyncReadStream, DynamicBuffer,
isRequest, isDirect, Derived, decltype(completion.handler)>{
completion.handler, stream, dynabuf, parser};
return completion.result.get();
}
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, class Body, class Fields,
class ReadHandler>
typename async_completion<
ReadHandler, void(error_code)>::result_type
async_read(
AsyncReadStream& stream,
DynamicBuffer& dynabuf,
message<isRequest, Body, Fields>& msg,
ReadHandler&& handler)
{
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(is_Body<Body>::value,
"Body requirements not met");
static_assert(has_reader<Body>::value,
"Body has no reader");
static_assert(is_Reader<typename Body::reader,
message<isRequest, Body, Fields>>::value,
"Reader requirements not met");
beast::async_completion<ReadHandler,
void(error_code)> completion{handler};
detail::read_message_op<AsyncReadStream, DynamicBuffer,
isRequest, Body, Fields, decltype(
completion.handler)>{completion.handler,
stream, dynabuf, msg};
return completion.result.get();
}
} // http
} // beast
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,106 @@
//
// 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_IMPL_ERROR_IPP
#define BEAST_HTTP_IMPL_ERROR_IPP
#include <type_traits>
namespace boost {
namespace system {
template<>
struct is_error_code_enum<beast::http::error>
{
static bool const value = true;
};
} // system
} // boost
namespace beast {
namespace http {
namespace detail {
class http_error_category : public error_category
{
public:
const char*
name() const noexcept override
{
return "http";
}
std::string
message(int ev) const override
{
switch(static_cast<error>(ev))
{
default:
case error::end_of_stream: return "end of stream";
case error::partial_message: return "partial message";
case error::buffer_overflow: return "buffer overflow";
case error::bad_line_ending: return "bad line ending";
case error::bad_method: return "bad method";
case error::bad_path: return "bad path";
case error::bad_version: return "bad version";
case error::bad_status: return "bad status";
case error::bad_reason: return "bad reason";
case error::bad_field: return "bad field";
case error::bad_value: return "bad value";
case error::bad_content_length: return "bad Content-Length";
case error::bad_transfer_encoding: return "bad Transfer-Encoding";
case error::bad_chunk: return "bad chunk";
}
}
error_condition
default_error_condition(
int ev) const noexcept override
{
return error_condition{ev, *this};
}
bool
equivalent(int ev,
error_condition const& condition
) const noexcept override
{
return condition.value() == ev &&
&condition.category() == this;
}
bool
equivalent(error_code const& error,
int ev) const noexcept override
{
return error.value() == ev &&
&error.category() == this;
}
};
inline
error_category const&
get_http_error_category()
{
static http_error_category const cat{};
return cat;
}
} // detail
inline
error_code
make_error_code(error ev)
{
return error_code{
static_cast<std::underlying_type<error>::type>(ev),
detail::get_http_error_category()};
}
} // http
} // beast
#endif

View File

@ -0,0 +1,25 @@
//
// 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_IMPL_HEADER_PARSER_IPP
#define BEAST_HTTP_IMPL_HEADER_PARSER_IPP
namespace beast {
namespace http {
template<bool isRequest, class Fields>
template<class... Args>
header_parser<isRequest, Fields>::
header_parser(Args&&... args)
: h_(std::forward<Args>(args)...)
{
}
} // http
} // beast
#endif

View File

@ -0,0 +1,38 @@
//
// 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_IMPL_MESSAGE_PARSER_IPP
#define BEAST_HTTP_IMPL_MESSAGE_PARSER_IPP
namespace beast {
namespace http {
template<bool isRequest, class Body, class Fields>
template<class Arg1, class... ArgN, class>
message_parser<isRequest, Body, Fields>::
message_parser(Arg1&& arg1, ArgN&&... argn)
: m_(std::forward<Arg1>(arg1),
std::forward<ArgN>(argn)...)
{
}
template<bool isRequest, class Body, class Fields>
template<class... Args>
message_parser<isRequest, Body, Fields>::
message_parser(header_parser<
isRequest, Fields>&& parser, Args&&... args)
: base_type(std::move(static_cast<basic_parser<
isRequest, false, header_parser<
isRequest, Fields>>&>(parser)))
, m_(parser.release(), std::forward<Args>(args)...)
{
}
} // http
} // beast
#endif

View File

@ -1,302 +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_IMPL_PARSE_IPP_HPP
#define BEAST_HTTP_IMPL_PARSE_IPP_HPP
#include <beast/http/concepts.hpp>
#include <beast/core/bind_handler.hpp>
#include <beast/core/handler_helpers.hpp>
#include <beast/core/handler_ptr.hpp>
#include <beast/core/stream_concepts.hpp>
#include <boost/assert.hpp>
namespace beast {
namespace http {
namespace detail {
template<class Stream,
class DynamicBuffer, class Parser, class Handler>
class parse_op
{
struct data
{
bool cont;
Stream& s;
DynamicBuffer& db;
Parser& p;
bool got_some = false;
int state = 0;
data(Handler& handler, Stream& s_,
DynamicBuffer& sb_, Parser& p_)
: cont(beast_asio_helpers::
is_continuation(handler))
, s(s_)
, db(sb_)
, p(p_)
{
BOOST_ASSERT(! p.complete());
}
};
handler_ptr<data, Handler> d_;
public:
parse_op(parse_op&&) = default;
parse_op(parse_op const&) = default;
template<class DeducedHandler, class... Args>
parse_op(DeducedHandler&& h, Stream& s, Args&&... args)
: d_(std::forward<DeducedHandler>(h),
s, std::forward<Args>(args)...)
{
(*this)(error_code{}, 0, false);
}
void
operator()(error_code ec,
std::size_t bytes_transferred, bool again = true);
friend
void* asio_handler_allocate(
std::size_t size, parse_op* op)
{
return beast_asio_helpers::
allocate(size, op->d_.handler());
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, parse_op* op)
{
return beast_asio_helpers::
deallocate(p, size, op->d_.handler());
}
friend
bool asio_handler_is_continuation(parse_op* op)
{
return op->d_->cont;
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, parse_op* op)
{
return beast_asio_helpers::
invoke(f, op->d_.handler());
}
};
template<class Stream,
class DynamicBuffer, class Parser, class Handler>
void
parse_op<Stream, DynamicBuffer, Parser, Handler>::
operator()(error_code ec, std::size_t bytes_transferred, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
while(d.state != 99)
{
switch(d.state)
{
case 0:
{
// Parse any bytes left over in the buffer
auto const used =
d.p.write(d.db.data(), ec);
if(ec)
{
// call handler
d.state = 99;
d.s.get_io_service().post(
bind_handler(std::move(*this), ec, 0));
return;
}
if(used > 0)
{
d.got_some = true;
d.db.consume(used);
}
if(d.p.complete())
{
// call handler
d.state = 99;
d.s.get_io_service().post(
bind_handler(std::move(*this), ec, 0));
return;
}
// Buffer must be empty,
// otherwise parse should be complete
BOOST_ASSERT(d.db.size() == 0);
d.state = 1;
break;
}
case 1:
{
// read
d.state = 2;
auto const size =
read_size_helper(d.db, 65536);
BOOST_ASSERT(size > 0);
d.s.async_read_some(
d.db.prepare(size), std::move(*this));
return;
}
// got data
case 2:
{
if(ec == boost::asio::error::eof)
{
// If we haven't processed any bytes,
// give the eof to the handler immediately.
if(! d.got_some)
{
// call handler
d.state = 99;
break;
}
// Feed the eof to the parser to complete
// the parse, and call the handler. The
// next call to parse will deliver the eof.
ec = {};
d.p.write_eof(ec);
BOOST_ASSERT(ec || d.p.complete());
// call handler
d.state = 99;
break;
}
if(ec)
{
// call handler
d.state = 99;
break;
}
BOOST_ASSERT(bytes_transferred > 0);
d.db.commit(bytes_transferred);
auto const used = d.p.write(d.db.data(), ec);
if(ec)
{
// call handler
d.state = 99;
break;
}
// The parser must either consume
// bytes or generate an error.
BOOST_ASSERT(used > 0);
d.got_some = true;
d.db.consume(used);
if(d.p.complete())
{
// call handler
d.state = 99;
break;
}
// If the parse is not complete,
// all input must be consumed.
BOOST_ASSERT(used == bytes_transferred);
d.state = 1;
break;
}
}
}
d_.invoke(ec);
}
} // detail
//------------------------------------------------------------------------------
template<class SyncReadStream, class DynamicBuffer, class Parser>
void
parse(SyncReadStream& stream,
DynamicBuffer& dynabuf, Parser& parser)
{
static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(is_Parser<Parser>::value,
"Parser requirements not met");
error_code ec;
parse(stream, dynabuf, parser, ec);
if(ec)
throw system_error{ec};
}
template<class SyncReadStream, class DynamicBuffer, class Parser>
void
parse(SyncReadStream& stream, DynamicBuffer& dynabuf,
Parser& parser, error_code& ec)
{
static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(is_Parser<Parser>::value,
"Parser requirements not met");
bool got_some = false;
for(;;)
{
auto used =
parser.write(dynabuf.data(), ec);
if(ec)
return;
dynabuf.consume(used);
if(used > 0)
got_some = true;
if(parser.complete())
break;
dynabuf.commit(stream.read_some(
dynabuf.prepare(read_size_helper(
dynabuf, 65536)), ec));
if(ec && ec != boost::asio::error::eof)
return;
if(ec == boost::asio::error::eof)
{
if(! got_some)
return;
// Caller will see eof on next read.
ec = {};
parser.write_eof(ec);
if(ec)
return;
BOOST_ASSERT(parser.complete());
break;
}
}
}
template<class AsyncReadStream,
class DynamicBuffer, class Parser, class ReadHandler>
typename async_completion<
ReadHandler, void(error_code)>::result_type
async_parse(AsyncReadStream& stream,
DynamicBuffer& dynabuf, Parser& parser, ReadHandler&& handler)
{
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(is_Parser<Parser>::value,
"Parser requirements not met");
beast::async_completion<ReadHandler,
void(error_code)> completion{handler};
detail::parse_op<AsyncReadStream, DynamicBuffer,
Parser, decltype(completion.handler)>{
completion.handler, stream, dynabuf, parser};
return completion.result.get();
}
} // http
} // beast
#endif

View File

@ -1,105 +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_IMPL_PARSE_ERROR_IPP
#define BEAST_HTTP_IMPL_PARSE_ERROR_IPP
namespace boost {
namespace system {
template<>
struct is_error_code_enum<beast::http::parse_error>
{
static bool const value = true;
};
} // system
} // boost
namespace beast {
namespace http {
namespace detail {
class parse_error_category : public error_category
{
public:
const char*
name() const noexcept override
{
return "http";
}
std::string
message(int ev) const override
{
switch(static_cast<parse_error>(ev))
{
case parse_error::connection_closed: return "data after Connection close";
case parse_error::bad_method: return "bad method";
case parse_error::bad_uri: return "bad request-target";
case parse_error::bad_version: return "bad HTTP-Version";
case parse_error::bad_crlf: return "missing CRLF";
case parse_error::bad_status: return "bad status-code";
case parse_error::bad_reason: return "bad reason-phrase";
case parse_error::bad_field: return "bad field token";
case parse_error::bad_value: return "bad field-value";
case parse_error::bad_content_length: return "bad Content-Length";
case parse_error::illegal_content_length: return "illegal Content-Length with chunked Transfer-Encoding";
case parse_error::invalid_chunk_size: return "invalid chunk size";
case parse_error::invalid_ext_name: return "invalid ext name";
case parse_error::invalid_ext_val: return "invalid ext val";
case parse_error::header_too_big: return "header size limit exceeded";
case parse_error::body_too_big: return "body size limit exceeded";
default:
case parse_error::short_read: return "unexpected end of data";
}
}
error_condition
default_error_condition(int ev) const noexcept override
{
return error_condition{ev, *this};
}
bool
equivalent(int ev,
error_condition const& condition
) const noexcept override
{
return condition.value() == ev &&
&condition.category() == this;
}
bool
equivalent(error_code const& error, int ev) const noexcept override
{
return error.value() == ev &&
&error.category() == this;
}
};
inline
error_category const&
get_parse_error_category()
{
static parse_error_category const cat{};
return cat;
}
} // detail
inline
error_code
make_error_code(parse_error ev)
{
return error_code{
static_cast<std::underlying_type<parse_error>::type>(ev),
detail::get_parse_error_category()};
}
} // http
} // beast
#endif

View File

@ -9,306 +9,275 @@
#define BEAST_HTTP_IMPL_READ_IPP_HPP
#include <beast/http/concepts.hpp>
#include <beast/http/header_parser_v1.hpp>
#include <beast/http/parse.hpp>
#include <beast/http/parser_v1.hpp>
#include <beast/http/error.hpp>
#include <beast/http/message_parser.hpp>
#include <beast/http/read.hpp>
#include <beast/core/bind_handler.hpp>
#include <beast/core/handler_helpers.hpp>
#include <beast/core/handler_ptr.hpp>
#include <beast/core/stream_concepts.hpp>
#include <boost/assert.hpp>
#include <boost/optional.hpp>
namespace beast {
namespace http {
namespace detail {
template<class Stream, class DynamicBuffer,
bool isRequest, class Fields,
class Handler>
class read_header_op
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
inline
std::size_t
read_some_buffer(
SyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, isDirect, Derived>& parser,
error_code& ec)
{
using parser_type =
header_parser_v1<isRequest, Fields>;
using message_type =
header<isRequest, Fields>;
struct data
std::size_t bytes_used;
if(dynabuf.size() == 0)
goto do_read;
for(;;)
{
bool cont;
Stream& s;
DynamicBuffer& db;
message_type& m;
parser_type p;
bool started = false;
int state = 0;
data(Handler& handler, Stream& s_,
DynamicBuffer& sb_, message_type& m_)
: cont(beast_asio_helpers::
is_continuation(handler))
, s(s_)
, db(sb_)
, m(m_)
bytes_used = parser.write(
dynabuf.data(), ec);
if(ec)
return 0;
if(bytes_used > 0)
goto do_finish;
do_read:
boost::optional<typename
DynamicBuffer::mutable_buffers_type> mb;
auto const size =
read_size_helper(dynabuf, 65536);
BOOST_ASSERT(size > 0);
try
{
mb.emplace(dynabuf.prepare(size));
}
};
handler_ptr<data, Handler> d_;
public:
read_header_op(read_header_op&&) = default;
read_header_op(read_header_op const&) = default;
template<class DeducedHandler, class... Args>
read_header_op(
DeducedHandler&& h, Stream& s, Args&&... args)
: d_(std::forward<DeducedHandler>(h),
s, std::forward<Args>(args)...)
{
(*this)(error_code{}, false);
}
void
operator()(error_code ec, bool again = true);
friend
void* asio_handler_allocate(
std::size_t size, read_header_op* op)
{
return beast_asio_helpers::
allocate(size, op->d_.handler());
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, read_header_op* op)
{
return beast_asio_helpers::
deallocate(p, size, op->d_.handler());
}
friend
bool asio_handler_is_continuation(read_header_op* op)
{
return op->d_->cont;
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, read_header_op* op)
{
return beast_asio_helpers::
invoke(f, op->d_.handler());
}
};
template<class Stream, class DynamicBuffer,
bool isRequest, class Fields,
class Handler>
void
read_header_op<Stream, DynamicBuffer, isRequest, Fields, Handler>::
operator()(error_code ec, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
while(! ec && d.state != 99)
{
switch(d.state)
catch(std::length_error const&)
{
case 0:
d.state = 1;
async_parse(d.s, d.db, d.p, std::move(*this));
return;
case 1:
// call handler
d.state = 99;
d.m = d.p.release();
ec = error::buffer_overflow;
return 0;
}
auto const bytes_transferred =
stream.read_some(*mb, ec);
if(ec == boost::asio::error::eof)
{
BOOST_ASSERT(bytes_transferred == 0);
bytes_used = 0;
if(parser.got_some())
{
// caller sees EOF on next read
ec = {};
parser.write_eof(ec);
if(ec)
return 0;
BOOST_ASSERT(parser.is_complete());
}
break;
}
else if(ec)
{
return 0;
}
BOOST_ASSERT(bytes_transferred > 0);
dynabuf.commit(bytes_transferred);
}
d_.invoke(ec);
do_finish:
return bytes_used;
}
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, class Derived>
inline
std::size_t
read_some_body(
SyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, true, Derived>& parser,
error_code& ec)
{
if(dynabuf.size() > 0)
return parser.copy_body(dynabuf);
boost::optional<typename
Derived::mutable_buffers_type> mb;
try
{
parser.prepare_body(mb, 65536);
}
catch(std::length_error const&)
{
ec = error::buffer_overflow;
return 0;
}
auto const bytes_transferred =
stream.read_some(*mb, ec);
if(ec == boost::asio::error::eof)
{
BOOST_ASSERT(bytes_transferred == 0);
// caller sees EOF on next read
ec = {};
parser.write_eof(ec);
if(ec)
return 0;
BOOST_ASSERT(parser.is_complete());
}
else if(! ec)
{
parser.commit_body(bytes_transferred);
return 0;
}
return 0;
}
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, class Derived>
inline
std::size_t
read_some(
SyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, true, Derived>& parser,
error_code& ec)
{
switch(parser.state())
{
case parse_state::header:
case parse_state::chunk_header:
return detail::read_some_buffer(
stream, dynabuf, parser, ec);
default:
return detail::read_some_body(
stream, dynabuf, parser, ec);
}
}
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, class Derived>
inline
std::size_t
read_some(
SyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, false, Derived>& parser,
error_code& ec)
{
return detail::read_some_buffer(
stream, dynabuf, parser, ec);
}
} // detail
template<class SyncReadStream, class DynamicBuffer,
bool isRequest, class Fields>
void
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
header<isRequest, Fields>& msg)
//------------------------------------------------------------------------------
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
std::size_t
read_some(
SyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, isDirect, Derived>& parser)
{
static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
BOOST_ASSERT(! parser.is_complete());
error_code ec;
beast::http::read(stream, dynabuf, msg, ec);
auto const bytes_used =
read_some(stream, dynabuf, parser, ec);
if(ec)
throw system_error{ec};
return bytes_used;
}
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
std::size_t
read_some(
SyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, isDirect, Derived>& parser,
error_code& ec)
{
static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
BOOST_ASSERT(! parser.is_complete());
return detail::read_some(stream, dynabuf, parser, ec);
}
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
void
read(
SyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, isDirect, Derived>& parser)
{
static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
BOOST_ASSERT(! parser.is_complete());
error_code ec;
read(stream, dynabuf, parser, ec);
if(ec)
throw system_error{ec};
}
template<class SyncReadStream, class DynamicBuffer,
bool isRequest, class Fields>
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
void
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
header<isRequest, Fields>& m,
error_code& ec)
read(
SyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, isDirect, Derived>& parser,
error_code& ec)
{
static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
header_parser_v1<isRequest, Fields> p;
beast::http::parse(stream, dynabuf, p, ec);
if(ec)
return;
BOOST_ASSERT(p.complete());
m = p.release();
}
template<class AsyncReadStream, class DynamicBuffer,
bool isRequest, class Fields,
class ReadHandler>
typename async_completion<
ReadHandler, void(error_code)>::result_type
async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
header<isRequest, Fields>& m,
ReadHandler&& handler)
{
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
beast::async_completion<ReadHandler,
void(error_code)> completion{handler};
detail::read_header_op<AsyncReadStream, DynamicBuffer,
isRequest, Fields, decltype(
completion.handler)>{completion.handler,
stream, dynabuf, m};
return completion.result.get();
}
//------------------------------------------------------------------------------
namespace detail {
template<class Stream, class DynamicBuffer,
bool isRequest, class Body, class Fields,
class Handler>
class read_op
{
using parser_type =
parser_v1<isRequest, Body, Fields>;
using message_type =
message<isRequest, Body, Fields>;
struct data
BOOST_ASSERT(! parser.is_complete());
do
{
bool cont;
Stream& s;
DynamicBuffer& db;
message_type& m;
parser_type p;
bool started = false;
int state = 0;
data(Handler& handler, Stream& s_,
DynamicBuffer& sb_, message_type& m_)
: cont(beast_asio_helpers::
is_continuation(handler))
, s(s_)
, db(sb_)
, m(m_)
{
}
};
handler_ptr<data, Handler> d_;
public:
read_op(read_op&&) = default;
read_op(read_op const&) = default;
template<class DeducedHandler, class... Args>
read_op(DeducedHandler&& h, Stream& s, Args&&... args)
: d_(std::forward<DeducedHandler>(h),
s, std::forward<Args>(args)...)
{
(*this)(error_code{}, false);
}
void
operator()(error_code ec, bool again = true);
friend
void* asio_handler_allocate(
std::size_t size, read_op* op)
{
return beast_asio_helpers::
allocate(size, op->d_.handler());
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, read_op* op)
{
return beast_asio_helpers::
deallocate(p, size, op->d_.handler());
}
friend
bool asio_handler_is_continuation(read_op* op)
{
return op->d_->cont;
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, read_op* op)
{
return beast_asio_helpers::
invoke(f, op->d_.handler());
}
};
template<class Stream, class DynamicBuffer,
bool isRequest, class Body, class Fields,
class Handler>
void
read_op<Stream, DynamicBuffer, isRequest, Body, Fields, Handler>::
operator()(error_code ec, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
while(! ec && d.state != 99)
{
switch(d.state)
{
case 0:
d.state = 1;
async_parse(d.s, d.db, d.p, std::move(*this));
auto const bytes_used =
read_some(stream, dynabuf, parser, ec);
if(ec)
return;
case 1:
// call handler
d.state = 99;
d.m = d.p.release();
break;
}
dynabuf.consume(bytes_used);
}
d_.invoke(ec);
while(! parser.is_complete());
}
} // detail
template<class SyncReadStream, class DynamicBuffer,
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, class Body, class Fields>
void
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
read(
SyncReadStream& stream,
DynamicBuffer& dynabuf,
message<isRequest, Body, Fields>& msg)
{
static_assert(is_SyncReadStream<SyncReadStream>::value,
@ -328,12 +297,16 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
throw system_error{ec};
}
template<class SyncReadStream, class DynamicBuffer,
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, class Body, class Fields>
void
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
message<isRequest, Body, Fields>& m,
error_code& ec)
read(
SyncReadStream& stream,
DynamicBuffer& dynabuf,
message<isRequest, Body, Fields>& msg,
error_code& ec)
{
static_assert(is_SyncReadStream<SyncReadStream>::value,
"SyncReadStream requirements not met");
@ -346,41 +319,11 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
static_assert(is_Reader<typename Body::reader,
message<isRequest, Body, Fields>>::value,
"Reader requirements not met");
parser_v1<isRequest, Body, Fields> p;
beast::http::parse(stream, dynabuf, p, ec);
message_parser<isRequest, Body, Fields> p;
beast::http::read(stream, dynabuf, p, ec);
if(ec)
return;
BOOST_ASSERT(p.complete());
m = p.release();
}
template<class AsyncReadStream, class DynamicBuffer,
bool isRequest, class Body, class Fields,
class ReadHandler>
typename async_completion<
ReadHandler, void(error_code)>::result_type
async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
message<isRequest, Body, Fields>& m,
ReadHandler&& handler)
{
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(is_Body<Body>::value,
"Body requirements not met");
static_assert(has_reader<Body>::value,
"Body has no reader");
static_assert(is_Reader<typename Body::reader,
message<isRequest, Body, Fields>>::value,
"Reader requirements not met");
beast::async_completion<ReadHandler,
void(error_code)> completion{handler};
detail::read_op<AsyncReadStream, DynamicBuffer,
isRequest, Body, Fields, decltype(
completion.handler)>{completion.handler,
stream, dynabuf, m};
return completion.result.get();
msg = p.release();
}
} // http

View File

@ -542,6 +542,26 @@ exists(T const& s)
) != end();
}
template<class Policy>
bool
validate_list(detail::basic_parsed_list<
Policy> const& list)
{
auto const last = list.end();
auto it = list.begin();
if(it.error())
return false;
while(it != last)
{
++it;
if(it.error())
return false;
if(it == last)
break;
}
return true;
}
} // http
} // beast

View File

@ -0,0 +1,286 @@
//
// 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_MESSAGE_PARSER_HPP
#define BEAST_HTTP_MESSAGE_PARSER_HPP
#include <beast/config.hpp>
#include <beast/http/message.hpp>
#include <beast/http/header_parser.hpp>
#include <beast/core/detail/clamp.hpp>
#include <boost/optional.hpp>
#include <array>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
/** 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 @ref message.
@tparam isRequest Indicates whether a request or response
will be parsed.
@tparam Body The type used to represent the body.
@tparam Fields The type of container used to represent the fields.
@note A new instance of the parser is required for each message.
*/
template<bool isRequest, class Body, class Fields>
class message_parser
: public basic_parser<isRequest,
Body::reader::is_direct,
message_parser<isRequest, Body, Fields>>
{
using base_type = basic_parser<isRequest, true,
message_parser<isRequest, Body, Fields>>;
using reader_type = typename Body::reader;
message<isRequest, Body, Fields> m_;
boost::optional<typename Body::reader> r_;
public:
/// The type of message returned by the parser
using value_type = message<isRequest, Body, Fields>;
/// The type of buffer sequence representing the body
using mutable_buffers_type =
typename reader_type::mutable_buffers_type;
/// Constructor (default)
message_parser() = default;
/// Copy constructor (disallowed)
message_parser(message_parser const&) = delete;
/// Copy assignment (disallowed)
message_parser& operator=(message_parser const&) = delete;
/** Move constructor.
After the move, the only valid operation
on the moved-from object is destruction.
*/
message_parser(message_parser&& other);
/** Constructor
@param args Optional arguments forwarded to the
@ref http::header constructor.
@note This function participates in overload
resolution only if the first argument is not a
@ref http::header_parser or @ref message_parser.
*/
#if GENERATING_DOCS
template<class... Args>
explicit
msesage_parser(Args&&... args);
#else
template<class Arg1, class... ArgN,
class = typename std::enable_if<
! std::is_same<typename
std::decay<Arg1>::type,
header_parser<isRequest, Fields>>::value &&
! std::is_same<typename
std::decay<Arg1>::type, message_parser>::value
>::type>
explicit
message_parser(Arg1&& arg1, ArgN&&... argn);
#endif
/** Construct a message parser from a @ref header_parser.
@param parser The header parser to construct from.
@param args Optional arguments forwarded to the message
constructor.
*/
template<class... Args>
explicit
message_parser(header_parser<
isRequest, Fields>&& parser, Args&&... args);
/** Returns the parsed message.
Depending on the progress of the parser, portions
of this object may be incomplete.
*/
value_type const&
get() const
{
return m_;
}
/** Returns the parsed message.
Depending on the progress of the parser, portions
of this object may be incomplete.
*/
value_type&
get()
{
return m_;
}
/** Returns ownership of the parsed message.
Ownership is transferred to the caller.
Depending on the progress of the parser, portions
of this object may be incomplete.
@par Requires
@ref value_type is @b MoveConstructible
*/
value_type
release()
{
static_assert(std::is_move_constructible<decltype(m_)>::value,
"MoveConstructible requirements not met");
return std::move(m_);
}
private:
friend class basic_parser<
isRequest, Body::reader::is_direct,
message_parser>;
void
on_request(
boost::string_ref const& method,
boost::string_ref const& path,
int version, error_code&)
{
m_.url = std::string{
path.data(), path.size()};
m_.method = std::string{
method.data(), method.size()};
m_.version = version;
}
void
on_response(int status,
boost::string_ref const& reason,
int version, error_code&)
{
m_.status = status;
m_.reason = std::string{
reason.data(), reason.size()};
m_.version = version;
}
void
on_field(boost::string_ref const& name,
boost::string_ref const& value,
error_code&)
{
m_.fields.insert(name, value);
}
void
on_header(error_code& ec)
{
}
void
on_body()
{
r_.emplace(m_);
r_->init();
}
void
on_body(std::uint64_t content_length)
{
r_.emplace(m_);
r_->init(content_length);
}
void
on_body(error_code& ec)
{
r_.emplace(m_);
r_->init(ec);
if(ec)
return;
}
void
on_body(std::uint64_t content_length,
error_code& ec)
{
r_.emplace(m_);
r_->init(content_length, ec);
if(ec)
return;
}
void
on_data(boost::string_ref const& s,
error_code& ec)
{
static_assert(! Body::reader::is_direct, "");
r_->write(s, ec);
}
mutable_buffers_type
on_prepare(std::size_t n)
{
return r_->prepare(n);
}
void
on_commit(std::size_t n)
{
r_->commit(n);
}
void
on_chunk(std::uint64_t,
boost::string_ref const&,
error_code&)
{
}
void
on_complete(error_code& ec)
{
if(r_)
do_on_complete(ec,
std::integral_constant<bool,
Body::reader::is_direct>{});
}
void
do_on_complete(
error_code& ec, std::true_type)
{
r_->finish();
}
void
do_on_complete(
error_code& ec, std::false_type)
{
r_->finish(ec);
if(ec)
return;
}
};
} // http
} // beast
#include <beast/http/impl/message_parser.ipp>
#endif

View File

@ -1,155 +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_PARSE_HPP
#define BEAST_HTTP_PARSE_HPP
#include <beast/config.hpp>
#include <beast/core/error.hpp>
#include <beast/core/async_completion.hpp>
namespace beast {
namespace http {
/** Parse an object from a stream.
This function synchronously reads from a stream and passes
data to the specified parser. The call will block until one
of the following conditions are met:
@li The parser indicates that parsing is complete.
@li An error occurs in the stream or parser.
This function is implemented in terms of one or more calls
to the stream's `read_some` function. The implementation may
read additional octets that lie past the end of the object
being parsed. This additional data is stored in the stream
buffer, which may be used in subsequent calls.
@note This algorithm is generic, and not specific to HTTP
messages. It is up to the parser to determine what predicate
defines a complete operation.
@param stream The stream from which the data is to be read.
The type must support the @b SyncReadStream concept.
@param dynabuf A @b DynamicBuffer holding additional bytes
read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the
stream buffer's input sequence will be given to the parser
first.
@param parser An object meeting the requirements of @b Parser
which will receive the data.
@throws system_error Thrown on failure.
*/
template<class SyncReadStream, class DynamicBuffer, class Parser>
void
parse(SyncReadStream& stream,
DynamicBuffer& dynabuf, Parser& parser);
/** Parse an object from a stream.
This function synchronously reads from a stream and passes
data to the specified parser. The call will block until one
of the following conditions are met:
@li The parser indicates that parsing is complete.
@li An error occurs in the stream or parser.
This function is implemented in terms of one or more calls
to the stream's `read_some` function. The implementation may
read additional octets that lie past the end of the object
being parsed. This additional data is stored in the stream
buffer, which may be used in subsequent calls.
@note This algorithm is generic, and not specific to HTTP
messages. It is up to the parser to determine what predicate
defines a complete operation.
@param stream The stream from which the data is to be read.
The type must support the @b SyncReadStream concept.
@param dynabuf A @b DynamicBuffer holding additional bytes
read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the
stream buffer's input sequence will be given to the parser
first.
@param parser An object meeting the requirements of @b Parser
which will receive the data.
@param ec Set to the error, if any occurred.
*/
template<class SyncReadStream, class DynamicBuffer, class Parser>
void
parse(SyncReadStream& stream,
DynamicBuffer& dynabuf, Parser& parser, error_code& ec);
/** Start an asynchronous operation to parse an object from a stream.
This function is used to asynchronously read from a stream and
pass the data to the specified parser. The function call always
returns immediately. The asynchronous operation will continue
until one of the following conditions is true:
@li The parser indicates that parsing is complete.
@li An error occurs in the stream or parser.
This operation is implemented in terms of one or more calls to
the next layer's `async_read_some` function, and is known as a
<em>composed operation</em>. The program must ensure that the
stream performs no other operations until this operation completes.
The implementation may read additional octets that lie past the
end of the object being parsed. This additional data is stored
in the stream buffer, which may be used in subsequent calls.
@param stream The stream from which the data is to be read.
The type must support the @b AsyncReadStream concept.
@param dynabuf A @b DynamicBuffer holding additional bytes
read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the
stream buffer's input sequence will be given to the parser
first.
@param parser An object meeting the requirements of @b Parser
which will receive the data. This object must remain valid
until the completion handler is invoked.
@param handler The handler to be called when the request
completes. Copies will be made of the handler as required.
The equivalent function signature of the handler must be:
@code void handler(
error_code const& error // result of operation
); @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class AsyncReadStream,
class DynamicBuffer, class Parser, class ReadHandler>
#if BEAST_DOXYGEN
void_or_deduced
#else
typename async_completion<
ReadHandler, void(error_code)>::result_type
#endif
async_parse(AsyncReadStream& stream, DynamicBuffer& dynabuf,
Parser& parser, ReadHandler&& handler);
} // http
} // beast
#include <beast/http/impl/parse.ipp>
#endif

View File

@ -1,48 +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_PARSE_ERROR_HPP
#define BEAST_HTTP_PARSE_ERROR_HPP
#include <beast/config.hpp>
#include <beast/core/error.hpp>
namespace beast {
namespace http {
enum class parse_error
{
connection_closed = 1,
bad_method,
bad_uri,
bad_version,
bad_crlf,
bad_status,
bad_reason,
bad_field,
bad_value,
bad_content_length,
illegal_content_length,
invalid_chunk_size,
invalid_ext_name,
invalid_ext_val,
header_too_big,
body_too_big,
short_read
};
} // http
} // beast
#include <beast/http/impl/parse_error.ipp>
#endif

View File

@ -1,339 +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_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 BEAST_DOXYGEN
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

View File

@ -11,135 +11,298 @@
#include <beast/config.hpp>
#include <beast/core/async_completion.hpp>
#include <beast/core/error.hpp>
#include <beast/http/basic_parser.hpp>
#include <beast/http/message.hpp>
namespace beast {
namespace http {
/** Read a HTTP/1 header from a stream.
/** Read some HTTP/1 message data from a stream.
This function is used to synchronously read a header
from a stream. The call blocks until one of the following
conditions is true:
This function synchronously advances the state of the
parser using the provided dynamic buffer and reading
from the input stream as needed. The call will block
until one of the following conditions is true:
@li An entire header is read in.
@li When expecting a message header, and the complete
header is received.
@li When expecting a chunk header, and the complete
chunk header is received.
@li When expecting body octets, one or more body octets
are received.
@li An error occurs in the stream or parser.
This function is implemented in terms of one or more calls
to the stream's `read_some` function. The implementation may
read additional octets that lie past the end of the message
fields being parsed. This additional data is stored in the
stream buffer, which may be used in subsequent calls.
If the message corresponding to the header being received
contains a message body, it is the callers responsibility
to cause the body to be read in before attempting to read
the next message.
read additional octets that lie past the end of the object
being parsed. This additional data is stored in the dynamic
buffer, which may be used in subsequent calls.
@param stream The stream from which the data is to be read.
The type must support the @b `SyncReadStream` concept.
The type must support the @b SyncReadStream concept.
@param dynabuf A @b `DynamicBuffer` holding additional bytes
@param dynabuf A @b DynamicBuffer holding additional bytes
read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the
stream buffer's input sequence will be given to the parser
dynamic buffer's input sequence will be given to the parser
first.
@param msg An object used to store the header. Any contents
will be overwritten. The type must support copy assignment
or move assignment.
@param parser The parser to use.
@return The number of bytes processed from the dynamic
buffer. The caller should remove these bytes by calling
`consume` on the dynamic buffer.
@throws system_error Thrown on failure.
*/
template<class SyncReadStream, class DynamicBuffer,
bool isRequest, class Fields>
void
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
header<isRequest, Fields>& msg);
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
std::size_t
read_some(
SyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, isDirect, Derived>& parser);
/** Read a HTTP/1 header from a stream.
/** Read some HTTP/1 message data from a stream.
This function is used to synchronously read a header
from a stream. The call blocks until one of the following
conditions is true:
This function synchronously advances the state of the
parser using the provided dynamic buffer and reading
from the input stream as needed. The call will block
until one of the following conditions is true:
@li An entire header is read in.
@li When expecting a message header, and the complete
header is received.
@li When expecting a chunk header, and the complete
chunk header is received.
@li When expecting body octets, one or more body octets
are received.
@li An error occurs in the stream or parser.
This function is implemented in terms of one or more calls
to the stream's `read_some` function. The implementation may
read additional octets that lie past the end of the message
fields being parsed. This additional data is stored in the
stream buffer, which may be used in subsequent calls.
If the message corresponding to the header being received
contains a message body, it is the callers responsibility
to cause the body to be read in before attempting to read
the next message.
read additional octets that lie past the end of the object
being parsed. This additional data is stored in the dynamic
buffer, which may be used in subsequent calls.
@param stream The stream from which the data is to be read.
The type must support the @b `SyncReadStream` concept.
The type must support the @b SyncReadStream concept.
@param dynabuf A @b `DynamicBuffer` holding additional bytes
@param dynabuf A @b DynamicBuffer holding additional bytes
read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the
stream buffer's input sequence will be given to the parser
dynamic buffer's input sequence will be given to the parser
first.
@param msg An object used to store the header. Any contents
will be overwritten. The type must support copy assignment
or move assignment.
@param parser The parser to use.
@param ec Set to the error, if any occurred.
@return The number of bytes processed from the dynamic
buffer. The caller should remove these bytes by calling
`consume` on the dynamic buffer.
*/
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
std::size_t
read_some(
SyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, isDirect, Derived>& parser,
error_code& ec);
/** Start an asynchronous operation to read some HTTP/1 message data from a stream.
This function asynchronously advances the state of the
parser using the provided dynamic buffer and reading from
the input stream as needed. The function call always
returns immediately. The asynchronous operation will
continue until one of the following conditions is true:
@li When expecting a message header, and the complete
header is received.
@li When expecting a chunk header, and the complete
chunk header is received.
@li When expecting body octets, one or more body octets
are received.
@li An error occurs in the stream or parser.
This operation is implemented in terms of zero or more calls to
the next layer's `async_read_some` function, and is known as a
<em>composed operation</em>. The program must ensure that the
stream performs no other operations until this operation completes.
The implementation may read additional octets that lie past the
end of the object being parsed. This additional data is stored
in the stream buffer, which may be used in subsequent calls.
The completion handler will be called with the number of bytes
processed from the dynamic buffer. The caller should remove
these bytes by calling `consume` on the dynamic buffer.
@param stream The stream from which the data is to be read.
The type must support the @b AsyncReadStream concept.
@param dynabuf A @b DynamicBuffer holding additional bytes
read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the
dynamic buffer's input sequence will be given to the parser
first.
@param parser The parser to use.
@param handler The handler to be called when the request
completes. Copies will be made of the handler as required.
The equivalent function signature of the handler must be:
@code void handler(
error_code const& error, // result of operation
std::size_t bytes_used // the number of bytes to consume
); @endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`.
*/
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived,
class ReadHandler>
#if GENERATING_DOCS
void_or_deduced
#else
typename async_completion<
ReadHandler, void(error_code, std::size_t)>::result_type
#endif
async_read_some(
AsyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, isDirect, Derived>& parser,
ReadHandler&& handler);
//------------------------------------------------------------------------------
/** Read an HTTP/1 message from a stream.
This function synchronously reads from a stream and passes
data to the specified parser. The call will block until one
of the following conditions is true:
@li The parser indicates no more additional data is needed.
@li An error occurs in the stream or parser.
This function is implemented in terms of one or more calls
to the stream's `read_some` function. The implementation may
read additional octets that lie past the end of the object
being parsed. This additional data is stored in the dynamic
buffer, which may be used in subsequent calls.
@param stream The stream from which the data is to be read.
The type must support the @b SyncReadStream concept.
@param dynabuf A @b DynamicBuffer holding additional bytes
read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the
dynamic buffer's input sequence will be given to the parser
first.
@param parser The parser to use.
@throws system_error Thrown on failure.
*/
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
void
read(
SyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, isDirect, Derived>& parser);
/** Read an HTTP/1 message from a stream.
This function synchronously reads from a stream and passes
data to the specified parser. The call will block until one
of the following conditions is true:
@li The parser indicates that no more data is needed.
@li An error occurs in the stream or parser.
This function is implemented in terms of one or more calls
to the stream's `read_some` function. The implementation may
read additional octets that lie past the end of the object
being parsed. This additional data is stored in the dynamic
buffer, which may be used in subsequent calls.
@param stream The stream from which the data is to be read.
The type must support the @b SyncReadStream concept.
@param dynabuf A @b DynamicBuffer holding additional bytes
read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the
dynamic buffer's input sequence will be given to the parser
first.
@param parser The parser to use.
@param ec Set to the error, if any occurred.
*/
template<class SyncReadStream, class DynamicBuffer,
bool isRequest, class Fields>
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
void
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
header<isRequest, Fields>& msg,
error_code& ec);
read(
SyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, isDirect, Derived>& parser,
error_code& ec);
/** Read a HTTP/1 header asynchronously from a stream.
/** Start an asynchronous operation to read an HTTP/1 message from a stream.
This function is used to asynchronously read a header from
a stream. The function call always returns immediately. The
asynchronous operation will continue until one of the following
conditions is true:
This function is used to asynchronously read from a stream and
pass the data to the specified parser. The function call always
returns immediately. The asynchronous operation will continue
until one of the following conditions is true:
@li An entire header is read in.
@li The parser indicates that no more data is needed.
@li An error occurs in the stream or parser.
This operation is implemented in terms of one or more calls to
the stream's `async_read_some` function, and is known as a
the next layer's `async_read_some` function, and is known as a
<em>composed operation</em>. The program must ensure that the
stream performs no other operations until this operation completes.
The implementation may read additional octets that lie past the
end of the message fields being parsed. This additional data is
stored in the stream buffer, which may be used in subsequent calls.
end of the object being parsed. This additional data is stored
in the stream buffer, which may be used in subsequent calls.
If the message corresponding to the header being received
contains a message body, it is the callers responsibility
to cause the body to be read in before attempting to read
the next message.
@param stream The stream from which the data is to be read.
The type must support the @b AsyncReadStream concept.
@param stream The stream to read the message from.
The type must support the @b `AsyncReadStream` concept.
@param dynabuf A @b `DynamicBuffer` holding additional bytes
@param dynabuf A @b DynamicBuffer holding additional bytes
read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the
stream buffer's input sequence will be given to the parser
dynamic buffer's input sequence will be given to the parser
first.
@param msg An object used to store the header. Any contents
will be overwritten. The type must support copy assignment or
move assignment. The object must remain valid at least until
the completion handler is called; ownership is not transferred.
@param parser The parser to use.
@param handler The handler to be called when the operation
@param handler The handler to be called when the request
completes. Copies will be made of the handler as required.
The equivalent function signature of the handler must be:
@code void handler(
@ -150,20 +313,24 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class AsyncReadStream, class DynamicBuffer,
bool isRequest, class Body, class Fields,
class ReadHandler>
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived,
class ReadHandler>
#if BEAST_DOXYGEN
void_or_deduced
#else
typename async_completion<
ReadHandler, void(error_code)>::result_type
#endif
async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
header<isRequest, Fields>& msg,
ReadHandler&& handler);
async_read(
AsyncReadStream& stream,
DynamicBuffer& dynabuf,
basic_parser<isRequest, isDirect, Derived>& parser,
ReadHandler&& handler);
/** Read a HTTP/1 message from a stream.
/** Read an HTTP/1 message from a stream.
This function is used to synchronously read a message from
a stream. The call blocks until one of the following conditions
@ -176,7 +343,7 @@ async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
This function is implemented in terms of one or more calls
to the stream's `read_some` function. The implementation may
read additional octets that lie past the end of the message
being parsed. This additional data is stored in the stream
being parsed. This additional data is stored in the dynamic
buffer, which may be used in subsequent calls.
@param stream The stream from which the data is to be read.
@ -185,7 +352,7 @@ async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
@param dynabuf A @b `DynamicBuffer` holding additional bytes
read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the
stream buffer's input sequence will be given to the parser
dynamic buffer's input sequence will be given to the parser
first.
@param msg An object used to store the message. Any
@ -194,10 +361,14 @@ async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
@throws system_error Thrown on failure.
*/
template<class SyncReadStream, class DynamicBuffer,
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, class Body, class Fields>
void
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
read(
SyncReadStream& stream,
DynamicBuffer& dynabuf,
message<isRequest, Body, Fields>& msg);
/** Read a HTTP/1 message from a stream.
@ -213,7 +384,7 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
This function is implemented in terms of one or more calls
to the stream's `read_some` function. The implementation may
read additional octets that lie past the end of the message
being parsed. This additional data is stored in the stream
being parsed. This additional data is stored in the dynamic
buffer, which may be used in subsequent calls.
@param stream The stream from which the data is to be read.
@ -222,7 +393,7 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
@param dynabuf A @b `DynamicBuffer` holding additional bytes
read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the
stream buffer's input sequence will be given to the parser
dynamic buffer's input sequence will be given to the parser
first.
@param msg An object used to store the message. Any
@ -231,12 +402,16 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
@param ec Set to the error, if any occurred.
*/
template<class SyncReadStream, class DynamicBuffer,
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, class Body, class Fields>
void
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
read(
SyncReadStream& stream,
DynamicBuffer& dynabuf,
message<isRequest, Body, Fields>& msg,
error_code& ec);
error_code& ec);
/** Read a HTTP/1 message asynchronously from a stream.
@ -255,7 +430,7 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
stream performs no other operations until this operation completes.
The implementation may read additional octets that lie past the
end of the message being parsed. This additional data is stored
in the stream buffer, which may be used in subsequent calls.
in the dynamic buffer, which may be used in subsequent calls.
@param stream The stream to read the message from.
The type must support the @b `AsyncReadStream` concept.
@ -263,7 +438,7 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
@param dynabuf A @b `DynamicBuffer` holding additional bytes
read by the implementation from the stream. This is both
an input and an output parameter; on entry, any data in the
stream buffer's input sequence will be given to the parser
dynamic buffer's input sequence will be given to the parser
first.
@param msg An object used to store the header. Any contents
@ -282,22 +457,27 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class AsyncReadStream, class DynamicBuffer,
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, class Body, class Fields,
class ReadHandler>
class ReadHandler>
#if BEAST_DOXYGEN
void_or_deduced
#else
typename async_completion<
ReadHandler, void(error_code)>::result_type
#endif
async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
async_read(
AsyncReadStream& stream,
DynamicBuffer& dynabuf,
message<isRequest, Body, Fields>& msg,
ReadHandler&& handler);
ReadHandler&& handler);
} // http
} // beast
#include <beast/http/impl/async_read.ipp>
#include <beast/http/impl/read.ipp>
#endif

View File

@ -10,6 +10,7 @@
#include <beast/config.hpp>
#include <beast/http/detail/rfc7230.hpp>
#include <beast/http/detail/basic_parsed_list.hpp>
namespace beast {
namespace http {
@ -276,6 +277,46 @@ public:
exists(T const& s);
};
/** A list of tokens in a comma separated HTTP field value.
This container allows iteration of a list of items in a
header field value. The input is a comma separated list of
tokens.
If a parsing error is encountered while iterating the string,
the behavior of the container will be as if a string containing
only characters up to but excluding the first invalid character
was used to construct the list.
@par BNF
@code
token-list = *( "," OWS ) token *( OWS "," [ OWS token ] )
@endcode
To use this class, construct with the string to be parsed and
then use `begin` and `end`, or range-for to iterate each item:
@par Example
@code
for(auto const& token : token_list{"apple, pear, banana"})
std::cout << token << "\n";
@endcode
*/
using opt_token_list =
detail::basic_parsed_list<
detail::opt_token_list_policy>;
/** Returns `true` if a parsed list is parsed without errors.
This function iterates a single pass through a parsed list
and returns `true` if there were no parsing errors, else
returns `false`.
*/
template<class Policy>
bool
validate_list(detail::basic_parsed_list<
Policy> const& list);
} // http
} // beast

View File

@ -34,29 +34,58 @@ private:
class reader
{
value_type& s_;
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,
string_body, Fields>& m) noexcept
: s_(m.body)
string_body, Fields>& m)
: body_(m.body)
{
}
void
init(error_code&) noexcept
init()
{
}
void
write(void const* data,
std::size_t size, error_code&) noexcept
init(std::uint64_t content_length)
{
auto const n = s_.size();
s_.resize(n + size);
std::memcpy(&s_[n], data, size);
if(content_length >
(std::numeric_limits<std::size_t>::max)())
throw std::length_error{
"Content-Length overflow"};
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_);
}
};

View File

@ -8,7 +8,6 @@
#ifndef BEAST_WEBSOCKET_DETAIL_DECORATOR_HPP
#define BEAST_WEBSOCKET_DETAIL_DECORATOR_HPP
#include <beast/http/empty_body.hpp>
#include <beast/http/message.hpp>
#include <beast/http/string_body.hpp>
#include <beast/version.hpp>
@ -19,9 +18,9 @@ namespace beast {
namespace websocket {
namespace detail {
using request_type = http::request<http::empty_body>;
using request_type = http::request_header;
using response_type = http::response<http::string_body>;
using response_type = http::response_header;
struct abstract_decorator
{

View File

@ -17,7 +17,6 @@
#include <beast/websocket/detail/mask.hpp>
#include <beast/websocket/detail/pmd_extension.hpp>
#include <beast/websocket/detail/utf8_checker.hpp>
#include <beast/http/empty_body.hpp>
#include <beast/http/message.hpp>
#include <beast/http/string_body.hpp>
#include <beast/zlib/deflate_stream.hpp>

View File

@ -9,7 +9,7 @@
#define BEAST_WEBSOCKET_IMPL_ACCEPT_IPP
#include <beast/http/message.hpp>
#include <beast/http/parser_v1.hpp>
#include <beast/http/header_parser.hpp>
#include <beast/http/read.hpp>
#include <beast/http/string_body.hpp>
#include <beast/http/write.hpp>
@ -35,13 +35,13 @@ class stream<NextLayer>::response_op
{
bool cont;
stream<NextLayer>& ws;
http::response<http::string_body> res;
http::response_header res;
error_code final_ec;
int state = 0;
template<class Body, class Fields>
template<class Fields>
data(Handler&, stream<NextLayer>& ws_,
http::request<Body, Fields> const& req,
http::header<true, Fields> const& req,
bool cont_)
: cont(cont_)
, ws(ws_)
@ -151,7 +151,7 @@ class stream<NextLayer>::accept_op
{
bool cont;
stream<NextLayer>& ws;
http::request<http::string_body> req;
http::header_parser<true, http::fields> p;
int state = 0;
template<class Buffers>
@ -190,8 +190,8 @@ public:
(*this)(ec, 0);
}
void operator()(error_code const& ec,
std::size_t bytes_transferred, bool again = true);
void operator()(error_code ec,
std::size_t bytes_used, bool again = true);
friend
void* asio_handler_allocate(
@ -228,36 +228,39 @@ template<class NextLayer>
template<class Handler>
void
stream<NextLayer>::accept_op<Handler>::
operator()(error_code const& ec,
std::size_t bytes_transferred, bool again)
operator()(error_code ec,
std::size_t bytes_used, bool again)
{
beast::detail::ignore_unused(bytes_transferred);
auto& d = *d_;
d.cont = d.cont || again;
while(! ec && d.state != 99)
if(ec)
goto upcall;
switch(d.state)
{
switch(d.state)
{
case 0:
// read message
d.state = 1;
http::async_read(d.ws.next_layer(),
d.ws.stream_.buffer(), d.req,
std::move(*this));
return;
case 0:
// read message
d.state = 1;
http::async_read_some(d.ws.next_layer(),
d.ws.stream_.buffer(), d.p,
std::move(*this));
return;
// got message
case 1:
{
// respond to request
auto& ws = d.ws;
auto req = std::move(d.req);
response_op<Handler>{
d_.release_handler(), ws, req, true};
return;
}
}
case 1:
{
BOOST_ASSERT(d.p.got_header());
d.ws.stream_.buffer().consume(bytes_used);
// Arguments from our state must be
// moved to the stack before releasing
// the handler.
auto& ws = d.ws;
auto m = d.p.release();
response_op<Handler>{
d_.release_handler(),
ws, std::move(m), true};
return;
}
}
upcall:
d_.invoke(ec);
}
@ -295,11 +298,11 @@ async_accept(ConstBufferSequence const& bs, AcceptHandler&& handler)
}
template<class NextLayer>
template<class Body, class Fields, class AcceptHandler>
template<class Fields, class AcceptHandler>
typename async_completion<
AcceptHandler, void(error_code)>::result_type
stream<NextLayer>::
async_accept(http::request<Body, Fields> const& req,
async_accept(http::header<true, Fields> const& req,
AcceptHandler&& handler)
{
static_assert(is_AsyncStream<next_layer_type>::value,
@ -372,18 +375,21 @@ accept(ConstBufferSequence const& buffers, error_code& ec)
stream_.buffer().commit(buffer_copy(
stream_.buffer().prepare(
buffer_size(buffers)), buffers));
http::request<http::string_body> m;
http::read(next_layer(), stream_.buffer(), m, ec);
http::header_parser<true, http::fields> p;
auto const bytes_used = http::read_some(
next_layer(), stream_.buffer(), p, ec);
if(ec)
return;
accept(m, ec);
BOOST_ASSERT(p.got_header());
stream_.buffer().consume(bytes_used);
accept(p.get(), ec);
}
template<class NextLayer>
template<class Body, class Fields>
template<class Fields>
void
stream<NextLayer>::
accept(http::request<Body, Fields> const& request)
accept(http::header<true, Fields> const& request)
{
static_assert(is_SyncStream<next_layer_type>::value,
"SyncStream requirements not met");
@ -394,10 +400,10 @@ accept(http::request<Body, Fields> const& request)
}
template<class NextLayer>
template<class Body, class Fields>
template<class Fields>
void
stream<NextLayer>::
accept(http::request<Body, Fields> const& req,
accept(http::header<true, Fields> const& req,
error_code& ec)
{
static_assert(is_SyncStream<next_layer_type>::value,
@ -418,8 +424,6 @@ accept(http::request<Body, Fields> const& req,
open(detail::role_type::server);
}
//------------------------------------------------------------------------------
} // websocket
} // beast

View File

@ -8,9 +8,9 @@
#ifndef BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP
#define BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP
#include <beast/http/empty_body.hpp>
#include <beast/http/message.hpp>
#include <beast/http/read.hpp>
#include <beast/http/streambuf_body.hpp>
#include <beast/http/write.hpp>
#include <beast/core/handler_helpers.hpp>
#include <beast/core/handler_ptr.hpp>
@ -34,8 +34,8 @@ class stream<NextLayer>::handshake_op
bool cont;
stream<NextLayer>& ws;
std::string key;
http::request<http::empty_body> req;
http::response<http::string_body> resp;
http::request_header req;
http::response<http::streambuf_body> res;
int state = 0;
data(Handler& handler, stream<NextLayer>& ws_,
@ -129,14 +129,14 @@ operator()(error_code ec, bool again)
// read http response
d.state = 2;
http::async_read(d.ws.next_layer(),
d.ws.stream_.buffer(), d.resp,
d.ws.stream_.buffer(), d.res,
std::move(*this));
return;
// got response
case 2:
{
d.ws.do_response(d.resp, d.key, ec);
d.ws.do_response(d.res, d.key, ec);
// call handler
d.state = 99;
break;
@ -196,7 +196,7 @@ handshake(boost::string_ref const& host,
}
if(ec)
return;
http::response<http::string_body> res;
http::response<http::streambuf_body> res;
http::read(next_layer(), stream_.buffer(), res, ec);
if(ec)
return;

View File

@ -83,17 +83,18 @@ reset()
}
template<class NextLayer>
http::request<http::empty_body>
http::request_header
stream<NextLayer>::
build_request(boost::string_ref const& host,
boost::string_ref const& resource, std::string& key)
{
http::request<http::empty_body> req;
http::request_header req;
req.url = { resource.data(), resource.size() };
req.version = 11;
req.method = "GET";
req.fields.insert("Host", host);
req.fields.insert("Upgrade", "websocket");
req.fields.insert("Connection", "upgrade");
key = detail::make_sec_ws_key(maskgen_);
req.fields.insert("Sec-WebSocket-Key", key);
req.fields.insert("Sec-WebSocket-Version", "13");
@ -113,15 +114,13 @@ build_request(boost::string_ref const& host,
req.fields, config);
}
d_(req);
http::prepare(req, http::connection::upgrade);
return req;
}
template<class NextLayer>
template<class Body, class Fields>
http::response<http::string_body>
http::response_header
stream<NextLayer>::
build_response(http::request<Body, Fields> const& req)
build_response(http::request_header const& req)
{
auto err =
[&](std::string const& text)
@ -170,7 +169,7 @@ build_response(http::request<Body, Fields> const& req)
return res;
}
}
http::response<http::string_body> res;
http::response_header res;
{
detail::pmd_offer offer;
detail::pmd_offer unused;
@ -182,23 +181,22 @@ build_response(http::request<Body, Fields> const& req)
res.reason = http::reason_string(res.status);
res.version = req.version;
res.fields.insert("Upgrade", "websocket");
res.fields.insert("Connection", "upgrade");
{
auto const key =
req.fields["Sec-WebSocket-Key"];
res.fields.insert("Sec-WebSocket-Accept",
detail::make_sec_ws_accept(key));
}
res.fields.replace("Server", "Beast.WSProto");
res.fields.replace("Server", "Beast.WebSocket");
d_(res);
http::prepare(res, http::connection::upgrade);
return res;
}
template<class NextLayer>
template<class Body, class Fields>
void
stream<NextLayer>::
do_response(http::response<Body, Fields> const& res,
do_response(http::response_header const& res,
boost::string_ref const& key, error_code& ec)
{
// VFALCO Review these error codes

View File

@ -597,9 +597,9 @@ public:
@throws system_error Thrown on failure.
*/
// VFALCO TODO This should also take a DynamicBuffer with any leftover bytes.
template<class Body, class Fields>
template<class Fields>
void
accept(http::request<Body, Fields> const& request);
accept(http::header<true, Fields> const& request);
/** Respond to a WebSocket HTTP Upgrade request
@ -629,10 +629,9 @@ public:
@param ec Set to indicate what error occurred, if any.
*/
template<class Body, class Fields>
template<class Fields>
void
accept(http::request<Body, Fields> const& request,
error_code& ec);
accept(http::header<true, Fields> const& request, error_code& ec);
/** Start responding to a WebSocket HTTP Upgrade request.
@ -675,15 +674,15 @@ public:
this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`.
*/
template<class Body, class Fields, class AcceptHandler>
template<class Fields, class AcceptHandler>
#if BEAST_DOXYGEN
void_or_deduced
#else
typename async_completion<
AcceptHandler, void(error_code)>::result_type
#endif
async_accept(http::request<Body, Fields> const& request,
AcceptHandler&& handler);
async_accept(http::header<true,
Fields> const& request, AcceptHandler&& handler);
/** Send a HTTP WebSocket Upgrade request and receive the response.
@ -1674,18 +1673,16 @@ private:
void
reset();
http::request<http::empty_body>
http::request_header
build_request(boost::string_ref const& host,
boost::string_ref const& resource,
std::string& key);
template<class Body, class Fields>
http::response<http::string_body>
build_response(http::request<Body, Fields> const& req);
http::response_header
build_response(http::request_header const& req);
template<class Body, class Fields>
void
do_response(http::response<Body, Fields> const& resp,
do_response(http::response_header const& resp,
boost::string_ref const& key, error_code& ec);
};

View File

@ -136,7 +136,7 @@ public:
of bytes needed to store the result of compressing a block of
data based on the current compression level and strategy.
@param bytes The size of the uncompressed data.
@param sourceLen The size of the uncompressed data.
@return The maximum number of resulting compressed bytes.
*/

View File

@ -181,7 +181,7 @@ public:
`Flush::trees` is used, and when `write` avoids the allocation of memory for a
sliding window when `Flush::finsih` is used.
If a preset dictionary is needed after this call (see @ref dictionary below),
If a preset dictionary is needed after this call,
`write` sets `zs.adler` to the Adler-32 checksum of the dictionary chosen by
the compressor and returns `error::need_dictionary`; otherwise it sets
`zs.adler` to the Adler-32 checksum of all output produced so far (that is,

View File

@ -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

View File

@ -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

960
test/http/basic_parser.cpp Normal file
View File

@ -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

534
test/http/design.cpp Normal file
View File

@ -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

View File

@ -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>

59
test/http/error.cpp Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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_);
});

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);
}
};

147
test/http/test_parser.hpp Normal file
View File

@ -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

View File

@ -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;

View File

@ -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;