Refactor HTTP serialization (API Change):

A new class `serializer` is introduced to permit incremental
serialization of HTTP messages. Existing free functions are
re-implemented in terms of this new class.

* The BodyReader concept is refined to support a greater variety
of strategies for providing buffers representing the body to
the serialization algorithms.

* Added buffer_body, a new model of Body which allows the caller
to provide a series of owned buffers using their own serialization
loop.

* Added empty_body, a model of Body which is for serialization only,
to represent HTTP messages with no content body.

* Removed overloads of write and async_write which send only
the HTTP header.

* Removed public interfaces for performing low-level chunk encoding.
This commit is contained in:
Vinnie Falco committed 2017-07-20 08:12:17 -07:00
1 parent b9bdb1bbbc
commit 3664329ea7
44 files changed
+2540 -1104

No files matched your search

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+179
View File
@@ -0,0 +1,179 @@
[/
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:Writer Writer requirements]
A [*Writer] provides an online algorithm to obtain a sequence of zero
or more buffers from a body during serialization. The implementation creates
an instance of this type when needed, and calls into it zero or more times to
retrieve buffers with body octets. The interface of [*Writer] is intended to
allow serialization in these scenarios:
* A body that does not entirely fit in memory.
* A body produced incrementally from coroutine output.
* A body represented by zero or more buffers already in memory.
* A body as a series of buffers when the content size is not known ahead of time.
* Body data generated on demand from other threads.
* Body data computed algorithmically.
In this table:
* `X` denotes a type meeting the requirements of [*Writer].
* `a` denotes a value of type `X`.
* `m` denotes a value of type `message const&` where
`std::is_same<decltype(m.body), Body::value_type>:value == true`.
* `ec` is a value of type [link beast.ref.error_code `error_code&`].
* `B<T>` is the type `boost::optional<std::pair<T, bool>>`.
[table Writer requirements
[[operation] [type] [semantics, pre/post-conditions]]
[
[`X::const_buffers_type`]
[]
[
A nested type which meets the requirements of __ConstBufferSequence__.
This is the type of buffer returned by `X::get`.
]
]
[
[`X(m);`]
[]
[
Constructible from `m`. The lifetime of `m` is guaranteed
to end no earlier than after the `X` is destroyed.
]
]
[
[`a.init(ec)`]
[`void`]
[
Called immediately after construction. If the function sets an
error code in `ec`, the serialization is aborted and the error
is propagated to the caller.
]
]
[
[`a.content_length()`]
[`std::uint64_t`]
[
If this member is present, it is called after initialization
and before calls to provide buffers. The serialized message will
have the Content-Length field set to the value returned from
this function. If this member is absent, the serialized message
body will be chunk-encoded for HTTP versions 1.1 and later, else
the serialized message body will be sent unmodified, with the
error `boost::asio::error::eof` returned to the caller, to notify
they should close the connection to indicate the end of the message.
This function must be `noexcept`.
]
]
[
[`a.get(ec)`]
[`B<X::const_buffers_type>`]
[
Called repeatedly after `init` succeeds. This function returns
`boost::none` if all buffers representing the body have been
returned in previous calls or if it sets `ec` to indicate an
error. Otherwise, if there are buffers remaining the function
should return a pair with the first element containing a non-zero
length buffer sequence representing the next set of octets in
the body, while the second element is a `bool` meaning `true`
if there may be additional buffers returned on a subsequent call,
or `false` if the buffer returned on this call is the last
buffer representing the body.
]
]
[
[`http::is_Writer<X>`]
[`std::true_type`]
[
An alias for `std::true_type` for `X`, otherwise an
alias for `std::false_type`.
]
]
]
[note
Definitions for required `Writer` member functions should be declared
inline so the generated code can become part of the implementation.
]
Exemplar:
```
struct writer
{
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.
*/
using is_deferred = std::false_type;
/** The type of buffer returned by `get`.
*/
using const_buffers_type = boost::asio::const_buffers_1;
/** Construct the writer.
The msg object is guaranteed to exist for the lifetime of the writer.
@param msg The message whose body is to be written.
*/
template<bool isRequest, class Body, class Headers>
explicit
writer(message<isRequest, Body, Headers> const& msg);
/** Initialize the writer.
Called once immediately after construction.
The writer can perform initialization which may fail.
@param ec Contains the error code if any errors occur.
*/
void
init(error_code& ec);
/** Returns the content length.
If this member is present, the implementation will set the
Content-Length field accordingly. If absent, the implementation will
use chunk-encoding or terminate the connection to indicate the end
of the message.
*/
std::uint64_t
content_length();
/** Returns the next buffer in the body.
@li If the return value is `boost::none` (unseated optional) and
`ec` does not contain an error, this indicates the end of the
body, no more buffers are present.
@li If the optional contains a value, the first element of the
pair represents a @b ConstBufferSequence containing one or
more octets of the body data. The second element indicates
whether or not there are additional octets of body data.
A value of `true` means there is more data, and that the
implementation will perform a subsequent call to `get`.
A value of `false` means there is no more body data.
@li If `ec` contains an error code, the return value is ignored.
*/
template<class WriteFunction>
boost::optional<std::pair<const_buffers_type, bool>>
get(error_code& ec);
};
```
[endsect]
+9
View File
@@ -193,6 +193,15 @@ used in the examples:
`value_type` of [link beast.ref.multi_buffer `multi_buffer`]: an efficient storage
object which uses multiple octet arrays of varying lengths to represent data.
* [link beast.ref.http__buffer_body [*`buffer_body`:]] A write-only body
with a `value_type` representing a __ConstBufferSequence__. This special
body allows the caller to implement their own write loop for advanced
use-cases such as relaying.
* [link beast.ref.http__empty_body [*`empty_body`:]] A write-only body
with an empty `value_type` representing an HTTP message with no content
body.
[heading Advanced]
User-defined types are possible for the message body, where the type meets the
+8 -8
View File
@@ -112,14 +112,14 @@ provides implementations of the HTTP and WebSocket protocols.
[section:ref Reference]
[xinclude quickref.xml]
[include types/Body.qbk]
[include types/BufferSequence.qbk]
[include types/DynamicBuffer.qbk]
[include types/Field.qbk]
[include types/FieldSequence.qbk]
[include types/Reader.qbk]
[include types/Streams.qbk]
[include types/Writer.qbk]
[include concept/Body.qbk]
[include concept/BufferSequence.qbk]
[include concept/DynamicBuffer.qbk]
[include concept/Field.qbk]
[include concept/FieldSequence.qbk]
[include concept/Reader.qbk]
[include concept/Streams.qbk]
[include concept/Writer.qbk]
[include reference.qbk]
[endsect]
+6 -3
View File
@@ -32,15 +32,19 @@
<member><link linkend="beast.ref.http__basic_dynamic_body">basic_dynamic_body</link></member>
<member><link linkend="beast.ref.http__basic_fields">basic_fields</link></member>
<member><link linkend="beast.ref.http__basic_parser">basic_parser</link></member>
<member><link linkend="beast.ref.http__buffer_body">buffer_body</link></member>
<member><link linkend="beast.ref.http__dynamic_body">dynamic_body</link></member>
<member><link linkend="beast.ref.http__empty_body">empty_body</link></member>
<member><link linkend="beast.ref.http__fields">fields</link></member>
<member><link linkend="beast.ref.http__header">header</link></member>
<member><link linkend="beast.ref.http__header_parser">header_parser</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__empty_decorator">empty_decorator</link></member>
<member><link linkend="beast.ref.http__request">request</link></member>
<member><link linkend="beast.ref.http__response">response</link></member>
<member><link linkend="beast.ref.http__string_body">string_body</link></member>
<member><link linkend="beast.ref.http__write_stream">write_stream</link></member>
</simplelist>
<bridgehead renderas="sect3">rfc7230</bridgehead>
<simplelist type="vert" columns="1">
@@ -57,16 +61,15 @@
<member><link linkend="beast.ref.http__async_read">async_read</link></member>
<member><link linkend="beast.ref.http__async_read_some">async_read_some</link></member>
<member><link linkend="beast.ref.http__async_write">async_write</link></member>
<member><link linkend="beast.ref.http__chunk_encode">chunk_encode</link></member>
<member><link linkend="beast.ref.http__chunk_encode_final">chunk_encode_final</link></member>
<member><link linkend="beast.ref.http__swap">swap</link></member>
<member><link linkend="beast.ref.http__is_keep_alive">is_keep_alive</link></member>
<member><link linkend="beast.ref.http__is_upgrade">is_upgrade</link></member>
<member><link linkend="beast.ref.http__make_write_stream">make_write_stream</link></member>
<member><link linkend="beast.ref.http__operator_ls_">operator&lt;&lt;</link></member>
<member><link linkend="beast.ref.http__prepare">prepare</link></member>
<member><link linkend="beast.ref.http__read">read</link></member>
<member><link linkend="beast.ref.http__read_some">read_some</link></member>
<member><link linkend="beast.ref.http__reason_string">reason_string</link></member>
<member><link linkend="beast.ref.http__swap">swap</link></member>
<member><link linkend="beast.ref.http__write">write</link></member>
</simplelist>
<bridgehead renderas="sect3">Type Traits</bridgehead>
-161
View File
@@ -1,161 +0,0 @@
[/
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:Writer Writer requirements]
A `Writer` serializes the message body. The implementation creates an instance
of this type when serializing a message, and calls into it zero or more times
to provide buffers containing the data. The interface of `Writer` is intended
to allow serialization in these scenarios:
* A body that does not entirely fit in memory.
* A body produced incrementally from coroutine output.
* A body represented by zero or more buffers already in memory.
* A body as a series of buffers when the content size is not known ahead of time.
* Body data generated on demand from other threads.
* Body data computed algorithmically.
In this table:
* `X` denotes a type meeting the requirements of `Writer`.
* `a` denotes a value of type `X`.
* `m` denotes a value of type `message const&` where
`std::is_same<decltype(m.body), Body::value_type>:value == true`.
* `ec` is a value of type [link beast.ref.error_code `error_code&`]
* `wf` is a [*write function]: a function object of unspecified type provided
by the implementation which accepts any value meeting the requirements
of __ConstBufferSequence__ as its single parameter.
[table Writer requirements
[[operation] [type] [semantics, pre/post-conditions]]
[
[`X a(m);`]
[]
[
`a` is constructible from `m`. The lifetime of `m` is guaranteed
to end no earlier than after `a` is destroyed. This function must
be `noexcept`.
]
]
[
[`a.init(ec)`]
[`void`]
[
Called immediately after construction. If the function sets an
error code in `ec`, the serialization is aborted and the error
is propagated to the caller. This function must be `noexcept`.
]
]
[
[`a.content_length()`]
[`std::uint64_t`]
[
If this member is present, it is called after initialization
and before calls to provide buffers. The serialized message will
have the Content-Length field set to the value returned from
this function. If this member is absent, the serialized message
body will be chunk-encoded for HTTP versions 1.1 and later, else
the serialized message body will be sent unmodified, with the
error `boost::asio::error::eof` returned to the caller, to notify
they should close the connection to indicate the end of the message.
This function must be `noexcept`.
]
]
[
[`a.write(ec, wf)`]
[`bool`]
[
Called repeatedly after `init` succeeds. `wf` is a function object
which takes as its single parameter any value meeting the requirements
of __ConstBufferSequence__. Buffers provided to this write function
must remain valid until the next member function of `writer` is
invoked (which may be the destructor). This function returns `true`
to indicate all message body data has been written, or `false` if
there is more body data.
]
]
]
[note
Definitions for required `Writer` member functions should be declared
inline so the generated code can become part of the implementation.
]
Exemplar:
```
struct writer
{
public:
/** Construct the writer.
The msg object is guaranteed to exist for the lifetime of the writer.
Exceptions:
No-throw guarantee.
@param msg The message whose body is to be written.
*/
template<bool isRequest, class Body, class Headers>
explicit
writer(message<isRequest, Body, Headers> const& msg) noexcept;
/** Initialize the writer.
Called once immediately after construction.
The writer can perform initialization which may fail.
@param ec Contains the error code if any errors occur.
*/
void
init(error_code& ec) noexcept;
/** Returns the content length.
If this member is present, the implementation will set the
Content-Length field accordingly. If absent, the implementation will
use chunk-encoding or terminate the connection to indicate the end
of the message.
*/
std::uint64_t
content_length() noexcept;
/** Write zero or one buffer representing the message body.
Postconditions:
If return value is `true`:
* Callee made zero or one calls to `write`.
* There is no more data remaining to write.
If return value is `false`:
* Callee does not take ownership of resume.
* Callee made one call to `write`.
@param ec Set to indicate an error. This will cause an
asynchronous write operation to complete with the error.
@param write A functor the writer will call to provide the next
set of buffers. Ownership of the buffers is not transferred,
the writer must guarantee that the buffers remain valid until the
next member function is invoked, which may be the destructor.
@return `true` if there is no more data to send,
`false` when there may be more data.
*/
template<class WriteFunction>
bool
write(
error_code&,
WriteFunction&& wf) noexcept;
};
```
[endsect]