This commit is contained in:
Vinnie Falco
2019-02-23 12:04:48 -08:00
parent 6331e03a3a
commit f92999b613
9 changed files with 327 additions and 257 deletions

View File

@@ -12,31 +12,20 @@
While the parsers included in the library will handle a broad number of
use-cases, the __basic_parser__ interface can be subclassed to implement
custom strategies for storing parsed results: the basic parser processes
the incoming octets into elements according to the HTTP/1 protocol
specification, while the derived class decides what to do with those
elements. In particular, users who create exotic containers for [*Fields]
may need to also create their own parser. Custom parsers will work with
all of the stream read operations that work on parsers, as those algorithms
use only the basic parser interface. Some use cases for implementing custom
input buffers into elements according to the HTTP/1 protocol specification,
while the derived class decides what to do with those elements. Custom parsers
will work with all of the HTTP stream read algorithms, as those algorithms use
only the basic parser interface. Some use cases for implementing custom
parsers are:
* Inspect incoming header fields and keep or discard them.
* Use a container provided by an external interface.
* Store header data in a user-defined __Fields__ type.
The basic parser uses virtual functions. To declare your user-defined parser,
derive from __basic_parser__ and implement all the required virtual functions.
A declaration for the derived class may look like this:
The basic parser uses the
[@https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern Curiously Recurring Template Pattern].
To declare your user defined parser, derive it from __basic_parser__.
The interface to the parser is event-driven. Member functions of the derived
class (termed "callbacks" in this context) are invoked with parsed elements
as they become available, requiring either the `friend` declaration as shown
above or that the member functions are declared public (not recommended).
Buffers provided by the parser are non-owning references; it is the
responsibility of the derived class to copy any information it needs before
returning from the callback.
[example_http_custom_parser]
[code_http_10_custom_parser]
[endsect]

View File

@@ -131,6 +131,7 @@
[import ../../test/doc/core_1_refresher.cpp]
[import ../../test/doc/core_3_layers.cpp]
[import ../../test/doc/http_10_custom_parser.cpp]
[import ../../test/doc/websocket_3_handshake.cpp]
[import ../../include/boost/beast/core/detect_ssl.hpp]

View File

