[/ 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]