mirror of
https://github.com/boostorg/beast.git
synced 2025-08-02 14:24:31 +02:00
Documentation work
This commit is contained in:
@@ -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]
|
||||
|
@@ -11,13 +11,19 @@
|
||||
<informaltable frame="all"><tgroup cols="1"><colspec colname="a"/><tbody><row><entry valign="top"><simplelist>
|
||||
<member><link linkend="beast.http.primer">HTTP Primer</link></member>
|
||||
<member><link linkend="beast.http.message">Message Containers</link></member>
|
||||
<member><link linkend="beast.http.streams">Stream Operations</link></member>
|
||||
<member><link linkend="beast.http.streams">Message Stream Operations</link></member>
|
||||
<member><link linkend="beast.http.serializer_streams">Serializer Stream Operations</link></member>
|
||||
<member><link linkend="beast.http.parser_streams">Parser Stream Operations</link></member>
|
||||
<member><link linkend="beast.http.serializer_buffers">Buffer-Oriented Serializing</link></member>
|
||||
<member><link linkend="beast.http.parser_buffers">Buffer-Oriented Parsing</link></member>
|
||||
<member><link linkend="beast.http.custom_parsers">Custom Parsers</link></member>
|
||||
<member><link linkend="beast.http.custom_body">Custom Body Types</link></member>
|
||||
</simplelist></entry></row></tbody></tgroup></informaltable>
|
||||
''']
|
||||
|
||||
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]
|
||||
|
@@ -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`]
|
||||
|
@@ -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<string_body> 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<string_body> 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<Body, Fields> 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<string_body> 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<string_body> res;
|
||||
async_read(sock, buffer,
|
||||
[&](error_code ec)
|
||||
{
|
||||
std::cerr << ec.message() << std::endl;
|
||||
});
|
||||
```
|
||||
|
||||
[endsect]
|
||||
|
306
doc/3_4_serializer_streams.qbk
Normal file
306
doc/3_4_serializer_streams.qbk
Normal file
@@ -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<char>
|
||||
>
|
||||
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<string_body> res;
|
||||
...
|
||||
serializer<false, string_body, fields> 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<class SyncWriteStream, bool isRequest, class Body, class Fields>
|
||||
void send(SyncWriteStream& stream, message<isRequest, Body, Fields> const& m)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
serializer<isRequest, Body, Fields> 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<Body, Fields>& req)
|
||||
{
|
||||
static_assert(is_sync_stream<SyncStream>::value,
|
||||
"SyncStream requirements not met");
|
||||
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::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<string_body> 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<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::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<false, buffer_body, fields> 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]
|
332
doc/3_5_parser_streams.qbk
Normal file
332
doc/3_5_parser_streams.qbk
Normal file
@@ -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<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]
|
@@ -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 '''
|
||||
<informaltable frame="all"><tgroup cols="1"><colspec colname="a"/><tbody><row><entry valign="top"><simplelist>
|
||||
<member><link linkend="beast.adv_http.serialize">Serialization</link></member>
|
||||
<member><link linkend="beast.adv_http.parsing">Parsing</link></member>
|
||||
<member><link linkend="beast.adv_http.fields">Field Containers</link></member>
|
||||
<member><link linkend="beast.adv_http.body">Body Types</link></member>
|
||||
</simplelist></entry></row></tbody></tgroup></informaltable>
|
||||
''']
|
||||
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<string_body> req;
|
||||
serializer<true, string_body, fields> 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<isRequest, Body, Fields> 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<class SyncWriteStream, bool isRequest, class Body, class Fields>
|
||||
void send(SyncWriteStream& stream, message<isRequest, Body, Fields> const& m)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
serializer<isRequest, Body, Fields> 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]
|
10
doc/3_7_parser_buffers.qbk
Normal file
10
doc/3_7_parser_buffers.qbk
Normal file
@@ -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]
|
10
doc/3_8_custom_parsers.qbk
Normal file
10
doc/3_8_custom_parsers.qbk
Normal file
@@ -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]
|
49
doc/3_9_custom_body.qbk
Normal file
49
doc/3_9_custom_body.qbk
Normal file
@@ -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]
|
@@ -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]
|
@@ -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<bool isRequest, class Body, class Headers>
|
||||
explicit
|
||||
|
@@ -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<X, B::writer>::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<std::uint64_t>`.
|
||||
|
||||
* `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<decltype(m.body), Body::value_type>::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<B>`]
|
||||
[`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<bool isRequest, class Body, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest, Body, Fields>& 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<std::uint64_t> 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<class ConstBufferSequence>
|
||||
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]
|
||||
|
@@ -42,18 +42,12 @@
|
||||
<member><link linkend="beast.ref.http__message">message</link></member>
|
||||
<member><link linkend="beast.ref.http__message_parser">message_parser</link></member>
|
||||
<member><link linkend="beast.ref.http__request">request</link></member>
|
||||
<member><link linkend="beast.ref.http__request_parser">request_parser</link></member>
|
||||
<member><link linkend="beast.ref.http__response">response</link></member>
|
||||
<member><link linkend="beast.ref.http__response_parser">response_parser</link></member>
|
||||
<member><link linkend="beast.ref.http__serializer">serializer</link></member>
|
||||
<member><link linkend="beast.ref.http__string_body">string_body</link></member>
|
||||
</simplelist>
|
||||
<bridgehead renderas="sect3">rfc7230</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
|
||||
<member><link linkend="beast.ref.http__ext_list">ext_list</link></member>
|
||||
<member><link linkend="beast.ref.http__opt_token_list">opt_token_list</link></member>
|
||||
<member><link linkend="beast.ref.http__param_list">param_list</link></member>
|
||||
<member><link linkend="beast.ref.http__token_list">token_list</link></member>
|
||||
</simplelist>
|
||||
</entry>
|
||||
<entry valign="top">
|
||||
<bridgehead renderas="sect3">Functions</bridgehead>
|
||||
@@ -82,6 +76,13 @@
|
||||
</simplelist>
|
||||
</entry>
|
||||
<entry valign="top">
|
||||
<bridgehead renderas="sect3">rfc7230</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
<member><link linkend="beast.ref.http__ext_list">ext_list</link></member>
|
||||
<member><link linkend="beast.ref.http__opt_token_list">opt_token_list</link></member>
|
||||
<member><link linkend="beast.ref.http__param_list">param_list</link></member>
|
||||
<member><link linkend="beast.ref.http__token_list">token_list</link></member>
|
||||
</simplelist>
|
||||
<bridgehead renderas="sect3">Constants</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
<member><link linkend="beast.ref.http__connection">connection</link></member>
|
||||
|
Reference in New Issue
Block a user