mirror of
https://github.com/boostorg/beast.git
synced 2025-08-02 22:34:32 +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_fields__ [link beast.ref.http__basic_fields `basic_fields`]]
|
||||||
[def __basic_multi_buffer__ [link beast.ref.basic_multi_buffer `basic_multi_buffer`]]
|
[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 __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 __fields__ [link beast.ref.http__fields `fields`]]
|
||||||
[def __flat_buffer__ [link beast.ref.flat_buffer `flat_buffer`]]
|
[def __flat_buffer__ [link beast.ref.flat_buffer `flat_buffer`]]
|
||||||
[def __header__ [link beast.ref.http__header `header`]]
|
[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__ [link beast.ref.http__message `message`]]
|
||||||
[def __message_parser__ [link beast.ref.http__message_parser `message_parser`]]
|
[def __message_parser__ [link beast.ref.http__message_parser `message_parser`]]
|
||||||
[def __multi_buffer__ [link beast.ref.multi_buffer `multi_buffer`]]
|
[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.
|
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]
|
[link beast.websocket Using WebSocket]
|
||||||
][
|
][
|
||||||
@@ -121,10 +118,9 @@ asynchronous model of __Asio__.
|
|||||||
[include 1_overview.qbk]
|
[include 1_overview.qbk]
|
||||||
[include 2_core.qbk]
|
[include 2_core.qbk]
|
||||||
[include 3_0_http.qbk]
|
[include 3_0_http.qbk]
|
||||||
[include 4_0_adv_http.qbk]
|
[include 4_websocket.qbk]
|
||||||
[include 5_websocket.qbk]
|
[include 5_examples.qbk]
|
||||||
[include 6_examples.qbk]
|
[include 6_0_design.qbk]
|
||||||
[include 7_0_design.qbk]
|
|
||||||
|
|
||||||
[section:ref Reference]
|
[section:ref Reference]
|
||||||
[xinclude quickref.xml]
|
[xinclude quickref.xml]
|
||||||
|
@@ -11,13 +11,19 @@
|
|||||||
<informaltable frame="all"><tgroup cols="1"><colspec colname="a"/><tbody><row><entry valign="top"><simplelist>
|
<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.primer">HTTP Primer</link></member>
|
||||||
<member><link linkend="beast.http.message">Message Containers</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>
|
</simplelist></entry></row></tbody></tgroup></informaltable>
|
||||||
''']
|
''']
|
||||||
|
|
||||||
This library offers programmers simple and performant models of HTTP
|
This library offers programmers simple and performant models of HTTP messages
|
||||||
messages and their associated operations including synchronous and
|
and their associated operations including synchronous, asynchronous, and
|
||||||
asynchronous parsing and serialization of messages in the HTTP/1 wire
|
buffer-oriented parsing and serialization of messages in the HTTP/1 wire
|
||||||
format using __Asio__. Specifically, the library provides:
|
format using __Asio__. Specifically, the library provides:
|
||||||
|
|
||||||
[variablelist
|
[variablelist
|
||||||
@@ -32,10 +38,12 @@ format using __Asio__. Specifically, the library provides:
|
|||||||
[
|
[
|
||||||
The functions
|
The functions
|
||||||
[link beast.ref.http__read `read`],
|
[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__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`]
|
[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].
|
[link beast.ref.streams stream].
|
||||||
]
|
]
|
||||||
][
|
][
|
||||||
@@ -43,10 +51,12 @@ format using __Asio__. Specifically, the library provides:
|
|||||||
[
|
[
|
||||||
The functions
|
The functions
|
||||||
[link beast.ref.http__write `write`],
|
[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__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`]
|
[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].
|
[link beast.ref.streams stream].
|
||||||
]
|
]
|
||||||
][
|
][
|
||||||
@@ -79,5 +89,11 @@ format using __Asio__. Specifically, the library provides:
|
|||||||
[include 3_1_primer.qbk]
|
[include 3_1_primer.qbk]
|
||||||
[include 3_2_message.qbk]
|
[include 3_2_message.qbk]
|
||||||
[include 3_3_streams.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]
|
[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
|
requests or responses. The default __fields__ is a provided associative
|
||||||
container using the standard allocator and supporting modification and
|
container using the standard allocator and supporting modification and
|
||||||
inspection of fields. As per __rfc7230__, a non-case-sensitive comparison
|
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 used for field names. User defined types for fields are possible.
|
||||||
is discussed in
|
|
||||||
[link beast.adv_http.fields Advanced Fields].
|
|
||||||
The `Body` type determines the type of the container used to represent the
|
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
|
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,
|
the container. The library comes with a collection of common body types.
|
||||||
described in
|
As with fields, user defined body types are possible.
|
||||||
[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].
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Sometimes it is desired to only work with a header. Beast provides a single
|
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:
|
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`]
|
[link beast.ref.http__buffer_body `buffer_body`]
|
||||||
][
|
][
|
||||||
A body with `value_type` holding a __ConstBufferSequence__ holding
|
A body whose `value_type` holds a raw pointer and size to a
|
||||||
caller provided buffers which must be updated during incremental
|
caller-provided buffer. This allows for serialization of body data
|
||||||
serialization. Messages with this body type only support serialization.
|
coming from external sources, and incremental parsing of message
|
||||||
|
body content using a fixed size buffer.
|
||||||
]]
|
]]
|
||||||
[[
|
[[
|
||||||
[link beast.ref.http__dynamic_body `dynamic_body`]
|
[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)
|
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
|
Beast provides synchronous and asynchronous algorithms to serialize and
|
||||||
parse HTTP/1 wire format messages on streams. These functions form the
|
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__.
|
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]]
|
[link beast.ref.http__async_read.overload2 [*async_read]]
|
||||||
][
|
][
|
||||||
Parse a __message__ from an __AsyncReadStream__.
|
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]]
|
[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
|
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
|
argument of type [link beast.ref.error_code `error_code&`]. If an error
|
||||||
occurs this argument will be set to contain the error code.
|
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]
|
[heading Writing]
|
||||||
|
|
||||||
A set of free functions allow serialization of an entire HTTP message to
|
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]
|
[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)
|
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 '''
|
In extreme cases, users may wish to create an instance of __serializer__
|
||||||
<informaltable frame="all"><tgroup cols="1"><colspec colname="a"/><tbody><row><entry valign="top"><simplelist>
|
and invoke its methods directly instead of using the provided stream
|
||||||
<member><link linkend="beast.adv_http.serialize">Serialization</link></member>
|
algorithms. This could be useful for implementing algorithms on streams
|
||||||
<member><link linkend="beast.adv_http.parsing">Parsing</link></member>
|
whose asynchronous interface does not conform to __AsyncStream__. For
|
||||||
<member><link linkend="beast.adv_http.fields">Field Containers</link></member>
|
example, a
|
||||||
<member><link linkend="beast.adv_http.body">Body Types</link></member>
|
[@https://github.com/libuv/libuv *libuv* socket].
|
||||||
</simplelist></entry></row></tbody></tgroup></informaltable>
|
|
||||||
''']
|
|
||||||
|
|
||||||
The basic interfaces for reading and writing complete messages are
|
The serializer interface is interactive; the caller invokes it repeatedly to
|
||||||
simple to use and convenient, but may not serve the needs of advanced
|
produce buffers until all of the buffers have been generated. Then the
|
||||||
use cases, including:
|
serializer is destroyed.
|
||||||
|
|
||||||
* Parsing a message from caller-provided buffers
|
After the serializer is created, the buffers are produced by first calling
|
||||||
|
|
||||||
* 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
|
|
||||||
[link beast.ref.http__serializer.get `serializer::get`]
|
[link beast.ref.http__serializer.get `serializer::get`]
|
||||||
to obtain a buffer sequence, and then calling
|
to obtain a buffer sequence, and then calling
|
||||||
[link beast.ref.http__serializer.consume `serializer::consume`]
|
[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]
|
[heading Split Serialization]
|
||||||
|
|
||||||
In some cases, such as the handling of the
|
In some cases, such as the handling of the
|
||||||
@@ -286,64 +198,3 @@ struct decorator
|
|||||||
```
|
```
|
||||||
|
|
||||||
[endsect]
|
[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.
|
* Allow for customizations, if the user needs it.
|
||||||
|
|
||||||
[include 7_1_http_message.qbk]
|
[include 6_1_http_message.qbk]
|
||||||
[include 7_2_http_comparison.qbk]
|
[include 6_2_http_comparison.qbk]
|
||||||
[include 7_3_websocket_zaphoyd.qbk]
|
[include 6_3_websocket_zaphoyd.qbk]
|
||||||
[include 7_4_review.qbk]
|
[include 6_4_review.qbk]
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
@@ -50,6 +50,17 @@ In this table:
|
|||||||
[`X::is_deferred`]
|
[`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.
|
/** Controls when the implementation requests buffers.
|
||||||
|
|
||||||
If false, the implementation will request the first buffer
|
If false, the implementation will request the first buffer
|
||||||
immediately and try to send both the header and the body
|
immediately and try to serialize both the header and some
|
||||||
buffer in a single call to the stream's `write_some`
|
or all of the body in a single buffer.
|
||||||
function.
|
|
||||||
*/
|
*/
|
||||||
using is_deferred = std::false_type;
|
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;
|
using const_buffers_type = boost::asio::const_buffers_1;
|
||||||
|
|
||||||
/** Construct the reader.
|
/** 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>
|
template<bool isRequest, class Body, class Headers>
|
||||||
explicit
|
explicit
|
||||||
|
@@ -7,206 +7,76 @@
|
|||||||
|
|
||||||
[section:BodyWriter BodyWriter requirements]
|
[section:BodyWriter BodyWriter requirements]
|
||||||
|
|
||||||
When HTTP messages are parsed, the implementation constructs a
|
A [*BodyWriter] provides an online algorithm to transfer a series of zero
|
||||||
[*BodyWriter] object to provide the means for transferring parsed body
|
or more buffers containing parsed body octets into a message container. The
|
||||||
octets into the message container. These body writers come in two flavors,
|
__message_parser__ creates an instance of this type when needed, and calls into
|
||||||
direct and indirect:
|
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.
|
* Storing a body in a dynamic buffer
|
||||||
This type of writer is used when the bytes corresponding to the body data
|
* Storing a body in a user defined container with a custom allocator
|
||||||
are stored without transformation. The parse algorithm performs stream or
|
* Transformation of incoming body data before storage, for example
|
||||||
socket reads directly into the buffer provided by the writer, hence the name
|
to compress it first.
|
||||||
"direct." This model avoids an unnecessary buffer copy. An example of
|
* Saving body data to a file
|
||||||
a __Body__ type with a direct writer is
|
|
||||||
[link beast.ref.http__string_body `string_body`].
|
|
||||||
|
|
||||||
Indirect writers are passed body data in a buffer managed by the parsing
|
In the table below:
|
||||||
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:
|
|
||||||
|
|
||||||
* When bytes corresponding to the body are written to a file
|
* `X` denotes a type meeting the requirements of [*BodyWriter].
|
||||||
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].
|
|
||||||
|
|
||||||
* `B` denotes a __Body__ where
|
* `B` denotes a __Body__ where
|
||||||
`std::is_same<X, B::writer>::value == true`.
|
`std::is_same<X, B::writer>::value == true`.
|
||||||
|
|
||||||
* `a` denotes a value of type `X`.
|
* `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.
|
* `b` is an object whose type meets the requirements of __ConstBufferSequence__
|
||||||
|
|
||||||
* `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&`].
|
|
||||||
|
|
||||||
* `m` denotes a value of type `message&` where
|
* `m` denotes a value of type `message&` where
|
||||||
`std::is_same<decltype(m.body), Body::value_type>::value == true`.
|
`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]]
|
[[expression] [type] [semantics, pre/post-conditions]]
|
||||||
[
|
[
|
||||||
[`X::is_direct`]
|
[`X(m);`]
|
||||||
[`bool`]
|
|
||||||
[
|
|
||||||
This static constant must be set to `true` to indicate this
|
|
||||||
is a direct writer.
|
|
||||||
]
|
|
||||||
]
|
|
||||||
[
|
|
||||||
[`X::mutable_buffers_type`]
|
|
||||||
[]
|
[]
|
||||||
[
|
[
|
||||||
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
|
This function is called after construction and before any body
|
||||||
to end no earlier than after `a` is destroyed. The constructor
|
octets are presented to the writer. The value of `length` will
|
||||||
will be called after all headers have been stored in `m`, and
|
be set to the content length of the body if known, otherwise
|
||||||
just before parsing bytes corresponding to the body for messages
|
`length` will be equal to `boost::none`. Implementations of
|
||||||
whose semantics indicate that a body is present with non-zero
|
[*BodyWriter] may use this information to optimize allocation.
|
||||||
length.
|
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
|
This function is called to append the buffers specified by `b`
|
||||||
to the body are presented to the writer, for messages whose
|
into the body representation.
|
||||||
body is determined by the end-of-file marker on a stream,
|
If `ec` is set, the error will be propagated to the caller.
|
||||||
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.
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
[`a.finish(ec)`]
|
[`a.finish(ec)`]
|
||||||
[]
|
[]
|
||||||
[
|
[
|
||||||
This function is called after all the bytes corresponding
|
This function is called when no more body octets are remaining.
|
||||||
to the body have been written to the buffers and committed.
|
If `ec` is set, the error will be propagated to the caller.
|
||||||
If `ec` is set before returning, parsing will stop
|
|
||||||
and the error will be returned to the caller.
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
@@ -219,8 +89,52 @@ In the tables below:
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
[note
|
[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.
|
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]
|
[endsect]
|
||||||
|
@@ -42,18 +42,12 @@
|
|||||||
<member><link linkend="beast.ref.http__message">message</link></member>
|
<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__message_parser">message_parser</link></member>
|
||||||
<member><link linkend="beast.ref.http__request">request</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">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__serializer">serializer</link></member>
|
||||||
<member><link linkend="beast.ref.http__string_body">string_body</link></member>
|
<member><link linkend="beast.ref.http__string_body">string_body</link></member>
|
||||||
</simplelist>
|
</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>
|
||||||
<entry valign="top">
|
<entry valign="top">
|
||||||
<bridgehead renderas="sect3">Functions</bridgehead>
|
<bridgehead renderas="sect3">Functions</bridgehead>
|
||||||
@@ -82,6 +76,13 @@
|
|||||||
</simplelist>
|
</simplelist>
|
||||||
</entry>
|
</entry>
|
||||||
<entry valign="top">
|
<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>
|
<bridgehead renderas="sect3">Constants</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
<member><link linkend="beast.ref.http__connection">connection</link></member>
|
<member><link linkend="beast.ref.http__connection">connection</link></member>
|
||||||
|
Reference in New Issue
Block a user