Files
boost_beast/doc/3_5_parser_streams.qbk
T
2017-07-20 08:12:17 -07:00

333 lines
10 KiB
Plaintext

[/
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<class SyncReadStream>
void print_response(SyncReadStream& stream)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
// Declare a parser for an HTTP response
response_parser<string_body> 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<SyncStream>::value,
"SyncStream requirements not met");
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
// Declare a parser for a request with a string body
request_parser<string_body> 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<empty_body> 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<isRequest, Fields>&, // 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<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
static_assert(is_sync_read_stream<SyncReadStream>::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<isRequest, buffer_body, Fields> p;
// Create a serializer from the message contained in the parser.
serializer<isRequest, buffer_body, Fields> 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]