[/ 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 void print(message const& m) { error_code ec; serializer 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 struct lambda { Serializer& sr; lambda(Serializer& sr_) : sr(sr_) {} template void operator()(error_code& ec, ConstBufferSequence const& buffer) { std::cout << buffers(buffer); sr.consume(boost::asio::buffer_size(buffer)); } }; template void print(message const& m) { error_code ec; serializer sr{m}; do { sr.get(ec, lambda{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 void print(message const& m) { error_code ec; serializer 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 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]