From fc83a031a2407eff13f78c3a09bc533e25a75f75 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Fri, 2 Jun 2017 19:12:39 -0700 Subject: [PATCH] Documentation work --- doc/0_main.qbk | 14 +- doc/3_0_http.qbk | 32 +- doc/3_2_message.qbk | 20 +- doc/3_3_streams.qbk | 99 +++--- doc/3_4_serializer_streams.qbk | 306 ++++++++++++++++ doc/3_5_parser_streams.qbk | 332 ++++++++++++++++++ ...dv_http.qbk => 3_6_serializer_buffers.qbk} | 171 +-------- doc/3_7_parser_buffers.qbk | 10 + doc/3_8_custom_parsers.qbk | 10 + doc/3_9_custom_body.qbk | 49 +++ doc/{5_websocket.qbk => 4_websocket.qbk} | 0 doc/{6_examples.qbk => 5_examples.qbk} | 0 doc/{7_0_design.qbk => 6_0_design.qbk} | 8 +- ..._http_message.qbk => 6_1_http_message.qbk} | 0 ...comparison.qbk => 6_2_http_comparison.qbk} | 0 ..._zaphoyd.qbk => 6_3_websocket_zaphoyd.qbk} | 0 doc/{7_4_review.qbk => 6_4_review.qbk} | 0 doc/concept/BodyReader.qbk | 21 +- doc/concept/BodyWriter.qbk | 248 +++++-------- doc/images/AlfaSlabOne-Regular.ttf | Bin 0 -> 36504 bytes doc/images/SIL Open Font License.txt | 43 +++ doc/quickref.xml | 17 +- 22 files changed, 956 insertions(+), 424 deletions(-) create mode 100644 doc/3_4_serializer_streams.qbk create mode 100644 doc/3_5_parser_streams.qbk rename doc/{4_0_adv_http.qbk => 3_6_serializer_buffers.qbk} (55%) create mode 100644 doc/3_7_parser_buffers.qbk create mode 100644 doc/3_8_custom_parsers.qbk create mode 100644 doc/3_9_custom_body.qbk rename doc/{5_websocket.qbk => 4_websocket.qbk} (100%) rename doc/{6_examples.qbk => 5_examples.qbk} (100%) rename doc/{7_0_design.qbk => 6_0_design.qbk} (95%) rename doc/{7_1_http_message.qbk => 6_1_http_message.qbk} (100%) rename doc/{7_2_http_comparison.qbk => 6_2_http_comparison.qbk} (100%) rename doc/{7_3_websocket_zaphoyd.qbk => 6_3_websocket_zaphoyd.qbk} (100%) rename doc/{7_4_review.qbk => 6_4_review.qbk} (100%) create mode 100644 doc/images/AlfaSlabOne-Regular.ttf create mode 100644 doc/images/SIL Open Font License.txt diff --git a/doc/0_main.qbk b/doc/0_main.qbk index 9d03c9a1..f62407cb 100644 --- a/doc/0_main.qbk +++ b/doc/0_main.qbk @@ -57,9 +57,11 @@ [def __basic_fields__ [link beast.ref.http__basic_fields `basic_fields`]] [def __basic_multi_buffer__ [link beast.ref.basic_multi_buffer `basic_multi_buffer`]] [def __basic_parser__ [link beast.ref.http__basic_parser `basic_parser`]] +[def __buffer_body__ [link beast.ref.http__buffer_body `buffer_body`]] [def __fields__ [link beast.ref.http__fields `fields`]] [def __flat_buffer__ [link beast.ref.flat_buffer `flat_buffer`]] [def __header__ [link beast.ref.http__header `header`]] +[def __header_parser__ [link beast.ref.http__header_parser `header_parser`]] [def __message__ [link beast.ref.http__message `message`]] [def __message_parser__ [link beast.ref.http__message_parser `message_parser`]] [def __multi_buffer__ [link beast.ref.multi_buffer `multi_buffer`]] @@ -85,11 +87,6 @@ asynchronous model of __Asio__. ][ How to use the basic algorithms in your applications. ]] - [[ - [link beast.adv_http Advanced HTTP] - ][ - A discussion of the advanced interfaces. - ]] [[ [link beast.websocket Using WebSocket] ][ @@ -121,10 +118,9 @@ asynchronous model of __Asio__. [include 1_overview.qbk] [include 2_core.qbk] [include 3_0_http.qbk] -[include 4_0_adv_http.qbk] -[include 5_websocket.qbk] -[include 6_examples.qbk] -[include 7_0_design.qbk] +[include 4_websocket.qbk] +[include 5_examples.qbk] +[include 6_0_design.qbk] [section:ref Reference] [xinclude quickref.xml] diff --git a/doc/3_0_http.qbk b/doc/3_0_http.qbk index 635cb467..687476a1 100644 --- a/doc/3_0_http.qbk +++ b/doc/3_0_http.qbk @@ -11,13 +11,19 @@ HTTP Primer Message Containers - Stream Operations + Message Stream Operations + Serializer Stream Operations + Parser Stream Operations + Buffer-Oriented Serializing + Buffer-Oriented Parsing + Custom Parsers + Custom Body Types '''] -This library offers programmers simple and performant models of HTTP -messages and their associated operations including synchronous and -asynchronous parsing and serialization of messages in the HTTP/1 wire +This library offers programmers simple and performant models of HTTP messages +and their associated operations including synchronous, asynchronous, and +buffer-oriented parsing and serialization of messages in the HTTP/1 wire format using __Asio__. Specifically, the library provides: [variablelist @@ -32,10 +38,12 @@ format using __Asio__. Specifically, the library provides: [ The functions [link beast.ref.http__read `read`], + [link beast.ref.http__read_header `read_header`], [link beast.ref.http__read_some `read_some`], - [link beast.ref.http__async_read `async_read`], and + [link beast.ref.http__async_read `async_read`], + [link beast.ref.http__async_read_header `async_read_header`], and [link beast.ref.http__async_read_some `async_read_some`] - read a __message__ from a + read HTTP/1 message data from a [link beast.ref.streams stream]. ] ][ @@ -43,10 +51,12 @@ format using __Asio__. Specifically, the library provides: [ The functions [link beast.ref.http__write `write`], + [link beast.ref.http__write_header `write_header`], [link beast.ref.http__write_some `write_some`], - [link beast.ref.http__async_write `async_write`], and + [link beast.ref.http__async_write `async_write`], + [link beast.ref.http__async_write_header `async_write_header`], and [link beast.ref.http__async_write_some `async_write_some`] - write a __message__ to a + write HTTP/1 message data to a [link beast.ref.streams stream]. ] ][ @@ -79,5 +89,11 @@ format using __Asio__. Specifically, the library provides: [include 3_1_primer.qbk] [include 3_2_message.qbk] [include 3_3_streams.qbk] +[include 3_4_serializer_streams.qbk] +[include 3_5_parser_streams.qbk] +[include 3_6_serializer_buffers.qbk] +[include 3_7_parser_buffers.qbk] +[include 3_8_custom_parsers.qbk] +[include 3_9_custom_body.qbk] [endsect] diff --git a/doc/3_2_message.qbk b/doc/3_2_message.qbk index 753d9a13..0005d07d 100644 --- a/doc/3_2_message.qbk +++ b/doc/3_2_message.qbk @@ -26,18 +26,11 @@ accept any message, or can use partial specialization to accept just requests or responses. The default __fields__ is a provided associative container using the standard allocator and supporting modification and inspection of fields. As per __rfc7230__, a non-case-sensitive comparison -is used for field names. User defined types for fields are possible. This -is discussed in -[link beast.adv_http.fields Advanced Fields]. +is used for field names. User defined types for fields are possible. The `Body` type determines the type of the container used to represent the body as well as the algorithms for transferring buffers to and from the -the container. The library comes with a collection of common body types, -described in -[link beast.http.message.body Body Types]. -As with fields, user defined body types are possible. This is described in -[link beast.adv_http.body Advanced Body]. - - +the container. The library comes with a collection of common body types. +As with fields, user defined body types are possible. Sometimes it is desired to only work with a header. Beast provides a single class template __header__ to model HTTP/1 and HTTP/2 headers: @@ -88,9 +81,10 @@ the __Body__ requirements: [[ [link beast.ref.http__buffer_body `buffer_body`] ][ - A body with `value_type` holding a __ConstBufferSequence__ holding - caller provided buffers which must be updated during incremental - serialization. Messages with this body type only support serialization. + A body whose `value_type` holds a raw pointer and size to a + caller-provided buffer. This allows for serialization of body data + coming from external sources, and incremental parsing of message + body content using a fixed size buffer. ]] [[ [link beast.ref.http__dynamic_body `dynamic_body`] diff --git a/doc/3_3_streams.qbk b/doc/3_3_streams.qbk index 92c3ed55..1d3b49f0 100644 --- a/doc/3_3_streams.qbk +++ b/doc/3_3_streams.qbk @@ -5,7 +5,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ] -[section:streams Stream Operations] +[section:streams Message Stream Operations] Beast provides synchronous and asynchronous algorithms to serialize and parse HTTP/1 wire format messages on streams. These functions form the @@ -19,16 +19,16 @@ requiring no separately managed state objects: ][ Parse a __message__ from a __SyncReadStream__. ]] -[[ - [link beast.ref.http__write.overload1 [*write]] -][ - Serialize a __message__ to a __SyncWriteStream__. -]] [[ [link beast.ref.http__async_read.overload2 [*async_read]] ][ Parse a __message__ from an __AsyncReadStream__. ]] +[[ + [link beast.ref.http__write.overload1 [*write]] +][ + Serialize a __message__ to a __SyncWriteStream__. +]] [[ [link beast.ref.http__async_write [*async_write]] ][ @@ -36,11 +36,54 @@ requiring no separately managed state objects: ]] ] -Synchronous stream operations come in two varieties. One which throws +All synchronous stream operations come in two varieties. One which throws an exception upon error, and another which accepts as the last parameter an argument of type [link beast.ref.error_code `error_code&`]. If an error occurs this argument will be set to contain the error code. +[heading Reading] + +Because a serialized header is not length-prefixed, algorithms which parse +messages from a stream may read past the end of a message for efficiency. +To hold this surplus data, all stream read operations use a passed-in +__DynamicBuffer__ which persists between calls. Each read operation may +consume bytes remaining in the buffer, and leave behind new bytes. In this +example we declare the buffer and a message variable, then read a complete +HTTP request synchronously: +``` + flat_buffer buffer; // (The parser is optimized for flat buffers) + request req; + read(sock, buffer, req); +``` + +In this example we used the __flat_buffer__. The parser in Beast is +optimized for structured HTTP data located in a single contiguous memory +buffer ("flat buffer"). Any dynamic buffer will work with reads. However, +when not using a flat buffer the implementation may perform an additional +memory allocation to restructure the input into a single buffer. + +[tip + User-defined implementations of __DynamicBuffer__ may avoid additional + parser memory allocation, if those implementations guarantee that + returned buffer sequences will always have length one. +] + +Messages may also be read asynchronously. When performing asynchronous +stream read operations, the buffer and message variables must remain +valid until the operation has completed. Beast asynchronous initiation +functions use Asio's completion handler model. Here we read a message +asynchronously. When the operation completes the message in the error +code indicating the result is printed: +``` + flat_buffer buffer; + response res; + async_read(sock, buffer, + [&](error_code ec) + { + std::cerr << ec.message() << std::endl; + }); +``` + [heading Writing] A set of free functions allow serialization of an entire HTTP message to @@ -95,46 +138,4 @@ void send_async(response const& res) } ``` -[heading Reading] - -Because a serialized header is not length-prefixed, algorithms which parse -messages from a stream may read past the end of a message for efficiency. -To hold this surplus data, all stream read operations use a passed-in -__DynamicBuffer__. Each read operation may consume bytes remaining in the -buffer, and leave behind new bytes. In this example we declare the buffer -and a message variable, then read a complete HTTP request synchronously: -``` - flat_buffer buffer; // (The parser is optimized for flat buffers) - request req; - read(sock, buffer, req); -``` - -In this example we used the __flat_buffer__. The parser in Beast is -optimized for structured HTTP data located in a single contiguous memory -buffer ("flat buffer"). Any dynamic buffer will work with reads. However, -when not using a flat buffer the implementation may perform an additional -memory allocation to restructure the input into a single buffer. - -[tip - User-defined implementations of __DynamicBuffer__ may avoid additional - parser memory allocation, if those implementations guarantee that - returned buffer sequences will always have length one. -] - -Messages may also be read asynchronously. When performing asynchronous -stream read operations, the buffer and message variables must remain -valid until the operation has completed. Beast asynchronous initiation -functions use Asio's completion handler model. Here we read a message -asynchronously. When the operation completes the message in the error -code indicating the result is printed: -``` - flat_buffer buffer; - response res; - async_read(sock, buffer, - [&](error_code ec) - { - std::cerr << ec.message() << std::endl; - }); -``` - [endsect] diff --git a/doc/3_4_serializer_streams.qbk b/doc/3_4_serializer_streams.qbk new file mode 100644 index 00000000..e94d2f3d --- /dev/null +++ b/doc/3_4_serializer_streams.qbk @@ -0,0 +1,306 @@ +[/ + Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +] + +[section:serializer_streams Serializer Stream Operations] + +Algorithms for sending entire messages to streams are intended for light +duty use-cases such as simple clients and low utilization servers. +Sophisticated algorithms will need to do more: + +* Send the message header first. + +* Send a message incrementally: bounded work in each I/O cycle. + +* Use a custom chunk decorator or allocator when sending messages. + +* Use a series of caller-provided buffers to represent the body. + +All of these operations require callers to manage the lifetime of state +information associated with the operation, by constructing a __serializer__ +object with the message to be sent. The serializer type has this declaration: +``` +template< + bool isRequest, + class Body, + class Fields, + class Decorator = empty_decorator, + class Allocator = std::allocator +> +class serializer; +``` + +The choices for template types must match the message passed on construction. +This code creates an HTTP response and the corresponding serializer: +``` + response res; + ... + serializer sr{res}; +``` +The convenience function +[link beast.ref.http__make_serializer `make_serializer`] +is provided to avoid repetition of template argument types. The declaration +for `sr` in the code above may be written as: +``` + ... + auto sr = make_serializer(res); +``` + +The stream operations which work on serializers are: + +[table Serializer Stream Operations +[[Name][Description]] +[[ + [link beast.ref.http__write_some.overload1 [*write_some]] +][ + Send some __serializer__ buffer data to a __SyncWriteStream__. +]] +[[ + [link beast.ref.http__async_write_some [*async_write_some]] +][ + Send some __serializer__ buffer data asynchronously to an __AsyncWriteStream__. +]] +[[ + [link beast.ref.http__write_header.overload1 [*write_header]] +][ + Send only the header from a __serializer__ to a __SyncWriteStream__. +]] +[[ + [link beast.ref.http__async_write_header [*async_write_header]] +][ + Send only the header from a __serializer__ asynchronously to an __AsyncWriteStream__. +]] +[[ + [link beast.ref.http__write.overload1 [*write]] +][ + Send everything in a __serializer__ to a __SyncWriteStream__. +]] +[[ + [link beast.ref.http__async_write.overload1 [*async_write]] +][ + Send everything in a __serializer__ asynchronously to an __AsyncWriteStream__. +]] +] + +Here is an example of using a serializer to send a message on a stream +synchronously. This performs the same operation as calling `write(stream, m)`: + +``` +template +void send(SyncWriteStream& stream, message const& m) +{ + static_assert(is_sync_write_stream::value, + "SyncWriteStream requirements not met"); + serializer sr{m}; + do + { + write_some(stream, sr); + } + while(! sr.is_done()); +} +``` + +[heading Example: Expect 100-continue] + +The Expect field with the value "100-continue" in a request is special. It +indicates that the after sending the message header, a client desires an +immediate informational response before sending the the message body, which +presumably may be expensive to compute or large. This behavior is described in +[@https://tools.ietf.org/html/rfc7231#section-5.1.1 rfc7231 section 5.1.1]. +Invoking the 100-continue behavior is implemented easily in a client by +constructing a __serializer__ to send the header first, then receiving +the server response, and finally conditionally send the body using the same +serializer instance. A synchronous, simplified version (no timeout) of +this client action looks like this: +``` +/** Send a request with Expect: 100-continue + + This function will send a request with the Expect: 100-continue + field by first sending the header, then waiting for a successful + response from the server before continuing to send the body. If + a non-successful server response is received, the function + returns immediately. + + @param stream The remote HTTP server stream. + + @param buffer The buffer used for reading. + + @param req The request to send. This function modifies the object: + the Expect header field is inserted into the message if it does + not already exist, and set to "100-continue". + + @param ec Set to the error, if any occurred. +*/ +template< + class SyncStream, + class DynamicBuffer, + class Body, class Fields> +void +send_expect_100_continue( + SyncStream& stream, + DynamicBuffer& buffer, + request& req) +{ + static_assert(is_sync_stream::value, + "SyncStream requirements not met"); + + static_assert(is_dynamic_buffer::value, + "DynamicBuffer requirements not met"); + + // Insert or replace the Expect field + req.fields.replace("Expect", "100-continue"); + + // Create the serializer + auto sr = make_serializer(req); + + // Send just the header + write_header(stream, sr); + + // Read the response from the server. + // A robust client could set a timeout here. + { + response res; + read(stream, buffer, res); + if(res.status != 100) + { + // The server indicated that it will not + // accept the request, so skip sending the body. + return; + } + } + + // Server is OK with the request, send the body + write(stream, sr); +} +``` + +[heading Example: Using Manual Buffers] + +Sometimes it is necessary to send a message whose body is not conveniently +described by a single container. For example, when implementing an HTTP relay +function a robust implementation needs to present body buffers individually +as they become available from the downstream host. These buffers should be +fixed in size, otherwise creating the unnecessary and inefficient burden of +reading the complete message body before forwarding it to the upstream host. + +To enable these use-cases, the body type __buffer_body__ is provided. This +body uses a caller-provided pointer and size instead of an owned container. +To use this body, instantiate an instance of the serializer and fill in +the pointer and size fields before calling a stream write function. + +This example reads from a child process and sends the output back in an +HTTP response. The output of the process is sent as it becomes available: +``` +/** Send the output of a child process as an HTTP response. + + The output of the child process comes from a @b SyncReadStream. Data + will be sent continuously as it is produced, without the requirement + that the entire process output is buffered before being sent. The + response will use the chunked transfer encoding. + + @param input A stream to read the child process output from. + + @param output A stream to write the HTTP response to. + + @param ec Set to the error, if any occurred. +*/ +template< + class SyncReadStream, + class SyncWriteStream> +void +send_cgi_response( + SyncReadStream& input, + SyncWriteStream& output, + error_code& ec) +{ + static_assert(is_sync_read_stream::value, + "SyncReadStream requirements not met"); + + static_assert(is_sync_write_stream::value, + "SyncWriteStream requirements not met"); + + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + + // Set up the response. We use the buffer_body type, + // allowing serialization to use manually provided buffers. + message res; + + res.status = 200; + res.version = 11; + res.fields.insert("Server", "Beast"); + res.fields.insert("Transfer-Encoding", "chunked"); + + // No data yet, but we set more = true to indicate + // that it might be coming later. Otherwise the + // serializer::is_done would return true right after + // sending the header. + res.body.data = nullptr; + res.body.more = true; + + // Create the serializer. We set the split option to + // produce the header immediately without also trying + // to acquire buffers from the body (which would return + // the error http::need_buffer because we set `data` + // to `nullptr` above). + auto sr = make_serializer(res); + + // Send the header immediately. + write_header(output, sr, ec); + if(ec) + return; + + // Alternate between reading from the child process + // and sending all the process output until there + // is no more output. + do + { + // Read a buffer from the child process + char buffer[2048]; + auto bytes_transferred = input.read_some( + boost::asio::buffer(buffer, sizeof(buffer)), ec); + if(ec == boost::asio::error::eof) + { + ec = {}; + + // `nullptr` indicates there is no buffer + res.body.data = nullptr; + + // `false` means no more data is coming + res.body.more = false; + } + else + { + if(ec) + return; + + // Point to our buffer with the bytes that + // we received, and indicate that there may + // be some more data coming + res.body.data = buffer; + res.body.size = bytes_transferred; + res.body.more = true; + } + + // Write everything in the body buffer + write(output, sr, ec); + + // This error is returned by body_buffer during + // serialization when it is done sending the data + // provided and needs another buffer. + if(ec == error::need_buffer) + { + ec = {}; + continue; + } + if(ec) + return; + } + while(! sr.is_done()); +} +``` + +[endsect] diff --git a/doc/3_5_parser_streams.qbk b/doc/3_5_parser_streams.qbk new file mode 100644 index 00000000..9966de4a --- /dev/null +++ b/doc/3_5_parser_streams.qbk @@ -0,0 +1,332 @@ +[/ + Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +] + +[section:parser_streams Parser Stream Operations] + +Algorithms for receiving entire messages from streams are helpful for simple +use-cases. Sophisticated algorithms will need to do more: + +* Receive the message header first. + +* Receive a message incrementally: bounded work in each I/O cycle. + +* Receive an arbitrarily-sized body using a fixed-size buffer. + +* Defer the commitment to a __Body__ type until after reading the header. + +All of these operations require callers to manage the lifetime of state +information associated with the operation, by constructing a class derived +from __basic_parser__. Beast comes with two instances of parsers, and user +defined types deriving from the basic parser are possible: + +[table Parser Implementations +[[Name][Description]] +[[ + __message_parser__ +][ + ``` + /// A parser for a message + template< + bool isRequest, // `true` to parse an HTTP request + class Body, // The Body type for the resulting message + class Fields> // The type of container representing the fields + class message_parser + : public basic_parser<...>; + ``` +]] +[[ + __header_parser__ +][ + ``` + /// A parser for a header + template< + bool isRequest, // `true` to parse an HTTP request + class Fields> // The type of container representing the fields + class header_parser + : public basic_parser<...>; + ``` +]] +] + +[note + The __basic_parser__ and classes derived from it handle octet streams + serialized in the HTTP/1 format described in __rfc7230__. +] + +The stream operations which work on parsers are: + +[table Parser Stream Operations +[[Name][Description]] +[[ + [link beast.ref.http__read_some.overload1 [*read_some]] +][ + Read some octets into a parser from a __SyncReadStream__. +]] +[[ + [link beast.ref.http__async_read_some [*async_read_some]] +][ + Read some octets into a parser asynchronously from an __AsyncWriteStream__. +]] +[[ + [link beast.ref.http__read_header.overload1 [*read_header]] +][ + Read only the header octets into a parser from a __SyncWriteStream__. +]] +[[ + [link beast.ref.http__async_read_header [*async_read_header]] +][ + Read only the header octets into a parser asynchronously from an __AsyncWriteStream__. +]] +[[ + [link beast.ref.http__read.overload1 [*read]] +][ + Read everything into a parser from a __SyncWriteStream__. +]] +[[ + [link beast.ref.http__async_read.overload1 [*async_read]] +][ + Read everything into a parser asynchronously from an __AsyncWriteStream__. +]] +] + +As with the stream parse algorithms which operate on entire messages, stream +operations for parsers require a passed-in __DynamicBuffer__ which persists +between calls to hold unused octets from the stream. The basic parser +implementation is optimized for the case where this dynamic buffer stores +its input sequence in a single contiguous memory buffer. It is advised to +use an instance of __flat_buffer__ for this purpose, although a user defined +instance of __DynamicBuffer__ which produces input sequences of length one +is also suitable. + +The provided parsers use a "captive object" model, acting as container for +the __message__ or __header__ produced as a result of parsing. The caller +accesses the contained object, and depending on the types used to instantiate +the parser, it may be possible to acquire ownership of the header or message +captive object and destroy the parser. In this example we read an HTTP +response with a string body using a parser, then print the response: +``` +template +void print_response(SyncReadStream& stream) +{ + static_assert(is_sync_read_stream::value, + "SyncReadStream requirements not met"); + + // Declare a parser for an HTTP response + response_parser parser; + + // Read the entire message + read(stream, parser); + + // Now print the message + std::cout << parser.get() << std::endl; +} +``` + +[heading Example: 100-continue] + +The Expect field with the value "100-continue" in a request is special. It +indicates that the after sending the message header, a client desires an +immediate informational response before sending the the message body, which +presumably may be expensive to compute or large. This behavior is described in +[@https://tools.ietf.org/html/rfc7231#section-5.1.1 rfc7231 section 5.1.1]. +Handling the Expect field can be implemented easily in a server by constructing +a __message_parser__ to read the header first, then send an informational +HTTP response, and finally read the body using the same parser instance. A +synchronous version of this server action looks like this: +``` +/** Receive a request, handling Expect: 100-continue if present. + + This function will read a request from the specified stream. + If the request contains the Expect: 100-continue field, a + status response will be delivered. + + @param stream The remote HTTP client stream. + + @param buffer The buffer used for reading. + + @param ec Set to the error, if any occurred. +*/ +template< + class SyncStream, + class DynamicBuffer> +void +receive_expect_100_continue( + SyncStream& stream, + DynamicBuffer& buffer, + error_code& ec) +{ + static_assert(is_sync_stream::value, + "SyncStream requirements not met"); + + static_assert(is_dynamic_buffer::value, + "DynamicBuffer requirements not met"); + + // Declare a parser for a request with a string body + request_parser parser; + + // Read the header + read_header(stream, buffer, parser, ec); + if(ec) + return; + + // Check for the Expect field value + if(parser.get().fields["Expect"] == "100-continue") + { + // send 100 response + response res; + res.version = 11; + res.status = 100; + res.reason("Continue"); + res.fields.insert("Server", "test"); + write(stream, res, ec); + if(ec) + return; + } + + // Read the rest of the message. + // + // We use parser.base() to return a basic_parser&, to avoid an + // ambiguous function error (from boost::asio::read). Another + // solution is to qualify the call, e.g. `beast::http::read` + // + read(stream, buffer, parser.base(), ec); +} +``` + +[heading Example: HTTP Relay] + +An HTTP proxy acts as a relay between client and server. The proxy reads a +request from the client and sends it to the server, possibly adjusting some +of the headers and representation of the body along the way. Then, the +proxy reads a response from the server and sends it back to the client, +also with the possibility of changing the headers and body representation. + +The example that follows implements a synchronous HTTP relay. It uses a +fixed size buffer, to avoid reading in the entire body so that the upstream +connection sees a header without unnecessary latency. This example brings +together all of the concepts discussed so far, it uses both a __serializer__ +and a __message_parser__ to achieve its goal: +``` +/** Relay an HTTP message. + + This function efficiently relays an HTTP message from a downstream + client to an upstream server, or from an upstream server to a + downstream client. After the message header is read from the input, + a user provided transformation function is invoked which may change + the contents of the header before forwarding to the output. This may + be used to adjust fields such as Server, or proxy fields. + + @param output The stream to write to. + + @param input The stream to read from. + + @param buffer The buffer to use for the input. + + @param transform The header transformation to apply. The function will + be called with this signature: + @code + void transform( + header&, // The header to transform + error_code&); // Set to the error, if any + @endcode + + @param ec Set to the error if any occurred. + + @tparam isRequest `true` to relay a request. + + @tparam Fields The type of fields to use for the message. +*/ +template< + bool isRequest, + class Fields = fields, + class SyncWriteStream, + class SyncReadStream, + class DynamicBuffer, + class Transform> +void +relay( + SyncWriteStream& output, + SyncReadStream& input, + DynamicBuffer& buffer, + error_code& ec, + Transform&& transform) +{ + static_assert(is_sync_write_stream::value, + "SyncWriteStream requirements not met"); + + static_assert(is_sync_read_stream::value, + "SyncReadStream requirements not met"); + + // A small buffer for relaying the body piece by piece + char buf[2048]; + + // Create a parser with a buffer body to read from the input. + message_parser p; + + // Create a serializer from the message contained in the parser. + serializer sr{p.get()}; + + // Read just the header from the input + read_header(input, buffer, p, ec); + if(ec) + return; + + // Apply the caller's header tranformation + // base() returns a reference to the header portion of the message. + transform(p.get().base(), ec); + if(ec) + return; + + // Send the transformed message to the output + write_header(output, sr, ec); + if(ec) + return; + + // Loop over the input and transfer it to the output + do + { + if(! p.is_done()) + { + // Set up the body for writing into our small buffer + p.get().body.data = buf; + p.get().body.size = sizeof(buf); + + // Read as much as we can + read(input, buffer, p, ec); + + // This error is returned when buffer_body uses up the buffer + if(ec == error::need_buffer) + ec = {}; + if(ec) + return; + + // Set up the body for reading. + // This is how much was parsed: + p.get().body.size = sizeof(buf) - p.get().body.size; + p.get().body.data = buf; + p.get().body.more = ! p.is_done(); + } + else + { + p.get().body.data = nullptr; + p.get().body.size = 0; + } + + // Write everything in the buffer (which might be empty) + write(output, sr, ec); + + // This error is returned when buffer_body uses up the buffer + if(ec == error::need_buffer) + ec = {}; + if(ec) + return; + } + while(! p.is_done() && ! sr.is_done()); +} +``` + +[endsect] diff --git a/doc/4_0_adv_http.qbk b/doc/3_6_serializer_buffers.qbk similarity index 55% rename from doc/4_0_adv_http.qbk rename to doc/3_6_serializer_buffers.qbk index 1541b4ab..bcccd3c8 100644 --- a/doc/4_0_adv_http.qbk +++ b/doc/3_6_serializer_buffers.qbk @@ -5,58 +5,20 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ] -[section:adv_http Advanced HTTP] +[section:serializer_buffers Buffer-Oriented Serializing] -[block ''' - - Serialization - Parsing - Field Containers - Body Types - -'''] +In extreme cases, users may wish to create an instance of __serializer__ +and invoke its methods directly instead of using the provided stream +algorithms. This could be useful for implementing algorithms on streams +whose asynchronous interface does not conform to __AsyncStream__. For +example, a +[@https://github.com/libuv/libuv *libuv* socket]. -The basic interfaces for reading and writing complete messages are -simple to use and convenient, but may not serve the needs of advanced -use cases, including: +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. -* Parsing a message from caller-provided buffers - -* Serializing a message to caller-provided buffers - -* Reading a message from a stream incrementally - -* Writing a message to a stream incrementally - -In some cases, users may wish to provide their own implementation for -the fields container and the body type. In the advanced sections we -discuss the buffer oriented message algorithms, incremental stream -algorithms, and customization points for messages. - - - -[section:serialize Serialization (Advanced HTTP)] - -Beast uses the __serializer__ class internally to generate a sequence of -buffers corresponding to the serialized representation of a __message__. -The basic algorithms send the entire set of buffers at once while the -incremental algorithms allow the caller to write a bounded amount to -the stream at each iteration. In between calls to generate buffers or -write buffers to the stream, the internal state of the serialization -must be saved. A __serializer__ initializes this internal state and -stores it in between calls to produce buffers, until all the message -buffers have been produced. Afterwards, the state object may be destroyed. - -The serializer is a class template constructed from an existing -message. The template types used to instantiate the serializer must -match the types in the message. Here we declare a request and construct -an accompanying serializer: -``` -request req; -serializer sr{req}; -``` - -The buffers are produced by first calling +After the serializer is created, the buffers are produced by first calling [link beast.ref.http__serializer.get `serializer::get`] to obtain a buffer sequence, and then calling [link beast.ref.http__serializer.consume `serializer::consume`] @@ -128,56 +90,6 @@ void print(message const& m) } ``` -[heading Stream Operations] - -When working with streams, the use of an explicitly declared serializer -allows control over the amount of network activity performed in each -system call. This allows for better application level flow control and -predictable timeouts. These advanced stream write operations work on -an object of type __serializer__ which has already been constructed, -and which must remain valid until the operation is complete: - -[table Advanced Streaming -[[Name][Description]] -[[ - [link beast.ref.http__write_some.overload1 [*write_some]] -][ - Send __serializer__ buffer data to a __SyncWriteStream__. -]] -[[ - [link beast.ref.http__write_header.overload1 [*write_header]] -][ - Send an entire header from a __serializer__ to a __SyncWriteStream__. -]] -[[ - [link beast.ref.http__async_write_some [*async_write_some]] -][ - Send some __serializer__ buffer data to an __AsyncWriteStream__. -]] -[[ - [link beast.ref.http__async_write_header [*async_write_header]] -][ - Send an entire header from a __serializer__ to a __AsyncWriteStream__. -]] -] - -Here is an example which synchronously sends a message on a stream using -a serializer: -``` -template -void send(SyncWriteStream& stream, message const& m) -{ - static_assert(is_sync_write_stream::value, - "SyncWriteStream requirements not met"); - serializer sr{m}; - do - { - write_some(stream, sr); - } - while(! sr.is_done()); -} -``` - [heading Split Serialization] In some cases, such as the handling of the @@ -286,64 +198,3 @@ struct decorator ``` [endsect] - - - -[section:parsing Parsing (Advanced HTTP)] - -[endsect] - - - -[section:fields Field Containers (Advanced HTTP)] - -[endsect] - - - -[section:body Body Types (Advanced HTTP)] - -User-defined types are possible for the message body, where the type meets the -[link beast.ref.Body [*`Body`]] requirements. This simplified class declaration -shows the customization points available to user-defined body types: - -[$images/body.png [width 525px] [height 190px]] - -The meanin of the nested types is as follows - -[table Body Type Members -[[Name][Description]] -[ - [`value_type`] - [ - Determines the type of the - [link beast.ref.http__message.body `message::body`] member. If this - type defines default construction, move, copy, or swap, then message objects - declared with this [*Body] will have those operations defined. - ] -][ - [`body_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. - ] -] -] - -The examples included with this library provide a [*Body] implementation that -serializing message bodies that come from a file. - -[endsect] - - - -[endsect] diff --git a/doc/3_7_parser_buffers.qbk b/doc/3_7_parser_buffers.qbk new file mode 100644 index 00000000..a90da4d4 --- /dev/null +++ b/doc/3_7_parser_buffers.qbk @@ -0,0 +1,10 @@ +[/ + Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +] + +[section:parser_buffers Buffer-Oriented Parsing] + +[endsect] diff --git a/doc/3_8_custom_parsers.qbk b/doc/3_8_custom_parsers.qbk new file mode 100644 index 00000000..3858a582 --- /dev/null +++ b/doc/3_8_custom_parsers.qbk @@ -0,0 +1,10 @@ +[/ + Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +] + +[section:custom_parsers Custom Parsers] + +[endsect] diff --git a/doc/3_9_custom_body.qbk b/doc/3_9_custom_body.qbk new file mode 100644 index 00000000..2f3f1f3b --- /dev/null +++ b/doc/3_9_custom_body.qbk @@ -0,0 +1,49 @@ +[/ + Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +] + +[section:custom_body Custom Body Types] + +User-defined types are possible for the message body, where the type meets the +[link beast.ref.Body [*`Body`]] requirements. This simplified class declaration +shows the customization points available to user-defined body types: + +[$images/body.png [width 525px] [height 190px]] + +The meaning of the nested types is as follows + +[table Body Type Members +[[Name][Description]] +[ + [`value_type`] + [ + Determines the type of the + [link beast.ref.http__message.body `message::body`] member. If this + type defines default construction, move, copy, or swap, then message objects + declared with this [*Body] will have those operations defined. + ] +][ + [`body_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. + ] +] +] + +The examples included with this library provide a [*Body] implementation that +serializing message bodies that come from a file. + +[endsect] diff --git a/doc/5_websocket.qbk b/doc/4_websocket.qbk similarity index 100% rename from doc/5_websocket.qbk rename to doc/4_websocket.qbk diff --git a/doc/6_examples.qbk b/doc/5_examples.qbk similarity index 100% rename from doc/6_examples.qbk rename to doc/5_examples.qbk diff --git a/doc/7_0_design.qbk b/doc/6_0_design.qbk similarity index 95% rename from doc/7_0_design.qbk rename to doc/6_0_design.qbk index a024f7db..619bb31c 100644 --- a/doc/7_0_design.qbk +++ b/doc/6_0_design.qbk @@ -59,9 +59,9 @@ start. Other design goals: * Allow for customizations, if the user needs it. -[include 7_1_http_message.qbk] -[include 7_2_http_comparison.qbk] -[include 7_3_websocket_zaphoyd.qbk] -[include 7_4_review.qbk] +[include 6_1_http_message.qbk] +[include 6_2_http_comparison.qbk] +[include 6_3_websocket_zaphoyd.qbk] +[include 6_4_review.qbk] [endsect] diff --git a/doc/7_1_http_message.qbk b/doc/6_1_http_message.qbk similarity index 100% rename from doc/7_1_http_message.qbk rename to doc/6_1_http_message.qbk diff --git a/doc/7_2_http_comparison.qbk b/doc/6_2_http_comparison.qbk similarity index 100% rename from doc/7_2_http_comparison.qbk rename to doc/6_2_http_comparison.qbk diff --git a/doc/7_3_websocket_zaphoyd.qbk b/doc/6_3_websocket_zaphoyd.qbk similarity index 100% rename from doc/7_3_websocket_zaphoyd.qbk rename to doc/6_3_websocket_zaphoyd.qbk diff --git a/doc/7_4_review.qbk b/doc/6_4_review.qbk similarity index 100% rename from doc/7_4_review.qbk rename to doc/6_4_review.qbk diff --git a/doc/concept/BodyReader.qbk b/doc/concept/BodyReader.qbk index 001eed45..3d0a20e6 100644 --- a/doc/concept/BodyReader.qbk +++ b/doc/concept/BodyReader.qbk @@ -50,6 +50,17 @@ In this table: [`X::is_deferred`] [] [ + The type `std::true_type` if the serialization implementation + should only attempt to retrieve buffers from the reader after + the header has been serialized. Otherwise, if this type is + `std::false_type` the implementation will activate an + optimization: the first buffer produced during serialization + will contain both the header and some or all of this body. + + Implementations of [*BodyReader] for which initialization is + expensive, should use `std::false_type` here, to reduce the + latency experienced by the remote host when expecting to read + the HTTP header. ] ] [ @@ -124,19 +135,17 @@ public: /** Controls when the implementation requests buffers. If false, the implementation will request the first buffer - immediately and try to send both the header and the body - buffer in a single call to the stream's `write_some` - function. + immediately and try to serialize both the header and some + or all of the body in a single buffer. */ using is_deferred = std::false_type; - /** The type of buffer returned by `get`. - */ + /// The type of buffer returned by `get`. using const_buffers_type = boost::asio::const_buffers_1; /** Construct the reader. - @param msg The message whose body is to be written. + @param msg The message whose body is to be retrieved. */ template explicit diff --git a/doc/concept/BodyWriter.qbk b/doc/concept/BodyWriter.qbk index ab7be12f..62b245f4 100644 --- a/doc/concept/BodyWriter.qbk +++ b/doc/concept/BodyWriter.qbk @@ -7,206 +7,76 @@ [section:BodyWriter BodyWriter requirements] -When HTTP messages are parsed, the implementation constructs a -[*BodyWriter] object to provide the means for transferring parsed body -octets into the message container. These body writers come in two flavors, -direct and indirect: +A [*BodyWriter] provides an online algorithm to transfer a series of zero +or more buffers containing parsed body octets into a message container. The +__message_parser__ creates an instance of this type when needed, and calls into +it zero or more times to transfer buffers. The interface of [*BodyWriter] +is intended to allow the conversion of buffers into these scenarios for +representation: -Direct writers provide a buffer to callers, into which body data is placed. -This type of writer is used when the bytes corresponding to the body data -are stored without transformation. The parse algorithm performs stream or -socket reads directly into the buffer provided by the writer, hence the name -"direct." This model avoids an unnecessary buffer copy. An example of -a __Body__ type with a direct writer is -[link beast.ref.http__string_body `string_body`]. +* Storing a body in a dynamic buffer +* Storing a body in a user defined container with a custom allocator +* Transformation of incoming body data before storage, for example + to compress it first. +* Saving body data to a file -Indirect writers are passed body data in a buffer managed by the parsing -algorithm. This writer is appropriate when the body data is transformed -or not otherwised stored verbatim. Some examples of when an indirect -writer is appropriate: +In the table below: -* When bytes corresponding to the body are written to a file - as they are parsed. - -* The content of the message is JSON, which is parsed as it is - being read in, and stored in a structured, hierarchical format. - -In the tables below: - -* `X` denotes a type meeting the requirements of [*Writer]. +* `X` denotes a type meeting the requirements of [*BodyWriter]. * `B` denotes a __Body__ where `std::is_same::value == true`. * `a` denotes a value of type `X`. -* `n` is a value convertible to `std::size_t` without loss of precision. +* `length` is a value of type `boost::optional`. -* `v` is a value convertible to `std::uint64_t` without loss of precision. - -* `s` is a value of type [link beast.ref.string_view `string_view`]. - -* `ec` is a value of type [link beast.ref.error_code `error_code&`]. +* `b` is an object whose type meets the requirements of __ConstBufferSequence__ * `m` denotes a value of type `message&` where `std::is_same::value == true`. -[table Direct Writer requirements +* `ec` is a value of type [link beast.ref.error_code `error_code&`]. + +[table Writer requirements [[expression] [type] [semantics, pre/post-conditions]] [ - [`X::is_direct`] - [`bool`] - [ - This static constant must be set to `true` to indicate this - is a direct writer. - ] -] -[ - [`X::mutable_buffers_type`] + [`X(m);`] [] [ - A type which meets the requirements of __MutableBufferSequence__. + Constructible from `m`. The lifetime of `m` is guaranteed + to end no earlier than after the `X` is destroyed. The constructor + will be called the complete header is stored in `m`, and before + parsing body octets for messages indicating that a body is present. ] ] [ - [`X a{m};`] + [`a.init(length,ec)`] [] [ - `a` is constructible from `m`. The lifetime of `m` is guaranteed - to end no earlier than after `a` is destroyed. The constructor - will be called after all headers have been stored in `m`, and - just before parsing bytes corresponding to the body for messages - whose semantics indicate that a body is present with non-zero - length. + This function is called after construction and before any body + octets are presented to the writer. The value of `length` will + be set to the content length of the body if known, otherwise + `length` will be equal to `boost::none`. Implementations of + [*BodyWriter] may use this information to optimize allocation. + If `ec` is set, the error will be propagated to the caller. ] ] [ - [`a.init()`] + [`a.put(b,ec)`] [] [ - This function is called once before any bytes corresponding - to the body are presented to the writer, for messages whose - body is determined by the end-of-file marker on a stream, - or for messages where the chunked Transfer-Encoding is - specified. - ] -] -[ - [`a.init(v)`] - [] - [ - This function is called once before any bytes corresponding - to the body are presented to the writer, for messages where - the Content-Length is specified. The value of `v` will be - set to the number of bytes indicated by the content length. - ] -] -[ - [`a.prepare(n)`] - [`X::mutable_buffers_type`] - [ - The implementation calls this function to obtain a mutable - buffer sequence of up to `n` bytes in size in which to place - data corresponding to the body. The buffer returned must - be at least one byte in size, and may be smaller than `n`. - ] -] -[ - [`a.commit(n)`] - [] - [ - The implementation calls this function to indicate to the - writer that `n` bytes of data have been successfully placed - into the buffer obtained through a prior call to `prepare`. - The value of `n` will be less than or equal to the size of - the buffer returned in the previous call to `prepare`. - ] -] -[ - [`a.finish()`] - [] - [ - This function is called after all the bytes corresponding - to the body have been written to the buffers and committed. - ] -] -[ - [`is_body_writer`] - [`std::true_type`] - [ - An alias for `std::true_type` for `B`, otherwise an alias - for `std::false_type`. - ] -] -] - -[table Indirect Writer requirements -[[expression] [type] [semantics, pre/post-conditions]] -[ - [`X::is_direct`] - [`bool`] - [ - This static constant must be set to `false` to indicate this - is an indirect writer. - ] -] -[ - [`X a{m};`] - [] - [ - `a` is constructible from `m`. The lifetime of `m` is guaranteed - to end no earlier than after `a` is destroyed. The constructor - will be called after all headers have been stored in `m`, and - just before parsing bytes corresponding to the body for messages - whose semantics indicate that a body is present with non-zero - length. - ] -] -[ - [`a.init(ec)`] - [] - [ - This function is called once before any bytes corresponding - to the body are presented to the writer, for messages whose - body is determined by the end-of-file market on a stream, - or for messages where the chunked Transfer-Encoding is - specified. - If `ec` is set before returning, parsing will stop - and the error will be returned to the caller. - - ] -] -[ - [`a.init(v,ec)`] - [] - [ - This function is called once before any bytes corresponding - to the body are presented to the writer, for messages where - the Content-Length is specified. The value of `v` will be - set to the number of bytes indicated by the content length. - If `ec` is set before returning, parsing will stop - and the error will be returned to the caller. - ] -] -[ - [`a.write(s,ec)`] - [] - [ - The implementation calls this function with `s` containing - bytes corresponding to the body, after removing any present - chunked encoding transformation. - If `ec` is set before returning, parsing will stop - and the error will be returned to the caller. + This function is called to append the buffers specified by `b` + into the body representation. + If `ec` is set, the error will be propagated to the caller. ] ] [ [`a.finish(ec)`] [] [ - This function is called after all the bytes corresponding - to the body have been written to the buffers and committed. - If `ec` is set before returning, parsing will stop - and the error will be returned to the caller. + This function is called when no more body octets are remaining. + If `ec` is set, the error will be propagated to the caller. ] ] [ @@ -219,8 +89,52 @@ In the tables below: ] ] [note - Definitions for required [*Writer] member functions should be declared + Definitions for required [*BodyWriter] member functions should be declared inline so the generated code can become part of the implementation. ] +Exemplar: +``` +struct writer +{ + /** Construct the writer. + + @param msg The message whose body is to be stored. + */ + template + explicit + writer(message& msg); + + /** Initialization. + + Called once immediately before storing any buffers. + + @param content_length The content length if known, else `boost::none`. + + @param ec Set to the error, if any occurred. + */ + void + init(boost::optional content_length, error_code& ec); + + /** Store buffers. + + This is called zero or more times with parsed body octets. + + @param buffers The constant buffer sequence to store. + + @param ec Set to the error, if any occurred. + */ + template + void + put(ConstBufferSequence const& buffers, error_code& ec); + + /** Called when the body is complete. + + @param ec Set to the error, if any occurred. + */ + void + finish(error_code& ec); +}; +``` + [endsect] diff --git a/doc/images/AlfaSlabOne-Regular.ttf b/doc/images/AlfaSlabOne-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ad70ebc75d0073d422f8d7995a31c9d8dd455dce GIT binary patch literal 36504 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 cB!J6lE{t+j6message message_parser request + request_parser response + response_parser serializer string_body - rfc7230 - - - ext_list - opt_token_list - param_list - token_list - Functions @@ -82,6 +76,13 @@ + rfc7230 + + ext_list + opt_token_list + param_list + token_list + Constants connection