mirror of
https://github.com/boostorg/beast.git
synced 2025-07-30 04:47:29 +02:00
Documentation work
This commit is contained in:
@ -22,6 +22,7 @@
|
||||
[template mdash[] '''— ''']
|
||||
[template indexterm1[term1] '''<indexterm><primary>'''[term1]'''</primary></indexterm>''']
|
||||
[template indexterm2[term1 term2] '''<indexterm><primary>'''[term1]'''</primary><secondary>'''[term2]'''</secondary></indexterm>''']
|
||||
[template repo_file[path] '''<ulink url="https://github.com/vinniefalco/Beast/blob/master/'''[path]'''">'''[path]'''</ulink>''']
|
||||
|
||||
[def __N3747__ [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3747.pdf [*N3747]]]
|
||||
[def __N4588__ [@http://cplusplus.github.io/networking-ts/draft.pdf [*N4588]]]
|
||||
@ -33,7 +34,9 @@
|
||||
[def __asio_handler_invoke__ [@http://www.boost.org/doc/html/boost_asio/reference/asio_handler_invoke.html `asio_handler_invoke`]]
|
||||
[def __asio_handler_allocate__ [@http://www.boost.org/doc/html/boost_asio/reference/asio_handler_allocate.html `asio_handler_allocate`]]
|
||||
[def __io_service__ [@http://www.boost.org/doc/html/boost_asio/reference/io_service.html `io_service`]]
|
||||
[def __streambuf__ [@http://www.boost.org/doc/html/boost_asio/reference/streambuf.html [*boost::asio::streambuf]]]
|
||||
[def __socket__ [@http://www.boost.org/doc/html/boost_asio/reference/ip__tcp/socket.html `boost::asio::ip::tcp::socket`]]
|
||||
[def __ssl_stream__ [@http://www.boost.org/doc/html/boost_asio/reference/ssl_stream.html `boost::asio::ssl::stream`]]
|
||||
[def __streambuf__ [@http://www.boost.org/doc/html/boost_asio/reference/streambuf.html `boost::asio::streambuf`]]
|
||||
[def __use_future__ [@http://www.boost.org/doc/html/boost_asio/reference/use_future_t.html `boost::asio::use_future`]]
|
||||
[def __void_or_deduced__ [@http://www.boost.org/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.return_type_of_an_initiating_function ['void-or-deduced]]]
|
||||
[def __yield_context__ [@http://www.boost.org/doc/html/boost_asio/reference/yield_context.html `boost::asio::yield_context`]]
|
||||
|
@ -12,15 +12,14 @@
|
||||
[*low-level HTTP/1, WebSocket, and network protocol] programming
|
||||
using the consistent asynchronous model of __Asio__. Beast is
|
||||
not an HTTP client or HTTP server, but it can be used to build
|
||||
those things. It is intended to be a foundation for writing other
|
||||
interoperable libraries by providing HTTP vocabulary types and
|
||||
algorithms. The provided examples show how clients and servers
|
||||
might be built.
|
||||
those things. It is a foundation for writing interoperable
|
||||
libraries by providing HTTP vocabulary types and algorithms. The
|
||||
examples show how client and server applications might be built.
|
||||
]
|
||||
|
||||
This library is designed for:
|
||||
|
||||
* [*Symmetry:] Interfaces are role-agnostic; build clients, servers, or both.
|
||||
* [*Symmetry:] Algorithms are role-agnostic; build clients, servers, or both.
|
||||
|
||||
* [*Ease of Use:] __Asio__ users will immediately understand Beast.
|
||||
|
||||
@ -29,8 +28,7 @@ This library is designed for:
|
||||
|
||||
* [*Performance:] Build applications handling thousands of connections or more.
|
||||
|
||||
* [*Basis for Further Abstraction.] Components are open-ended and
|
||||
suited for building higher level libraries.
|
||||
* [*Basis for Further Abstraction.] Components are well-suited for building upon.
|
||||
|
||||
[heading Audience]
|
||||
|
||||
@ -44,12 +42,11 @@ Asio callbacks or coroutines.
|
||||
|
||||
An absence of high quality C++ network protocol libraries has led
|
||||
to a patchwork of open-source solutions lacking suitable conciseness
|
||||
and expressive power for standardization. Part of this problem is
|
||||
caused by the lack of a standardized C++ networking interface. This
|
||||
will change soon with the Networking Technical Specification
|
||||
(__N4588__): a uniform interface for networking on track to become
|
||||
standardized. This technical specification is modeled closely after
|
||||
Boost.Asio.
|
||||
and expressive power for standardization. Part of this problem is a
|
||||
lack of common C++ networking interfaces. This is changing soon with
|
||||
the Networking Technical Specification (__N4588__): a uniform interface
|
||||
on track to become standardized. This technical specification is modeled
|
||||
closely after Boost.Asio.
|
||||
The HTTP and WebSocket protocols drive most of the World Wide Web.
|
||||
Every web browser implements these protocols to load webpages and
|
||||
to enable client side programs (often written in JavaScript) to
|
||||
@ -57,8 +54,8 @@ communicate interactively. C++ benefits greatly from having a
|
||||
standardized implementation of these protocols.
|
||||
|
||||
[note
|
||||
The Beast roadmap includes a port to the standardized C++
|
||||
networking interface based on __N4588__.
|
||||
The Beast roadmap includes a port to the networking
|
||||
interface based on __N4588__.
|
||||
]
|
||||
|
||||
[heading Requirements]
|
||||
|
@ -7,9 +7,8 @@
|
||||
|
||||
[section:example Example Programs]
|
||||
|
||||
These usage examples are intended to quickly impress upon readers the
|
||||
flavor of the library. They are complete programs which may be built
|
||||
and run. Source code and build scripts for these programs may be found
|
||||
These complete programs are intended to quickly impress upon readers the
|
||||
flavor of the library. Source code and build scripts for them are located
|
||||
in the examples directory.
|
||||
|
||||
|
||||
@ -18,47 +17,47 @@ in the examples directory.
|
||||
|
||||
Use HTTP to make a GET request to a website and print the response:
|
||||
|
||||
File: [repo_file examples/http_example.cpp]
|
||||
|
||||
[http_example_get]
|
||||
|
||||
|
||||
|
||||
[heading WebSocket]
|
||||
|
||||
Establish a WebSocket connection, send a message and receive the reply:
|
||||
|
||||
File: [repo_file examples/websocket_example.cpp]
|
||||
|
||||
[websocket_example_client_echo]
|
||||
|
||||
|
||||
|
||||
[heading WebSocket Echo Server]
|
||||
|
||||
This example demonstrates both synchronous and asynchronous
|
||||
WebSocket server implementations.
|
||||
|
||||
* [@examples/websocket_async_echo_server.hpp]
|
||||
* [@examples/websocket_sync_echo_server.hpp]
|
||||
* [@examples/websocket_echo.cpp]
|
||||
* [repo_file examples/websocket_async_echo_server.hpp]
|
||||
* [repo_file examples/websocket_sync_echo_server.hpp]
|
||||
* [repo_file examples/websocket_echo.cpp]
|
||||
|
||||
[heading Secure WebSocket]
|
||||
|
||||
Establish a WebSocket connection over an encrypted TLS connection,
|
||||
send a message and receive the reply. Requires OpenSSL to build.
|
||||
|
||||
* [@examples/websocket_ssl_example.cpp]
|
||||
* [repo_file examples/ssl/websocket_ssl_example.cpp]
|
||||
|
||||
[heading HTTPS GET]
|
||||
|
||||
This example demonstrates sending and receiving HTTP messages
|
||||
over a TLS connection. Requires OpenSSL to build.
|
||||
|
||||
* [@examples/http_ssl_example.cpp]
|
||||
* [repo_file examples/ssl/http_ssl_example.cpp]
|
||||
|
||||
[heading HTTP Crawl]
|
||||
|
||||
This example retrieves the page at each of the most popular domains
|
||||
as measured by Alexa.
|
||||
|
||||
* [@examples/http_crawl.cpp]
|
||||
* [repo_file examples/http_crawl.cpp]
|
||||
|
||||
[heading HTTP Server]
|
||||
|
||||
@ -66,10 +65,10 @@ This example demonstrates both synchronous and asynchronous server
|
||||
implementations. It also provides an example of implementing a [*Body]
|
||||
type, in `file_body`.
|
||||
|
||||
* [@examples/file_body.hpp]
|
||||
* [@examples/http_async_server.hpp]
|
||||
* [@examples/http_sync_server.hpp]
|
||||
* [@examples/http_server.cpp]
|
||||
* [repo_file examples/file_body.hpp]
|
||||
* [repo_file examples/http_async_server.hpp]
|
||||
* [repo_file examples/http_sync_server.hpp]
|
||||
* [repo_file examples/http_server.cpp]
|
||||
|
||||
[heading Composed Operations]
|
||||
|
||||
@ -78,13 +77,16 @@ composable asynchronous initiation function with associated composed
|
||||
operation implementation. This is a complete, runnable version of
|
||||
the example described in the Core Foundations document section.
|
||||
|
||||
* [@examples/echo_op.cpp]
|
||||
* [repo_file examples/echo_op.cpp]
|
||||
|
||||
[heading Listings]
|
||||
[heading Documentation Samples]
|
||||
|
||||
These are stand-alone listings of the HTTP and WebSocket examples.
|
||||
Here are all of the example functions and classes presented
|
||||
throughout the documentation, they can be included and used
|
||||
in your program without modification
|
||||
|
||||
* [@examples/http_example.cpp]
|
||||
* [@examples/websocket_example.cpp]
|
||||
* [repo_file examples/doc_core_samples.hpp]
|
||||
|
||||
* [repo_file examples/doc_http_samples.hpp]
|
||||
|
||||
[endsect]
|
||||
|
@ -7,11 +7,12 @@
|
||||
|
||||
[section:core Using Networking]
|
||||
|
||||
A goal of the library is expose implementation primitives in order that
|
||||
users may build their own library-like components. These primitives include
|
||||
traits, buffers, buffer algorithms, and helpers for implementing asynchronous
|
||||
operations compatible with __Asio__ and described in __N3747__. This section
|
||||
lists these facilities by group, with descriptions.
|
||||
This library makes network primitives used by the implementation publicly
|
||||
available so users can take advantage of them in their own libraries.
|
||||
These primitives include traits, buffers, buffer algorithms, and helpers
|
||||
for implementing asynchronous operations compatible with __Asio__ and
|
||||
described in __N3747__. This section lists these facilities by group,
|
||||
with descriptions.
|
||||
|
||||
[important
|
||||
This documentation assumes familiarity with __Asio__. Sample
|
||||
|
@ -15,11 +15,10 @@
|
||||
left to the interfaces already existing on the underlying streams.
|
||||
]
|
||||
|
||||
Library stream algorithms require an already-connected socket, SSL stream,
|
||||
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:
|
||||
Library stream algorithms require a __socket__, __ssl_stream__, or other
|
||||
__Stream__ object that has already established communication with an
|
||||
endpoint. This example is provided as a reminder of how to work with
|
||||
sockets:
|
||||
|
||||
[snippet_core_2]
|
||||
|
||||
|
@ -7,17 +7,13 @@
|
||||
|
||||
[section:streams Using Streams]
|
||||
|
||||
A __Stream__ is a communication channel where data expressed as octet
|
||||
buffers is transferred sequentially. Streams are either synchronous
|
||||
A __Stream__ is a communication channel where data is transferred as
|
||||
an ordered sequence of octet buffers. Streams are either synchronous
|
||||
or asynchronous, and may allow reading, writing, or both. Note that
|
||||
a particular type may model more than one concept. For example, the
|
||||
Asio types
|
||||
[@http://www.boost.org/doc/html/boost_asio/reference/ip__tcp/socket.html `boost::asio::ip::tcp::socket`]
|
||||
and
|
||||
[@http://www.boost.org/doc/html/boost_asio/reference/ssl__stream.html `boost::asio::ssl::stream`]
|
||||
support everything. All stream algorithms in Beast are declared as
|
||||
template functions with specific concept requirements chosen from
|
||||
this list:
|
||||
Asio types __socket__ and __ssl_stream__ and support everything.
|
||||
All stream algorithms in Beast are declared as template functions
|
||||
with specific concept requirements chosen from this list:
|
||||
|
||||
[table Stream Concepts
|
||||
[[Concept][Description]]
|
||||
|
@ -11,13 +11,18 @@ __Asio__ provides the __ConstBufferSequence__ and __MutableBufferSequence__
|
||||
concepts, whose models provide ranges of buffers, as well as the __streambuf__
|
||||
class which encapsulates memory storage that may be automatically resized as
|
||||
required, where the memory is divided into an input sequence followed by an
|
||||
output sequence. The Networking TS (__N4588__) generalizes the `streambuf`
|
||||
output sequence. The Networking TS (__N4588__) generalizes this `streambuf`
|
||||
interface into the __DynamicBuffer__ concept. Beast algorithms which require
|
||||
resizable buffers accept as parameters dynamic buffer objects. These
|
||||
metafunctions check types against these buffer concepts:
|
||||
resizable buffers accept dynamic buffer objects as templated parameters.
|
||||
These metafunctions check if types match the buffer concepts:
|
||||
|
||||
[table Buffer Type Checks
|
||||
[[Name][Description]]
|
||||
[[
|
||||
[link beast.ref.is_dynamic_buffer `is_dynamic_buffer`]
|
||||
][
|
||||
Determine if a type meets the requirements of __DynamicBuffer__.
|
||||
]]
|
||||
[[
|
||||
[link beast.ref.is_const_buffer_sequence `is_const_buffer_sequence`]
|
||||
][
|
||||
@ -28,11 +33,6 @@ metafunctions check types against these buffer concepts:
|
||||
][
|
||||
Determine if a type meets the requirements of __MutableBufferSequence__.
|
||||
]]
|
||||
[[
|
||||
[link beast.ref.is_dynamic_buffer `is_dynamic_buffer`]
|
||||
][
|
||||
Determine if a type meets the requirements of __DynamicBuffer__.
|
||||
]]
|
||||
]
|
||||
|
||||
To suit various needs, several implementation of dynamic buffer are available:
|
||||
@ -88,11 +88,11 @@ To suit various needs, several implementation of dynamic buffer are available:
|
||||
Network applications frequently need to manipulate buffer sequences. To
|
||||
facilitate working with buffers the library treats these sequences as
|
||||
a special type of range. Algorithms and wrappers are provided which
|
||||
transform these buffer sequences ranges efficiently using lazy evaluation.
|
||||
No memory allocations are used in the transformations; instead, they
|
||||
create lightweight iterators over the existing, unmodified memory
|
||||
buffers. Control of buffers is retained by the caller; ownership is
|
||||
not transferred.
|
||||
transform these ranges efficiently using lazy evaluation. No memory
|
||||
allocations are used in the transformations; instead, they create
|
||||
lightweight iterators over the existing, unmodified memory buffers.
|
||||
Control of buffers is retained by the caller; ownership is not
|
||||
transferred.
|
||||
|
||||
[table Buffer Algorithms
|
||||
[[Name][Description]]
|
||||
|
@ -32,11 +32,13 @@ in this library. Non-trivial applications will want to provide their own
|
||||
asynchronous initiation functions which perform a series of other,
|
||||
intermediate asynchronous operations before invoking the final completion
|
||||
handler. The set of intermediate actions produced by calling an initiation
|
||||
function is known as a ['composed operation]. To ensure full interoperability
|
||||
and well-defined behavior, __Asio__ imposes requirements on the implementation
|
||||
of composed operations. A number of useful classes and macros to facilitate
|
||||
the development of composed operations and the associated asynchronous
|
||||
initiation functions used to launch them are available:
|
||||
function is known as a
|
||||
[@http://blog.think-async.com/2009/08/composed-operations-coroutines-and-code.html ['composed operation]].
|
||||
To ensure full interoperability and well-defined behavior, __Asio__ imposes
|
||||
requirements on the implementation of composed operations. A number of useful
|
||||
classes and macros to facilitate the development of composed operations and
|
||||
the associated asynchronous initiation functions used to launch them are
|
||||
available:
|
||||
|
||||
[table Asynchronous Helpers
|
||||
[[Name][Description]]
|
||||
|
@ -21,7 +21,7 @@ class message;
|
||||
```
|
||||
|
||||
The container offers value semantics including move and copy if supported
|
||||
by `Body` and `Fields`. User defined template function parameters can
|
||||
by __Body__ and __Fields__. User defined template function parameters can
|
||||
accept any message, or can use partial specialization to accept just
|
||||
requests or responses. The default __fields__ is a provided associative
|
||||
container using the standard allocator and supporting modification and
|
||||
@ -47,17 +47,19 @@ a few members unique to the type. This is implemented by declaring the
|
||||
header classes as partial specializations of `isRequest`. Furthermore,
|
||||
__message__ is derived from __header__; a message may be passed as an
|
||||
argument to a function taking a suitably typed header as a parameter.
|
||||
This diagram shows the inheritance relationship between header and message,
|
||||
along with the fields from the different partial specializations for each
|
||||
possible value of `isRequest`:
|
||||
Furthermore, `header` is publicly derived from `Fields`; a message
|
||||
inherits all of the fields member functions. Since `fields` is a
|
||||
container, iterating a message iterates its header fields. This
|
||||
diagram shows the inheritance relationship between header and message,
|
||||
along with the fields from the different partial specializations for
|
||||
each possible value of `isRequest`:
|
||||
|
||||
[$images/message.png [width 730px] [height 410px]]
|
||||
|
||||
The template type aliases
|
||||
[link beast.ref.http__request `request`] and
|
||||
[link beast.ref.http__response `response`]
|
||||
are provided for notational convenience. They also come with the default
|
||||
__fields__, a common choice.
|
||||
are provided for brevity. They specify the common default of `fields`.
|
||||
|
||||
```
|
||||
/// A typical HTTP request
|
||||
@ -72,21 +74,24 @@ using response = message<false, Body, Fields>;
|
||||
[heading:body Body Types]
|
||||
|
||||
Beast defines the __Body__ concept, which determines both the type of
|
||||
the `message::body` member (as seen in the diagram above) and may also
|
||||
include algorithms for transferring buffers in and out. These algorithms
|
||||
are used during parsing and serialization. These body types are available
|
||||
within the library, and users may define their own body types which meet
|
||||
the __Body__ requirements:
|
||||
the [link beast.ref.http__message.body `message::body`] member
|
||||
(as seen in the diagram above) and may also include algorithms for
|
||||
transferring buffers in and out. These algorithms are used during
|
||||
parsing and serialization. These body types are available within the
|
||||
library, and users may define their own body types which meet the
|
||||
__Body__ requirements:
|
||||
|
||||
[table
|
||||
[[Name][Description]]
|
||||
[[
|
||||
[link beast.ref.http__buffer_body `buffer_body`]
|
||||
][
|
||||
A body whose `value_type` holds a raw pointer and size to a
|
||||
caller-provided buffer. This allows for serialization of body data
|
||||
coming from external sources, and incremental parsing of message
|
||||
body content using a fixed size buffer.
|
||||
A body whose
|
||||
[link beast.ref.http__buffer_body__value_type `value_type`]
|
||||
holds a raw pointer and size to a caller-provided buffer.
|
||||
This allows for serialization of body data coming from
|
||||
external sources, and incremental parsing of message body
|
||||
content using a fixed size buffer.
|
||||
]]
|
||||
[[
|
||||
[link beast.ref.http__dynamic_body `dynamic_body`]
|
||||
@ -141,7 +146,7 @@ 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 content and type of the `body` member. The use
|
||||
of prepare is optional; these fields may also be set.
|
||||
of prepare is optional; these fields may also be set explicitly.
|
||||
|
||||
[table Create Response
|
||||
[[Statements] [Serialized Result]]
|
||||
@ -158,4 +163,10 @@ of prepare is optional; these fields may also be set.
|
||||
]]
|
||||
]
|
||||
|
||||
The implementation will automatically fill in the obsolete
|
||||
[@https://tools.ietf.org/html/rfc7230#section-3.1.2 reason-phrase]
|
||||
from the status code when serializing a message. Or it may
|
||||
be set directly using
|
||||
[link beast.ref.http__header.reason.overload2 `header::reason`].
|
||||
|
||||
[endsect]
|
||||
|
@ -7,11 +7,11 @@
|
||||
|
||||
[section:streams Message Stream Operations]
|
||||
|
||||
Beast provides synchronous and asynchronous algorithms to serialize and
|
||||
parse HTTP/1 wire format messages on streams. These functions form a
|
||||
basic interface for operating on entire messages:
|
||||
Beast provides synchronous and asynchronous algorithms to parse and
|
||||
serialize HTTP/1 wire format messages on streams. These functions form
|
||||
the message-oriented stream interface:
|
||||
|
||||
[table Basic Interface
|
||||
[table Message Stream Operations
|
||||
[[Name][Description]]
|
||||
[[
|
||||
[link beast.ref.http__read.overload3 [*read]]
|
||||
@ -54,16 +54,16 @@ message variable, then read a complete HTTP request synchronously:
|
||||
|
||||
[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
|
||||
buffer ("flat buffer"). Any dynamic buffer will work with reads. However,
|
||||
when not using a flat buffer the implementation may perform an additional
|
||||
memory allocation to restructure the input into a single buffer.
|
||||
This example uses __flat_buffer__. Beast's __basic_parser__ is
|
||||
optimized for structured HTTP data located in a single contiguous
|
||||
(['flat]) memory buffer. When not using a flat buffer the implementation
|
||||
may perform an additional memory allocations to restructure the input
|
||||
into a single buffer for parsing.
|
||||
|
||||
[tip
|
||||
User-defined implementations of __DynamicBuffer__ may avoid additional
|
||||
parser memory allocation, if those implementations guarantee that
|
||||
returned buffer sequences will always have length one.
|
||||
Other Implementations of __DynamicBuffer__ may avoid parser
|
||||
memory allocation by always returning buffer sequences of
|
||||
length one.
|
||||
]
|
||||
|
||||
Messages may also be read asynchronously. When performing asynchronous
|
||||
|
@ -7,8 +7,8 @@
|
||||
|
||||
[section:serializer_streams Serializer Stream Operations]
|
||||
|
||||
Message oriented stream operations provide for limited control.
|
||||
Sophisticated algorithms may need to do more, such as:
|
||||
Non-trivial algorithms need to do more than send entire messages
|
||||
at once, such as:
|
||||
|
||||
* Send the header first, and the body later.
|
||||
|
||||
@ -108,7 +108,7 @@ 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
|
||||
This defines a decorator which sets an extension variable `x` equal
|
||||
to the size of the chunk in bytes, and returns a single trailer field:
|
||||
|
||||
[http_snippet_17]
|
||||
|
@ -7,10 +7,11 @@
|
||||
|
||||
[section:parser_streams Parser Stream Operations]
|
||||
|
||||
Message oriented stream operations provide for limited control.
|
||||
Sophisticated algorithms may need to do more, such as:
|
||||
Non-trivial algorithms need to do more than receive entire messages
|
||||
at once, such as:
|
||||
|
||||
* Receive the header first, then the body later.
|
||||
|
||||
* Receive the header first and body later.
|
||||
|
||||
* Receive a large body using a fixed-size buffer.
|
||||
|
||||
@ -18,10 +19,10 @@ Sophisticated algorithms may need to do more, such as:
|
||||
|
||||
* Defer the commitment to a __Body__ type until after reading the header.
|
||||
|
||||
All of these operations require callers to manage the lifetime of state
|
||||
information associated with the operation, by constructing a class derived
|
||||
from __basic_parser__. Beast comes with two instances of parsers, and user
|
||||
defined types deriving from the basic parser are possible:
|
||||
These types of operations require callers to manage the lifetime of
|
||||
associated state, by constructing a class derived from __basic_parser__.
|
||||
Beast comes with the derived instance __parser__ which creates complete
|
||||
__message__ objects; user-defined parsers are also possible:
|
||||
|
||||
[table Parser Implementations
|
||||
[[Name][Description]]
|
||||
@ -33,7 +34,7 @@ defined types deriving from the basic parser are possible:
|
||||
template<
|
||||
bool isRequest, // `true` to parse an HTTP request
|
||||
class Body, // The Body type for the resulting message
|
||||
class Fields> // The type of container representing the fields
|
||||
class Fields = fields> // The type of container representing the fields
|
||||
class parser
|
||||
: public basic_parser<...>;
|
||||
```
|
||||
@ -107,12 +108,15 @@ advised to use an instance of __flat_buffer__, __static_buffer__, or
|
||||
__static_buffer_n__ for this purpose, although a user defined instance of
|
||||
__DynamicBuffer__ which produces input sequences of length one is also suitable.
|
||||
|
||||
The provided parsers use a "captive object" model, acting as container for
|
||||
the __message__ produced as a result of parsing. The caller 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:
|
||||
The parser contains a message constructed internally. Arguments passed
|
||||
to the parser's constructor are forwarded into the message container.
|
||||
The caller can access the message inside the parser by calling
|
||||
[link beast.ref.http__parser.get `parser::get`].
|
||||
If the `Fields` and `Body` types are [*MoveConstructible], the caller
|
||||
can take ownership of the message by calling
|
||||
[link beast.ref.http__parser.release `parser::release`]. In this example
|
||||
we read an HTTP response with a string body using a parser, then print
|
||||
the response:
|
||||
|
||||
[http_snippet_13]
|
||||
|
||||
|
@ -7,26 +7,24 @@
|
||||
|
||||
[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
|
||||
An instance of __serializer__ can be invoked directly, without using
|
||||
the provided stream operations. 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
|
||||
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
|
||||
To obtain the serialized next buffer sequence, call
|
||||
[link beast.ref.http__serializer.get `serializer::get`].
|
||||
Then, call
|
||||
[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
|
||||
to indicate the number of bytes consumed. This updates the next
|
||||
set of buffers to be returned, if any.
|
||||
`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:
|
||||
@ -50,9 +48,27 @@ 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
|
||||
informs the caller whether the header been serialized fully. In this
|
||||
C++14 example we print the header first, followed by the body:
|
||||
|
||||
[http_snippet_16]
|
||||
|
||||
[heading Example: Write To std::ostream]
|
||||
|
||||
The standard library provides the type `std::ostream` for performing high
|
||||
level write operations on character streams. The variable `std::cout` is
|
||||
based on this output stream. This example uses the buffer oriented interface
|
||||
of __serializer__ to write an HTTP message to a `std::ostream`:
|
||||
|
||||
[http_sample_write_ostream]
|
||||
|
||||
[tip
|
||||
Serializing to a `std::ostream` could be implemented using an alternate
|
||||
strategy: adapt the `std::ostream` interface to a __SyncWriteStream__,
|
||||
enable use with the library's existing stream algorithms. This is
|
||||
left as an exercise for the reader.
|
||||
]
|
||||
|
||||
|
||||
|
||||
[endsect]
|
||||
|
@ -7,11 +7,10 @@
|
||||
|
||||
[section:parser_buffers Buffer-Oriented Parsing]
|
||||
|
||||
In some cases, users may wish to create an instance of __parser__, or a
|
||||
user-defined type derived from __basic_parser__ and invoke its methods
|
||||
directly instead of using the provided stream algorithms. This could be
|
||||
useful for implementing algorithms on objects whose interface does not
|
||||
conform to any __Stream__. For example, a
|
||||
A subclass of __basic_parser__ can be invoked directly, without using
|
||||
the provided stream operations. This could be useful for implementing
|
||||
algorithms on objects whose interface does not conform to any __Stream__.
|
||||
For example, a
|
||||
[@http://zeromq.org/ *ZeroMQ* socket].
|
||||
The basic parser interface is interactive; the caller invokes the function
|
||||
[link beast.ref.http__basic_parser.put `basic_parser::put`]
|
||||
@ -60,4 +59,21 @@ The parser provides two options which may be set before parsing begins:
|
||||
]]
|
||||
]
|
||||
|
||||
[heading Example: Read From std::istream]
|
||||
|
||||
The standard library provides the type `std::istream` for performing high
|
||||
level read operations on character streams. The variable `std::cin` is based
|
||||
on this input stream. This example uses the buffer oriented interface of
|
||||
__basic_parser__ to build a stream operation which parses an HTTP message
|
||||
from a `std::istream`:
|
||||
|
||||
[http_sample_read_istream]
|
||||
|
||||
[tip
|
||||
Parsing from a `std::istream` could be implemented using an alternate
|
||||
strategy: adapt the `std::istream` interface to a __SyncReadStream__,
|
||||
enabling use with the library's existing stream algorithms. This is
|
||||
left as an exercise for the reader.
|
||||
]
|
||||
|
||||
[endsect]
|
||||
|
@ -14,8 +14,14 @@ elements according to the HTTP/1 protocol specification, while the derived
|
||||
class decides what to do with those elements. In particular, users who
|
||||
create exotic containers for [*Fields] may need to also create their own
|
||||
parser. Custom parsers will work with all of the stream read operations
|
||||
that work on parsers, as those algorithms use only the basic parser interface.
|
||||
that work on parsers, as those algorithms use only the basic parser
|
||||
interface. Some use cases for implementing custom parsers are:
|
||||
|
||||
* Inspect incoming header fields and keep or discard them.
|
||||
|
||||
* Use a container provided by an external interface.
|
||||
|
||||
* Store header data in a user-defined __Fields__ type.
|
||||
|
||||
The basic parser uses the Curiously Recurring Template Pattern
|
||||
([@https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern CRTP]).
|
||||
@ -24,7 +30,7 @@ The interface to the parser is event-driven. Member functions of the derived
|
||||
class (termed "callbacks" in this context) are invoked with parsed elements
|
||||
as they become available, requiring either the `friend` declaration as shown
|
||||
above or that the member functions are declared public (not recommended).
|
||||
Buffers provided by the parser are non-owning references, it is the
|
||||
Buffers provided by the parser are non-owning references; it is the
|
||||
responsibility of the derived class to copy any information it needs before
|
||||
returning from the callback.
|
||||
|
||||
|
@ -22,19 +22,23 @@ The meaning of the nested types is as follows
|
||||
[
|
||||
Determines the type of the
|
||||
[link beast.ref.http__message.body `message::body`]
|
||||
member. If this type defines default construction, move, copy,
|
||||
or swap, then message objects declared with this __Body__ will
|
||||
have those operations defined.
|
||||
member.
|
||||
]
|
||||
][
|
||||
[`reader`]
|
||||
[
|
||||
An optional nested type meeting the requirements of __BodyReader__.
|
||||
An optional nested type meeting the requirements of __BodyReader__,
|
||||
which provides the algorithm for converting the body representation
|
||||
to a forward range of buffer sequences.
|
||||
If present this body type may be used with a __serializer__.
|
||||
]
|
||||
][
|
||||
[`writer`]
|
||||
[
|
||||
An optional nested type meeting the requirements of __BodyWriter__.
|
||||
An optional nested type meeting the requirements of __BodyWriter__,
|
||||
which provides the algorithm for storing a forward range of buffer
|
||||
sequences in the body representation.
|
||||
If present, this body type may be used with a __parser__.
|
||||
]
|
||||
]
|
||||
]
|
||||
@ -44,41 +48,72 @@ The meaning of the nested types is as follows
|
||||
The `value_type` nested type allows the body to define the declaration of
|
||||
the body type as it appears in the message. This can be any type. For
|
||||
example, a body's value type may specify `std::vector<char>` or even
|
||||
`std::list<std::string>`. By also providing suitable definitions of
|
||||
corresponding `reader` and `writer` types, messages with that body
|
||||
become serializable and parsable respectively.
|
||||
|
||||
A custom body may even set the value type to something that is not a container
|
||||
for body octets, such as a
|
||||
`std::list<std::string>`. A custom body may even set the value type to
|
||||
something that is not a container for body octets, such as a
|
||||
[@http://www.boost.org/libs/filesystem/doc/reference.html#class-path `boost::filesystem::path`].
|
||||
In this case the reader may obtain buffers corresponding to a file on disk,
|
||||
while the writer may store incoming buffers to a file on disk.
|
||||
Or, a more structured container may be chosen. This declares a body's
|
||||
value type as a JSON tree structure produced from a
|
||||
[@http://www.boost.org/doc/html/property_tree/parsers.html#property_tree.parsers.json_parser `json_parser`]:
|
||||
```
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
|
||||
Another option is to use a structured container for the value type. For
|
||||
example, a JSON tree structure such as the property tree produced by Boost's
|
||||
[@http://www.boost.org/doc/html/property_tree/parsers.html#property_tree.parsers.json_parser `json_parser`]
|
||||
As long as a suitable reader or writer is available to provide the algorithm
|
||||
for transferring buffers in and out of the value type, even if abstract,
|
||||
struct Body
|
||||
{
|
||||
using value_type = boost::property_tree::ptree;
|
||||
|
||||
class reader;
|
||||
|
||||
class writer;
|
||||
};
|
||||
```
|
||||
|
||||
As long as a suitable reader or writer is available to provide the
|
||||
algorithm for transferring buffers in and out of the value type,
|
||||
those bodies may be serialized or parsed.
|
||||
|
||||
[note
|
||||
The examples included with this library provide a [*Body]
|
||||
implementation that serializes message bodies coming from a file.
|
||||
This is part of the HTTP server example.
|
||||
]
|
||||
[heading Example: File Body Type]
|
||||
|
||||
[heading Reader]
|
||||
Use of the flexible __Body__ concept customization point enables authors to
|
||||
preserve the self-contained nature of the __message__ object while allowing
|
||||
domain specific behaviors. Common operations for HTTP servers include sending
|
||||
responses which deliver file contents, and allowing for file uploads. In this
|
||||
example we build the `file_body` type which supports both reading and writing
|
||||
to a file on the file system.
|
||||
|
||||
The reader provides the algorithm for transferring buffers containing body
|
||||
octets obtained during parsing into the body container. The requirements
|
||||
for this type are described in the __BodyReader__ concept. When a body type
|
||||
defines a reader it may then be parsed using a __parser__.
|
||||
First we declare the type itself, along with the required members:
|
||||
|
||||
[heading Writer]
|
||||
[http_sample_file_body_1]
|
||||
|
||||
The writer provides the algorithm for converting the body container into a
|
||||
series of buffers. The requirements for this type are described in the
|
||||
__BodyWriter__ concept. When a body type defines a writer it may then be
|
||||
serialized using a __serializer__.
|
||||
The `size` function is a simple call to retrieve the file size:
|
||||
|
||||
[http_sample_file_body_2]
|
||||
|
||||
Our implementation of __BodyReader__ will contain a small buffer
|
||||
from which the file contents are read. The buffer is provided to
|
||||
the implementation on each call until everything has been read in.
|
||||
|
||||
[http_sample_file_body_3]
|
||||
|
||||
And here are the definitions for the functions we have declared:
|
||||
|
||||
[http_sample_file_body_4]
|
||||
|
||||
Files can be read now, and the next step is to allow writing to files
|
||||
by implementing the __BodyWriter__. The style is similar to the reader,
|
||||
except that buffers are incoming instead of outgoing. Here's the
|
||||
declaration:
|
||||
|
||||
[http_sample_file_body_5]
|
||||
|
||||
Finally, here is the implementation of the writer member functions:
|
||||
|
||||
[http_sample_file_body_6]
|
||||
|
||||
We have created a full featured body type capable of reading and
|
||||
writing files on the filesystem, integrating seamlessly with the
|
||||
HTTP algorithms and message container. Source code for this body
|
||||
type, and HTTP servers that use it, are available in the examples
|
||||
directory.
|
||||
|
||||
[endsect]
|
||||
|
@ -70,10 +70,6 @@ synchronous version of this server action looks like this:
|
||||
|
||||
|
||||
|
||||
[include 6_1_file_body.qbk]
|
||||
|
||||
|
||||
|
||||
[section HEAD request (Client)]
|
||||
|
||||
The
|
||||
@ -121,26 +117,6 @@ and a __parser__ to achieve its goal:
|
||||
|
||||
|
||||
|
||||
[section Read From std::istream]
|
||||
|
||||
The standard library provides the type `std::istream` for performing high
|
||||
level read operations on character streams. The variable `std::cin` is based
|
||||
on this input stream. In this example, we build a stream operation which
|
||||
parses an HTTP message from a `std::istream`:
|
||||
|
||||
[http_sample_read_istream]
|
||||
|
||||
[tip
|
||||
Parsing from a `std::istream` could be implemented using an alternate
|
||||
strategy: adapt the `std::istream` interface to a __SyncReadStream__.
|
||||
This lets all the library's existing algorithms work on `std::istream`.
|
||||
We leave this as an exercise for the reader.
|
||||
]
|
||||
|
||||
[endsect]
|
||||
|
||||
|
||||
|
||||
[section Send Child Process Output]
|
||||
|
||||
Sometimes it is necessary to send a message whose body is not conveniently
|
||||
@ -164,24 +140,4 @@ HTTP response. The output of the process is sent as it becomes available:
|
||||
|
||||
|
||||
|
||||
[section Write To std::ostream]
|
||||
|
||||
The standard library provides the type `std::ostream` for performing high
|
||||
level write operations on character streams. The variable `std::cout` is
|
||||
based on this output stream. In this example, we build a stream operation
|
||||
which serializes an HTTP message to a `std::ostream`:
|
||||
|
||||
[http_sample_write_ostream]
|
||||
|
||||
[tip
|
||||
Serializing to a `std::ostream` could be implemented using an alternate
|
||||
strategy: adapt the `std::ostream` interface to a __SyncWriteStream__.
|
||||
This lets all the library's existing algorithms work on `std::ostream`.
|
||||
We leave this as an exercise for the reader.
|
||||
]
|
||||
|
||||
[endsect]
|
||||
|
||||
|
||||
|
||||
[endsect]
|
||||
|
@ -1,52 +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 File Body Type]
|
||||
|
||||
Use of the flexible __Body__ concept customization point enables authors to
|
||||
preserve the self-contained nature of the __message__ object while allowing
|
||||
domain specific behaviors. Common operations for HTTP servers include sending
|
||||
responses which deliver file contents, and allowing for file uploads. In this
|
||||
example we build the `file_body` type which supports both reading and writing
|
||||
to a file on the file system.
|
||||
|
||||
First we declare the type itself, along with the required members:
|
||||
|
||||
[http_sample_file_body_1]
|
||||
|
||||
The `size` function is a simple call to retrieve the file size:
|
||||
|
||||
[http_sample_file_body_2]
|
||||
|
||||
Our implementation of __BodyReader__ will contain a small buffer
|
||||
from which the file contents are read. The buffer is provided to
|
||||
the implementation on each call until everything has been read in.
|
||||
|
||||
[http_sample_file_body_3]
|
||||
|
||||
And here are the definitions for the functions we have declared:
|
||||
|
||||
[http_sample_file_body_4]
|
||||
|
||||
Files can be read now, and the next step is to allow writing to files
|
||||
by implementing the __BodyWriter__. The style is similar to the reader,
|
||||
except that buffers are incoming instead of outgoing. Here's the
|
||||
declaration:
|
||||
|
||||
[http_sample_file_body_5]
|
||||
|
||||
Finally, here is the implementation of the writer member functions:
|
||||
|
||||
[http_sample_file_body_6]
|
||||
|
||||
We have created a full featured body type capable of reading and
|
||||
writing files on the filesystem, integrating seamlessly with the
|
||||
HTTP algorithms and message container. Source code for this body
|
||||
type, and HTTP servers that use it, are available in the examples
|
||||
directory.
|
||||
|
||||
[endsect]
|
@ -7,12 +7,13 @@
|
||||
|
||||
[section:concept Concepts]
|
||||
|
||||
This section describes all of the concepts defined by the library.
|
||||
|
||||
[include concept/Body.qbk]
|
||||
[include concept/BodyReader.qbk]
|
||||
[include concept/BodyWriter.qbk]
|
||||
[include concept/BufferSequence.qbk]
|
||||
[include concept/DynamicBuffer.qbk]
|
||||
[include concept/Field.qbk]
|
||||
[include concept/Fields.qbk]
|
||||
[include concept/FieldsReader.qbk]
|
||||
[include concept/Streams.qbk]
|
||||
|
15
doc/Jamfile
15
doc/Jamfile
@ -49,20 +49,6 @@ install callouts
|
||||
|
||||
explicit callout ;
|
||||
|
||||
install examples
|
||||
:
|
||||
[ glob
|
||||
../examples/*.cpp
|
||||
../examples/*.hpp
|
||||
../examples/ssl/*.cpp
|
||||
../examples/ssl/*.hpp
|
||||
]
|
||||
:
|
||||
<location>$(here)/html/examples
|
||||
;
|
||||
|
||||
explicit examples ;
|
||||
|
||||
xml doc
|
||||
:
|
||||
0_main.qbk
|
||||
@ -90,7 +76,6 @@ boostbook boostdoc
|
||||
<include>$(broot)/tools/boostbook/dtd
|
||||
:
|
||||
<location>temp
|
||||
<dependency>examples
|
||||
<dependency>images
|
||||
<dependency>stylesheets
|
||||
;
|
||||
|
@ -1,41 +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:Field Field]
|
||||
|
||||
A [*Field] represents a single HTTP header field/value pair.
|
||||
|
||||
In this table:
|
||||
|
||||
* `X` denotes a type meeting the requirements of [*Field].
|
||||
* `a` denotes a value of type `X`.
|
||||
|
||||
[table Field requirements
|
||||
|
||||
[[expression][type][semantics, pre/post-conditions]]
|
||||
[
|
||||
[`a.name()`]
|
||||
[[link beast.ref.string_view `string_view`]]
|
||||
[
|
||||
This function returns a value implicitly convertible to
|
||||
`boost::string_ref` containing the case-insensitive field
|
||||
name, without leading or trailing white space.
|
||||
]
|
||||
]
|
||||
[
|
||||
[`a.value()`]
|
||||
[[link beast.ref.string_view `string_view`]]
|
||||
[
|
||||
This function returns a value implicitly convertible to
|
||||
`boost::string_ref` containing the value for the field. The
|
||||
value is considered canonical if there is no leading or
|
||||
trailing whitespace.
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
[endsect]
|
@ -101,7 +101,6 @@
|
||||
<member><link linkend="beast.concept.Body">Body</link></member>
|
||||
<member><link linkend="beast.concept.BodyReader">BodyReader</link></member>
|
||||
<member><link linkend="beast.concept.BodyWriter">BodyWriter</link></member>
|
||||
<member><link linkend="beast.concept.Field">Field</link></member>
|
||||
<member><link linkend="beast.concept.Fields">Fields</link></member>
|
||||
<member><link linkend="beast.concept.FieldsReader">FieldsReader</link></member>
|
||||
</simplelist>
|
||||
|
@ -686,7 +686,13 @@ read_istream(
|
||||
error_code& ec)
|
||||
{
|
||||
// Create the message parser
|
||||
parser<isRequest, Body, Fields> parser;
|
||||
//
|
||||
// Arguments passed to the parser's constructor are
|
||||
// forwarded to the message constructor. Here, we use
|
||||
// a move construction in case the caller has constructed
|
||||
// their message in a non-default way.
|
||||
//
|
||||
parser<isRequest, Body, Fields> p{std::move(msg)};
|
||||
|
||||
do
|
||||
{
|
||||
@ -729,7 +735,7 @@ read_istream(
|
||||
else
|
||||
{
|
||||
// Inform the parser that we've reached the end of the istream.
|
||||
parser.put_eof(ec);
|
||||
p.put_eof(ec);
|
||||
if(ec)
|
||||
return;
|
||||
break;
|
||||
@ -737,7 +743,7 @@ read_istream(
|
||||
}
|
||||
|
||||
// Write the data to the parser
|
||||
auto const bytes_used = parser.put(buffer.data(), ec);
|
||||
auto const bytes_used = p.put(buffer.data(), ec);
|
||||
|
||||
// This error means that the parser needs additional octets.
|
||||
if(ec == error::need_more)
|
||||
@ -748,10 +754,10 @@ read_istream(
|
||||
// Consume the buffer octets that were actually parsed.
|
||||
buffer.consume(bytes_used);
|
||||
}
|
||||
while(! parser.is_done());
|
||||
while(! p.is_done());
|
||||
|
||||
// Transfer ownership of the message container in the parser to the caller.
|
||||
msg = parser.release();
|
||||
msg = p.release();
|
||||
}
|
||||
|
||||
//]
|
||||
|
@ -59,7 +59,7 @@ namespace beast {
|
||||
// This helper converts the handler into the real handler type
|
||||
async_completion<WriteHandler, void(error_code)> init{handler};
|
||||
|
||||
... // Create and the composed operation
|
||||
... // Create and invoke the composed operation
|
||||
|
||||
// This provides the return value and executor customization
|
||||
return init.result.get();
|
||||
|
@ -179,11 +179,9 @@ send(
|
||||
|
||||
//[http_snippet_13
|
||||
|
||||
template<
|
||||
class SyncReadStream>
|
||||
template<class SyncReadStream>
|
||||
void
|
||||
print_response(
|
||||
SyncReadStream& stream)
|
||||
print_response(SyncReadStream& stream)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
@ -238,7 +236,7 @@ struct lambda
|
||||
lambda(Serializer& sr_) : sr(sr_) {}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void operator()(error_code& ec, ConstBufferSequence const& buffer)
|
||||
void operator()(error_code& ec, ConstBufferSequence const& buffer) const
|
||||
{
|
||||
std::cout << buffers(buffer);
|
||||
sr.consume(boost::asio::buffer_size(buffer));
|
||||
|
Reference in New Issue
Block a user