diff --git a/doc/0_main.qbk b/doc/0_main.qbk
index d8b727c9..a1425c0a 100644
--- a/doc/0_main.qbk
+++ b/doc/0_main.qbk
@@ -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]
diff --git a/doc/1_overview.qbk b/doc/1_overview.qbk
index a07e9d11..e22768f0 100644
--- a/doc/1_overview.qbk
+++ b/doc/1_overview.qbk
@@ -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.
diff --git a/doc/2_0_core.qbk b/doc/2_0_core.qbk
new file mode 100644
index 00000000..cc0a773a
--- /dev/null
+++ b/doc/2_0_core.qbk
@@ -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 '''
+
+ Working With Asio
+ Stream Concepts
+ Buffer Concepts
+ Asynchronous Utilities
+ Writing Composed Operations
+
+''']
+
+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
+ #include
+ #include
+ #include
+
+ using namespace beast;
+
+ boost::asio::io_service ios;
+ boost::asio::io_service work{ios};
+ std::thread t{[&](){ ios.run(); }};
+
+ error_code ec;
+
+ ```
+]
+
+[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]
diff --git a/doc/2_1_asio.qbk b/doc/2_1_asio.qbk
new file mode 100644
index 00000000..78d0b63e
--- /dev/null
+++ b/doc/2_1_asio.qbk
@@ -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`]
+ 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`]
+ which is already connected with a remote host.
+]]
+]
diff --git a/doc/2_2_streams.qbk b/doc/2_2_streams.qbk
new file mode 100644
index 00000000..9d63e411
--- /dev/null
+++ b/doc/2_2_streams.qbk
@@ -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
+ void write_string(SyncWriteStream& stream, string_view s)
+ {
+ static_assert(is_sync_write_stream::value,
+ "SyncWriteStream requirements not met");
+ boost::asio::write(stream, boost::asio::const_buffers_1(s.data(), s.size()));
+ }
+```
diff --git a/doc/2_3_buffers.qbk b/doc/2_3_buffers.qbk
new file mode 100644
index 00000000..639d2c8d
--- /dev/null
+++ b/doc/2_3_buffers.qbk
@@ -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.
+]]
+]
diff --git a/doc/2_4_async.qbk b/doc/2_4_async.qbk
new file mode 100644
index 00000000..2f2a7974
--- /dev/null
+++ b/doc/2_4_async.qbk
@@ -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.
+]]
+]
diff --git a/doc/2_5_tutorial.qbk b/doc/2_5_tutorial.qbk
new file mode 100644
index 00000000..535470c0
--- /dev/null
+++ b/doc/2_5_tutorial.qbk
@@ -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
+ #include
+ #include
+ #include
+ #include
+
+ // Read a line and echo it back
+ //
+ template
+ beast::async_return_type
+ 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 echo_op; // This is our composed operation implementation
+
+ // Read a line and echo it back
+ //
+ template
+ beast::async_return_type
+ 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::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 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>{
+ 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 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 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> 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::max)(),
+ beast::handler_alloc{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 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
+ echo_op(AsyncStream& stream, DeducedHandler&& handler)
+ : p_(std::forward(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
+ 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
+ void echo_op::
+ 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]
diff --git a/doc/3_3_streams.qbk b/doc/3_3_streams.qbk
index 047d6e32..847e4f48 100644
--- a/doc/3_3_streams.qbk
+++ b/doc/3_3_streams.qbk
@@ -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
-void send(response const& res)
-{
- write(sock, res);
-}
+ template
+ void send(response 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 res;
- res.version = 11;
- res.status = 200;
- res.reason("OK");
- res.fields.insert("Server", "Beast");
- res.body = "Hello, world!";
+ void send()
+ {
+ response 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
-void send_async(response const& res)
-{
- async_write(sock, res,
- [&](error_code)
- {
- if(ec)
- std::cerr << ec.message() << std::endl;
- });
-}
+ template
+ void send_async(response const& res)
+ {
+ async_write(sock, res,
+ [&](error_code)
+ {
+ if(ec)
+ std::cerr << ec.message() << std::endl;
+ });
+ }
```
[endsect]
diff --git a/doc/3_9_custom_body.qbk b/doc/3_9_custom_body.qbk
index 2b139424..45ee1e10 100644
--- a/doc/3_9_custom_body.qbk
+++ b/doc/3_9_custom_body.qbk
@@ -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` or even
+`std::list`. 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]
diff --git a/doc/4_0_websocket.qbk b/doc/4_0_websocket.qbk
new file mode 100644
index 00000000..a56560bd
--- /dev/null
+++ b/doc/4_0_websocket.qbk
@@ -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 '''
+
+ Creating Streams
+ Establishing Connections
+ Handshaking (Clients)
+ Handshaking (Servers)
+ Send and Receive Messages
+ Control Frames
+ Additional Notes
+
+''']
+
+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
+ ```
+]
+
+[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]
diff --git a/doc/4_1_streams.qbk b/doc/4_1_streams.qbk
new file mode 100644
index 00000000..ac41330c
--- /dev/null
+++ b/doc/4_1_streams.qbk
@@ -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 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
+ #include
+
+ boost::asio::io_service ios;
+ boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
+ beast::websocket::stream ws{ios, ctx};
+```
+
+[note
+ Code which declares stream objects using Asio SSL types must
+ toinclude the file ``.
+]
+
+[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 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 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> 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]
diff --git a/doc/4_2_connect.qbk b/doc/4_2_connect.qbk
new file mode 100644
index 00000000..f5c97592
--- /dev/null
+++ b/doc/4_2_connect.qbk
@@ -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 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 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> 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]
diff --git a/doc/4_3_client.qbk b/doc/4_3_client.qbk
new file mode 100644
index 00000000..2f69bb86
--- /dev/null
+++ b/doc/4_3_client.qbk
@@ -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]
diff --git a/doc/4_4_server.qbk b/doc/4_4_server.qbk
new file mode 100644
index 00000000..9928f73a
--- /dev/null
+++ b/doc/4_4_server.qbk
@@ -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 req;
+ http::read(sock, buffer, req);
+ if(websocket::is_upgrade(req))
+ {
+ websocket::stream 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 ws{sock};
+ ws.accept(sb.data());
+ ...
+}
+```
+
+[endsect]
diff --git a/doc/4_5_messages.qbk b/doc/4_5_messages.qbk
new file mode 100644
index 00000000..801aa3a9
--- /dev/null
+++ b/doc/4_5_messages.qbk
@@ -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
+ void echo(websocket::stream& 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
+ void echo(websocket::stream& 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]
diff --git a/doc/4_6_control.qbk b/doc/4_6_control.qbk
new file mode 100644
index 00000000..cb09f343
--- /dev/null
+++ b/doc/4_6_control.qbk
@@ -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]
diff --git a/doc/4_7_notes.qbk b/doc/4_7_notes.qbk
new file mode 100644
index 00000000..7c567976
--- /dev/null
+++ b/doc/4_7_notes.qbk
@@ -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& ws,
+ boost::asio::yield_context yield)
+ {
+ ws.async_read(sb, yield);
+ std::future 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]
diff --git a/doc/4_websocket.qbk b/doc/4_websocket.qbk
deleted file mode 100644
index f28eb553..00000000
--- a/doc/4_websocket.qbk
+++ /dev/null
@@ -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 '''
-
- Creation
- Making connections
- Handshaking
- Accepting
- Messages
- Control Frames
- Notes
-
-''']
-
-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 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
-#include
-#include
-
-boost::asio::io_service ios;
-boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
-beast::websocket::stream ws{ios, ctx};
-```
-
-[note
- When creating websocket stream objects using SSL, it is necessary
- to include the file ``.
-]
-
-[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 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 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> 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 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 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> 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 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 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 req;
- beast::http::read(sock, buffer, req);
- if(beast::websocket::is_upgrade(req))
- {
- beast::websocket::stream 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 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& 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& 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& ws,
- boost::asio::yield_context yield)
-{
- ws.async_read(sb, yield);
- std::future 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]
diff --git a/doc/6_0_design.qbk b/doc/6_0_design.qbk
index 619bb31c..bc34d674 100644
--- a/doc/6_0_design.qbk
+++ b/doc/6_0_design.qbk
@@ -12,7 +12,7 @@
HTTP Message ContainerHTTP Comparison to Other LibrariesComparison to Zaphoyd Studios WebSocket++
- Boost Formal Review FAQ
+ FAQ
''']
@@ -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]
diff --git a/doc/6_4_review.qbk b/doc/6_4_faq.qbk
similarity index 99%
rename from doc/6_4_review.qbk
rename to doc/6_4_faq.qbk
index 2369625f..d768a0ff 100644
--- a/doc/6_4_review.qbk
+++ b/doc/6_4_faq.qbk
@@ -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
diff --git a/doc/images/body.png b/doc/images/body.png
index 581bc57c..91671f52 100644
Binary files a/doc/images/body.png and b/doc/images/body.png differ
diff --git a/doc/images/body.psd b/doc/images/body.psd
index ab83b5d5..420565d1 100644
Binary files a/doc/images/body.psd and b/doc/images/body.psd differ
diff --git a/doc/2_core.qbk b/doc/x2_0_core.qbk
similarity index 98%
rename from doc/2_core.qbk
rename to doc/x2_0_core.qbk
index 1c82e07f..2067e932 100644
--- a/doc/2_core.qbk
+++ b/doc/x2_0_core.qbk
@@ -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
diff --git a/include/beast/core/async_result.hpp b/include/beast/core/async_result.hpp
index 36cc2828..8380e7f8 100644
--- a/include/beast/core/async_result.hpp
+++ b/include/beast/core/async_result.hpp
@@ -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::value,
+ "AsyncStream requirements not met");
+
+ // This helper converts the handler into the real handler type
+ async_completion 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
diff --git a/include/beast/core/flat_buffer.hpp b/include/beast/core/flat_buffer.hpp
index bbb5e78d..a592ea1f 100644
--- a/include/beast/core/flat_buffer.hpp
+++ b/include/beast/core/flat_buffer.hpp
@@ -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
diff --git a/include/beast/core/static_string.hpp b/include/beast/core/static_string.hpp
index 2a189864..8574fe03 100644
--- a/include/beast/core/static_string.hpp
+++ b/include/beast/core/static_string.hpp
@@ -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.
diff --git a/include/beast/http/serializer.hpp b/include/beast/http/serializer.hpp
index 4e45c362..c0b53f48 100644
--- a/include/beast/http/serializer.hpp
+++ b/include/beast/http/serializer.hpp
@@ -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
- string_view
- operator()(ConstBufferSequence const&) const;
+ template
+ 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::value,
"Body requirements not met");
+
static_assert(is_body_reader::value,
"BodyReader requirements not met");
diff --git a/include/beast/websocket/stream.hpp b/include/beast/websocket/stream.hpp
index 98560567..773d5697 100644
--- a/include/beast/websocket/stream.hpp
+++ b/include/beast/websocket/stream.hpp
@@ -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