From 67df48daa69b6d957ee7d13a5b97cc3a4369a15a Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Wed, 7 Jun 2017 18:18:50 -0700 Subject: [PATCH] Documentation work --- doc/0_main.qbk | 2 + doc/3_0_core.qbk | 16 +- doc/3_1_asio.qbk | 10 +- doc/3_2_streams.qbk | 11 +- doc/3_5_op_tutorial.qbk | 6 - doc/4_00_http.qbk | 6 +- doc/4_02_message.qbk | 35 ++-- doc/4_03_streams.qbk | 81 ++------ doc/4_04_serializer_streams.qbk | 41 +--- doc/4_05_parser_streams.qbk | 17 +- doc/4_06_serializer_buffers.qbk | 114 +--------- examples/echo_op.cpp | 3 + test/Jamfile | 2 + test/core/CMakeLists.txt | 5 +- test/core/doc_snippets.cpp | 66 ++++++ test/http/CMakeLists.txt | 1 + test/http/doc_snippets.cpp | 356 ++++++++++++++++++++++++++++++++ 17 files changed, 486 insertions(+), 286 deletions(-) create mode 100644 test/core/doc_snippets.cpp create mode 100644 test/http/doc_snippets.cpp diff --git a/doc/0_main.qbk b/doc/0_main.qbk index afa936fa..3e635a92 100644 --- a/doc/0_main.qbk +++ b/doc/0_main.qbk @@ -72,6 +72,8 @@ [import ../examples/websocket_example.cpp] [import ../examples/echo_op.cpp] [import ../examples/doc_http_samples.hpp] +[import ../test/core/doc_snippets.cpp] +[import ../test/http/doc_snippets.cpp] [include 1_overview.qbk] [include 2_examples.qbk] diff --git a/doc/3_0_core.qbk b/doc/3_0_core.qbk index 9168397f..f5b9fb36 100644 --- a/doc/3_0_core.qbk +++ b/doc/3_0_core.qbk @@ -17,21 +17,9 @@ lists these facilities by group, with descriptions. This documentation assumes familiarity with __Asio__. Sample code and identifiers used throughout are written as if the following declarations are in effect: - ``` - #include - #include - #include - #include - - using namespace beast; - boost::asio::io_service ios; - boost::asio::io_service work{ios}; - std::thread t{[&](){ ios.run(); }}; - - error_code ec; - - ``` + [snippet_core_1a] + [snippet_core_1b] ] [include 3_1_asio.qbk] diff --git a/doc/3_1_asio.qbk b/doc/3_1_asio.qbk index ed738cdd..d6de9ea3 100644 --- a/doc/3_1_asio.qbk +++ b/doc/3_1_asio.qbk @@ -20,16 +20,8 @@ or other object which meets the required stream concepts and already has communication established with an endpoint. This example is provided as a reminder of how to work with sockets: -``` - auto host = "www.example.com"; - boost::asio::ip::tcp::resolver r{ios}; - boost::asio::ip::tcp::socket sock{ios}; - boost::asio::connect(sock, r.resolve( - boost::asio::ip::tcp::resolver::query{host, "http"})); - // At this point `sock` is a connected to a remote - // host and may be used to perform stream operations. -``` +[snippet_core_2] Throughout this documentation identifiers with the following names have special meaning: diff --git a/doc/3_2_streams.qbk b/doc/3_2_streams.qbk index 5bee9163..d2cb03b5 100644 --- a/doc/3_2_streams.qbk +++ b/doc/3_2_streams.qbk @@ -118,14 +118,7 @@ Using the type checks with `static_assert` on function or class template types will provide users with helpful error messages and prevent undefined behaviors. This example shows how a template function which writes to a synchronous stream may check its argument: -``` - template - void write_string(SyncWriteStream& stream, string_view s) - { - static_assert(is_sync_write_stream::value, - "SyncWriteStream requirements not met"); - boost::asio::write(stream, boost::asio::const_buffers_1(s.data(), s.size())); - } -``` + +[snippet_core_3] [endsect] diff --git a/doc/3_5_op_tutorial.qbk b/doc/3_5_op_tutorial.qbk index e6a28202..0fcde8d6 100644 --- a/doc/3_5_op_tutorial.qbk +++ b/doc/3_5_op_tutorial.qbk @@ -54,12 +54,6 @@ this echo operation: [core_sample_echo_op_4] -``` -// echo_op is callable with the signature void(error_code, bytes_transferred), -// allowing `*this` to be used as both a ReadHandler and a WriteHandler. -// -``` - A complete, runnable version of this example may be found in the examples directory. diff --git a/doc/4_00_http.qbk b/doc/4_00_http.qbk index 2da80f95..da98332d 100644 --- a/doc/4_00_http.qbk +++ b/doc/4_00_http.qbk @@ -66,10 +66,8 @@ format using __Asio__. Specifically, the library provides: the HTTP protocol specification described in __rfc7230__. Sample code and identifiers mentioned in this section is written as if these declarations are in effect: - ``` - #include - using namespace beast::http; - ``` + + [http_snippet_1] ] [include 4_01_primer.qbk] diff --git a/doc/4_02_message.qbk b/doc/4_02_message.qbk index 4bcb1a26..5105e82e 100644 --- a/doc/4_02_message.qbk +++ b/doc/4_02_message.qbk @@ -116,42 +116,39 @@ the __Body__ requirements: [heading Usage] The code examples below show how to create and fill in request and response -objects: +objects: Here is simple example of building an +[@https://tools.ietf.org/html/rfc7231#section-4.3.1 HTTP GET] +request. The function +[link beast.ref.http__message.prepare prepare] +allows for various connection options. The use of prepare is optional, +the Connection field may be set manually if desired. [table Create Request [[Statements] [Serialized Result]] [[ -``` - request req; - req.version = 11; // HTTP/1.1 - req.method(verb::get); - req.target("/index.htm"); - req.insert("Accept", "text/html"); - req.insert("Connection", "keep-alive"); - req.insert("User-Agent", "Beast"); -``` + [http_snippet_2] ][ ``` GET /index.htm HTTP/1.1\r\n Accept: text/html\r\n - Connection: keep-alive\r\n User-Agent: Beast\r\n + Connection: close\r\n \r\n ``` ]] ] +Here we create an HTTP response indicating success. Note that this +message has a body. The function +[link beast.ref.http__message.prepare prepare] +automatically sets the Content-Length or Transfer-Encoding field +depending on the body type. The use of prepare is optional, the +Content-Length and other fields may be set manually if desired. + [table Create Response [[Statements] [Serialized Result]] [[ -``` - response res; - res.version = 11; // HTTP/1.1 - res.result(status::ok); - res.body = "Hello, world!"; - res.insert("Server", "Beast"); - res.insert("Content-Length", res.body.size()); -``` + [http_snippet_3] ][ ``` HTTP/1.1 200 OK\r\n diff --git a/doc/4_03_streams.qbk b/doc/4_03_streams.qbk index e8b180d9..d49c7743 100644 --- a/doc/4_03_streams.qbk +++ b/doc/4_03_streams.qbk @@ -41,6 +41,8 @@ an exception upon error, and another which accepts as the last parameter an argument of type [link beast.ref.error_code `error_code&`]. If an error occurs this argument will be set to contain the error code. + + [heading Reading] Because a serialized header is not length-prefixed, algorithms which parse @@ -50,11 +52,8 @@ __DynamicBuffer__ which persists between calls. Each read operation may consume bytes remaining in the buffer, and leave behind new bytes. In this example we declare the buffer and a message variable, then read a complete HTTP request synchronously: -``` - flat_buffer buffer; // (The parser is optimized for flat buffers) - request req; - read(sock, buffer, req); -``` + +[http_snippet_4] In this example we used the __flat_buffer__. The parser in Beast is optimized for structured HTTP data located in a single contiguous memory @@ -74,15 +73,8 @@ valid until the operation has completed. Beast asynchronous initiation functions use Asio's completion handler model. Here we read a message asynchronously. When the operation completes the message in the error code indicating the result is printed: -``` - flat_buffer buffer; - response res; - async_read(sock, buffer, - [&](error_code ec) - { - std::cerr << ec.message() << std::endl; - }); -``` + +[http_snippet_5] If a read stream algorithm cannot complete its operation without exceeding the maximum specified size of the dynamic buffer provided, the error @@ -90,70 +82,27 @@ the maximum specified size of the dynamic buffer provided, the error is returned. This may be used to impose a limit on the maximum size of an HTTP message header for protection from buffer overflow attacks. The following code will generate an error: -``` - // This buffer is too small for much of anything - flat_buffer buffer{10}; - // Try to read a request - error_code ec; - request req; - read(sock, buffer, req, ec); - if(ec == error::buffer_overflow) - std::cerr << "Buffer limit exceeded!" << std::endl; +[http_snippet_6] + -``` [heading Writing] A set of free functions allow serialization of an entire HTTP message to -a stream. This function sends a message synchronously on the socket, -throwing an exception if an error occurs: -``` - template - void send(response const& res) - { - write(sock, res); - } -``` - -If a response has no declared content length, and no chunked transfer -encoding, the end of the message is indicated by the server closing +a stream. If a response has no declared content length, and no chunked +transfer encoding, the end of the message is indicated by the server closing the connection. When sending such a response, Beast will return the -error `boost::asio::error::eof` from the write algorithm to indicate +[link beast.ref.http__error `error::end_of_stream`] +from the write algorithm to indicate to the caller that the connection should be closed. This example constructs and sends a response whose body length is determined by the number of octets received prior to the server closing the connection: -``` - void send() - { - response res; - res.version = 11; - res.result(status::ok); - res.insert("Server", "Beast"); - res.body = "Hello, world!"; - error_code ec; - write(sock, res, ec); - if(ec == boost::asio::error::eof) - sock.close(); - else - BOOST_ASSERT(ec); - } -``` +[http_snippet_7] -An asynchronous version is also available: +The asynchronous version could be used instead: -``` - template - void send_async(response const& res) - { - async_write(sock, res, - [&](error_code) - { - if(ec) - std::cerr << ec.message() << std::endl; - }); - } -``` +[http_snippet_8] [endsect] diff --git a/doc/4_04_serializer_streams.qbk b/doc/4_04_serializer_streams.qbk index 972dda3c..36ef7dd2 100644 --- a/doc/4_04_serializer_streams.qbk +++ b/doc/4_04_serializer_streams.qbk @@ -22,32 +22,20 @@ Sophisticated algorithms will need to do more: All of these operations require callers to manage the lifetime of state information associated with the operation, by constructing a __serializer__ object with the message to be sent. The serializer type has this declaration: -``` -template< - bool isRequest, - class Body, - class Fields, - class ChunkDecorator = no_chunk_decorator, - class Allocator = std::allocator -> -class serializer; -``` + +[http_snippet_9] The choices for template types must match the message passed on construction. This code creates an HTTP response and the corresponding serializer: -``` - response res; - ... - serializer sr{res}; -``` + +[http_snippet_10] + The convenience function [link beast.ref.http__make_serializer `make_serializer`] is provided to avoid repetition of template argument types. The declaration for `sr` in the code above may be written as: -``` - ... - auto sr = make_serializer(res); -``` + +[http_snippet_11] The stream operations which work on serializers are: @@ -88,19 +76,6 @@ The stream operations which work on serializers are: Here is an example of using a serializer to send a message on a stream synchronously. This performs the same operation as calling `write(stream, m)`: -``` -template -void send(SyncWriteStream& stream, message const& m) -{ - static_assert(is_sync_write_stream::value, - "SyncWriteStream requirements not met"); - serializer sr{m}; - do - { - write_some(stream, sr); - } - while(! sr.is_done()); -} -``` +[http_snippet_12] [endsect] diff --git a/doc/4_05_parser_streams.qbk b/doc/4_05_parser_streams.qbk index a9123535..e75af846 100644 --- a/doc/4_05_parser_streams.qbk +++ b/doc/4_05_parser_streams.qbk @@ -114,22 +114,7 @@ accesses the contained object, and depending on the types used to instantiate the parser, it may be possible to acquire ownership of the header or message captive object and destroy the parser. In this example we read an HTTP response with a string body using a parser, then print the response: -``` -template -void print_response(SyncReadStream& stream) -{ - static_assert(is_sync_read_stream::value, - "SyncReadStream requirements not met"); - // Declare a parser for an HTTP response - response_parser parser; - - // Read the entire message - read(stream, parser); - - // Now print the message - std::cout << parser.get() << std::endl; -} -``` +[http_snippet_13] [endsect] diff --git a/doc/4_06_serializer_buffers.qbk b/doc/4_06_serializer_buffers.qbk index ac2442c3..99602a2f 100644 --- a/doc/4_06_serializer_buffers.qbk +++ b/doc/4_06_serializer_buffers.qbk @@ -30,63 +30,13 @@ 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; -} -``` + +[http_snippet_14] 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; -} -``` +[http_snippet_15] [heading Split Serialization] @@ -102,42 +52,8 @@ 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; -} -``` + +[http_snippet_16] [heading Chunk Decorators] @@ -173,25 +89,7 @@ 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"; - } -}; -``` +[http_snippet_17] [endsect] diff --git a/examples/echo_op.cpp b/examples/echo_op.cpp index c92dc8cd..3d740fdf 100644 --- a/examples/echo_op.cpp +++ b/examples/echo_op.cpp @@ -143,6 +143,9 @@ public: //[core_sample_echo_op_4 +// echo_op is callable with the signature void(error_code, bytes_transferred), +// allowing `*this` to be used as both a ReadHandler and a WriteHandler. +// template void echo_op:: operator()(beast::error_code ec, std::size_t bytes_transferred) diff --git a/test/Jamfile b/test/Jamfile index 2af8bdc0..12165f8a 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -24,6 +24,7 @@ unit-test core-tests : core/buffers_adapter.cpp core/clamp.cpp core/consuming_buffers.cpp + core/doc_snippets.cpp core/error.cpp core/flat_buffer.cpp core/handler_alloc.cpp @@ -44,6 +45,7 @@ unit-test http-tests : http/basic_parser.cpp http/buffer_body.cpp http/doc_http_samples.cpp + http/doc_snippets.cpp http/dynamic_body.cpp http/error.cpp http/field.cpp diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt index 8a36adff..9b012af0 100644 --- a/test/core/CMakeLists.txt +++ b/test/core/CMakeLists.txt @@ -8,15 +8,16 @@ add_executable (core-tests ${BEAST_INCLUDES} ${EXTRAS_INCLUDES} ../../extras/beast/unit_test/main.cpp - buffer_test.hpp async_result.cpp bind_handler.cpp buffer_cat.cpp buffer_prefix.cpp + buffer_test.hpp + buffered_read_stream.cpp buffers_adapter.cpp clamp.cpp consuming_buffers.cpp - buffered_read_stream.cpp + doc_snippets.cpp error.cpp flat_buffer.cpp handler_alloc.cpp diff --git a/test/core/doc_snippets.cpp b/test/core/doc_snippets.cpp new file mode 100644 index 00000000..26142c70 --- /dev/null +++ b/test/core/doc_snippets.cpp @@ -0,0 +1,66 @@ +// +// 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) +// + +//[snippet_core_1a + +#include +#include +#include +#include + +//] + +using namespace beast; + +namespace doc_core_snippets { + +void fxx() +{ + +//[snippet_core_1b +// + using namespace beast; + + boost::asio::io_service ios; + boost::asio::io_service::work work{ios}; + std::thread t{[&](){ ios.run(); }}; + + error_code ec; + boost::asio::ip::tcp::socket sock{ios}; + +//] + +{ +//[snippet_core_2 + +auto host = "www.example.com"; +boost::asio::ip::tcp::resolver r{ios}; +boost::asio::ip::tcp::socket stream{ios}; +boost::asio::connect(stream, r.resolve( + boost::asio::ip::tcp::resolver::query{host, "http"})); + +// At this point `stream` is a connected to a remote +// host and may be used to perform stream operations. + +//] +} + +} // fxx() + +//[snippet_core_3 + +template +void write_string(SyncWriteStream& stream, string_view s) +{ + static_assert(is_sync_write_stream::value, + "SyncWriteStream requirements not met"); + boost::asio::write(stream, boost::asio::const_buffers_1(s.data(), s.size())); +} + +//] + +} // doc_core_snippets diff --git a/test/http/CMakeLists.txt b/test/http/CMakeLists.txt index d19dd1cb..4f302ff3 100644 --- a/test/http/CMakeLists.txt +++ b/test/http/CMakeLists.txt @@ -15,6 +15,7 @@ add_executable (http-tests basic_parser.cpp buffer_body.cpp doc_http_samples.cpp + doc_snippets.cpp dynamic_body.cpp empty_body.cpp error.cpp diff --git a/test/http/doc_snippets.cpp b/test/http/doc_snippets.cpp new file mode 100644 index 00000000..7e7d6a0c --- /dev/null +++ b/test/http/doc_snippets.cpp @@ -0,0 +1,356 @@ +// +// 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) +// + +#include +#include +#include +#include + +using namespace beast; + +//[http_snippet_1 + +#include +using namespace beast::http; + +//] + +namespace doc_http_snippets { + +void fxx() { + + boost::asio::io_service ios; + boost::asio::io_service::work work{ios}; + std::thread t{[&](){ ios.run(); }}; + error_code ec; + boost::asio::ip::tcp::socket sock{ios}; + +{ +//[http_snippet_2 + + request req; + req.version = 11; // HTTP/1.1 + req.method(verb::get); + req.target("/index.htm"); + req.insert("Accept", "text/html"); + req.insert("User-Agent", "Beast"); + req.prepare(connection::close); + +//] +} + +{ +//[http_snippet_3 + + response res; + res.version = 11; // HTTP/1.1 + res.result(status::ok); + res.insert("Server", "Beast"); + res.body = "Hello, world!"; + res.prepare(); + +//] +} + +{ +//[http_snippet_4 + + flat_buffer buffer; // (The parser is optimized for flat buffers) + request req; + read(sock, buffer, req); + +//] +} + +{ +//[http_snippet_5 + + flat_buffer buffer; + response res; + async_read(sock, buffer, res, + [&](error_code ec) + { + std::cerr << ec.message() << std::endl; + }); + +//] +} + +{ +//[http_snippet_6 + + // This buffer's max size is too small for much of anything + flat_buffer buffer{10}; + + // Try to read a request + request req; + read(sock, buffer, req, ec); + if(ec == error::buffer_overflow) + std::cerr << "Buffer limit exceeded!" << std::endl; + +//] +} + +{ +//[http_snippet_7 + + response res; + res.version = 11; + res.result(status::ok); + res.insert("Server", "Beast"); + res.body = "Hello, world!"; + + write(sock, res, ec); + if(ec == error::end_of_stream) + sock.close(); +//] + +//[http_snippet_8 + async_write(sock, res, + [&](error_code) + { + if(ec) + std::cerr << ec.message() << std::endl; + }); +//] +} + +{ +//[http_snippet_10 + + response res; + + serializer sr{res}; + +//] +} + +{ + response res; +//[http_snippet_11 + + auto sr = make_serializer(res); + +//] +} + +} // fxx() + +//[http_snippet_12 + +/** Send a message to a stream synchronously. + + @param stream The stream to write to. This type must support + the @b SyncWriteStream concept. + + @param m The message to send. The Body type must support + the @b BodyReader concept. +*/ +template< + class SyncWriteStream, + bool isRequest, class Body, class Fields> +void +send( + SyncWriteStream& stream, + message const& m) +{ + // Check the template types + static_assert(is_sync_write_stream::value, + "SyncWriteStream requirements not met"); + static_assert(is_body_reader::value, + "BodyReader requirements not met"); + + // Create the instance of serializer for the message + serializer sr{m}; + + // Loop until the serializer is finished + do + { + // This call guarantees it will make some + // forward progress, or otherwise return an error. + write_some(stream, sr); + } + while(! sr.is_done()); +} + +//] + +//[http_snippet_13 + +template< + class SyncReadStream> +void +print_response( + SyncReadStream& stream) +{ + static_assert(is_sync_read_stream::value, + "SyncReadStream requirements not met"); + + // Declare a parser for an HTTP response + response_parser parser; + + // Read the entire message + read(stream, parser); + + // Now print the message + std::cout << parser.get() << std::endl; +} + +//] + +#ifdef _MSC_VER +//[http_snippet_14 + +template +void +print_cxx14(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; +} + +//] +#endif + +//[http_snippet_15 + +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; +} + +//] + +#ifdef _MSC_VER +//[http_snippet_16 + +template +void +split_print_cxx14(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; +} + +//] +#endif + +//[http_snippet_17 + +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"; + } +}; + +//] + +} // doc_http_snippets + +namespace beast { +namespace http { + +#if 0 +//[http_snippet_9] + +template< + bool isRequest, + class Body, + class Fields, + class ChunkDecorator = no_chunk_decorator, + class Allocator = std::allocator +> +class serializer; + +//] +#endif + +} // http +} // beast +