forked from boostorg/beast
198 lines
6.9 KiB
Plaintext
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]
|