forked from boostorg/beast
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:
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.
@@ -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]
|
||||
@@ -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
@@ -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
@@ -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<<</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>
|
||||
|
||||
@@ -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]
|
||||
Reference in new issue
Block a user