diff --git a/doc/qbk/04_http/10_custom_parsers.qbk b/doc/qbk/04_http/10_custom_parsers.qbk index 0dba4da5..86b430f2 100644 --- a/doc/qbk/04_http/10_custom_parsers.qbk +++ b/doc/qbk/04_http/10_custom_parsers.qbk @@ -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] diff --git a/doc/qbk/main.qbk b/doc/qbk/main.qbk index 878cabc7..7b609630 100644 --- a/doc/qbk/main.qbk +++ b/doc/qbk/main.qbk @@ -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] diff --git a/doc/qbk/release_notes.qbk b/doc/qbk/release_notes.qbk index 9609cee0..6de0c4ac 100644 --- a/doc/qbk/release_notes.qbk +++ b/doc/qbk/release_notes.qbk @@ -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. diff --git a/example/doc/http_examples.hpp b/example/doc/http_examples.hpp index 062832fc..af8e65ce 100644 --- a/example/doc/http_examples.hpp +++ b/example/doc/http_examples.hpp @@ -860,199 +860,6 @@ do_form_request( //] - - -//------------------------------------------------------------------------------ -// -// Example: Custom Parser -// -//------------------------------------------------------------------------------ - -//[example_http_custom_parser - -template -class custom_parser : public basic_parser -{ -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 -void custom_parser:: -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 -void custom_parser:: -on_response_impl( - int status, - string_view reason, - int version, - error_code& ec) -{ - boost::ignore_unused(status, reason, version); - ec = {}; -} - -template -void custom_parser:: -on_field_impl( - field f, - string_view name, - string_view value, - error_code& ec) -{ - boost::ignore_unused(f, name, value); - ec = {}; -} - -template -void custom_parser:: -on_header_impl(error_code& ec) -{ - ec = {}; -} - -template -void custom_parser:: -on_body_init_impl( - boost::optional const& content_length, - error_code& ec) -{ - boost::ignore_unused(content_length); - ec = {}; -} - -template -std::size_t custom_parser:: -on_body_impl(string_view body, error_code& ec) -{ - boost::ignore_unused(body); - ec = {}; - return body.size(); -} - -template -void custom_parser:: -on_chunk_header_impl( - std::uint64_t size, - string_view extension, - error_code& ec) -{ - boost::ignore_unused(size, extension); - ec = {}; -} - -template -std::size_t custom_parser:: -on_chunk_body_impl( - std::uint64_t remain, - string_view body, - error_code& ec) -{ - boost::ignore_unused(remain); - ec = {}; - return body.size(); -} - -template -void custom_parser:: -on_finish_impl(error_code& ec) -{ - ec = {}; -} - //------------------------------------------------------------------------------ // // Example: Incremental Read diff --git a/include/boost/beast/http/basic_parser.hpp b/include/boost/beast/http/basic_parser.hpp index 8e51393e..1b57460e 100644 --- a/include/boost/beast/http/basic_parser.hpp +++ b/include/boost/beast/http/basic_parser.hpp @@ -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. diff --git a/test/doc/CMakeLists.txt b/test/doc/CMakeLists.txt index ec6fcb40..4d69a5cb 100644 --- a/test/doc/CMakeLists.txt +++ b/test/doc/CMakeLists.txt @@ -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 diff --git a/test/doc/Jamfile b/test/doc/Jamfile index 44161a1c..829d66ee 100644 --- a/test/doc/Jamfile +++ b/test/doc/Jamfile @@ -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 ; diff --git a/test/doc/http_10_custom_parser.cpp b/test/doc/http_10_custom_parser.cpp new file mode 100644 index 00000000..cc3603a5 --- /dev/null +++ b/test/doc/http_10_custom_parser.cpp @@ -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 + +namespace boost { +namespace beast { +namespace http { + +//[code_http_10_custom_parser + +template +class custom_parser : public basic_parser +{ +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 +void custom_parser:: +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 +void custom_parser:: +on_response_impl( + int status, + string_view reason, + int version, + error_code& ec) +{ + boost::ignore_unused(status, reason, version); + ec = {}; +} + +template +void custom_parser:: +on_field_impl( + field f, + string_view name, + string_view value, + error_code& ec) +{ + boost::ignore_unused(f, name, value); + ec = {}; +} + +template +void custom_parser:: +on_header_impl(error_code& ec) +{ + ec = {}; +} + +template +void custom_parser:: +on_body_init_impl( + boost::optional const& content_length, + error_code& ec) +{ + boost::ignore_unused(content_length); + ec = {}; +} + +template +std::size_t custom_parser:: +on_body_impl(string_view body, error_code& ec) +{ + boost::ignore_unused(body); + ec = {}; + return body.size(); +} + +template +void custom_parser:: +on_chunk_header_impl( + std::uint64_t size, + string_view extension, + error_code& ec) +{ + boost::ignore_unused(size, extension); + ec = {}; +} + +template +std::size_t custom_parser:: +on_chunk_body_impl( + std::uint64_t remain, + string_view body, + error_code& ec) +{ + boost::ignore_unused(remain); + ec = {}; + return body.size(); +} + +template +void custom_parser:: +on_finish_impl(error_code& ec) +{ + ec = {}; +} + +template class custom_parser; +template class custom_parser; + +} // http +} // beast +} // boost diff --git a/test/doc/http_examples.cpp b/test/doc/http_examples.cpp index a8e51127..df34813c 100644 --- a/test/doc/http_examples.cpp +++ b/test/doc/http_examples.cpp @@ -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 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 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();