forked from boostorg/beast
333 lines
10 KiB
Plaintext
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]
|