From 78d9e3bbce85567addf8f1c83ec67cfc0f2dd098 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sat, 3 Jun 2017 09:45:09 -0700 Subject: [PATCH] Documentation work --- doc/1_overview.qbk | 10 +- doc/3_5_parser_streams.qbk | 16 +- doc/3_6_serializer_buffers.qbk | 1 - doc/3_7_parser_buffers.qbk | 40 ++- doc/3_8_custom_parsers.qbk | 99 +++++++ doc/3_9_custom_body.qbk | 16 +- include/beast/http/basic_parser.hpp | 212 +++++++------- include/beast/http/parser.hpp | 7 +- include/beast/http/read.hpp | 436 ++++++++++++++-------------- include/beast/http/write.hpp | 11 +- test/http/design.cpp | 168 +++++++++++ 11 files changed, 665 insertions(+), 351 deletions(-) diff --git a/doc/1_overview.qbk b/doc/1_overview.qbk index a44f96f7..a07e9d11 100644 --- a/doc/1_overview.qbk +++ b/doc/1_overview.qbk @@ -93,9 +93,13 @@ of redirects, gzipped transfer encodings, caching, or proxying (to name a few) are not directly provided, but nothing stops users from creating these features using Beast's HTTP message types. -The library is intended to be the foundation upon which other networking -libraries are built. It is a goal that other architects will build -interoperable solutions on top of Beast. +[important + Beast is not an HTTP client or HTTP server, but it can be used to + build both. The library is intended to be a foundation upon which + new libraries may be built. It is a goal that other architects will + build interoperable solutions on top of Beast. The provided example + code shows how a client or server may be built. +] [heading Credits] diff --git a/doc/3_5_parser_streams.qbk b/doc/3_5_parser_streams.qbk index 6ffb74fd..6002b28c 100644 --- a/doc/3_5_parser_streams.qbk +++ b/doc/3_5_parser_streams.qbk @@ -80,14 +80,14 @@ The stream operations which work on parsers are: [table Parser Stream Operations [[Name][Description]] [[ - [link beast.ref.http__read_some.overload1 [*read_some]] + [link beast.ref.http__read.overload1 [*read]] ][ - Read some octets into a parser from a __SyncReadStream__. + Read everything into a parser from a __SyncWriteStream__. ]] [[ - [link beast.ref.http__async_read_some [*async_read_some]] + [link beast.ref.http__async_read.overload1 [*async_read]] ][ - Read some octets into a parser asynchronously from an __AsyncWriteStream__. + Read everything into a parser asynchronously from an __AsyncWriteStream__. ]] [[ [link beast.ref.http__read_header.overload1 [*read_header]] @@ -100,14 +100,14 @@ The stream operations which work on parsers are: Read only the header octets into a parser asynchronously from an __AsyncWriteStream__. ]] [[ - [link beast.ref.http__read.overload1 [*read]] + [link beast.ref.http__read_some.overload1 [*read_some]] ][ - Read everything into a parser from a __SyncWriteStream__. + Read some octets into a parser from a __SyncReadStream__. ]] [[ - [link beast.ref.http__async_read.overload1 [*async_read]] + [link beast.ref.http__async_read_some [*async_read_some]] ][ - Read everything into a parser asynchronously from an __AsyncWriteStream__. + Read some octets into a parser asynchronously from an __AsyncWriteStream__. ]] ] diff --git a/doc/3_6_serializer_buffers.qbk b/doc/3_6_serializer_buffers.qbk index ae30da4e..ac2442c3 100644 --- a/doc/3_6_serializer_buffers.qbk +++ b/doc/3_6_serializer_buffers.qbk @@ -12,7 +12,6 @@ and invoke its methods directly instead of using the provided stream algorithms. This could be useful for implementing algorithms on streams whose interface does not conform to __Stream__. For example, a [@https://github.com/libuv/libuv *libuv* socket]. - The serializer interface is interactive; the caller invokes it repeatedly to produce buffers until all of the buffers have been generated. Then the serializer is destroyed. diff --git a/doc/3_7_parser_buffers.qbk b/doc/3_7_parser_buffers.qbk index 9f65e3f4..bb519ac5 100644 --- a/doc/3_7_parser_buffers.qbk +++ b/doc/3_7_parser_buffers.qbk @@ -13,7 +13,6 @@ invoke its methods directly instead of using the provided stream algorithms. This could be useful for implementing algorithms on streams whose interface does not conform to any __Stream__. For example, a [@http://zeromq.org/ *ZeroMQ* socket]. - The basic parser interface is interactive; the caller invokes the function [link beast.ref.http__basic_parser.put `basic_parser::put`] repeatedly with buffers until an error occurs or the parsing is done. The @@ -22,9 +21,44 @@ function Is used when the caller knows that there will never be more data (for example, if the underlying connection is closed), -[heading Split Parsing] +[heading Parser Options] -[heading Eager Parsing] +The parser provides two options which may be set before parsing begins: + +[table Parser Options +[[Name][Default][Description]] +[[ + [link beast.ref.http__basic_parser.eager.overload2 `eager`] +][ + `false` +][ + Normally the parser returns after successfully parsing a structured + element (header, chunk header, or chunk body) even if there are octets + remaining in the input. This is necessary when attempting to parse the + header first, or when the caller wants to inspect information which may + be invalidated by subsequent parsing, such as a chunk extension. The + `eager` option controls whether the parser keeps going after parsing + structured element if there are octets remaining in the buffer and no + error occurs. This option is automatically set or cleared during certain + stream operations to improve performance with no change in functionality. +]] +[[ + [link beast.ref.http__basic_parser.skip.overload2 `skip`] +][ + `false` +][ + This 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 or a chunked Transfer-Encoding. Depending on the request, + some responses do not carry a body. For example, a 200 response to a + [@https://tools.ietf.org/html/rfc7231#section-4.3.6 CONNECT] request + from a tunneling proxy, or a response to a + [@https://tools.ietf.org/html/rfc7231#section-4.3.2 HEAD] request. + 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. +]] +] [heading Example: Parsing from a std::istream] diff --git a/doc/3_8_custom_parsers.qbk b/doc/3_8_custom_parsers.qbk index 3858a582..45542076 100644 --- a/doc/3_8_custom_parsers.qbk +++ b/doc/3_8_custom_parsers.qbk @@ -7,4 +7,103 @@ [section:custom_parsers Custom Parsers] +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 parsing strategies: the 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. + + +The basic parser uses the Curiously Recurring Template Pattern +([@https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern CRTP]). +To declare your user defined parser, first derive it from __basic_parser__: +``` +template +class custom_parser + : public basic_parser> +{ + friend class 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. + + +``` +template +class custom_parser + : public basic_parser> +{ + friend class basic_parser; + + /// Called after receiving the request-line (isRequest == true). + void + on_request( + string_view method, // The method + string_view target, // The request-target + int version, // The HTTP-version + error_code& ec); // The error returned to the caller, if any + + /// Called after receiving the start-line (isRequest == false). + void + on_response( + int status, // The status-code + string_view reason, // The obsolete reason-phrase + int version, // The HTTP-version + error_code& ec); // The error returned to the caller, if any + + /// Called after receiving a header field. + void + on_field( + string_view name, // The field name + string_view value, // The field value + error_code& ec); // The error returned to the caller, if any + + /// Called after the complete header is received. + void + on_header( + error_code& ec); // The error returned to the caller, if any + + /// Called just before processing the body, if a body exists. + void + on_body(boost::optional< + std::uint64_t> const& + content_length, // Content length if known, else `boost::none` + error_code& ec); // The error returned to the caller, if any + + /// Called for each piece of the body, if a body exists. + // + // If present, the chunked Transfer-Encoding will be removed + // before this callback is invoked. + // + void + on_data( + string_view s, // A portion of the body + error_code& ec); // The error returned to the caller, if any + + /// Called for each chunk header. + void + on_chunk( + std::uint64_t size, // The size of the upcoming chunk + string_view extension, // The chunk-extension (may be empty) + error_code& ec); // The error returned to the caller, if any + + /// Called when the complete message is parsed. + void + on_complete(error_code& ec); + +public: + custom_parser() = default; +}; +``` + [endsect] diff --git a/doc/3_9_custom_body.qbk b/doc/3_9_custom_body.qbk index 2f3f1f3b..2b139424 100644 --- a/doc/3_9_custom_body.qbk +++ b/doc/3_9_custom_body.qbk @@ -26,20 +26,20 @@ The meaning of the nested types is as follows declared with this [*Body] will have those operations defined. ] ][ - [`body_writer`] + [`reader`] + [ + An optional nested type meeting the requirements of + [link beast.ref.BodyReader [*BodyReader]]. If present, this defines + the algorithm used to obtain buffers representing a body of this type. + ] +][ + [`writer`] [ An optional nested type meeting the requirements of [link beast.ref.BodyWriter [*BodyWriter]]. If present, this defines the algorithm used to transfer parsed octets into buffers representing the body. ] -][ - [`body_reader`] - [ - An optional nested type meeting the requirements of - [link beast.ref.BodyReader [*BodyReader]]. If present, this defines - the algorithm used to obtain buffers representing a body of this type. - ] ] ] diff --git a/include/beast/http/basic_parser.hpp b/include/beast/http/basic_parser.hpp index d704e1a1..75a62535 100644 --- a/include/beast/http/basic_parser.hpp +++ b/include/beast/http/basic_parser.hpp @@ -29,95 +29,6 @@ namespace http { 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 - struct derived - : basic_parser> - { - // When isRequest == true, called - // after the Request Line is received. - // - void - on_request( - string_view method, - string_view target, - int version, - error_code& ec); - - // When isRequest == false, called - // after the Status Line is received. - // - void - on_response( - int status, - string_view reason, - int version, - error_code& ec); - - // Called after receiving a field/value pair. - // - void - on_field( - string_view name, - string_view 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. - // - void - on_body( - boost::optional content_length, - error_code& ec); - - // Called zero or more times to provide body data. - // - void - on_data( - string_view s, - error_code& ec); - - // 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 - string_view 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. - The parser is optimized for the case where the input buffer sequence consists of a single contiguous buffer. The @ref beast::flat_buffer class is provided, which guarantees @@ -130,6 +41,88 @@ namespace http { the structured portion of the HTTP message (header or chunk header) is contained in a linear buffer. + The interface uses CRTP (Curiously Recurring Template Pattern). + To use this class directly, 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 (termed "callbacks" + in this context) 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. If a callback sets the error code, the error + will be propagated to the caller of the parser. + + @par Example + + @code + template + class custom_parser + : public basic_parser> + { + friend class basic_parser; + + /// Called after receiving the request-line (isRequest == true). + void + on_request( + string_view method, // The method + string_view target, // The request-target + int version, // The HTTP-version + error_code& ec); // The error returned to the caller, if any + + /// Called after receiving the start-line (isRequest == false). + void + on_response( + int status, // The status-code + string_view reason, // The obsolete reason-phrase + int version, // The HTTP-version + error_code& ec); // The error returned to the caller, if any + + /// Called after receiving a header field. + void + on_field( + string_view name, // The field name + string_view value, // The field value + error_code& ec); // The error returned to the caller, if any + + /// Called after the complete header is received. + void + on_header( + error_code& ec); // The error returned to the caller, if any + + /// Called just before processing the body, if a body exists. + void + on_body(boost::optional< + std::uint64_t> const& + content_length, // Content length if known, else `boost::none` + error_code& ec); // The error returned to the caller, if any + + /// Called for each piece of the body, if a body exists. + // + // If present, the chunked Transfer-Encoding will be removed + // before this callback is invoked. + // + void + on_data( + string_view s, // A portion of the body + error_code& ec); // The error returned to the caller, if any + + /// Called for each chunk header. + void + on_chunk( + std::uint64_t size, // The size of the upcoming chunk + string_view extension, // The chunk-extension (may be empty) + error_code& ec); // The error returned to the caller, if any + + /// Called when the complete message is parsed. + void + on_complete(error_code& ec); + + public: + custom_parser() = default; + }; + @endcode + @tparam isRequest A `bool` indicating whether the parser will be presented with request or response message. @@ -319,16 +312,19 @@ public: /** Set the eager parse option. - This option controls whether or not the parser will attempt - to consume all of the octets provided during parsing, or - if it will stop when it reaches one of the following positions - within the serialized message: + Normally the parser returns after successfully parsing a structured + element (header, chunk header, or chunk body) even if there are octets + remaining in the input. This is necessary when attempting to parse the + header first, or when the caller wants to inspect information which may + be invalidated by subsequent parsing, such as a chunk extension. The + `eager` option controls whether the parser keeps going after parsing + structured element if there are octets remaining in the buffer and no + error occurs. This option is automatically set or cleared during certain + stream operations to improve performance with no change in functionality. - @li Immediately after the header + The default setting is `false`. - @li Immediately after any chunk header - - The default is to stop after the header or any chunk header. + @param v `true` to set the eager parse option or `false` to disable it. */ void eager(bool v) @@ -339,25 +335,25 @@ public: f_ &= ~flagEager; } - /// Returns `true` if the parser will ignore the message body. + /// Returns `true` if the skip parse option is set. bool skip() { return (f_ & flagSkipBody) != 0; } - /** Set the skip body option. + /** Set the skip parse 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. + This 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 or a chunked Transfer-Encoding. Depending on the request, + some responses do not carry a body. For example, a 200 response to a + CONNECT request from a tunneling proxy, or a response to a HEAD request. + 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. - 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. + @param v `true` to set the skip body option or `false` to disable it. @note This function must called before any bytes are processed. */ diff --git a/include/beast/http/parser.hpp b/include/beast/http/parser.hpp index aacc3b07..313a5b03 100644 --- a/include/beast/http/parser.hpp +++ b/include/beast/http/parser.hpp @@ -227,10 +227,8 @@ class parser using base_type = basic_parser>; - using writer_type = typename Body::writer; - message m_; - boost::optional wr_; + boost::optional wr_; public: /// The type of message returned by the parser @@ -329,8 +327,7 @@ public: } private: - friend class basic_parser< - isRequest, parser>; + friend class basic_parser; void on_request( diff --git a/include/beast/http/read.hpp b/include/beast/http/read.hpp index b60ee6d4..adc9c3c4 100644 --- a/include/beast/http/read.hpp +++ b/include/beast/http/read.hpp @@ -17,35 +17,30 @@ namespace beast { namespace http { -/** Read some HTTP/1 message data from a stream. +/** Read part of a message from a stream using a parser. - 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: + This function is used to read part of a message from a stream into a + subclass of @ref basic_parser. + The call will block until one of the following conditions is true: - @li When expecting a message header, and the complete - header is received. + @li A call to @ref basic_parser::put with a non-empty buffer sequence + is successful. - @li When expecting a chunk header, and the complete - chunk header is received. + @li An error occurs. - @li When expecting body octets, one or more body octets - are received. + This operation 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 read. This additional data is stored + in the dynamic buffer, which must be retained for subsequent reads. - @li An error occurs in the stream or parser. + If the stream returns the error `boost::asio::error::eof` indicating the + end of file during a read, the error returned from this function will be: - 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. + @li @ref error::end_of_stream if no octets were parsed, or + + @li @ref error::partial_message if any octets were parsed. - If the end of the stream is reached during the read, the - value @ref error::partial_message is indicated as the - error if bytes have been processed, else the error - @ref error::end_of_stream is indicated. - @param stream The stream from which the data is to be read. The type must support the @b SyncReadStream concept. @@ -57,9 +52,9 @@ namespace http { @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. + @return The number of octets processed from the dynamic buffer. + The octets should be removed by calling `consume` on the dynamic + buffer after the read completes, regardless of any error. @throws system_error Thrown on failure. */ @@ -73,34 +68,33 @@ read_some( DynamicBuffer& buffer, basic_parser& parser); -/** Read some HTTP/1 message data from a stream. +/** Read part of a message from a stream using a parser. - 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: + This function is used to read part of a message from a stream into a + subclass of @ref basic_parser. + The call will block until one of the following conditions is true: - @li When expecting a message header, and the complete - header is received. + @li A call to @ref basic_parser::put with a non-empty buffer sequence + is successful. - @li When expecting a chunk header, and the complete - chunk header is received. + @li An error occurs. - @li When expecting body octets, one or more body octets - are received. + This operation 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 read. This additional data is stored + in the dynamic buffer, which must be retained for subsequent reads. - @li An error occurs in the stream or parser. + If the stream returns the error `boost::asio::error::eof` indicating the + end of file during a read, the error returned from this function will be: - 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. + @li @ref error::end_of_stream if no octets were parsed, or - If the end of the stream is reached during the read, the - value @ref error::partial_message is indicated as the - error if bytes have been processed, else the error - @ref error::end_of_stream is indicated. + @li @ref error::partial_message if any octets were parsed. + + The function returns the number of bytes processed from the dynamic + buffer. The caller should remove these bytes by calling `consume` on + the dynamic buffer, regardless of any error. @param stream The stream from which the data is to be read. The type must support the @b SyncReadStream concept. @@ -115,9 +109,9 @@ read_some( @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. + @return The number of octets processed from the dynamic buffer. + The octets should be removed by calling `consume` on the dynamic + buffer after the read completes, regardless of any error. */ template< class SyncReadStream, @@ -130,24 +124,17 @@ read_some( basic_parser& parser, error_code& ec); -/** Start an asynchronous operation to read some HTTP/1 message data from a stream. +/** Read part of a message asynchronously from a stream using a parser. - 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: + This function is used to asynchronously read part of a message from + a stream into a subclass of @ref basic_parser. + 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 A call to @ref basic_parser::put with a non-empty buffer sequence + is successful. - @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. + @li An error occurs. 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 @@ -157,14 +144,12 @@ read_some( 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. + If the stream returns the error `boost::asio::error::eof` indicating the + end of file during a read, the error returned from this function will be: - If the end of the stream is reached during the read, the - value @ref error::partial_message is indicated as the - error if bytes have been processed, else the error - @ref error::end_of_stream is indicated. + @li @ref error::end_of_stream if no octets were parsed, or + + @li @ref error::partial_message if any octets were parsed. @param stream The stream from which the data is to be read. The type must support the @b AsyncReadStream concept. @@ -176,6 +161,8 @@ read_some( first. @param parser The parser to use. + The object must remain valid at least until the + handler is called; ownership is not transferred. @param handler The handler to be called when the request completes. Copies will be made of the handler as required. @@ -188,6 +175,11 @@ read_some( 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`. + + The completion handler will receive as a parameter the number + of octets processed from the dynamic buffer. The octets should + be removed by calling `consume` on the dynamic buffer after + the read completes, regardless of any error. */ template< class AsyncReadStream, @@ -208,26 +200,28 @@ async_read_some( //------------------------------------------------------------------------------ -/** Read a header into an HTTP/1 parser from a stream. +/** Read a header from a stream using a parser. - 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: + This function is used to read a header from a stream into a subclass + of @ref basic_parser. + The call will block until one of the following conditions is true: - @li The parser indicates that the complete header is received + @li @ref basic_parser::is_header_done returns `true` - @li An error occurs in the stream or parser. + @li An error occurs. - 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. + This operation 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 read. This additional data is stored + in the dynamic buffer, which must be retained for subsequent reads. - If the end of the stream is reached during the read, the - value @ref error::partial_message is indicated as the - error if bytes have been processed, else the error - @ref error::end_of_stream is indicated. + If the stream returns the error `boost::asio::error::eof` indicating the + end of file during a read, the error returned from this function will be: + + @li @ref error::end_of_stream if no octets were parsed, or + + @li @ref error::partial_message if any octets were parsed. @param stream The stream from which the data is to be read. The type must support the @b SyncReadStream concept. @@ -255,26 +249,28 @@ read_header( DynamicBuffer& buffer, basic_parser& parser); -/** Read a header into an HTTP/1 parser from a stream. +/** Read a header from a stream using a parser. - 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: + This function is used to read a header from a stream into a subclass + of @ref basic_parser. + The call will block until one of the following conditions is true: - @li The parser indicates that the complete header is received + @li @ref basic_parser::is_header_done returns `true` - @li An error occurs in the stream or parser. + @li An error occurs. - 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. + This operation 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 read. This additional data is stored + in the dynamic buffer, which must be retained for subsequent reads. - If the end of the stream is reached during the read, the - value @ref error::partial_message is indicated as the - error if bytes have been processed, else the error - @ref error::end_of_stream is indicated. + If the stream returns the error `boost::asio::error::eof` indicating the + end of file during a read, the error returned from this function will be: + + @li @ref error::end_of_stream if no octets were parsed, or + + @li @ref error::partial_message if any octets were parsed. @param stream The stream from which the data is to be read. The type must support the @b SyncReadStream concept. @@ -303,29 +299,31 @@ read_header( basic_parser& parser, error_code& ec); -/** Read a header into an HTTP/1 parser asynchronously from a stream. +/** Read a header from a stream asynchronously using a parser. - 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: + This function is used to asynchronously read a header from a stream + into a subclass of @ref basic_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 the complete header is received + @li @ref basic_parser::is_header_done returns `true` - @li An error occurs in the stream or parser. + @li An error occurs. 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 + the stream's `async_read_some` function, and is known as a composed operation. 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. + end of the message being read. This additional data is stored + in the dynamic buffer, which must be retained for subsequent reads. - If the end of the stream is reached during the read, the - value @ref error::partial_message is indicated as the - error if bytes have been processed, else the error - @ref error::end_of_stream is indicated. + If the stream returns the error `boost::asio::error::eof` indicating the + end of file during a read, the error returned from this function will be: + + @li @ref error::end_of_stream if no octets were parsed, or + + @li @ref error::partial_message if any octets were parsed. @param stream The stream from which the data is to be read. The type must support the @b AsyncReadStream concept. @@ -337,6 +335,8 @@ read_header( first. @param parser The parser to use. + The object must remain valid at least until the + handler is called; ownership is not transferred. @param handler The handler to be called when the request completes. Copies will be made of the handler as required. @@ -371,26 +371,28 @@ async_read_header( //------------------------------------------------------------------------------ -/** Read into an HTTP/1 parser from a stream. +/** Read a complete message from a stream using a parser. - 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: + This function is used to read a complete message from a stream into a + subclass of @ref basic_parser. + The call will block until one of the following conditions is true: - @li The parser indicates no more additional data is needed. + @li @ref basic_parser::is_done returns `true` - @li An error occurs in the stream or parser. + @li An error occurs. - 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. + This operation 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 read. This additional data is stored + in the dynamic buffer, which must be retained for subsequent reads. - If the end of the stream is reached during the read, the - value @ref error::partial_message is indicated as the - error if bytes have been processed, else the error - @ref error::end_of_stream is indicated. + If the stream returns the error `boost::asio::error::eof` indicating the + end of file during a read, the error returned from this function will be: + + @li @ref error::end_of_stream if no octets were parsed, or + + @li @ref error::partial_message if any octets were parsed. @param stream The stream from which the data is to be read. The type must support the @b SyncReadStream concept. @@ -418,26 +420,28 @@ read( DynamicBuffer& buffer, basic_parser& parser); -/** Read into an HTTP/1 parser from a stream. +/** Read a complete message from a stream using a parser. - 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: + This function is used to read a complete message from a stream into a + subclass of @ref basic_parser. + The call will block until one of the following conditions is true: - @li The parser indicates that no more data is needed. + @li @ref basic_parser::is_done returns `true` - @li An error occurs in the stream or parser. + @li An error occurs. - 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. + This operation 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 read. This additional data is stored + in the dynamic buffer, which must be retained for subsequent reads. - If the end of the stream is reached during the read, the - value @ref error::partial_message is indicated as the - error if bytes have been processed, else the error - @ref error::end_of_stream is indicated. + If the stream returns the error `boost::asio::error::eof` indicating the + end of file during a read, the error returned from this function will be: + + @li @ref error::end_of_stream if no octets were parsed, or + + @li @ref error::partial_message if any octets were parsed. @param stream The stream from which the data is to be read. The type must support the @b SyncReadStream concept. @@ -466,29 +470,31 @@ read( basic_parser& parser, error_code& ec); -/** Read into an HTTP/1 parser asynchronously from a stream. +/** Read a complete message from a stream asynchronously using a parser. - 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: + This function is used to asynchronously read a complete message from a + stream into a subclass of @ref basic_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 no more data is needed. + @li @ref basic_parser::is_done returns `true` - @li An error occurs in the stream or parser. + @li An error occurs. 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 + the stream's `async_read_some` function, and is known as a composed operation. 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. + end of the message being read. This additional data is stored + in the dynamic buffer, which must be retained for subsequent reads. - If the end of the stream is reached during the read, the - value @ref error::partial_message is indicated as the - error if bytes have been processed, else the error - @ref error::end_of_stream is indicated. + If the stream returns the error `boost::asio::error::eof` indicating the + end of file during a read, the error returned from this function will be: + + @li @ref error::end_of_stream if no octets were parsed, or + + @li @ref error::partial_message if any octets were parsed. @param stream The stream from which the data is to be read. The type must support the @b AsyncReadStream concept. @@ -500,6 +506,8 @@ read( first. @param parser The parser to use. + The object must remain valid at least until the + handler is called; ownership is not transferred. @param handler The handler to be called when the request completes. Copies will be made of the handler as required. @@ -534,26 +542,27 @@ async_read( //------------------------------------------------------------------------------ -/** Read an HTTP/1 message from a stream. +/** Read a complete 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 - is true: + This function is used to read a complete message from a stream using HTTP/1. + The call will block until one of the following conditions is true: - @li A complete message is read in. + @li The entire message is read. - @li An error occurs in the stream or parser. + @li An error occurs. - 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 dynamic - buffer, which may be used in subsequent calls. + This operation 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 read. This additional data is stored + in the dynamic buffer, which must be retained for subsequent reads. - If the end of the stream is reached during the read, the - value @ref error::partial_message is indicated as the - error if bytes have been processed, else the error - @ref error::end_of_stream is indicated. + If the stream returns the error `boost::asio::error::eof` indicating the + end of file during a read, the error returned from this function will be: + + @li @ref error::end_of_stream if no octets were parsed, or + + @li @ref error::partial_message if any octets were parsed. @param stream The stream from which the data is to be read. The type must support the @b SyncReadStream concept. @@ -564,9 +573,8 @@ async_read( dynamic buffer's input sequence will be given to the parser first. - @param msg An object used to store the message. Any - contents will be overwritten. The type must support - copy assignment or move assignment. + @param msg An object used to store the message. Any contents will + be overwritten. The type must support copy or move assignment. @throws system_error Thrown on failure. */ @@ -580,26 +588,27 @@ read( DynamicBuffer& buffer, message& msg); -/** Read an HTTP/1 message from a stream. +/** Read a complete 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 - is true: + This function is used to read a complete message from a stream using HTTP/1. + The call will block until one of the following conditions is true: - @li A complete message is read in. + @li The entire message is read. - @li An error occurs in the stream or parser. + @li An error occurs. - 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 dynamic - buffer, which may be used in subsequent calls. + This operation 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 read. This additional data is stored + in the dynamic buffer, which must be retained for subsequent reads. - If the end of the stream is reached during the read, the - value @ref error::partial_message is indicated as the - error if bytes have been processed, else the error - @ref error::end_of_stream is indicated. + If the stream returns the error `boost::asio::error::eof` indicating the + end of file during a read, the error returned from this function will be: + + @li @ref error::end_of_stream if no octets were parsed, or + + @li @ref error::partial_message if any octets were parsed. @param stream The stream from which the data is to be read. The type must support the @b SyncReadStream concept. @@ -610,9 +619,8 @@ read( dynamic buffer's input sequence will be given to the parser first. - @param msg An object used to store the message. Any - contents will be overwritten. The type must support - copy assignment or move assignment. + @param msg An object used to store the message. Any contents will + be overwritten. The type must support copy or move assignment. @param ec Set to the error, if any occurred. */ @@ -627,31 +635,33 @@ read( message& msg, error_code& ec); -/** Read an HTTP/1 message asynchronously from a stream. +/** Read a complete message from a stream asynchronously. - This function is used to asynchronously read a message 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 a complete message from a + stream using HTTP/1. + The function call always returns immediately. The asynchronous operation + will continue until one of the following conditions is true: - @li A complete message is read in. + @li The entire message is read. - @li An error occurs in the stream or parser. + @li An error occurs. This operation is implemented in terms of one or more calls to the stream's `async_read_some` function, and is known as a composed operation. 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 being parsed. This additional data is stored - in the dynamic buffer, which may be used in subsequent calls. + end of the message being read. This additional data is stored + in the dynamic buffer, which must be retained for subsequent reads. - If the end of the stream is reached during the read, the - value @ref error::partial_message is indicated as the - error if bytes have been processed, else the error - @ref error::end_of_stream is indicated. + If the stream returns the error `boost::asio::error::eof` indicating the + end of file during a read, the error returned from this function will be: - @param stream The stream to read the message from. + @li @ref error::end_of_stream if no octets were parsed, or + + @li @ref error::partial_message if any octets were parsed. + + @param stream The stream from which the data is to be read. The type must support the @b AsyncReadStream concept. @param buffer A @b DynamicBuffer holding additional bytes @@ -660,10 +670,10 @@ read( 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 msg An object used to store the message. Any contents will + be overwritten. The type must support copy or move assignment. + The object must remain valid at least until the + handler is called; ownership is not transferred. @param handler The handler to be called when the operation completes. Copies will be made of the handler as required. diff --git a/include/beast/http/write.hpp b/include/beast/http/write.hpp index 8ef829c1..459133a6 100644 --- a/include/beast/http/write.hpp +++ b/include/beast/http/write.hpp @@ -131,6 +131,8 @@ write_some(SyncWriteStream& stream, serializer< The type must support the @b AsyncWriteStream concept. @param sr The serializer to use. + The object must remain valid at least until the + handler is called; ownership is not transferred. @param handler The handler to be called when the operation completes. Copies will be made of the handler as required. @@ -244,6 +246,8 @@ write_header(SyncWriteStream& stream, serializer< The type must support the @b AsyncWriteStream concept. @param sr The serializer to use. + The object must remain valid at least until the + handler is called; ownership is not transferred. @param handler The handler to be called when the operation completes. Copies will be made of the handler as required. @@ -354,6 +358,8 @@ write(SyncWriteStream& stream, serializer< The type must support the @b AsyncWriteStream concept. @param sr The serializer to use. + The object must remain valid at least until the + handler is called; ownership is not transferred. @param handler The handler to be called when the operation completes. Copies will be made of the handler as required. @@ -465,8 +471,9 @@ write(SyncWriteStream& stream, @param stream The stream to which the data is to be written. The type must support the @b AsyncWriteStream concept. - @param msg The message to write. The object must remain valid at least - until the completion handler is called; ownership is not transferred. + @param msg The message to write. + The object must remain valid at least until the + handler is called; ownership is not transferred. @param handler The handler to be called when the operation completes. Copies will be made of the handler as required. diff --git a/test/http/design.cpp b/test/http/design.cpp index 24bdb4f9..3eb30b34 100644 --- a/test/http/design.cpp +++ b/test/http/design.cpp @@ -21,6 +21,130 @@ namespace beast { namespace http { +namespace design { + +//-------------------------------------------------------------------------- +// +// Example: Custom Parser +// +//-------------------------------------------------------------------------- + +template +class custom_parser + : public basic_parser> +{ + friend class basic_parser; + + /// Called after receiving the request-line (isRequest == true). + void + on_request( + string_view method, // The method + string_view target, // The request-target + int version, // The HTTP-version + error_code& ec); // The error returned to the caller, if any + + /// Called after receiving the start-line (isRequest == false). + void + on_response( + int status, // The status-code + string_view reason, // The obsolete reason-phrase + int version, // The HTTP-version + error_code& ec); // The error returned to the caller, if any + + /// Called after receiving a header field. + void + on_field( + string_view name, // The field name + string_view value, // The field value + error_code& ec); // The error returned to the caller, if any + + /// Called after the complete header is received. + void + on_header( + error_code& ec); // The error returned to the caller, if any + + /// Called just before processing the body, if a body exists. + void + on_body(boost::optional< + std::uint64_t> const& + content_length, // Content length if known, else `boost::none` + error_code& ec); // The error returned to the caller, if any + + /// Called for each piece of the body, if a body exists. + // + // If present, the chunked Transfer-Encoding will be removed + // before this callback is invoked. + // + void + on_data( + string_view s, // A portion of the body + error_code& ec); // The error returned to the caller, if any + + /// Called for each chunk header. + void + on_chunk( + std::uint64_t size, // The size of the upcoming chunk + string_view extension, // The chunk-extension (may be empty) + error_code& ec); // The error returned to the caller, if any + + /// Called when the complete message is parsed. + void + on_complete(error_code& ec); + +public: + custom_parser() = default; +}; + +template +void custom_parser:: +on_request(string_view method, string_view path, int version, error_code& ec) +{ +} + +template +void custom_parser:: +on_response(int status, string_view reason, int version, error_code& ec) +{ +} + +template +void custom_parser:: +on_field(string_view name, string_view value, error_code& ec) +{ +} + +template +void custom_parser:: +on_header(error_code& ec) +{ +} + +template +void custom_parser:: +on_body(boost::optional const& content_length, error_code& ec) +{ +} + +template +void custom_parser:: +on_data(string_view s, error_code& ec) +{ +} + +template +void custom_parser:: +on_chunk(std::uint64_t size, string_view extension, error_code& ec) +{ +} + +template +void custom_parser:: +on_complete(error_code& ec) +{ +} + +} // design + class design_test : public beast::unit_test::suite , public beast::test::enable_yield_to @@ -618,6 +742,48 @@ public: BEAST_EXPECTS(! ec, ec.message()); } + //-------------------------------------------------------------------------- + // + // Example: Custom Parser + // + //-------------------------------------------------------------------------- + + 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; + design::custom_parser p; + p.put(boost::asio::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; + design::custom_parser p; + p.put(boost::asio::buffer( + s.data(), s.size()), ec); + BEAST_EXPECTS(! ec, ec.message()); + } + } + //-------------------------------------------------------------------------- // // Deferred Body type commitment @@ -788,6 +954,8 @@ public: doCgiResponse(); doRelay(); doParseStdStream(); + doCustomParser(); + doDeferredBody(); } };