Documentation work

This commit is contained in:
Vinnie Falco
2017-06-03 18:40:28 -07:00
parent 8a22350119
commit 5c46845208
29 changed files with 1650 additions and 801 deletions

View File

@@ -49,6 +49,8 @@
[def __AsyncStream__ [link beast.ref.streams.AsyncStream [*AsyncStream]]]
[def __Body__ [link beast.ref.Body [*Body]]]
[def __BodyReader__ [link beast.ref.BodyReader [*BodyReader]]]
[def __BodyWriter__ [link beast.ref.BodyWriter [*BodyWriter]]]
[def __DynamicBuffer__ [link beast.ref.DynamicBuffer [*DynamicBuffer]]]
[def __FieldSequence__ [link beast.ref.FieldSequence [*FieldSequence]]]
[def __Stream__ [link beast.ref.streams [*Stream]]]
@@ -100,8 +102,7 @@ asynchronous model of __Asio__.
[[
[link beast.design Design]
][
Design rationale, answers to questions,
library comparisons, and Boost Formal Review FAQ.
Rationale, comparison to other libraries, and FAQ.
]]
[[
[link beast.ref Reference]
@@ -116,9 +117,9 @@ asynchronous model of __Asio__.
]
[include 1_overview.qbk]
[include 2_core.qbk]
[include 2_0_core.qbk]
[include 3_0_http.qbk]
[include 4_websocket.qbk]
[include 4_0_websocket.qbk]
[include 5_examples.qbk]
[include 6_0_design.qbk]

View File

@@ -122,7 +122,7 @@ and
[@https://www.ripple.com Ripple Labs]
for supporting its early development. Also thanks to
Agustín Bergé,
Glen Fernandes,
[@http://www.boost.org/users/people/glen_fernandes.html Glen Fernandes],
and
Peter Dimov
for helping me considerably on Slack.

53
doc/2_0_core.qbk Normal file
View File

@@ -0,0 +1,53 @@
[/
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:core Core Concepts]
[block '''
<informaltable frame="all"><tgroup cols="1"><colspec colname="a"/><tbody><row><entry valign="top"><simplelist>
<member><link linkend="beast.core.asio">Working With Asio</link></member>
<member><link linkend="beast.core.streams">Stream Concepts</link></member>
<member><link linkend="beast.core.buffers">Buffer Concepts</link></member>
<member><link linkend="beast.core.async">Asynchronous Utilities</link></member>
<member><link linkend="beast.core.tutorial">Writing Composed Operations</link></member>
</simplelist></entry></row></tbody></tgroup></informaltable>
''']
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.
[important
This documentation assumes familiarity with __Asio__. Sample
code and identifiers used throughout are written as if the
following declarations are in effect:
```
#include <beast/core.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <thread>
using namespace beast;
boost::asio::io_service ios;
boost::asio::io_service work{ios};
std::thread t{[&](){ ios.run(); }};
error_code ec;
```
]
[include 2_1_asio.qbk]
[include 2_2_streams.qbk]
[include 2_3_buffers.qbk]
[include 2_4_async.qbk]
[include 2_5_tutorial.qbk]
[endsect]

61
doc/2_1_asio.qbk Normal file
View File

@@ -0,0 +1,61 @@
[/
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)
]
[heading:asio Working With Asio]
Beast does not manage sockets, make outgoing connections, accept incoming
connections, or handle any aspect of connection management. In order to
invoke library algorithms it is necessary to first have a connected socket,
SSL stream, or other object which meets the required stream concepts. 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.
```
Throughout this documentation identifiers with the following names have
special meaning:
[table Global Variables
[[Name][Description]]
[[
[@http://www.boost.org/doc/html/boost_asio/reference/io_service.html [*`ios`]]
][
A variable of type
[@http://www.boost.org/doc/html/boost_asio/reference/io_service.html `boost::asio::io_service`]
which is running on one separate thread, and upon which a
[@http://www.boost.org/doc/html/boost_asio/reference/io_service__work.html `boost::asio::io_service::work`]
object has been constructed.
]]
[[
[@http://www.boost.org/doc/html/boost_asio/reference/ip__tcp/socket.html [*`sock`]]
][
A variable of type
[@http://www.boost.org/doc/html/boost_asio/reference/ip__tcp/socket.html `boost::asio::ip::tcp::socket`]
which has already been connected to a remote host.
]]
[[
[@http://www.boost.org/doc/html/boost_asio/reference/ssl__stream.html [*`ssl_sock`]]
][
A variable of type
[@http://www.boost.org/doc/html/boost_asio/reference/ssl__stream.html `boost::asio::ssl::stream<boost::asio::ip::tcp::socket>`]
which is already connected and has handshaked with a remote host.
]]
[[
[link beast.ref.websocket__stream [*`ws`]]
][
A variable of type
[link beast.ref.websocket__stream `websocket::stream<boost::asio::ip::tcp::socket>`]
which is already connected with a remote host.
]]
]

129
doc/2_2_streams.qbk Normal file
View File

@@ -0,0 +1,129 @@
[/
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)
]
[heading:streams Stream Concepts]
A __Stream__ is communication channel where data expressed as octet
buffers is transferred sequentially. 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:
[table Stream Concepts
[[Concept][Description]]
[
[__SyncReadStream__]
[
Supports buffer-oriented blocking reads.
]
][
[__SyncWriteStream__]
[
Supports buffer-oriented blocking writes.
]
][
[__SyncStream__]
[
A stream supporting buffer-oriented blocking reads and writes.
]
][
[__AsyncReadStream__]
[
Supports buffer-oriented asynchronous reads.
]
][
[__AsyncWriteStream__]
[
Supports buffer-oriented asynchronous writes.
]
][
[__AsyncStream__]
[
A stream supporting buffer-oriented asynchronous reads and writes.
]
]
]
These template metafunctions check whether a given type meets the
requirements for the various stream concepts, and some additional
useful utilities. The library uses these type checks internally
and also provides them as public interfaces so users may use the
same techniques to augment their own code. The use of these type
checks helps provide more concise errors during compilation:
[table Stream Type Checks
[[Name][Description]]
[[
[link beast.ref.get_lowest_layer `get_lowest_layer`]
][
Returns `T::lowest_layer_type` if it exists, else returns `T`.
]]
[[
[link beast.ref.has_get_io_service `has_get_io_service`]
][
Determine if the `get_io_service` member function is present,
and returns an __io_service__.
]]
[[
[link beast.ref.is_async_read_stream `is_async_read_stream`]
][
Determine if a type meets the requirements of __AsyncReadStream__.
]]
[[
[link beast.ref.is_async_stream `is_async_stream`]
][
Determine if a type meets the requirements of both __AsyncReadStream__
and __AsyncWriteStream__.
]]
[[
[link beast.ref.is_async_write_stream `is_async_write_stream`]
][
Determine if a type meets the requirements of __AsyncWriteStream__.
]]
[[
[link beast.ref.is_completion_handler `is_completion_handler`]
][
Determine if a type meets the requirements of __CompletionHandler__,
and is callable with a specified signature.
]]
[[
[link beast.ref.is_sync_read_stream `is_sync_read_stream`]
][
Determine if a type meets the requirements of __SyncReadStream__.
]]
[[
[link beast.ref.is_sync_stream `is_sync_stream`]
][
Determine if a type meets the requirements of both __SyncReadStream__
and __SyncWriteStream__.
]]
[[
[link beast.ref.is_sync_write_stream `is_sync_write_stream`]
][
Determine if a type meets the requirements of __SyncWriteStream__.
]]
]
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<class SyncWriteStream>
void write_string(SyncWriteStream& stream, string_view s)
{
static_assert(is_sync_write_stream<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
boost::asio::write(stream, boost::asio::const_buffers_1(s.data(), s.size()));
}
```

147
doc/2_3_buffers.qbk Normal file
View File

@@ -0,0 +1,147 @@
[/
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)
]
[heading:buffers Buffer Concepts]
__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`
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:
[table Buffer Type Checks
[[Name][Description]]
[[
[link beast.ref.is_const_buffer_sequence `is_const_buffer_sequence`]
][
Determine if a type meets the requirements of __ConstBufferSequence__.
]]
[[
[link beast.ref.is_mutable_buffer_sequence `is_mutable_buffer_sequence`]
][
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:
[table Dynamic Buffer Implementations
[[Name][Description]]
[[
[link beast.ref.buffers_adapter `buffers_adapter`]
][
This wrapper adapts any __MutableBufferSequence__ into a
__DynamicBuffer__ with an upper limit on the total size of the input and
output areas equal to the size of the underlying mutable buffer sequence.
The implementation does not perform heap allocations.
]]
[[
[link beast.ref.flat_buffer `flat_buffer`]
[link beast.ref.basic_flat_buffer `basic_flat_buffer`]
][
Guarantees that input and output areas are buffer sequences with
length one. Upon construction an optional upper limit to the total
size of the input and output areas may be set. The basic container
supports the standard allocator model.
]]
[[
[link beast.ref.multi_buffer `multi_buffer`]
[link beast.ref.basic_multi_buffer `basic_multi_buffer`]
][
Uses a sequence of one or more character arrays of varying sizes.
Additional character array objects are appended to the sequence to
accommodate changes in the size of the character sequence. The basic
container supports the standard allocator model.
]]
[[
[link beast.ref.static_buffer `static_buffer`]
[link beast.ref.static_buffer `static_buffer_n`]
][
Provides the facilities of a dynamic buffer, subject to an upper
limit placed on the total size of the input and output areas defined
by a constexpr template parameter. The storage for the sequences are
kept in the class; the implementation does not perform heap allocations.
]]
]
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. The lifetime of the transformed buffers is retained by the
caller; ownership is not transferred.
[table Buffer Algorithms
[[Name][Description]]
[[
[link beast.ref.buffer_cat `buffer_cat`]
][
This functions returns a new buffer sequence which, when iterated,
traverses the sequence which would be formed if all of the input buffer
sequences were concatenated. With this routine, multiple calls to a
stream's `write_some` function may be combined into one, eliminating
expensive system calls.
]]
[[
[link beast.ref.buffer_cat_view `buffer_cat_view`]
][
This class represents the buffer sequence formed by concatenating
two or more buffer sequences. This is type of object returned by
[link beast.ref.buffer_cat `buffer_cat`].
]]
[[
[link beast.ref.buffer_prefix `buffer_prefix`]
][
This function returns a new buffer or buffer sequence which represents
a prefix of the original buffers.
]]
[[
[link beast.ref.buffer_prefix_view `buffer_prefix_view`]
][
This class represents the buffer sequence formed from a prefix of
an existing buffer sequence. This is the type of buffer returned by
[link beast.ref.buffer_prefix `buffer_prefix`].
]]
[[
[link beast.ref.consuming_buffers `consuming_buffers`]
][
This class wraps the underlying memory of an existing buffer sequence
and presents a suffix of the original sequence. The length of the suffix
may be progressively shortened. This lets callers work with sequential
increments of a buffer sequence.
]]
]
These two functions facilitate buffer interoperability with standard
output streams.
[table Buffer Output Streams
[[Name][Description]]
[[
[link beast.ref.buffers `buffers`]
][
This function wraps a __ConstBufferSequence__ so it may be
used with `operator<<` and `std::ostream`.
]]
[[
[link beast.ref.ostream `ostream`]
][
This function returns a `std::ostream` which wraps a dynamic buffer.
Characters sent to the stream using `operator<<` is stored in the
dynamic buffer.
]]
]

100
doc/2_4_async.qbk Normal file
View File

@@ -0,0 +1,100 @@
[/
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)
]
[heading:async Asynchronous Utilities]
Asynchronous operations are started by calling a free function or member
function known as an ['asynchronous initiation function]. The initiation
function accepts parameters specific to the operation as well as a "completion
token." This token is either a completion handler, or another type allowing for
customization of how the result of the asynchronous operation is conveyed to
callers. __Asio__ allows the special completion tokens __use_future__ and
objects of type __yield_context__ to allow callers to specify the use of futures
and coroutines respectively. This system, where the return value and method of
indicating completion may be customize at the call site of the asynchronous
initiation function, is known as the ['Extensible Asynchronous Model] described
in __N3747__, and built-in to __N4588__.
[note
A full explanation of completion handlers, the Extensible Asynchronous
Model and how these asynchronous interfaces are used is beyond the
scope of this document. Interested readers should consult the
__Asio__ documentation.
]
Since the interfaces provided here are low level, authors of libraries
may wish to create higher level interfaces using the primitives found
in this library. Non-trivial applications will want to provide their own
asychronous 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:
[table Asynchronous Helpers
[[Name][Description]]
[[
[link beast.ref.async_completion `async_completion`]
][
This class aggregates the completion handler customization point and
the asynchronous initiation function return value customization point
into a single object which exposes the appropriate output types for the
given input types, and also contains boilerplate that is necessary to
implement an initiation function using the Extensible Model.
]]
[[
[link beast.ref.async_return_type `async_return_type`]
][
This template alias determines the return value of an asynchronous
initiation function given the completion token and signature. It is used
by asynchronous initiation functions to meet the requirements of the
Extensible Asynchronous Model.
]]
[[
[link beast.ref.bind_handler `bind_handler`]
][
This function returns a new, nullary completion handler which when
invoked with no arguments invokes the original completion handler with a
list of bound arguments. The invocation is made from the same implicit
or explicit strand as that which would be used to invoke the original
handler. This is accomplished by using the correct overload of
`asio_handler_invoke` associated with the original completion handler.
]]
[[
[link beast.ref.handler_alloc `handler_alloc`]
][
This class meets the requirements of [*Allocator], and uses any custom
memory allocation and deallocation hooks associated with a given handler.
It is useful for when a composed operation requires temporary dynamic
allocations to achieve its result. Memory allocated using this allocator
must be freed before the final completion handler is invoked.
]]
[[
[link beast.ref.handler_ptr `handler_ptr`]
][
This is a smart pointer container used to manage the internal state of a
composed operation. It is useful when the state is non trivial. For example
when the state has non-copyable or expensive to copy types. The container
takes ownership of the final completion handler, and provides boilerplate
to invoke the final handler in a way that also deletes the internal state.
The internal state is allocated using the final completion handler's
associated allocator, benefiting from all handler memory management
optimizations transparently.
]]
[[
[link beast.ref.handler_type `handler_type`]
][
This template alias converts a completion token and signature to the
correct completion handler type. It is used in the implementation of
asynchronous initiation functions to meet the requirements of the
Extensible Asynchronous Model.
]]
]

268
doc/2_5_tutorial.qbk Normal file
View File

@@ -0,0 +1,268 @@
[/
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:tutorial Writing Composed Operations]
To illustrate the usage of the asynchronous helpers in the core section of
this library, we will develop a simple asynchronous composed operation called
[*echo]. This operation will read up to the first newline on a stream, and
then write the same line including the newline back on the stream.
First we define the input parameters and results, then declare our initiation
function. For our echo operation the only inputs are the stream and the
completion token. The output is the error code which is usually included in
all completion handler signatures.
```
#include <beast/core.hpp>
#include <boost/asio.hpp>
#include <cstddef>
#include <memory>
#include <utility>
// Read a line and echo it back
//
template<class AsyncStream, class CompletionToken>
beast::async_return_type<CompletionToken, void(beast::error_code)>
async_echo(AsyncStream& stream, CompletionToken&& token)
```
Now that we have a declaration, we will define the body of the function. We
want to achieve the following goals: perform static type checking on the input
parameters, set up the return value as per __N3747__, and launch the composed
operation by constructing the object and invoking it.
```
template<class AsyncStream, class Handler>
class echo_op; // This is our composed operation implementation
// Read a line and echo it back
//
template<class AsyncStream, class CompletionToken>
beast::async_return_type<CompletionToken, void(beast::error_code)>
async_echo(AsyncStream& stream, CompletionToken&& token)
{
// Make sure stream meets the requirements. We use static_assert
// to cause a friendly message instead of an error novel.
//
static_assert(beast::is_async_stream<AsyncStream>::value,
"AsyncStream requirements not met");
// This helper manages some of the handler's lifetime and
// uses the result and handler specializations associated with
// the completion token to help customize the return value.
//
beast::async_completion<CompletionToken, void(beast::error_code)> init{token};
// Create the composed operation and launch it. This is a constructor
// call followed by invocation of operator(). We use handler_type
// to convert the completion token into the correct handler type,
// allowing user defined specializations of the async result template
// to take effect.
//
echo_op<AsyncStream, beast::handler_type<CompletionToken, void(beast::error_code)>>{
stream, init.completion_handler}(beast::error_code{}, 0);
// This hook lets the caller see a return value when appropriate.
// For example this might return std::future<error_code> if
// CompletionToken is boost::asio::use_future, or this might
// return an error code if CompletionToken specifies a coroutine.
//
return init.result.get();
}
```
The initiation function contains a few relatively simple parts. There is the
customization of the return value type, static type checking, building the
return value type using the helper, and creating and launching the composed
operation object. The [*`echo_op`] object does most of the work here, and has
a somewhat non-trivial structure. This structure is necessary to meet the
stringent requirements of composed operations (described in more detail in
the __Asio__ documentation). We will touch on these requirements without
explaining them in depth.
First we will create boilerplate which is present in all composed operations
written in this style:
```
// This composed operation reads a line of input and echoes it back.
//
template<class AsyncStream, class Handler>
class echo_op
{
// This holds all of the state information required by the operation.
struct state
{
// The stream to read and write to
AsyncStream& stream;
// Indicates what step in the operation's state machine
// to perform next, starting from zero.
int step = 0;
// The buffer used to hold the input and output data.
// Note that we use a custom allocator for performance,
// this allows the implementation of the io_service to
// make efficient re-use of memory allocated by composed
// operations during continuations.
//
boost::asio::basic_streambuf<beast::handler_alloc<char, Handler>> buffer;
// handler_ptr requires that the first parameter to the
// contained object constructor is a reference to the
// managed final completion handler.
//
explicit state(Handler& handler, AsyncStream& stream_)
: stream(stream_)
, buffer((std::numeric_limits<std::size_t>::max)(),
beast::handler_alloc<char, Handler>{handler})
{
}
};
// This smart pointer container allocates our state using the
// memory allocation hooks associated with the final completion
// handler, manages the lifetime of that handler for us, and
// enforces the destroy-before-invocation requirement on memory
// allocated using the hooks.
//
beast::handler_ptr<state, Handler> p_;
public:
// Boost.Asio requires that handlers are CopyConstructible.
// In some cases, it takes advantage of handlers that are
// MoveConstructible. This operation supports both.
//
echo_op(echo_op&&) = default;
echo_op(echo_op const&) = default;
// The constructor simply creates our state variables in
// the smart pointer container.
//
template<class DeducedHandler, class... Args>
echo_op(AsyncStream& stream, DeducedHandler&& handler)
: p_(std::forward<DeducedHandler>(handler), stream)
{
}
// Determines if the next asynchronous operation represents a
// continuation of the asynchronous flow of control associated
// with the final handler. If we are past step one, it means
// we have performed an asynchronous operation therefore any
// subsequent operation would represent a continuation.
// Otherwise, we propagate the handler's associated value of
// is_continuation. Getting this right means the implementation
// may schedule the invokation of the invoked functions more
// efficiently.
//
friend bool asio_handler_is_continuation(echo_op* op)
{
// This next call is structured to permit argument
// dependent lookup to take effect.
using boost::asio::asio_handler_is_continuation;
// Always use std::addressof to pass the pointer to the handler,
// otherwise an unwanted overload of operator& may be called instead.
return op->p_->step > 1 ||
asio_handler_is_continuation(std::addressof(op->p_.handler()));
}
// Handler hook forwarding. These free functions invoke the hooks
// associated with the final completion handler. In effect, they
// make the Asio implementation treat our composed operation the
// same way it would treat the final completion handler for the
// purpose of memory allocation and invocation.
//
// Our implementation just passes through the call to the hook
// associated with the final handler.
friend void* asio_handler_allocate(std::size_t size, echo_op* op)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(size, std::addressof(op->p_.handler()));
}
friend void asio_handler_deallocate(void* p, std::size_t size, echo_op* op)
{
using boost::asio::asio_handler_deallocate;
return asio_handler_deallocate(p, size, std::addressof(op->p_.handler()));
}
template<class Function>
friend void asio_handler_invoke(Function&& f, echo_op* op)
{
using boost::asio::asio_handler_invoke;
return asio_handler_invoke(f, std::addressof(op->p_.handler()));
}
// Our main entry point. This will get called as our
// intermediate operations complete. Definition below.
//
void operator()(beast::error_code ec, std::size_t bytes_transferred);
};
```
We have the common boilerplate for a composed operation and now we just need
to implement the function call operator. Our strategy is to make our composed
object meet the requirements of a completion handler by being copyable (also
movable), and by providing the function call operator with the correct
signature. Rather than using `std::bind` or `boost::bind`, which destroys
the type information and therefore breaks the allocation and invocation
hooks, we will simply pass `std::move(*this)` as the completion handler
parameter for any operations that we initiate. For the move to work correctly,
care must be taken to ensure that no access to data members are made after the
move takes place. Here is the implementation of the function call operator for
this echo operation:
```
// We are callable with the signature void(error_code, bytes_transferred),
// allowing `*this` to be used as both a ReadHandler and a WriteHandler.
//
template<class AsyncStream, class Handler>
void echo_op<AsyncStream, Handler>::
operator()(beast::error_code ec, std::size_t bytes_transferred)
{
// Store a reference to our state. The address of the state won't
// change, and this solves the problem where dereferencing the
// data member is undefined after a move.
auto& p = *p_;
// Now perform the next step in the state machine
switch(ec ? 2 : p.step)
{
// initial entry
case 0:
// read up to the first newline
p.step = 1;
return boost::asio::async_read_until(p.stream, p.buffer, "\n", std::move(*this));
case 1:
// write everything back
p.step = 2;
// async_read_until could have read past the newline,
// use buffer_prefix to make sure we only send one line
return boost::asio::async_write(p.stream,
beast::buffer_prefix(bytes_transferred, p.buffer.data()), std::move(*this));
case 2:
p.buffer.consume(bytes_transferred);
break;
}
// Invoke the final handler. If we wanted to pass any arguments
// which come from our state, they would have to be moved to the
// stack first, since the `handler_ptr` guarantees that the state
// is destroyed before the handler is invoked.
//
p_.invoke(ec);
return;
}
```
A complete, runnable version of this example may be found in the examples
directory.
[endsect]

View File

@@ -109,11 +109,11 @@ 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<class Body, class Fields>
void send(response<Body, Fields> const& res)
{
write(sock, res);
}
template<class Body, class Fields>
void send(response<Body, Fields> const& res)
{
write(sock, res);
}
```
If a response has no declared content length, and no chunked transfer
@@ -124,37 +124,37 @@ 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<string_body> res;
res.version = 11;
res.status = 200;
res.reason("OK");
res.fields.insert("Server", "Beast");
res.body = "Hello, world!";
void send()
{
response<string_body> res;
res.version = 11;
res.status = 200;
res.reason("OK");
res.fields.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);
}
error_code ec;
write(sock, res, ec);
if(ec == boost::asio::error::eof)
sock.close();
else
BOOST_ASSERT(ec);
}
```
An asynchronous version is also available:
```
template<class Body, class Fields>
void send_async(response<Body, Fields> const& res)
{
async_write(sock, res,
[&](error_code)
{
if(ec)
std::cerr << ec.message() << std::endl;
});
}
template<class Body, class Fields>
void send_async(response<Body, Fields> const& res)
{
async_write(sock, res,
[&](error_code)
{
if(ec)
std::cerr << ec.message() << std::endl;
});
}
```
[endsect]

View File

@@ -8,7 +8,7 @@
[section:custom_body Custom Body Types]
User-defined types are possible for the message body, where the type meets the
[link beast.ref.Body [*`Body`]] requirements. This simplified class declaration
__Body__ requirements. This simplified class declaration
shows the customization points available to user-defined body types:
[$images/body.png [width 525px] [height 190px]]
@@ -21,29 +21,64 @@ The meaning of the nested types is as follows
[`value_type`]
[
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.
[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.
]
][
[`reader`]
[
An optional nested type meeting the requirements of
[link beast.ref.BodyReader [*BodyReader]]. If present, this defines
the algorithm used to obtain buffers representing a body of this type.
An optional nested type meeting the requirements of __BodyReader__.
]
][
[`writer`]
[
An optional nested type meeting the requirements of
[link beast.ref.BodyWriter [*BodyWriter]]. If present, this defines the
algorithm used to transfer parsed octets into buffers representing the
body.
An optional nested type meeting the requirements of __BodyWriter__.
]
]
]
The examples included with this library provide a [*Body] implementation that
serializing message bodies that come from a file.
[heading Value Type]
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 parseable respectively.
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.
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,
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 Reader]
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__.
[heading Writer]
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__.
[endsect]

50
doc/4_0_websocket.qbk Normal file
View File

@@ -0,0 +1,50 @@
[/
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:websocket Using WebSocket]
[block '''
<informaltable frame="all"><tgroup cols="1"><colspec colname="a"/><tbody><row><entry valign="top"><simplelist>
<member><link linkend="beast.websocket.streams">Creating Streams</link></member>
<member><link linkend="beast.websocket.connect">Establishing Connections</link></member>
<member><link linkend="beast.websocket.client">Handshaking (Clients)</link></member>
<member><link linkend="beast.websocket.server">Handshaking (Servers)</link></member>
<member><link linkend="beast.websocket.messages">Send and Receive Messages</link></member>
<member><link linkend="beast.websocket.control">Control Frames</link></member>
<member><link linkend="beast.websocket.notes">Additional Notes</link></member>
</simplelist></entry></row></tbody></tgroup></informaltable>
''']
The WebSocket Protocol enables two-way communication between a client
running untrusted code in a controlled environment to a remote host that has
opted-in to communications from that code. The protocol consists of an opening
handshake followed by basic message framing, layered over TCP. The goal of
this technology is to provide a mechanism for browser-based applications
needing two-way communication with servers without relying on opening multiple
HTTP connections.
Beast provides developers with a robust WebSocket implementation built on
Boost.Asio with a consistent asynchronous model using a modern C++ approach.
[note
The WebSocket documentation assumes familiarity the WebSocket protocol
specification described in __rfc6455__. Code appearing in these
sections is written as if the following declarations are in effect:
```
#include <beast/websocket.hpp>
```
]
[include 4_1_streams.qbk]
[include 4_2_connect.qbk]
[include 4_3_client.qbk]
[include 4_4_server.qbk]
[include 4_5_messages.qbk]
[include 4_6_control.qbk]
[include 4_7_notes.qbk]
[endsect]

78
doc/4_1_streams.qbk Normal file
View File

@@ -0,0 +1,78 @@
[/
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:streams Creating Streams]
The interface to the WebSocket implementation is a single template class
[link beast.ref.websocket__stream `websocket::stream`]
which wraps an existing network transport object or other type of
octet oriented stream. The wrapped object is called the "next layer"
and must meet the requirements of __SyncStream__ if synchronous
operations are performed, __AsyncStream__ if asynchronous operations
are performed, or both. Any arguments supplied during construction of
the stream wrapper are passed to next layer's constructor.
Here we declare a websocket stream over a TCP/IP socket with ownership
of the socket. The `io_service` argument is forwarded to the wrapped
socket's constructor:
```
boost::asio::io_service ios;
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
```
[heading Using SSL]
To use WebSockets over SSL, use an instance of the `boost::asio::ssl::stream`
class template as the template type for the stream. The required `io_service`
and `ssl::context` arguments are forwarded to the wrapped stream's constructor:
```
#include <beast/websocket/ssl.hpp>
#include <boost/asio/ssl.hpp>
boost::asio::io_service ios;
boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ws{ios, ctx};
```
[note
Code which declares stream objects using Asio SSL types must
toinclude the file `<beast/websocket/ssl.hpp>`.
]
[heading Non-owning References]
If a socket type supports move construction, a websocket stream may be
constructed around the already existing socket by invoke the move
constructor signature:
```
...
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{std::move(sock)};
```
Or, the wrapper can be constructed with a non-owning reference. In
this case, the caller is responsible for managing the lifetime of the
underlying socket being wrapped:
```
...
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
```
Once the WebSocket stream wrapper is created, the wrapped object may be
accessed by calling [link beast.ref.websocket__stream.next_layer.overload1 `stream::next_layer`]:
```
boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws{ios, ctx};
...
ws.next_layer().shutdown(); // ssl::stream shutdown
```
[warning
Initiating operations on the next layer while websocket
operations are being performed may result in undefined behavior.
]
[endsect]

54
doc/4_2_connect.qbk Normal file
View File

@@ -0,0 +1,54 @@
[/
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:connect Establishing Connections]
Connections are established by invoking functions directly on the next layer
object. For example, to make an outgoing connection using a standard TCP/IP
socket:
```
std::string const host = "mywebapp.com";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r{ios};
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
boost::asio::connect(ws.next_layer(),
r.resolve(boost::asio::ip::tcp::resolver::query{host, "ws"}));
```
Similarly, to accept an incoming connection using a standard TCP/IP
socket, pass the next layer object to the acceptor:
```
void do_accept(boost::asio::ip::tcp::acceptor& acceptor)
{
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{acceptor.get_io_service()};
acceptor.accept(ws.next_layer());
}
```
When using SSL, which itself wraps a next layer object that is usually a
TCP/IP socket, multiple calls to retrieve the next layer may be required.
In this example, the websocket stream wraps the SSL stream which wraps
the TCP/IP socket:
```
beast::websocket::stream<boost::asio::ssl::stream<
boost::asio::ip::tcp::socket>> ws{ios, ctx};
// connect the underlying TCP/IP socket
ws.next_layer().next_layer().connect(ep);
// perform SSL handshake
ws.next_layer().handshake(boost::asio::ssl::stream_base::client);
// perform WebSocket handshake
ws.handshake("localhost", "/");
```
[note
Examples use synchronous interfaces for clarity of exposition.
]
[endsect]

109
doc/4_3_client.qbk Normal file
View File

@@ -0,0 +1,109 @@
[/
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:client Handshaking (Clients)]
A WebSocket session begins when a client sends the HTTP
[@https://tools.ietf.org/html/rfc7230#section-6.7 Upgrade]
request for
[@https://tools.ietf.org/html/rfc6455#section-1.3 websocket],
and the server sends an appropriate HTTP response indicating that
the request was accepted and that the connection has been upgraded.
The HTTP Upgrade request must include the
[@https://tools.ietf.org/html/rfc7230#section-5.4 Host]
field, and the
[@https://tools.ietf.org/html/rfc7230#section-5.3 target]
of the resource to request. The stream member functions
[link beast.ref.websocket__stream.handshake.overload1 `handshake`] and
[link beast.ref.websocket__stream.async_handshake.overload1 `async_handshake`]
are used to send the request with the required host and target strings.
```
...
ws.set_option(websocket::keep_alive(true));
ws.handshake("localhost", "/");
```
The implementation will create and send an HTTP request that typically
looks like this:
[table WebSocket Upgrade HTTP Request
[[Serialized Octets][Description]]
[[
```
GET / HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Key: 2pGeTR0DsE4dfZs2pH+8MA==
Sec-WebSocket-Version: 13
User-Agent: Beast
```
][
The host and target parameters become part of the Host field
and request-target in the resulting HTTP request. The key is
generated by the implementation. Callers may add fields or
modify fields by providing a ['decorator], described below.
]]]
[heading Decorators]
If the caller wishes to add or modify fields, the member functions
[link beast.ref.websocket__stream.handshake_ex `handshake_ex`] and
[link beast.ref.websocket__stream.async_handshake_ex `async_handshake_ex`]
are provided which allow an additional function object, called a
['decorator], to be passed. The decorator is invoked to modify
the HTTP Upgrade request as needed. This example sets a subprotocol
on the request:
```
void decorate(websocket::request_type& req)
{
req.fields.insert("Sec-WebSocket-Protocol", "xmpp;ws-chat");
}
...
ws.handshake_ex("localhost", "/", &decorate);
```
The HTTP Upgrade request produced by the previous call will look thusly:
[table Decorated WebSocket Upgrade HTTP Request
[[Serialized Octets][Description]]
[[
```
GET / HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Key: 2pGeTR0DsE4dfZs2pH+8MA==
Sec-WebSocket-Version: 13
Sec-WebSocket-Protocol: xmpp;ws-chat
User-Agent: Beast
```
][
Undefined behavior results if the decorator modifies the fields
specific to perform the WebSocket Upgrade , such as the Upgrade
and Connection fields.
]]]
[heading Filtering]
When a client receives an HTTP Upgrade response from the server indicating
a successful upgrade, the caller may wish to perform additional validation
on the received HTTP response message. For example, to check that the
response to a basic authentication challenge is valid. To achieve this,
overloads of the handshake member function allow the caller to store the
received HTTP message in an output reference argument as
[link beast.ref.websocket__response_type `response_type`]
as follows:
```
websocket::response_type res;
ws.handshake(res, "localhost", "/");
if(! res.fields.exists("Sec-WebSocket-Protocol"))
throw std::invalid_argument("missing subprotocols");
```
[endsect]

144
doc/4_4_server.qbk Normal file
View File

@@ -0,0 +1,144 @@
[/
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:server Handshaking (Servers)]
A
[link beast.ref.websocket__stream `stream`]
automatically handles receiving and processing the HTTP response to the
handshake request. The call to handshake is successful if a HTTP response
is received with the 101 "Switching Protocols" status code. On failure,
an error is returned or an exception is thrown. Depending on the keep alive
setting, the connection may remain open for a subsequent handshake attempt.
Performing a handshake for an incoming websocket upgrade request operates
similarly. If the handshake fails, an error is returned or exception thrown:
```
...
ws.accept();
```
Successful WebSocket Upgrade responses generated by the implementation will
typically look like this:
[table Decorated WebSocket Upgrade HTTP Request
[[Serialized Octets][Description]]
[[
```
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Server: Beast/40
```
][
The Sec-WebSocket-Accept field value is generated from the
request in a fashion specified by the WebSocket protocol.
]]]
[heading Decorators]
If the caller wishes to add or modify fields, the member functions
[link beast.ref.websocket__stream.accept_ex `accept_ex`] and
[link beast.ref.websocket__stream.async_accept_ex `async_accept_ex`]
are provided which allow an additional function object, called a
['decorator], to be passed. The decorator is invoked to modify
the HTTP Upgrade request as needed. This example sets the Server
field on the response:
```
ws.accept_ex(
[](websocket::response_type& res)
{
res.fields.insert("Server", "AcmeServer");
});
```
The HTTP Upgrade response produced by the previous call will look thusly:
[table Decorated WebSocket Upgrade HTTP Request
[[Serialized Octets][Description]]
[[
```
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Server: AcmeServer
```
][
When the Upgrade request fails, the implementation will still invoke
the decorator to modify the response. In this case, the response
object will have a status code other than 101.
Undefined behavior results when the upgrade request is successful
and the decorator modifies the fields specific to perform the
WebSocket Upgrade , such as the Upgrade and Connection fields.
]]]
[heading Passing HTTP Requests]
When implementing an HTTP server that also supports WebSocket, the
server usually reads the HTTP request from the client. To detect when
the incoming HTTP request is a WebSocket Upgrade request, the function
[link beast.ref.websocket__is_upgrade `is_upgrade`] may be used.
Once the caller determines that the HTTP request is a WebSocket Upgrade,
additional overloads of
[link beast.ref.websocket__stream.accept `accept`],
[link beast.ref.websocket__stream.accept_ex `accept_ex`],
[link beast.ref.websocket__stream.async_accept `async_accept`], and
[link beast.ref.websocket__stream.async_accept_ex `async_accept_ex`]
are provided which receive the entire HTTP request header as an object
to perform the handshake. In this example, the request is first read
in using the HTTP algorithms, and then passed to a newly constructed
stream:
```
void handle_connection(boost::asio::ip::tcp::socket& sock)
{
flat_buffer buffer;
http::request<http::string_body> req;
http::read(sock, buffer, req);
if(websocket::is_upgrade(req))
{
websocket::stream<decltype(sock)> ws{std::move(sock)};
ws.accept(req);
}
}
```
[heading Buffered Handshakes]
Sometimes a server implementation wishes to read octets on the stream
in order to route the incoming request. For example, a server may read
the first 6 octets after accepting an incoming connection to determine
if a TLS protocol is being negotiated, and choose a suitable implementation
at run-time. In the case where the server wishes to accept the incoming
request as an HTTP WebSocket Upgrade request, additional overloads of
[link beast.ref.websocket__stream.accept `accept`],
[link beast.ref.websocket__stream.accept_ex `accept_ex`],
[link beast.ref.websocket__stream.async_accept `async_accept`], and
[link beast.ref.websocket__stream.async_accept_ex `async_accept_ex`]
are provided which receive the additional buffered octects and consume
them as part of the handshake.
In this example, the server reads the initial HTTP message into the
specified dynamic buffer as an octet sequence in the buffer's output
area, and later uses those octets to attempt an HTTP WebSocket Upgrade:
```
void do_accept(boost::asio::ip::tcp::socket& sock)
{
boost::asio::streambuf sb;
boost::asio::read_until(sock, sb, "\r\n\r\n");
...
websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
ws.accept(sb.data());
...
}
```
[endsect]

77
doc/4_5_messages.qbk Normal file
View File

@@ -0,0 +1,77 @@
[/
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:messages Send and Receive Messages]
After the WebSocket handshake is accomplished, callers may send and receive
messages using the message oriented interface. This interface requires that
all of the buffers representing the message are known ahead of time:
```
template<class NextLayer>
void echo(websocket::stream<NextLayer>& ws)
{
multi_buffer b;
websocket::opcode::value op;
ws.read(op, b);
ws.set_option(websocket::message_type{op});
ws.write(b.data());
b.consume(b.size());
}
```
[important
Calls to [link beast.ref.websocket__stream.set_option `set_option`]
must be made from the same implicit or explicit strand as that used
to perform other operations.
]
[heading Frames]
Some use-cases make it impractical or impossible to buffer the entire
message ahead of time:
* Streaming multimedia to an endpoint.
* Sending a message that does not fit in memory at once.
* Providing incremental results as they become available.
For these cases, the frame oriented interface may be used. This
example reads and echoes a complete message using this interface:
```
template<class NextLayer>
void echo(websocket::stream<NextLayer>& ws)
{
multi_buffer b;
websocket::frame_info fi;
for(;;)
{
ws.read_frame(fi, b);
if(fi.fin)
break;
}
ws.set_option(websocket::message_type{fi.op});
consuming_buffers<
multi_buffer::const_buffers_type> cb{b.data()};
for(;;)
{
using boost::asio::buffer_size;
std::size_t size = std::min(buffer_size(cb));
if(size > 512)
{
ws.write_frame(false, prepare_buffers(512, cb));
cb.consume(512);
}
else
{
ws.write_frame(true, cb);
break;
}
}
}
```
[endsect]

116
doc/4_6_control.qbk Normal file
View File

@@ -0,0 +1,116 @@
[/
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:control Control Frames]
Control frames are small (less than 128 bytes) messages entirely contained
in an individual WebSocket frame. They may be sent at any time by either
peer on an established connection, and can appear in between continuation
frames for a message. There are three types of control frames: ping, pong,
and close.
A sent ping indicates a request that the sender wants to receive a pong. A
pong is a response to a ping. Pongs may be sent unsolicited, at any time.
One use for an unsolicited pong is to inform the remote peer that the
session is still active after a long period of inactivity. A close frame
indicates that the remote peer wishes to close the WebSocket connection.
The connection is considered gracefully closed when each side has sent
and received a close frame.
During read operations, Beast automatically reads and processes control
frames. Pings are replied to as soon as possible with a pong, received
ping and pongs are delivered to the ping callback. The receipt of a close
frame initiates the WebSocket close procedure, eventually resulting in the
error code [link beast.ref.websocket__error `error::closed`] being delivered
to the caller in a subsequent read operation, assuming no other error
takes place.
A consequence of this automatic behavior is that caller-initiated read
operations can cause socket writes. However, these writes will not
compete with caller-initiated write operations. For the purposes of
correctness with respect to the stream invariants, caller-initiated
read operations still only count as a read. This means that callers can
have a simultaneously active read, write, and ping operation in progress,
while the implementation also automatically handles control frames.
[heading Ping and Pong Frames]
Ping and pong messages are control frames which may be sent at any time
by either peer on an established WebSocket connection. They are sent
using the functions
[link beast.ref.websocket__stream.ping `ping`] and
[link beast.ref.websocket__stream.pong `pong`].
To be notified of ping and pong control frames, callers may register a
"ping callback" using [link beast.ref.websocket__stream.set_option `set_option`].
The object provided with this option should be callable with the following
signature:
```
void on_ping(bool is_pong, websocket::ping_data const& payload);
...
ws.set_option(ping_callback{&on_ping});
```
When a ping callback is registered, all pings and pongs received through
either synchronous read functions or asynchronous read functions will
invoke the ping callback, with the value of `is_pong` set to `true` if a
pong was received else `false` if a ping was received. The payload of
the ping or pong control frame is passed in the payload argument.
Unlike regular completion handlers used in calls to asynchronous initiation
functions, the ping callback only needs to be set once. The callback is not
reset when a ping or pong is received. The same callback is used for both
synchronous and asynchronous reads. The ping callback is passive; in order
to receive pings and pongs, a synchronous or asynchronous stream read
function must be active.
[note
When an asynchronous read function receives a ping or pong, the
ping callback is invoked in the same manner as that used to invoke
the final completion handler of the corresponding read function.
]
[heading Close Frames]
The WebSocket protocol defines a procedure and control message for initiating
a close of the session. Handling of close initiated by the remote end of the
connection is performed automatically. To manually initiate a close, use
the
[link beast.ref.websocket__stream.close `close`]
function:
```
ws.close();
```
When the remote peer initiates a close by sending a close frame, Beast
will handle it for you by causing the next read to return `error::closed`.
When this error code is delivered, it indicates to the application that
the WebSocket connection has been closed cleanly, and that the TCP/IP
connection has been closed. After initiating a close, it is necessary to
continue reading messages until receiving the error `error::closed`. This
is because the remote peer may still be sending message and control frames
before it receives and responds to the close frame.
[important
To receive the
[link beast.ref.websocket__error `error::closed`]
error, a read operation is required.
]
[heading Auto-fragment]
To ensure timely delivery of control frames, large messages can be broken up
into smaller sized frames. The automatic fragment option turns on this
feature, and the write buffer size option determines the maximum size of
the fragments:
```
...
ws.set_option(websocket::auto_fragment{true});
ws.set_option(websocket::write_buffer_size{16384});
```
[endsect]

69
doc/4_7_notes.qbk Normal file
View File

@@ -0,0 +1,69 @@
[/
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:notes Notes]
Because calls to read data may return a variable amount of bytes, the
interface to calls that read data require an object that meets the requirements
of __DynamicBuffer__. This concept is modeled on __streambuf__.
The implementation does not perform queueing or buffering of messages. If
desired, these features should be provided by callers. The impact of this
design is that library users are in full control of the allocation strategy
used to store data and the back-pressure applied on the read and write side
of the underlying TCP/IP connection.
[heading Asynchronous Operations]
Asynchronous versions are available for all functions:
```
websocket::opcode op;
ws.async_read(op, sb,
[](boost::system::error_code const& ec)
{
...
});
```
Calls to asynchronous initiation functions support the extensible asynchronous
model developed by the Boost.Asio author, allowing for traditional completion
handlers, stackful or stackless coroutines, and even futures:
```
void echo(websocket::stream<ip::tcp::socket>& ws,
boost::asio::yield_context yield)
{
ws.async_read(sb, yield);
std::future<websocket::error_code> fut =
ws.async_write, sb.data(), boost::use_future);
...
}
```
[heading The io_service]
The creation and operation of the __io_service__ associated with the
underlying stream is left to the callers, permitting any implementation
strategy including one that does not require threads for environments
where threads are unavailable. Beast WebSocket itself does not use
or require threads.
[heading Thread Safety]
Like a regular __Asio__ socket, a
[link beast.ref.websocket__stream `stream`]
is not thread safe. Callers are responsible for synchronizing operations on
the socket using an implicit or explicit strand, as per the Asio documentation.
The asynchronous interface supports one active read and one active write
simultaneously. Undefined behavior results if two or more reads or two or
more writes are attempted concurrently. Caller initiated WebSocket ping, pong,
and close operations each count as an active write.
The implementation uses composed asynchronous operations internally; a high
level read can cause both reads and writes to take place on the underlying
stream. This behavior is transparent to callers.
[endsect]

View File

@@ -1,651 +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:websocket WebSocket]
[block '''
<informaltable frame="all"><tgroup cols="1"><colspec colname="a"/><tbody><row><entry valign="top"><simplelist>
<member><link linkend="beast.websocket.creation">Creation</link></member>
<member><link linkend="beast.websocket.connections">Making connections</link></member>
<member><link linkend="beast.websocket.handshaking">Handshaking</link></member>
<member><link linkend="beast.websocket.accepting">Accepting</link></member>
<member><link linkend="beast.websocket.messages">Messages</link></member>
<member><link linkend="beast.websocket.control">Control Frames</link></member>
<member><link linkend="beast.websocket.notes">Notes</link></member>
</simplelist></entry></row></tbody></tgroup></informaltable>
''']
The WebSocket Protocol enables two-way communication between a client
running untrusted code in a controlled environment to a remote host that has
opted-in to communications from that code. The protocol consists of an opening
handshake followed by basic message framing, layered over TCP. The goal of
this technology is to provide a mechanism for browser-based applications that
need two-way communication with servers that does not rely on opening multiple
HTTP connections.
Beast provides developers with a robust WebSocket implementation built on
Boost.Asio with a consistent asynchronous model using a modern C++ approach.
The WebSocket protocol is described fully in
[@https://tools.ietf.org/html/rfc6455 rfc6455]
[note
The following documentation assumes familiarity with both
Boost.Asio and the WebSocket protocol specification described in __rfc6455__.
]
[section:creation Creation]
The interface to Beast's WebSocket implementation is a single template
class [link beast.ref.websocket__stream `websocket::stream`] which
wraps a "next layer" object. The next layer object must meet the requirements
of [link beast.ref.streams.SyncStream [*SyncStream]] if synchronous
operations are performed, or
[link beast.ref.streams.AsyncStream [*AsyncStream]] if asynchronous
operations are performed, or both. Arguments supplied during construction are
passed to next layer's constructor. Here we declare a websocket stream over
a TCP/IP socket with ownership of the socket:
```
boost::asio::io_service ios;
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
```
[heading Using SSL]
To use WebSockets over SSL, choose an SSL stream for the next layer template
argument when constructing the stream.
```
#include <beast/websocket/ssl.hpp>
#include <beast/websocket.hpp>
#include <boost/asio/ssl.hpp>
boost::asio::io_service ios;
boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ws{ios, ctx};
```
[note
When creating websocket stream objects using SSL, it is necessary
to include the file `<beast/websocket/ssl.hpp>`.
]
[heading Non-owning references]
For servers that can handshake in multiple protocols, it may be desired
to wrap an object that already exists. This socket can be moved in:
```
boost::asio::ip::tcp::socket sock;
...
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{std::move(sock)};
```
Or, the wrapper can be constructed with a non-owning reference. In
this case, the caller is responsible for managing the lifetime of the
underlying socket being wrapped:
```
boost::asio::ip::tcp::socket sock;
...
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
```
The layer being wrapped can be accessed through the websocket's "next layer",
permitting callers to interact directly with its interface.
```
boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
beast::websocket::stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws{ios, ctx};
...
ws.next_layer().shutdown(); // ssl::stream shutdown
```
[warning
Initiating read and write operations on the next layer while
stream operations are being performed can break invariants, and
result in undefined behavior.
]
[endsect]
[section:connections Making connections]
Connections are established by using the interfaces which already exist
for the next layer. For example, making an outgoing connection:
```
std::string const host = "mywebapp.com";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r{ios};
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
boost::asio::connect(ws.next_layer(),
r.resolve(boost::asio::ip::tcp::resolver::query{host, "ws"}));
```
Accepting an incoming connection:
```
void do_accept(boost::asio::ip::tcp::acceptor& acceptor)
{
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{acceptor.get_io_service()};
acceptor.accept(ws.next_layer());
}
```
When using SSL, which itself wraps a next layer object that is usualy a
TCP/IP socket, multiple calls to retrieve the next layer may be required:
```
beast::websocket::stream<boost::asio::ssl::stream<
boost::asio::ip::tcp::socket>> ws{ios, ctx};
// connect the underlying TCP/IP socket
ws.next_layer().next_layer().connect(ep);
// perform SSL handshake
ws.next_layer().handshake(boost::asio::ssl::stream_base::client);
// perform WebSocket handshake
ws.handshake("localhost", "/");
```
[note
Examples use synchronous interfaces for clarity of exposition.
]
[endsect]
[section:handshaking Handshaking]
A WebSocket session begins when one side sends the HTTP Upgrade request
for websocket, and the other side sends an appropriate HTTP response
indicating that the request was accepted and that the connection has
been upgraded. The HTTP Upgrade request must include the Host HTTP field,
and the target of the resource to request. The stream member functions
[link beast.ref.websocket__stream.handshake `handshake`] and
[link beast.ref.websocket__stream.async_handshake `async_handshake`]
are used to send the request with the required host and target strings.
```
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
...
ws.set_option(beast::websocket::keep_alive(true));
ws.handshake("localhost", "/");
```
The implementation will create and send an HTTP request that typically
looks like this:
[table WebSocket Upgrade HTTP Request
[[Serialized Octets][Description]]
[[
```
GET / HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Key: 2pGeTR0DsE4dfZs2pH+8MA==
Sec-WebSocket-Version: 13
User-Agent: Beast
```
][
The host and target parameters become part of the Host field
and request-target in the resulting HTTP request. The key is
generated by the implementation. Callers may add fields or
modify fields by providing a ['decorator], described below.
]]]
[heading Decorators]
If the caller wishes to add or modify fields, the member functions
[link beast.ref.websocket__stream.handshake_ex `handshake_ex`] and
[link beast.ref.websocket__stream.async_handshake_ex `async_handshake_ex`]
are provided which allow an additional function object, called a
['decorator], to be passed. The decorator is invoked to modify
the HTTP Upgrade request as needed. This example sets a subprotocol
on the request:
```
void decorate(beast::websocket::request_type& req)
{
req.fields.insert("Sec-WebSocket-Protocol", "xmpp;ws-chat");
}
...
ws.handshake_ex("localhost", "/", &decorate);
```
The HTTP Upgrade request produced by the previous call will look thusly:
[table Decorated WebSocket Upgrade HTTP Request
[[Serialized Octets][Description]]
[[
```
GET / HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Key: 2pGeTR0DsE4dfZs2pH+8MA==
Sec-WebSocket-Version: 13
Sec-WebSocket-Protocol: xmpp;ws-chat
User-Agent: Beast
```
][
Undefined behavior results if the decorator modifies the fields
specific to perform the WebSocket Upgrade , such as the Upgrade
and Connection fields.
]]]
[heading Filtering]
When a client receives an HTTP Upgrade response from the server indicating
a successful upgrade, the caller may wish to perform additional validation
on the received HTTP response message. For example, to check that the
response to a basic authentication challenge is valid. To achieve this,
overloads of the handshake member function allow the caller to store the
received HTTP message in an output reference argument as
[link beast.ref.websocket__response_type `response_type`]
as follows:
```
beast::websocket::response_type res;
ws.handshake(res, "localhost", "/");
if(! res.fields.exists("Sec-WebSocket-Protocol"))
throw std::invalid_argument("missing subprotocols");
```
[endsect]
[section:accepting Accepting]
A [link beast.ref.websocket__stream `stream`] automatically
handles receiving and processing the HTTP response to the handshake request.
The call to handshake is successful if a HTTP response is received with the
101 "Switching Protocols" status code. On failure, an error is returned or an
exception is thrown. Depending on the keep alive setting, the socket may remain
open for a subsequent handshake attempt
Performing a handshake for an incoming websocket upgrade request operates
similarly. If the handshake fails, an error is returned or exception thrown:
```
beast::websocket::stream<boost::asio::ip::tcp::socket> ws{ios};
...
ws.accept();
```
Successful WebSocket Upgrade responses generated by the implementation will
typically look like this:
[table Decorated WebSocket Upgrade HTTP Request
[[Serialized Octets][Description]]
[[
```
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Server: Beast/40
```
][
The Sec-WebSocket-Accept field value is generated from the
request in a fashion specified by the WebSocket protocol.
]]]
[heading Decorators]
If the caller wishes to add or modify fields, the member functions
[link beast.ref.websocket__stream.accept_ex `accept_ex`] and
[link beast.ref.websocket__stream.async_accept_ex `async_accept_ex`]
are provided which allow an additional function object, called a
['decorator], to be passed. The decorator is invoked to modify
the HTTP Upgrade request as needed. This example sets the Server
field on the response:
```
ws.accept_ex(
[](beast::websocket::response_type& res)
{
res.fields.insert("Server", "AcmeServer");
});
```
The HTTP Upgrade response produced by the previous call will look thusly:
[table Decorated WebSocket Upgrade HTTP Request
[[Serialized Octets][Description]]
[[
```
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Server: AcmeServer
```
][
When the Upgrade request fails, the implementation will still invoke
the decorator to modify the response. In this case, the response
object will have a status code other than 101.
Undefined behavior results when the upgrade request is successful
and the decorator modifies the fields specific to perform the
WebSocket Upgrade , such as the Upgrade and Connection fields.
]]]
[heading Passing HTTP Requests]
When implementing an HTTP server that also supports WebSocket, the
server usually reads the HTTP request from the client. To detect when
the incoming HTTP request is a WebSocket Upgrade request, the function
[link beast.ref.websocket__is_upgrade `is_upgrade`] may be used.
Once the caller determines that the HTTP request is a WebSocket Upgrade,
additional overloads of
[link beast.ref.websocket__stream.accept `accept`],
[link beast.ref.websocket__stream.accept_ex `accept_ex`],
[link beast.ref.websocket__stream.async_accept `async_accept`], and
[link beast.ref.websocket__stream.async_accept_ex `async_accept_ex`]
are provided which receive the entire HTTP request header as an object
to perform the handshake. In this example, the request is first read
in using the HTTP algorithms, and then passed to a newly constructed
stream:
```
void handle_connection(boost::asio::ip::tcp::socket& sock)
{
beast::flat_buffer buffer;
beast::http::request<beast::http::string_body> req;
beast::http::read(sock, buffer, req);
if(beast::websocket::is_upgrade(req))
{
beast::websocket::stream<decltype(sock)> ws{std::move(sock)};
ws.accept(req);
}
}
```
[heading Buffered Handshakes]
Sometimes a server implementation wishes to read octets on the stream
in order to route the incoming request. For example, a server may read
the first 6 octets after accepting an incoming connection to determine
if a TLS protocol is being negotiated, and choose a suitable implementation
at run-time. In the case where the server wishes to accept the incoming
request as an HTTP WebSocket Upgrade request, additional overloads of
[link beast.ref.websocket__stream.accept `accept`],
[link beast.ref.websocket__stream.accept_ex `accept_ex`],
[link beast.ref.websocket__stream.async_accept `async_accept`], and
[link beast.ref.websocket__stream.async_accept_ex `async_accept_ex`]
are provided which receive the additional buffered octects and consume
them as part of the handshake.
In this example, the server reads the initial HTTP message into the
specified dynamic buffer as an octet sequence in the buffer's output
area, and later uses those octets to attempt an HTTP WebSocket Upgrade:
```
void do_accept(boost::asio::ip::tcp::socket& sock)
{
boost::asio::streambuf sb;
boost::asio::read_until(sock, sb, "\r\n\r\n");
...
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
ws.accept(sb.data());
...
}
```
[endsect]
[section:messages Messages]
After the WebSocket handshake is accomplished, callers may send and receive
messages using the message oriented interface. This interface requires that
all of the buffers representing the message are known ahead of time:
```
void echo(beast::websocket::stream<boost::asio::ip::tcp::socket>& ws)
{
beast::multi_buffer b;
beast::websocket::opcode::value op;
ws.read(op, b);
ws.set_option(beast::websocket::message_type{op});
ws.write(b.data());
sb.consume(b.size());
}
```
[important
Calls to [link beast.ref.websocket__stream.set_option `set_option`]
must be made from the same implicit or explicit strand as that used
to perform other operations.
]
[heading Frames]
Some use-cases make it impractical or impossible to buffer the entire
message ahead of time:
* Streaming multimedia to an endpoint.
* Sending a message that does not fit in memory at once.
* Providing incremental results as they become available.
For these cases, the frame oriented interface may be used. This
example reads and echoes a complete message using this interface:
```
void echo(beast::websocket::stream<boost::asio::ip::tcp::socket>& ws)
{
beast::multi_buffer b;
beast::websocket::frame_info fi;
for(;;)
{
ws.read_frame(fi, b);
if(fi.fin)
break;
}
ws.set_option(beast::websocket::message_type{fi.op});
beast::consuming_buffers<
beast::multi_buffer::const_buffers_type> cb{b.data()};
for(;;)
{
using boost::asio::buffer_size;
std::size_t size = std::min(buffer_size(cb));
if(size > 512)
{
ws.write_frame(false, beast::prepare_buffers(512, cb));
cb.consume(512);
}
else
{
ws.write_frame(true, cb);
break;
}
}
}
```
[endsect]
[section:control Control Frames]
Control frames are small (less than 128 bytes) messages entirely contained
in an individual WebSocket frame. They may be sent at any time by either
peer on an established connection, and can appear in between continuation
frames for a message. There are three types of control frames: ping, pong,
and close.
A sent ping indicates a request that the sender wants to receive a pong. A
pong is a response to a ping. Pongs may be sent unsolicited, at any time.
One use for an unsolicited pong is to inform the remote peer that the
session is still active after a long period of inactivity. A close frame
indicates that the remote peer wishes to close the WebSocket connection.
The connection is considered gracefully closed when each side has sent
and received a close frame.
During read operations, Beast automatically reads and processes control
frames. Pings are replied to as soon as possible with a pong, received
ping and pongs are delivered to the ping callback. The receipt of a close
frame initiates the WebSocket close procedure, eventually resulting in the
error code [link beast.ref.websocket__error `error::closed`] being delivered
to the caller in a subsequent read operation, assuming no other error
takes place.
A consequence of this automatic behavior is that caller-initiated read
operations can cause socket writes. However, these writes will not
compete with caller-initiated write operations. For the purposes of
correctness with respect to the stream invariants, caller-initiated
read operations still only count as a read. This means that callers can
have a simultaneously active read, write, and ping operation in progress,
while the implementation also automatically handles control frames.
[heading Ping and Pong Frames]
Ping and pong messages are control frames which may be sent at any time
by either peer on an established WebSocket connection. They are sent
using the functions
[link beast.ref.websocket__stream.ping `ping`] and
[link beast.ref.websocket__stream.pong `pong`].
To be notified of ping and pong control frames, callers may register a
"ping callback" using [link beast.ref.websocket__stream.set_option `set_option`].
The object provided with this option should be callable with the following
signature:
```
void on_ping(bool is_pong, ping_data const& payload);
...
ws.set_option(ping_callback{&on_ping});
```
When a ping callback is registered, all pings and pongs received through
either synchronous read functions or asynchronous read functions will
invoke the ping callback, with the value of `is_pong` set to `true` if a
pong was received else `false` if a ping was received. The payload of
the ping or pong control frame is passed in the payload argument.
Unlike regular completion handlers used in calls to asynchronous initiation
functions, the ping callback only needs to be set once. The callback is not
reset when a ping or pong is received. The same callback is used for both
synchronous and asynchronous reads. The ping callback is passive; in order
to receive pings and pongs, a synchronous or asynchronous stream read
function must be active.
[note
When an asynchronous read function receives a ping or pong, the
ping callback is invoked in the same manner as that used to invoke
the final completion handler of the corresponding read function.
]
[heading Close Frames]
The WebSocket protocol defines a procedure and control message for initiating
a close of the session. Handling of close initiated by the remote end of the
connection is performed automatically. To manually initiate a close, use
the [link beast.ref.websocket__stream.close `close`] function:
```
ws.close();
```
When the remote peer initiates a close by sending a close frame, Beast
will handle it for you by causing the next read to return `error::closed`.
When this error code is delivered, it indicates to the application that
the WebSocket connection has been closed cleanly, and that the TCP/IP
connection has been closed. After initiating a close, it is necessary to
continue reading messages until receiving the error `error::closed`. This
is because the remote peer may still be sending message and control frames
before it receives and responds to the close frame.
[important
To receive the [link beast.ref.websocket__error `error::closed`]
error, a read operation is required.
]
[heading Auto-fragment]
To ensure timely delivery of control frames, large messages can be broken up
into smaller sized frames. The automatic fragment option turns on this
feature, and the write buffer size option determines the maximum size of
the fragments:
```
...
ws.set_option(beast::websocket::auto_fragment{true});
ws.set_option(beast::websocket::write_buffer_size{16384});
```
[endsect]
[section:notes Notes]
Because calls to read data may return a variable amount of bytes, the
interface to calls that read data require an object that meets the requirements
of __DynamicBuffer__. This concept is modeled on
[@http://www.boost.org/doc/html/boost_asio/reference/basic_streambuf.html `boost::asio::basic_streambuf`].
The implementation does not perform queueing or buffering of messages. If
desired, these features should be provided by callers. The impact of this
design is that library users are in full control of the allocation strategy
used to store data and the back-pressure applied on the read and write side
of the underlying TCP/IP connection.
[heading Asynchronous Operations]
Asynchronous versions are available for all functions:
```
websocket::opcode op;
ws.async_read(op, sb,
[](boost::system::error_code const& ec)
{
...
});
```
Calls to asynchronous initiation functions support the extensible asynchronous
model developed by the Boost.Asio author, allowing for traditional completion
handlers, stackful or stackless coroutines, and even futures:
```
void echo(websocket::stream<ip::tcp::socket>& ws,
boost::asio::yield_context yield)
{
ws.async_read(sb, yield);
std::future<websocket::error_code> fut =
ws.async_write, sb.data(), boost::use_future);
...
}
```
[heading The io_service]
The creation and operation of the
[@http://www.boost.org/doc/html/boost_asio/reference/io_service.html `boost::asio::io_service`]
associated with the underlying stream is left to the callers, permitting any
implementation strategy including one that does not require threads for
environments where threads are unavailable. Beast WebSocket itself does not
use or require threads.
[heading Thread Safety]
Like a regular asio socket, a [link beast.ref.websocket__stream `stream`] is
not thread safe. Callers are responsible for synchronizing operations on the
socket using an implicit or explicit strand, as per the Asio documentation.
The asynchronous interface supports one active read and one active write
simultaneously. Undefined behavior results if two or more reads or two or
more writes are attempted concurrently. Caller initiated WebSocket ping, pong,
and close operations each count as an active write.
The implementation uses composed asynchronous operations internally; a high
level read can cause both reads and writes to take place on the underlying
stream. This behavior is transparent to callers.
[endsect]
[endsect]
[include quickref.xml]

View File

@@ -12,7 +12,7 @@
<member><link linkend="beast.design.http_message">HTTP Message Container</link></member>
<member><link linkend="beast.design.http_comparison">HTTP Comparison to Other Libraries</link></member>
<member><link linkend="beast.design.websocket_zaphoyd">Comparison to Zaphoyd Studios WebSocket++</link></member>
<member><link linkend="beast.design.review">Boost Formal Review FAQ</link></member>
<member><link linkend="beast.design.faq">FAQ</link></member>
</simplelist></entry></row></tbody></tgroup></informaltable>
''']
@@ -62,6 +62,6 @@ start. Other design goals:
[include 6_1_http_message.qbk]
[include 6_2_http_comparison.qbk]
[include 6_3_websocket_zaphoyd.qbk]
[include 6_4_review.qbk]
[include 6_4_faq.qbk]
[endsect]

View File

@@ -5,7 +5,7 @@
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[section:review Boost Formal Review FAQ]
[section:faq Boost Formal Review FAQ]
To set realistic expectations and prevent a litany of duplicate review
statements, these notes address the most common questions and comments

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

View File

@@ -54,14 +54,14 @@ invoke library algorithms it is necessary to first have a connected socket,
SSL stream, or other object which meets the required stream concepts. 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"}));
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.
// At this point `sock` is a connected to a remote
// host and may be used to perform stream operations.
```
Throughout this documentation identifiers with the following names have
@@ -94,9 +94,6 @@ special meaning:
]]
]
[heading:streams Stream Concepts]
A __Stream__ is communication channel where data expressed as octet