@@ -61,17 +61,15 @@
[link beast.ref.boost__beast__async_op_base `async_op_base`] and
[link beast.ref.boost__beast__stable_async_op_base `stable_async_op_base`]
handle all composed operation boilerplate for you.
* [link beast.ref.boost__beast__ssl_stream `ssl_stream`] provides a
movable, assignable SSL stream with a flat write optimization.
* All asynchronous operations use Asio's
[@boost:/doc/html/boost_asio/reference/async_initiate.html `async_initiate`]
for efficient integration with Coroutines TS.
* '''
⚡
'''
[*['faster compilation]],
'''
⚡
'''
define `BOOST_BEAST_SPLIT_COMPILATION` and include
[*['faster compilation]], define `BOOST_BEAST_SPLIT_COMPILATION` and include
[@../../include/boost/beast/src.hpp src.hpp] in one of your .cpp files!
* See the full [link beast.release_notes [*Release Notes]] for a complete list
of changes.

View File

@@ -860,199 +860,6 @@ do_form_request(
//]
//------------------------------------------------------------------------------
//
// Example: Custom Parser
//
//------------------------------------------------------------------------------
//[example_http_custom_parser
template<bool isRequest>
class custom_parser : public basic_parser<isRequest>
{
private:
/// Called after receiving the request-line (isRequest == true).
void
on_request_impl(
verb method, // The method verb, verb::unknown if no match
string_view method_str, // The method as a string
string_view target, // The request-target
int version, // The HTTP-version
error_code& ec) override; // The error returned to the caller, if any
/// Called after receiving the start-line (isRequest == false).
void
on_response_impl(
int code, // The status-code
string_view reason, // The obsolete reason-phrase
int version, // The HTTP-version
error_code& ec) override; // The error returned to the caller, if any
/// Called after receiving a header field.
void
on_field_impl(
field f, // The known-field enumeration constant
string_view name, // The field name string.
string_view value, // The field value
error_code& ec) override; // The error returned to the caller, if any
/// Called after the complete header is received.
void
on_header_impl(
error_code& ec) override; // The error returned to the caller, if any
/// Called just before processing the body, if a body exists.
void
on_body_init_impl(
boost::optional<
std::uint64_t> const&
content_length, // Content length if known, else `boost::none`
error_code& ec) override; // The error returned to the caller, if any
/// Called for each piece of the body, if a body exists.
//!
//! This is used when there is no chunked transfer coding.
//!
//! The function returns the number of bytes consumed from the
//! input buffer. Any input octets not consumed will be will be
//! presented on subsequent calls.
//!
std::size_t
on_body_impl(
string_view s, // A portion of the body
error_code& ec) override; // The error returned to the caller, if any
/// Called for each chunk header.
void
on_chunk_header_impl(
std::uint64_t size, // The size of the upcoming chunk,
// or zero for the last chunk
string_view extension, // The chunk extensions (may be empty)
error_code& ec) override; // The error returned to the caller, if any
/// Called to deliver the chunk body.
//!
//! This is used when there is a chunked transfer coding. The
//! implementation will automatically remove the encoding before
//! calling this function.
//!
//! The function returns the number of bytes consumed from the
//! input buffer. Any input octets not consumed will be will be
//! presented on subsequent calls.
//!
std::size_t
on_chunk_body_impl(
std::uint64_t remain, // The number of bytes remaining in the chunk,
// including what is being passed here.
// or zero for the last chunk
string_view body, // The next piece of the chunk body
error_code& ec) override; // The error returned to the caller, if any
/// Called when the complete message is parsed.
void
on_finish_impl(
error_code& ec) override; // The error returned to the caller, if any
public:
custom_parser() = default;
};
//]
// Definitions are not part of the docs but necessary to link
template<bool isRequest>
void custom_parser<isRequest>::
on_request_impl(verb method, string_view method_str,
string_view path, int version, error_code& ec)
{
boost::ignore_unused(method, method_str, path, version);
ec = {};
}
template<bool isRequest>
void custom_parser<isRequest>::
on_response_impl(
int status,
string_view reason,
int version,
error_code& ec)
{
boost::ignore_unused(status, reason, version);
ec = {};
}
template<bool isRequest>
void custom_parser<isRequest>::
on_field_impl(
field f,
string_view name,
string_view value,
error_code& ec)
{
boost::ignore_unused(f, name, value);
ec = {};
}
template<bool isRequest>
void custom_parser<isRequest>::
on_header_impl(error_code& ec)
{
ec = {};
}
template<bool isRequest>
void custom_parser<isRequest>::
on_body_init_impl(
boost::optional<std::uint64_t> const& content_length,
error_code& ec)
{
boost::ignore_unused(content_length);
ec = {};
}
template<bool isRequest>
std::size_t custom_parser<isRequest>::
on_body_impl(string_view body, error_code& ec)
{
boost::ignore_unused(body);
ec = {};
return body.size();
}
template<bool isRequest>
void custom_parser<isRequest>::
on_chunk_header_impl(
std::uint64_t size,
string_view extension,
error_code& ec)
{
boost::ignore_unused(size, extension);
ec = {};
}
template<bool isRequest>
std::size_t custom_parser<isRequest>::
on_chunk_body_impl(
std::uint64_t remain,
string_view body,
error_code& ec)
{
boost::ignore_unused(remain);
ec = {};
return body.size();
}
template<bool isRequest>
void custom_parser<isRequest>::
on_finish_impl(error_code& ec)
{
ec = {};
}
//------------------------------------------------------------------------------
//
// Example: Incremental Read

View File

@@ -545,9 +545,7 @@ protected:
std::size_t
on_body_impl(
string_view body,
error_code& ec)
{
}
error_code& ec) = 0;
/** Called each time a new chunk header of a chunk encoded body is received.

View File

@@ -22,6 +22,7 @@ add_executable (tests-doc
core_snippets.cpp
core_1_refresher.cpp
core_3_layers.cpp
http_10_custom_parser.cpp
http_examples.cpp
http_snippets.cpp
websocket_3_handshake.cpp

View File

@@ -21,6 +21,7 @@ alias run-tests :
[ compile websocket_snippets.cpp ]
[ run core_1_refresher.cpp $(TEST_MAIN) ]
[ run core_3_layers.cpp $(TEST_MAIN) ]
[ run http_10_custom_parser.cpp $(TEST_MAIN) ]
[ run http_examples.cpp $(TEST_MAIN) ]
[ run websocket_3_handshake.cpp $(TEST_MAIN) ]
;
@@ -29,6 +30,7 @@ exe fat-tests :
$(TEST_MAIN)
core_1_refresher.cpp
core_3_layers.cpp
http_10_custom_parser.cpp
http_examples.cpp
websocket_3_handshake.cpp
;

View File

@@ -0,0 +1,311 @@
//
// Copyright (c) 2016-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)
//
// Official repository: https://github.com/boostorg/beast
//
#include <boost/beast/http/basic_parser.hpp>
namespace boost {
namespace beast {
namespace http {
//[code_http_10_custom_parser
template<bool isRequest>
class custom_parser : public basic_parser<isRequest>
{
private:
/** Called after receiving the request-line.
This virtual function is invoked after receiving a request-line
when parsing HTTP requests.
It can only be called when `isRequest == true`.
@param method The verb enumeration. If the method string is not
one of the predefined strings, this value will be @ref verb::unknown.
@param method_str The unmodified string representing the verb.
@param target The request-target.
@param version The HTTP-version. This will be 10 for HTTP/1.0,
and 11 for HTTP/1.1.
@param ec An output parameter which the function may set to indicate
an error. The error will be clear before this function is invoked.
*/
void
on_request_impl(
verb method, // The method verb, verb::unknown if no match
string_view method_str, // The method as a string
string_view target, // The request-target
int version, // The HTTP-version
error_code& ec) override; // The error returned to the caller, if any
/** Called after receiving the status-line.
This virtual function is invoked after receiving a status-line
when parsing HTTP responses.
It can only be called when `isRequest == false`.
@param code The numeric status code.
@param reason The reason-phrase. Note that this value is
now obsolete, and only provided for historical or diagnostic
purposes.
@param version The HTTP-version. This will be 10 for HTTP/1.0,
and 11 for HTTP/1.1.
@param ec An output parameter which the function may set to indicate
an error. The error will be clear before this function is invoked.
*/
void
on_response_impl(
int code, // The status-code
string_view reason, // The obsolete reason-phrase
int version, // The HTTP-version
error_code& ec) override; // The error returned to the caller, if any
/** Called once for each complete field in the HTTP header.
This virtual function is invoked for each field that is received
while parsing an HTTP message.
@param name The known field enum value. If the name of the field
is not recognized, this value will be @ref field::unknown.
@param name_string The exact name of the field as received from
the input, represented as a string.
@param value A string holding the value of the field.
@param ec An output parameter which the function may set to indicate
an error. The error will be clear before this function is invoked.
*/
void
on_field_impl(
field f, // The known-field enumeration constant
string_view name, // The field name string.
string_view value, // The field value
error_code& ec) override; // The error returned to the caller, if any
/** Called once after the complete HTTP header is received.
This virtual function is invoked once, after the complete HTTP
header is received while parsing a message.
@param ec An output parameter which the function may set to indicate
an error. The error will be clear before this function is invoked.
*/
void
on_header_impl(
error_code& ec) override; // The error returned to the caller, if any
/** Called once before the body is processed.
This virtual function is invoked once, before the content body is
processed (but after the complete header is received).
@param content_length A value representing the content length in
bytes if the length is known (this can include a zero length).
Otherwise, the value will be `boost::none`.
@param ec An output parameter which the function may set to indicate
an error. The error will be clear before this function is invoked.
*/
void
on_body_init_impl(
boost::optional<
std::uint64_t> const&
content_length, // Content length if known, else `boost::none`
error_code& ec) override; // The error returned to the caller, if any
/** Called each time additional data is received representing the content body.
This virtual function is invoked for each piece of the body which is
received while parsing of a message. This function is only used when
no chunked transfer encoding is present.
@param body A string holding the additional body contents. This may
contain nulls or unprintable characters.
@param ec An output parameter which the function may set to indicate
an error. The error will be clear before this function is invoked.
@see on_chunk_body_impl
*/
std::size_t
on_body_impl(
string_view s, // A portion of the body
error_code& ec) override; // The error returned to the caller, if any
/** Called each time a new chunk header of a chunk encoded body is received.
This function is invoked each time a new chunk header is received.
The function is only used when the chunked transfer encoding is present.
@param size The size of this chunk, in bytes.
@param extensions A string containing the entire chunk extensions.
This may be empty, indicating no extensions are present.
@param ec An output parameter which the function may set to indicate
an error. The error will be clear before this function is invoked.
*/
void
on_chunk_header_impl(
std::uint64_t size, // The size of the upcoming chunk,
// or zero for the last chunk
string_view extension, // The chunk extensions (may be empty)
error_code& ec) override; // The error returned to the caller, if any
/** Called each time additional data is received representing part of a body chunk.
This virtual function is invoked for each piece of the body which is
received while parsing of a message. This function is only used when
no chunked transfer encoding is present.
@param remain The number of bytes remaining in this chunk. This includes
the contents of passed `body`. If this value is zero, then this represents
the final chunk.
@param body A string holding the additional body contents. This may
contain nulls or unprintable characters.
@param ec An output parameter which the function may set to indicate
an error. The error will be clear before this function is invoked.
@return This function should return the number of bytes actually consumed
from the `body` value. Any bytes that are not consumed on this call
will be presented in a subsequent call.
@see on_body_impl
*/
std::size_t
on_chunk_body_impl(
std::uint64_t remain, // The number of bytes remaining in the chunk,
// including what is being passed here.
// or zero for the last chunk
string_view body, // The next piece of the chunk body
error_code& ec) override; // The error returned to the caller, if any
/** Called once when the complete message is received.
This virtual function is invoked once, after successfully parsing
a complete HTTP message.
@param ec An output parameter which the function may set to indicate
an error. The error will be clear before this function is invoked.
*/
void
on_finish_impl(
error_code& ec) override; // The error returned to the caller, if any
public:
custom_parser() = default;
};
//]
// Definitions are not part of the docs but necessary to link
template<bool isRequest>
void custom_parser<isRequest>::
on_request_impl(verb method, string_view method_str,
string_view path, int version, error_code& ec)
{
boost::ignore_unused(method, method_str, path, version);
ec = {};
}
template<bool isRequest>
void custom_parser<isRequest>::
on_response_impl(
int status,
string_view reason,
int version,
error_code& ec)
{
boost::ignore_unused(status, reason, version);
ec = {};
}
template<bool isRequest>
void custom_parser<isRequest>::
on_field_impl(
field f,
string_view name,
string_view value,
error_code& ec)
{
boost::ignore_unused(f, name, value);
ec = {};
}
template<bool isRequest>
void custom_parser<isRequest>::
on_header_impl(error_code& ec)
{
ec = {};
}
template<bool isRequest>
void custom_parser<isRequest>::
on_body_init_impl(
boost::optional<std::uint64_t> const& content_length,
error_code& ec)
{
boost::ignore_unused(content_length);
ec = {};
}
template<bool isRequest>
std::size_t custom_parser<isRequest>::
on_body_impl(string_view body, error_code& ec)
{
boost::ignore_unused(body);
ec = {};
return body.size();
}
template<bool isRequest>
void custom_parser<isRequest>::
on_chunk_header_impl(
std::uint64_t size,
string_view extension,
error_code& ec)
{
boost::ignore_unused(size, extension);
ec = {};
}
template<bool isRequest>
std::size_t custom_parser<isRequest>::
on_chunk_body_impl(
std::uint64_t remain,
string_view body,
error_code& ec)
{
boost::ignore_unused(remain);
ec = {};
return body.size();
}
template<bool isRequest>
void custom_parser<isRequest>::
on_finish_impl(error_code& ec)
{
ec = {};
}
template class custom_parser<true>;
template class custom_parser<false>;
} // http
} // beast
} // boost

View File

@@ -188,42 +188,6 @@ public:
BEAST_EXPECT(to_string(req) == os.str());
}
void
doCustomParser()
{
{
string_view s{
"POST / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Content-Length: 13\r\n"
"\r\n"
"Hello, world!"
};
error_code ec;
custom_parser<true> p;
p.put(net::buffer(
s.data(), s.size()), ec);
BEAST_EXPECTS(! ec, ec.message());
}
{
string_view s{
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"d\r\n"
"Hello, world!"
"\r\n"
"0\r\n\r\n"
};
error_code ec;
custom_parser<false> p;
p.put(net::buffer(
s.data(), s.size()), ec);
BEAST_EXPECTS(! ec, ec.message());
}
}
void
doHEAD()
{
@@ -428,7 +392,6 @@ public:
doRelay();
doReadStdStream();
doWriteStdStream();
doCustomParser();
doHEAD();
doDeferredBody();
doIncrementalRead();