Files
boost_beast/doc/4_06_serializer_buffers.qbk
T
2017-07-20 08:12:18 -07:00

198 lines
6.9 KiB
Plaintext

[/
Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[section:serializer_buffers Buffer-Oriented Serializing]
In extreme cases, users may wish to create an instance of __serializer__
and invoke its methods directly instead of using the provided stream
algorithms. This could be useful for implementing algorithms on streams
whose interface does not conform to __Stream__. For example, a
[@https://github.com/libuv/libuv *libuv* socket].
The serializer interface is interactive; the caller invokes it repeatedly to
produce buffers until all of the buffers have been generated. Then the
serializer is destroyed.
After the serializer is created, the buffers are produced by first calling
[link beast.ref.http__serializer.get `serializer::get`]
to obtain a buffer sequence, and then calling
[link beast.ref.http__serializer.consume `serializer::consume`]
to indicate how many bytes in the buffer sequence were consumed.
This advanced the internal state of the serializer and prepares the
next set of buffers for delivery.
[link beast.ref.http__serializer.get `serializer::get`]
takes an error code parameter and invokes a visitor argument with the
error code and buffer of unspecified type. In C++14 this is easily
expressed with a generic lambda. The function
[link beast.ref.http__serializer.is_done `serializer::is_done`]
will return `true` when all the buffers have been produced. This C++14
example prints the buffers to standard output:
```
template<bool isRequest, class Body, class Fields>
void print(message<isRequest, Body, Fields> const& m)
{
error_code ec;
serializer<isRequest, Body, Fields> sr{m};
do
{
sr.get(ec,
[&sr](error_code& ec, auto const& buffer)
{
std::cout << buffers(buffer);
sr.consume(boost::asio::buffer_size(buffer));
});
}
while(! ec && ! sr.is_done());
if(! ec)
std::cout << std::endl;
else
std::cerr << ec.message() << std::endl;
}
```
Generic lambda expressions are only available in C++14 or later. A functor
with a templated function call operator is necessary to use C++11 as shown:
```
template<class Serializer>
struct lambda
{
Serializer& sr;
lambda(Serializer& sr_) : sr(sr_) {}
template<class ConstBufferSequence>
void operator()(error_code& ec, ConstBufferSequence const& buffer)
{
std::cout << buffers(buffer);
sr.consume(boost::asio::buffer_size(buffer));
}
};
template<bool isRequest, class Body, class Fields>
void print(message<isRequest, Body, Fields> const& m)
{
error_code ec;
serializer<isRequest, Body, Fields> sr{m};
do
{
sr.get(ec, lambda<decltype(sr)>{sr});
}
while(! ec && ! sr.is_done());
if(! ec)
std::cout << std::endl;
else
std::cerr << ec.message() << std::endl;
}
```
[heading Split Serialization]
In some cases, such as the handling of the
[@https://tools.ietf.org/html/rfc7231#section-5.1.1 Expect: 100-continue]
field, it may be desired to first serialize the header, perform some other
action, and then continue with serialization of the body. This is
accomplished by calling
[link beast.ref.http__serializer.split `serializer::split`]
with a boolean indicating that when buffers are produced, the last buffer
containing serialized header octets will not contain any octets corresponding
to the body. The function
[link beast.ref.http__serializer.is_header_done `serializer::is_header_done`]
informs the caller whether the header has completed serialization. In this
C++14 example we print the header first, followed by the body:
```
template<bool isRequest, class Body, class Fields>
void print(message<isRequest, Body, Fields> const& m)
{
error_code ec;
serializer<isRequest, Body, Fields> sr{m};
sr.split(true);
std::cout << "Header:" << std::endl;
do
{
sr.get(ec,
[&sr](error_code& ec, auto const& buffer)
{
std::cout << buffers(buffer);
sr.consume(boost::asio::buffer_size(buffer));
});
}
while(! sr.is_header_done());
if(! ec && ! sr.is_done())
{
std::cout << "Body:" << std::endl;
do
{
sr.get(ec,
[&sr](error_code& ec, auto const& buffer)
{
std::cout << buffers(buffer);
sr.consume(boost::asio::buffer_size(buffer));
});
}
while(! ec && ! sr.is_done());
}
if(ec)
std::cerr << ec.message() << std::endl;
}
```
[heading Chunk Decorators]
When the message used to construct the serializer indicates the chunked
transfer encoding, the serializer will automatically generate the proper
encoding in the output buffers. __rfc7230__ defines additional fields
called the
[@https://tools.ietf.org/html/rfc7230#section-4.1.1 chunk extensions]
in chunks with body octets, and the
[@https://tools.ietf.org/html/rfc7230#section-4.1.2 chunked trailer part]
for the final chunk. Applications that wish to emit chunk extensions
and trailers may instantiate the serializer with a "chunk decorator" type,
and pass an instance of the type upon construction. This decorator is
a function object which, when invoked with a __ConstBufferSequence__,
returns a
[link beast.ref.string_view `string_view`] containing either the extensions
or the trailer. For chunks containing body data, the passed buffer will
contain one or more corresponding body octets. The decorator may use this
information as needed. For example, to compute a digest on the data and
store it as a chunk extension. For the trailers, the serializer will
invoke the decorator with a buffer sequence of size zero. Or more
specifically, with an object of type
[@http://www.boost.org/doc/html/boost_asio/reference/null_buffers.html `boost::asio::null_buffers`].
For body chunks the string returned by the decorator must follow the
[@https://tools.ietf.org/html/rfc7230#section-4.1.1 correct syntax]
for the entire chunk extension. For the trailer, the returned string
should consist of zero or more lines ending in a CRLF and containing
a field name/value pair in the format prescribed by __rfc7230__. It
is the responsibility of the decorator to manage returned string buffers.
The implementation guarantees it will not reference previous strings
after subsequent calls.
Here, we declare a decorator which sets an extension variable `x` equal
to the size of the chunk in bytes, and returns a single trailer field:
```
struct decorator
{
std::string s;
template<class ConstBufferSequence>
string_view
operator()(ConstBufferSequence const& buffers)
{
s = ";x=" + std::to_string(boost::asio::buffer_size(buffers));
return s;
}
string_view
operator()(boost::asio::null_buffers)
{
return "Result: OK\r\n";
}
};
```
[endsect]