View File

@@ -36,6 +36,36 @@ namespace beast {
completion token types. The primary template assumes that the
@b CompletionToken is the completion handler.
@par Example
The example shows how to define an asynchronous initiation function
whose completion handler receives an error code:
@code
template<
class AsyncStream, // A stream supporting asynchronous read and write
class Handler // The handler to call with signature void(error_code)
>
async_return_type< // This provides the return type customization
Handler, void(error_code)>
do_async(
AsyncStream& stream, // The stream to work on
Handler&& handler) // Could be an rvalue or const reference
{
// Make sure we have an async stream
static_assert(is_async_stream<AsyncWriteStream>::value,
"AsyncStream requirements not met");
// This helper converts the handler into the real handler type
async_completion<WriteHandler, void(error_code)> init{handler};
... // Create and the composed operation
// This provides the return value and executor customization
return init.result.get();
}
@endcode
@see @ref async_completion, @ref async_return_type, @ref handler_type
*/
template<class CompletionToken, class Signature>

View File

@@ -28,6 +28,10 @@ namespace beast {
construction. Attempts to exceed the buffer size will throw
`std::length_error`.
Upon construction, a maximum size for the buffer may be
specified. If this limit is exceeded, the `std::length_error`
exception will be thrown.
@note This class is designed for use with algorithms that
take dynamic buffers as parameters, and are optimized
for the case where the input sequence or output sequence

View File

@@ -22,7 +22,7 @@
namespace beast {
/** A string with a fixed-size storage area.
/** A modifiable string with a fixed-size storage area.
These objects behave like `std::string` except that the storage
is not dynamically allocated but rather fixed in size.

View File

@@ -81,32 +81,33 @@ struct no_chunk_decorator
struct with a templated operator() thusly:
@code
// The implementation guarantees that operator()
// will be called only after the view returned by
// any previous calls to operator() are no longer
// needed. The decorator instance is intended to
// manage the lifetime of the storage for all returned
// views.
// The implementation guarantees that operator()
// will be called only after the view returned by
// any previous calls to operator() are no longer
// needed. The decorator instance is intended to
// manage the lifetime of the storage for all returned
// views.
//
struct decorator
{
// Returns the chunk-extension for each chunk,
// or an empty string for no chunk extension. The
// buffer must include the leading semicolon (";")
// and follow the format for chunk extensions defined
// in rfc7230.
//
struct decorator
{
// Returns the chunk-extension for each chunk.
// The buffer returned must include a trailing "\r\n",
// and the leading semicolon (";") if one or more
// chunk extensions are specified.
//
template<class ConstBufferSequence>
string_view
operator()(ConstBufferSequence const&) const;
template<class ConstBufferSequence>
string_view
operator()(ConstBufferSequence const&) const;
// Returns a set of field trailers for the final chunk.
// Each field should be formatted according to rfc7230
// including the trailing "\r\n" for each field. If
// no trailers are indicated, an empty string is returned.
//
string_view
operator()(boost::asio::null_buffers) const;
};
// Returns a set of field trailers for the final chunk.
// Each field should be formatted according to rfc7230
// including the trailing "\r\n" for each field. If
// no trailers are indicated, an empty string is returned.
//
string_view
operator()(boost::asio::null_buffers) const;
};
@endcode
@tparam isRequest `true` if the message is a request.
@@ -130,6 +131,7 @@ class serializer
{
static_assert(is_body<Body>::value,
"Body requirements not met");
static_assert(is_body_reader<Body>::value,
"BodyReader requirements not met");

View File

@@ -318,8 +318,7 @@ public:
Upgrade request and send the HTTP response. The call blocks
until one of the following conditions is true:
@li The HTTP request finishes receiving, and the HTTP response
finishes sending.
@li The request is received and the response finishes sending.
@li An error occurs on the stream.
@@ -346,8 +345,7 @@ public:
Upgrade request and send the HTTP response. The call blocks
until one of the following conditions is true:
@li The HTTP request finishes receiving, and the HTTP response
finishes sending.
@li The request is received and the response finishes sending.
@li An error occurs on the stream.
@@ -384,8 +382,7 @@ public:
Upgrade request and send the HTTP response. The call blocks
until one of the following conditions is true:
@li The HTTP request finishes receiving, and the HTTP response
finishes sending.
@li The request is received and the response finishes sending.
@li An error occurs on the stream.
@@ -412,8 +409,7 @@ public:
Upgrade request and send the HTTP response. The call blocks
until one of the following conditions is true:
@li The HTTP request finishes receiving, and the HTTP response
finishes sending.
@li The request is received and the response finishes sending.
@li An error occurs on the stream.
@@ -451,8 +447,7 @@ public:
Upgrade request and send the HTTP response. The call blocks
until one of the following conditions is true:
@li The HTTP request finishes receiving, and the HTTP response
finishes sending.
@li The request is received and the response finishes sending.
@li An error occurs on the stream.
@@ -489,8 +484,7 @@ public:
Upgrade request and send the HTTP response. The call blocks
until one of the following conditions is true:
@li The HTTP request finishes receiving, and the HTTP response
finishes sending.
@li The request is received and the response finishes sending.
@li An error occurs on the stream.
@@ -538,8 +532,7 @@ public:
Upgrade request and send the HTTP response. The call blocks
until one of the following conditions is true:
@li The HTTP request finishes receiving, and the HTTP response
finishes sending.
@li The request is received and the response finishes sending.
@li An error occurs on the stream.
@@ -576,8 +569,7 @@ public:
Upgrade request and send the HTTP response. The call blocks
until one of the following conditions is true:
@li The HTTP request finishes receiving, and the HTTP response
finishes sending.
@li The request is received and the response finishes sending.
@li An error occurs on the stream.
@@ -626,7 +618,7 @@ public:
to an HTTP request possibly containing a WebSocket Upgrade.
The call blocks until one of the following conditions is true:
@li The HTTP response finishes sending.
@li The response finishes sending.
@li An error occurs on the stream.
@@ -658,7 +650,7 @@ public:
to an HTTP request possibly containing a WebSocket Upgrade.
The call blocks until one of the following conditions is true:
@li The HTTP response finishes sending.
@li The response finishes sending.
@li An error occurs on the stream.
@@ -700,7 +692,7 @@ public:
to an HTTP request possibly containing a WebSocket Upgrade.
The call blocks until one of the following conditions is true:
@li The HTTP response finishes sending.
@li The response finishes sending.
@li An error occurs on the stream.
@@ -733,7 +725,7 @@ public:
to an HTTP request possibly containing a WebSocket Upgrade.
The call blocks until one of the following conditions is true:
@li The HTTP response finishes sending.
@li The response finishes sending.
@li An error occurs on the stream.
@@ -776,7 +768,7 @@ public:
to an HTTP request possibly containing a WebSocket Upgrade.
The call blocks until one of the following conditions is true:
@li The HTTP response finishes sending.
@li The response finishes sending.
@li An error occurs on the stream.
@@ -814,7 +806,7 @@ public:
to an HTTP request possibly containing a WebSocket Upgrade.
The call blocks until one of the following conditions is true:
@li The HTTP response finishes sending.
@li The response finishes sending.
@li An error occurs on the stream.
@@ -863,7 +855,7 @@ public:
to an HTTP request possibly containing a WebSocket Upgrade.
The call blocks until one of the following conditions is true:
@li The HTTP response finishes sending.
@li The response finishes sending.
@li An error occurs on the stream.
@@ -901,7 +893,7 @@ public:
to an HTTP request possibly containing a WebSocket Upgrade.
The call blocks until one of the following conditions is true:
@li The HTTP response finishes sending.
@li The response finishes sending.
@li An error occurs on the stream.
@@ -952,8 +944,7 @@ public:
always returns immediately. The asynchronous operation will
continue until one of the following conditions is true:
@li The HTTP request finishes receiving, and the HTTP response
finishes sending.
@li The request is received and the response finishes sending.
@li An error occurs on the stream.
@@ -1002,8 +993,7 @@ public:
always returns immediately. The asynchronous operation will
continue until one of the following conditions is true:
@li The HTTP request finishes receiving, and the HTTP response
finishes sending.
@li The request is received and the response finishes sending.
@li An error occurs on the stream.
@@ -1062,8 +1052,7 @@ public:
always returns immediately. The asynchronous operation will
continue until one of the following conditions is true:
@li The HTTP request finishes receiving, and the HTTP response
finishes sending.
@li The request is received and the response finishes sending.
@li An error occurs on the stream.
@@ -1121,8 +1110,7 @@ public:
always returns immediately. The asynchronous operation will
continue until one of the following conditions is true:
@li The HTTP request finishes receiving, and the HTTP response
finishes sending.
@li The request is received and the response finishes sending.
@li An error occurs on the stream.
@@ -1192,7 +1180,7 @@ public:
asynchronous operation will continue until one of the following
conditions is true:
@li The HTTP response finishes sending.
@li The response finishes sending.
@li An error occurs on the stream.
@@ -1247,7 +1235,7 @@ public:
asynchronous operation will continue until one of the following
conditions is true:
@li The HTTP response finishes sending.
@li The response finishes sending.
@li An error occurs on the stream.
@@ -1313,7 +1301,7 @@ public:
asynchronous operation will continue until one of the following
conditions is true:
@li The HTTP response finishes sending.
@li The response finishes sending.
@li An error occurs on the stream.
@@ -1377,7 +1365,7 @@ public:
asynchronous operation will continue until one of the following
conditions is true:
@li The HTTP response finishes sending.
@li The response finishes sending.
@li An error occurs on the stream.
@@ -1449,8 +1437,7 @@ public:
upgrade HTTP request. The call blocks until one of the
following conditions is true:
@li A HTTP request finishes sending and an HTTP response finishes
receiving.
@li The request is sent and the response is received.
@li An error occurs on the stream
@@ -1493,8 +1480,7 @@ public:
upgrade HTTP request. The call blocks until one of the
following conditions is true:
@li A HTTP request finishes sending and an HTTP response finishes
receiving.
@li The request is sent and the response is received.
@li An error occurs on the stream
@@ -1542,8 +1528,7 @@ public:
upgrade HTTP request. The call blocks until one of the
following conditions is true:
@li A HTTP request finishes sending and an HTTP response finishes
receiving.
@li The request is sent and the response is received.
@li An error occurs on the stream
@@ -1601,8 +1586,7 @@ public:
upgrade HTTP request. The call blocks until one of the
following conditions is true:
@li A HTTP request finishes sending and an HTTP response finishes
receiving.
@li The request is sent and the response is received.
@li An error occurs on the stream
@@ -1665,8 +1649,7 @@ public:
upgrade HTTP request. The call blocks until one of the
following conditions is true:
@li A HTTP request finishes sending and an HTTP response finishes
receiving.
@li The request is sent and the response is received.
@li An error occurs on the stream
@@ -1707,8 +1690,7 @@ public:
upgrade HTTP request. The call blocks until one of the
following conditions is true:
@li A HTTP request finishes sending and an HTTP response finishes
receiving.
@li The request is sent and the response is received.
@li An error occurs on the stream
@@ -1755,8 +1737,7 @@ public:
upgrade HTTP request. The call blocks until one of the
following conditions is true:
@li A HTTP request finishes sending and an HTTP response finishes
receiving.
@li The request is sent and the response is received.
@li An error occurs on the stream
@@ -1814,8 +1795,7 @@ public:
upgrade HTTP request. The call blocks until one of the
following conditions is true:
@li A HTTP request finishes sending and an HTTP response finishes
receiving.
@li The request is sent and the response is received.
@li An error occurs on the stream
@@ -1880,10 +1860,9 @@ public:
operation will continue until one of the following conditions is
true:
@li A HTTP request finishes sending and an HTTP response finishes
receiving.
@li The request is sent and the response is received.
@li An error occurs on the stream.
@li An error occurs on the stream
This operation is implemented in terms of one or more calls to the
next layer's `async_read_some` and `async_write_some` functions, and
@@ -1932,10 +1911,9 @@ public:
operation will continue until one of the following conditions is
true:
@li A HTTP request finishes sending and an HTTP response finishes
receiving.
@li The request is sent and the response is received.
@li An error occurs on the stream.
@li An error occurs on the stream
This operation is implemented in terms of one or more calls to the
next layer's `async_read_some` and `async_write_some` functions, and
@@ -1989,10 +1967,9 @@ public:
operation will continue until one of the following conditions is
true:
@li A HTTP request finishes sending and an HTTP response finishes
receiving.
@li The request is sent and the response is received.
@li An error occurs on the stream.
@li An error occurs on the stream
This operation is implemented in terms of one or more calls to the
next layer's `async_read_some` and `async_write_some` functions, and
@@ -2051,10 +2028,9 @@ public:
operation will continue until one of the following conditions is
true:
@li A HTTP request finishes sending and an HTTP response finishes
receiving.
@li The request is sent and the response is received.
@li An error occurs on the stream.
@li An error occurs on the stream
This operation is implemented in terms of one or more calls to the
next layer's `async_read_some` and `async_write_some` functions, and