Files
boost_beast/doc/design.qbk

655 lines
28 KiB
Plaintext
Raw Normal View History

2017-07-20 08:01:46 -07:00
[/
Copyright (c) 2013-2016 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:design Design Choices]
[block '''
<informaltable frame="all"><tgroup cols="1"><colspec colname="a"/><tbody><row><entry valign="top"><simplelist>
<member><link linkend="beast.design.http">HTTP FAQ</link></member>
<member><link linkend="beast.design.websocket">WebSocket FAQ</link></member>
<member><link linkend="beast.design.websocketpp">Comparison to Zaphoyd Studios WebSocket++</link></member>
</simplelist></entry></row></tbody></tgroup></informaltable>
''']
2017-07-20 08:01:46 -07:00
The implementations are driven by business needs of cryptocurrency server
applications (e.g. [@https://ripple.com Ripple]) written in C++. These
needs were not met by existing solutions so Beast was written from scratch
2016-10-19 20:13:39 +02:00
as a solution. Beast's design philosophy avoids flaws exhibited by other
2017-07-20 08:01:46 -07:00
libraries:
* Don't try to do too much.
2017-07-20 08:01:46 -07:00
* Don't sacrifice performance.
2017-07-20 08:01:46 -07:00
* Mimic Boost.Asio; familiarity breeds confidence.
2017-07-20 08:01:46 -07:00
* Role-symmetric interfaces; client and server the same (or close to it).
2017-07-20 08:01:46 -07:00
2016-10-19 20:13:39 +02:00
* Leave important decisions to the user, such as allocating memory or
managing flow control.
2017-07-20 08:01:46 -07:00
Beast uses the __DynamicBuffer__ concept presented in the Networking TS
(__N4588__), and relies heavily on the Boost.Asio __ConstBufferSequence__
and __MutableBufferSequence__ concepts for passing buffers to functions.
The authors have found the dynamic buffer and buffer sequence interfaces to
be optimal for interacting with Asio, and for other tasks such as incremental
parsing of data in buffers (for example, parsing websocket frames stored
in a [link beast.ref.static_streambuf `static_streambuf`]).
2017-07-20 08:01:46 -07:00
During the development of Beast the authors have studied other software
packages and in particular the comments left during the Boost Review process
of other packages offering similar functionality. In this section and the
FAQs that follow we attempt to answer those questions that are also applicable
to Beast.
2017-07-20 08:01:46 -07:00
[variablelist
[[
"I would also like to see instances of this library being used
in production. That would give some evidence that the design
2016-10-19 20:13:39 +02:00
works in practice."
2017-07-20 08:01:46 -07:00
][
Beast.HTTP and Beast.WebSocket are production ready and currently
running on public servers receiving traffic and handling millions of
dollars worth of financial transactions daily. The servers run [*rippled],
open source software ([@https://github.com/ripple/rippled repository])
implementing the
[@https://ripple.com/files/ripple_consensus_whitepaper.pdf [*Ripple Consensus Protocol]],
technology provided by [@http://ripple.com Ripple].
2017-07-20 08:01:46 -07:00
]]
]
[section:http HTTP FAQ]
2017-07-20 08:01:46 -07:00
2016-10-19 20:13:39 +02:00
For HTTP we model the message to maximize flexibility of implementation
2017-07-20 08:01:46 -07:00
strategies while allowing familiar verbs such as [*`read`] and [*`write`].
The HTTP interface is further driven by the needs of the WebSocket module,
as a WebSocket session requires a HTTP Upgrade handshake exchange at the
start. Other design goals:
* Keep it simple.
2017-07-20 08:01:46 -07:00
2016-10-19 20:13:39 +02:00
* Stay low level; don't invent a whole web server or client.
2017-07-20 08:01:46 -07:00
* Allow for customizations, if the user needs it.
2017-07-20 08:01:46 -07:00
[variablelist
[[
"Some more advanced examples, e.g. including TLS with client/server
2016-10-19 20:13:39 +02:00
certificates would help."
2017-07-20 08:01:46 -07:00
][
The HTTP interface doesn't try to reinvent the wheel, it just uses
the `boost::asio::ip::tcp::socket` or `boost::asio::ssl::stream` that
you set up beforehand. Callers use the interfaces already existing
on those objects to make outgoing connections, accept incoming connections,
or establish TLS sessions with certificates. We find the available Asio
examples for performing these tasks sufficient.
]]
[[
"A built-in router?"
][
We presume this means a facility to match expressions against the URI
in HTTP requests, and dispatch them to calling code. The authors feel
that this is a responsibility of higher level code. Beast.HTTP does
not try to offer a web server.
]]
[[
"Cookies? Forms/File Uploads?"
2017-07-20 08:01:46 -07:00
][
Cookies, or managing these types of HTTP headers in general, is the
responsibility of higher levels. Beast.HTTP just tries to get complete
messages to and from the calling code. It deals in the HTTP headers just
enough to process the message body and leaves the rest to callers. However,
for forms and file uploads the symmetric interface of the message class
allows HTTP requests to include arbitrary body types including those needed
to upload a file or fill out a form.
]]
[[
"...supporting TLS (is this a feature? If not this would be a show-stopper),
2016-10-19 20:13:39 +02:00
etc."
2017-07-20 08:01:46 -07:00
][
Beast.HTTP does not provide direct facilities for implementing TLS
connections; however, the interfaces already existing on the
`boost::asio::ssl::stream` are available and can be used to establish
secure connections. Then, functions like `http::read` or `http::async_write`
can work with those encrypted connections with no problem.
]]
[[
"There should also be more examples of how to integrate the http service
with getting files from the file system, generating responses CGI-style"
][
The design goal for the library is to not try to invent a web server.
We feel that there is a strong need for a basic implementation that
models the HTTP message and provides functions to send and receive them
over Asio. Such an implementation should serve as a building block upon
which higher abstractions such as the aforementioned HTTP service or
cgi-gateway can be built.
One of the example programs implements a simple HTTP server that
delivers files from the filesystem.
2017-07-20 08:01:46 -07:00
]]
[[
"You should send a 100-continue to ask for the rest of the body if required."
][
The Beast interface needs to support this functionality (by allowing this
special case of partial message parsing and serialization). Specifically,
it should let callers read the request up to just before the body,
and let callers write the request up to just before the body. However,
making use of this behavior should be up to callers (since Beast is low
level).
2017-07-20 08:01:46 -07:00
]]
[[
"What about HTTP/2?"
2017-07-20 08:01:46 -07:00
][
Many reviewers feel that HTTP/2 support is an essential feature of
a HTTP library. The authors agree that HTTP/2 is important but also
feel that the most sensible implementation is one that does not re-use
the same network reading and writing interface for 2 as that for 1.0
and 1.1.
The Beast.HTTP message model is suitable for HTTP/2 and can be re-used.
2016-10-19 20:13:39 +02:00
The IETF HTTP Working Group adopted message compatiblity with HTTP/1.x
2017-07-20 08:01:46 -07:00
as an explicit goal. A parser can simply emit full headers after
decoding the compressed HTTP/2 headers. The stream ID is not logically
2017-07-20 08:01:46 -07:00
part of the message but rather message metadata and should be
communicated out-of-band (see below). HTTP/2 sessions begin with a
traditional HTTP/1.1 Upgrade similar in fashion to the WebSocket
2016-10-19 20:13:39 +02:00
upgrade. An HTTP/2 implementation can use existing Beast.HTTP primitives
2017-07-20 08:01:46 -07:00
to perform this handshake.
Free functions for HTTP/2 sessions are not possible because of the
requirement to maintain per-session state. For example, to decode the
compressed headers. Or to remember and respect the remote peer's window
settings. The authors propose that a HTTP/2 implementation be written
as a separate class template, similar to the `websocket::stream` but with
additional interfaces to support version 2 features. We feel that
Beast.HTTP offers enough useful functionality to justify inclusion,
so that developers can take advantage of it right away instead of
waiting.
]]
]
[endsect]
[section:websocket WebSocket FAQ]
[variablelist
[[
What about message compression?
][
Beast WebSocket supports the permessage-deflate extension described in
[@https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-00 draft-ietf-hybi-permessage-compression-00].
The library comes with a header-only, C++11 port of ZLib's "deflate" codec
used in the implementation of the permessage-deflate extension.
]]
[[
Where is the TLS/SSL interface?
][
The `websocket::stream` wraps the socket or stream that you provide
(for example, a `boost::asio::ip::tcp::socket` or a
`boost::asio::ssl::stream`). You establish your TLS connection using the
2016-10-19 20:13:39 +02:00
interface on `ssl::stream` like shown in all of the Asio examples, then
construct your `websocket::stream` around it. It works perfectly fine;
Beast.WebSocket doesn't try to reinvent the wheel or put a fresh coat of
interface paint on the `ssl::stream`.
2016-10-19 20:13:39 +02:00
The WebSocket implementation [*does] provide support for shutting down
the TLS connection through the use of the ADL compile-time virtual functions
[link beast.ref.websocket__teardown `teardown`] and
[link beast.ref.websocket__async_teardown `async_teardown`]. These will
properly close the connection as per rfc6455 and overloads are available
for TLS streams. Callers may provide their own overloads of these functions
for user-defined next layer types.
]]
]
2017-07-20 08:01:46 -07:00
[endsect]
[section:websocketpp Comparison to Zaphoyd Studios WebSocket++]
2017-07-20 08:01:46 -07:00
[variablelist
[[
How does this compare to [@https://www.zaphoyd.com/websocketpp websocketpp],
an alternate header-only WebSocket implementation?
][
[variablelist
[[1. Synchronous Interface][
Beast offers full support for WebSockets using a synchronous interface. It
uses the same style of interfaces found in Boost.Asio: versions that throw
exceptions, or versions that return the error code in a reference parameter:
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L774 Beast]]
[websocketpp]
][
[```
template<class DynamicBuffer>
void
read(opcode& op, DynamicBuffer& dynabuf)
```]
[
/<not available>/
]
]]]]
[[2. Connection Model][
websocketpp supports multiple transports by utilizing a trait, the `config::transport_type`
([@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/asio/connection.hpp#L60 asio transport example])
To get an idea of the complexity involved with implementing a transport,
compare the asio transport to the
[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L59 `iostream` transport]
(a layer that allows websocket communication over a `std::iostream`).
In contrast, Beast abstracts the transport by defining just one [*`NextLayer`]
template argument The type requirements for [*`NextLayer`] are
already familiar to users as they are documented in Asio:
__AsyncReadStream__, __AsyncWriteStream__, __SyncReadStream__, __SyncWriteStream__.
The type requirements for instantiating `beast::websocket::stream` versus
`websocketpp::connection` with user defined types are vastly reduced
(18 functions versus 2). Note that websocketpp connections are passed by
`shared_ptr`. Beast does not use `shared_ptr` anywhere in its public interface.
A `beast::websocket::stream` is constructible and movable in a manner identical
to a `boost::asio::ip::tcp::socket`. Callers can put such objects in a
`shared_ptr` if they want to, but there is no requirement to do so.
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp Beast]]
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L234 websocketpp]]
][
[```
template<class NextLayer>
class stream
{
NextLayer next_layer_;
...
}
```]
[```
template <typename config>
class connection
: public config::transport_type::transport_con_type
, public config::connection_base
{
public:
typedef lib::shared_ptr<type> ptr;
...
}
```]
]]]]
[[3. Client and Server Role][
websocketpp provides multi-role support through a hierarchy of
different classes. A `beast::websocket::stream` is role-agnostic, it
offers member functions to perform both client and server handshakes
in the same class. The same types are used for client and server
streams.
[table
[
[Beast]
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/roles/server_endpoint.hpp#L39 websocketpp],
[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/roles/client_endpoint.hpp#L42 also]]
][
[
/<not needed>/
]
[```
template <typename config>
class client : public endpoint<connection<config>,config>;
template <typename config>
class server : public endpoint<connection<config>,config>;
```]
]]]]
[[4. Thread Safety][
websocketpp uses mutexes to protect shared data from concurrent
access. In contrast, Beast does not use mutexes anywhere in its
implementation. Instead, it follows the Asio pattern. Calls to
asynchronous initiation functions use the same method to invoke
intermediate handlers as the method used to invoke the final handler,
through the __asio_handler_invoke__ mechanism.
The only requirement in Beast is that calls to asynchronous initiation
functions are made from the same implicit or explicit strand. For
example, if the `io_service` associated with a `beast::websocket::stream`
is single threaded, this counts as an implicit strand and no performance
costs associated with mutexes are incurred.
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/impl/read_frame_op.ipp#L118 Beast]]
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L706 websocketpp]]
][
[```
template <class Function>
friend
void asio_handler_invoke(Function&& f, read_frame_op* op)
{
return boost_asio_handler_invoke_helpers::invoke(f, op->d_->h);
}
```]
[```
mutex_type m_read_mutex;
```]
]]]]
[[5. Callback Model][
websocketpp requires a one-time call to set the handler for each event
in its interface (for example, upon message receipt). The handler is
represented by a `std::function` equivalent. Its important to recognize
that the websocketpp interface performs type-erasure on this handler.
In comparison, Beast handlers are specified in a manner identical to
Boost.Asio. They are function objects which can be copied or moved but
most importantly they are not type erased. The compiler can see
through the type directly to the implementation, permitting
optimization. Furthermore, Beast follows the Asio rules for treatment
of handlers. It respects any allocation, continuation, or invocation
customizations associated with the handler through the use of argument
dependent lookup overloads of functions such as `asio_handler_allocate`.
The Beast completion handler is provided at the call site. For each
call to an asynchronous initiation function, it is guaranteed that
there will be exactly one final call to the handler. This functions
exactly the same way as the asynchronous initiation functions found in
Boost.Asio, allowing the composition of higher level abstractions.
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L834 Beast]]
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L281 websocketpp],
[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L473 also]]
][
[```
template<class DynamicBuffer, class ReadHandler>
typename async_completion<ReadHandler, void(error_code)>::result_type
async_read(opcode& op, DynamicBuffer& dynabuf, ReadHandler&& handler);
```]
[```
typedef lib::function<void(connection_hdl,message_ptr)> message_handler;
void set_message_handler(message_handler h);
```]
]]]]
[[6. Extensible Asynchronous Model][
Beast fully supports the
[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf Extensible Asynchronous Model]
developed by Christopher Kohlhoff, author of Boost.Asio (see Section 8).
Beast websocket asynchronous interfaces may be used seamlessly with
`std::future` stackful/stackless coroutines, or user defined customizations.
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/impl/stream.ipp#L378 Beast]]
[websocketpp]
][
[```
beast::async_completion<ReadHandler, void(error_code)> completion(handler);
read_op<DynamicBuffer, decltype(completion.handler)>{
completion.handler, *this, op, streambuf};
return completion.result.get();
```]
[
/<not available>/
]
]]]]
[[7. Message Buffering][
websocketpp defines a message buffer, passed in arguments by
`shared_ptr`, and an associated message manager which permits
2016-10-19 20:13:39 +02:00
aggregation and reuse of memory. The implementation of
`websocketpp::message` uses a `std::string` to hold the payload. If an
incoming message is broken up into multiple frames, the string may be
reallocated for each continuation frame. The `std::string` always uses
the standard allocator, it is not possible to customize the choice of
allocator.
Beast allows callers to specify the object for receiving the message
or frame data, which is of any type meeting the requirements of
__DynamicBuffer__ (modeled after `boost::asio::streambuf`).
Beast comes with the class __basic_streambuf__, an efficient
implementation of the __DynamicBuffer__ concept which makes use of multiple
allocated octet arrays. If an incoming message is broken up into
multiple pieces, no reallocation occurs. Instead, new allocations are
appended to the sequence when existing allocations are filled. Beast
does not impose any particular memory management model on callers. The
__basic_streambuf__ provided by beast supports standard allocators through
a template argument. Use the __DynamicBuffer__ that comes with beast,
customize the allocator if you desire, or provide your own type that
meets the requirements.
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L774 Beast]]
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/message_buffer/message.hpp#L78 websocketpp]]
][
[```
template<class DynamicBuffer>
read(opcode& op, DynamicBuffer& dynabuf);
```]
[```
template <template<class> class con_msg_manager>
class message {
public:
typedef lib::shared_ptr<message> ptr;
...
std::string m_payload;
...
};
```]
]]]]
[[8. Sending Messages][
When sending a message, websocketpp requires that the payload is
packaged in a `websocketpp::message` object using `std::string` as the
storage, or it requires a copy of the caller provided buffer by
constructing a new message object. Messages are placed onto an
outgoing queue. An asynchronous write operation runs in the background
to clear the queue. No user facing handler can be registered to be
notified when messages or frames have completed sending.
Beast doesn't allocate or make copies of buffers when sending data. The
caller's buffers are sent in-place. You can use any object meeting the
requirements of
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html ConstBufferSequence],
permitting efficient scatter-gather I/O.
The [*ConstBufferSequence] interface allows callers to send data from
memory-mapped regions (not possible in websocketpp). Callers can also
use the same buffers to send data to multiple streams, for example
broadcasting common subscription data to many clients at once. For
each call to `async_write` the completion handler is called once when
the data finishes sending, in a manner identical to `boost::asio::async_write`.
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L1048 Beast]]
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L672 websocketpp]]
][
[```
template<class ConstBufferSequence>
void
write(ConstBufferSequence const& buffers);
```]
[```
lib::error_code send(std::string const & payload,
frame::opcode::value op = frame::opcode::text);
...
lib::error_code send(message_ptr msg);
```]
]]]]
[[9. Streaming Messages][
websocketpp requires that the entire message fit into memory, and that
the size is known ahead of time.
Beast allows callers to compose messages in individual frames. This is
useful when the size of the data is not known ahead of time or if it
is not desired to buffer the entire message in memory at once before
sending it. For example, sending periodic output of a database query
running on a coroutine. Or sending the contents of a file in pieces,
without bringing it all into memory.
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L1151 Beast]]
[websocketpp]
][
[```
template<class ConstBufferSequence>
void
write_frame(bool fin,
ConstBufferSequence const& buffers);
```]
[
/<not available>/
]
]]]]
[[10. Flow Control][
The websocketpp read implementation continuously reads asynchronously
from the network and buffers message data. To prevent unbounded growth
and leverage TCP/IP's flow control mechanism, callers can periodically
turn this 'read pump' off and back on.
In contrast a `beast::websocket::stream` does not independently begin
background activity, nor does it buffer messages. It receives data only
when there is a call to an asynchronous initiation function (for
example `beast::websocket::stream::async_read`) with an associated handler.
Applications do not need to implement explicit logic to regulate the
flow of data. Instead, they follow the traditional model of issuing a
read, receiving a read completion, processing the message, then
issuing a new read and repeating the process.
[table
[
[Beast]
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L728 websocketpp]]
][
[
/<implicit>/
]
[```
lib::error_code pause_reading();
lib::error_code resume_reading();
```]
]]]]
[[11. Connection Establishment][
websocketpp offers the `endpoint` class which can handle binding and
listening to a port, and spawning connection objects.
Beast does not reinvent the wheel here, callers use the interfaces
already in `boost::asio` for receiving incoming connections resolving
host names, or establishing outgoing connections. After the socket (or
`boost::asio::ssl::stream`) is connected, the `beast::websocket::stream`
is constructed around it and the WebSocket handshake can be performed.
Beast users are free to implement their own "connection manager", but
there is no requirement to do so.
[table
[
[[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/async_connect.html Beast],
[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/basic_socket_acceptor/async_accept.html also]]
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/asio/endpoint.hpp#L52 websocketpp]]
][
[```
#include <boost/asio.hpp>
```]
[```
template <typename config>
class endpoint : public config::socket_type;
```]
]]]]
[[12. WebSocket Handshaking][
Callers invoke `beast::websocket::accept` to perform the WebSocket
handshake, but there is no requirement to use this function. Advanced
users can perform the WebSocket handshake themselves. Beast WebSocket
provides the tools for composing the request or response, and the
Beast HTTP interface provides the container and algorithms for sending
and receiving HTTP/1 messages including the necessary HTTP Upgrade
request for establishing the WebSocket session.
Beast allows the caller to pass the incoming HTTP Upgrade request for
the cases where the caller has already received an HTTP message.
This flexibility permits novel and robust implementations. For example,
a listening socket that can handshake in multiple protocols on the
same port.
Sometimes callers want to read some bytes on the socket before reading
the WebSocket HTTP Upgrade request. Beast allows these already-received
bytes to be supplied to an overload of the accepting function to permit
sophisticated features. For example, a listening socket that can
accept both regular WebSocket and Secure WebSocket (SSL) connections.
[table
[
[[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L501 Beast],
[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L401 also]]
[websocketpp]
][
[```
template<class ConstBufferSequence>
void
accept(ConstBufferSequence const& buffers);
template<class Body, class Headers>
void
accept(http::request_v1<Body, Headers> const& request);
```]
[
/<not available>/
]
]]]]
]
]]
2017-07-20 08:01:46 -07:00
]
[endsect]
[endsect]