diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2bc4d506..70ba7d54 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
Version 41
* Trim Appveyor matrix rows
+* Concept revision and documentation
API Changes
diff --git a/doc/core.qbk b/doc/core.qbk
new file mode 100644
index 00000000..45b6dec5
--- /dev/null
+++ b/doc/core.qbk
@@ -0,0 +1,611 @@
+[/
+ 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 '''
+
+ Concepts
+ DynamicBuffer
+ Buffer Algorithms
+ Asynchronous Model
+ Tutorial
+
+''']
+
+In addition to network protocols, Beast provides users with robust concepts,
+implementations, and algorithms which can be used to design higher level
+abstractions that interoperate with Boost.Asio. In the sections that follow
+are descriptions of these concepts, followed by algorithms using the concepts,
+and then concluding with a tutorial on developing composed asynchronous
+operations which are compatible with the Extensible Asynchronous Model provided
+in Boost.Asio, __N3747__, and __N4588__.
+
+
+
+[section:concepts Concepts]
+
+Beast provides template metaprogramming functions to allow users to perform
+concept checks on parameters, preventing undefined or illegal use. These trait
+traits are used in the implementation of the library and part of its public
+interface so users may build on the library using the same features. Use of
+concept checks benefit users by providing accurate, concise compilation error
+diagnostics.
+
+[table Type Traits
+[[Name][Description]]
+[[
+ [link beast.ref.has_get_io_service `has_get_io_service`]
+][
+ Determine if the `get_io_service` member function is present with the
+ correct signature.
+]]
+[[
+ [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_const_buffer_sequence `is_const_buffer_sequence`]
+][
+ Determine if a type meets the requirements of __ConstBufferSequence__.
+]]
+[[
+ [link beast.ref.is_dynamic_buffer `is_dynamic_buffer`]
+][
+ Determine if a type meets the requirements of __DynamicBuffer__.
+]]
+[[
+ [link beast.ref.is_mutable_buffer_sequence `is_mutable_buffer_sequence`]
+][
+ Determine if a type meets the requirements of __MutableBufferSequence__.
+]]
+[[
+ [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__.
+]]
+]
+
+[endsect]
+
+
+
+
+[section:dynamic_buffer DynamicBuffer]
+
+The networking technical specification described in __N4588__ provides a
+concept called a __DynamicBuffer__. Instances of this concept define
+an output area expressed as a __MutableBufferSequence__ and an input area
+expressed as a __ConstBufferSequence__. Octets may be moved from the output
+area to the input area through a structured interface, and later consumed
+from the input area when the data is no longer needed. Beast adopts this
+concept by providing a concise definition taken directly from the technical
+specification and using it as the interface for operations which require
+dynamically sized buffers. Several classes are provided which implement the
+dynamic buffer concept using various strategies:
+
+[table Dynamic buffer implementations
+[[Name][Description]]
+[[
+ [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.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.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.
+]]
+[[
+ [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.
+]]
+]
+
+[endsect]
+
+
+
+[section:algorithms Buffer Algorithms]
+
+Beast provides a collection of algorithms to work on __ConstBufferSequence__
+or __MutableBufferSequence__ objects. These algorithms allow composition of
+new algorithms which work on any objects meeting the buffer sequence
+requirements, in an efficient way: no memory allocations are performed;
+instead, the algorithms implement lightweight iterators over the existing
+underlying memory, whose lifetime is retained by the caller.
+
+[table Buffer algorithms
+[[Name][Description]]
+[[
+ [link beast.ref.buffer_cat `buffer_cat`]
+][
+ The 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. This powerful function allows multiple calls
+ to a stream's `write_some` function to be combined into one, eliminating
+ expensive system calls.
+]]
+[[
+ [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.
+]]
+[[
+ [link beast.ref.prepare_buffer `prepare_buffer`]
+ [link beast.ref.prepare_buffers `prepare_buffers`]
+][
+ These functions return a new buffer sequence which wraps the underlying
+ memory of an existing buffer sequence, but with a smaller size. This
+ lets callers work with a prefix of a buffer sequence.
+]]
+]
+
+[endsect]
+
+
+
+[section:async Asynchronous Model]
+
+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. Boost.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 Boost.Asio's asynchronous interfaces are used is beyond the
+ scope of this document. Readers should consult the Boost.Asio documentation
+ for a comprehensive treatment.
+]
+
+Since Beast is 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, Boost.Asio imposes requirements on
+the implementation of composed operations. Beast provides a number of useful
+classes and macros to facilitate the development of composed operations and
+the associated asynchronous initiation functions used to launch them.
+
+[table Asynchronous Helpers
+[[Name][Description]]
+[[
+ [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.
+]]
+[[
+ [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.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.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.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.
+
+]]
+]
+
+When implementing composed operations it is necessary to provide hooks for
+the composed operation which forward the hook calls to the hooks associated
+with the final completion handler. These calls must be made from a namespace
+that does not include overloads of the hook functions, since they depend on
+argument dependent lookup to resolve. Beast provides a set of public helper
+functions to invoke these hooks. These hooks are all located in the
+`beast_asio_helpers` namespace rather than the `beast` namespace:
+
+[table Handler Hooks
+[[Name][Description]]
+[[
+ [link beast.ref.beast_asio_helpers__allocate `allocate`]
+][
+ Allocation function for a handler. Asynchronous operations may need to
+ allocate temporary objects. Since asynchronous operations have a handler
+ function object, these temporary objects can be said to be associated with
+ the handler.
+]]
+[[
+ [link beast.ref.beast_asio_helpers__deallocate `deallocate`]
+][
+ Deallocation function for a handler. The implementation guarantees that
+ the deallocation will occur before the associated handler is invoked, which
+ means the memory is ready to be reused for any new asynchronous operations
+ started by the handler.
+]]
+[[
+ [link beast.ref.beast_asio_helpers__invoke `invoke`]
+][
+ Invocation function for a handler. When asynchronous operations are
+ composed from other asynchronous operations, all intermediate handlers
+ should be invoked using the same method as the final handler. This is
+ required to ensure that user-defined objects are not accessed in a way
+ that may violate the guarantees. This hooking function ensures that the
+ invoked method used for the final handler is accessible at each
+ intermediate step.
+]]
+[[
+ [link beast.ref.beast_asio_helpers__is_continuation `is_continuation`]
+][
+ Continuation function for a handler. Asynchronous operations may represent
+ a continuation of the asynchronous control flow associated with the current
+ handler. The implementation can use this knowledge to optimise scheduling
+ of the handler.
+]]
+]
+
+[endsect]
+
+
+
+[section:tutorial Tutorial]
+
+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
+ // instead of letting the compiler generate several pages of hard
+ // to read error messages.
+ //
+ 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 BEAST_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 Boost.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;
+ return boost::asio::async_write(p.stream, p.buffer.data(), std::move(*this));
+
+ case 2:
+ 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]
+
+
+
+[endsect]
diff --git a/doc/design.qbk b/doc/design.qbk
index f836bcf0..40310019 100644
--- a/doc/design.qbk
+++ b/doc/design.qbk
@@ -486,7 +486,7 @@ start. Other design goals:
Beast doesn't allocate or make copies of buffers when sending data. The
caller's buffers are sent in-place. You can use any object meeting the
requirements of
- [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html ConstBufferSequence],
+ [@http://www.boost.org/doc/html/boost_asio/reference/ConstBufferSequence.html ConstBufferSequence],
permitting efficient scatter-gather I/O.
The [*ConstBufferSequence] interface allows callers to send data from
@@ -588,8 +588,8 @@ start. Other design goals:
[table
[
- [[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/async_connect.html Beast],
- [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/basic_socket_acceptor/async_accept.html also]]
+ [[@http://www.boost.org/doc/html/boost_asio/reference/async_connect.html Beast],
+ [@http://www.boost.org/doc/html/boost_asio/reference/basic_socket_acceptor/async_accept.html also]]
[[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/asio/endpoint.hpp#L52 websocketpp]]
][
[```
diff --git a/doc/examples.qbk b/doc/examples.qbk
index aecd3083..6383a998 100644
--- a/doc/examples.qbk
+++ b/doc/examples.qbk
@@ -127,6 +127,15 @@ type, in `file_body`.
* [@examples/http_sync_server.hpp]
* [@examples/http_server.cpp]
+[heading Composed Operations]
+
+This program shows how to use Beast's core foundations to build a
+composable asynchronous initiation function with associated composed
+operation implementation. This is a complete, runnable version of
+the example described in the Core Foundations document section.
+
+* [@examples/echo_op.cpp]
+
[heading Listings]
These are stand-alone listings of the HTTP and WebSocket examples.
diff --git a/doc/images/message.png b/doc/images/message.png
index 3ea62509..7a85af12 100644
Binary files a/doc/images/message.png and b/doc/images/message.png differ
diff --git a/doc/master.qbk b/doc/master.qbk
index 4b0200b8..8047a0e8 100644
--- a/doc/master.qbk
+++ b/doc/master.qbk
@@ -23,22 +23,25 @@
[template indexterm1[term1] ''''''[term1]'''''']
[template indexterm2[term1 term2] ''''''[term1]''''''[term2]'''''']
+[def __N3747__ [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3747.pdf [*N3747]]]
[def __N4588__ [@http://cplusplus.github.io/networking-ts/draft.pdf [*N4588]]]
[def __rfc6455__ [@https://tools.ietf.org/html/rfc6455 rfc6455]]
[def __rfc7230__ [@https://tools.ietf.org/html/rfc7230 rfc7230]]
-[def __asio_handler_invoke__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/asio_handler_invoke.html `asio_handler_invoke`]]
-[def __asio_handler_allocate__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/asio_handler_allocate.html `asio_handler_allocate`]]
-[def __void_or_deduced__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.return_type_of_an_initiating_function ['void-or-deduced]]]
+[def __asio_handler_invoke__ [@http://www.boost.org/doc/html/boost_asio/reference/asio_handler_invoke.html `asio_handler_invoke`]]
+[def __asio_handler_allocate__ [@http://www.boost.org/doc/html/boost_asio/reference/asio_handler_allocate.html `asio_handler_allocate`]]
+[def __void_or_deduced__ [@http://www.boost.org/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.return_type_of_an_initiating_function ['void-or-deduced]]]
+[def __use_future__ [@http://www.boost.org/doc/html/boost_asio/reference/use_future_t.html `boost::asio::use_future`]]
+[def __yield_context__ [@http://www.boost.org/doc/html/boost_asio/reference/yield_context.html `boost::asio::yield_context`]]
-[def __AsyncReadStream__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/AsyncReadStream.html [*AsyncReadStream]]]
-[def __AsyncWriteStream__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/AsyncWriteStream.html [*AsyncWriteStream]]]
-[def __CompletionHandler__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/CompletionHandler.html [*CompletionHandler]]]
-[def __ConstBufferSequence__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*ConstBufferSequence]]]
-[def __Handler__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/Handler.html [*Handler]]]
-[def __MutableBufferSequence__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*MutableBufferSequence]]]
-[def __SyncReadStream__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/SyncReadStream.html [*SyncReadStream]]]
-[def __SyncWriteStream__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/SyncWriteStream.html [*SyncWriteStream]]]
+[def __AsyncReadStream__ [@http://www.boost.org/doc/html/boost_asio/reference/AsyncReadStream.html [*AsyncReadStream]]]
+[def __AsyncWriteStream__ [@http://www.boost.org/doc/html/boost_asio/reference/AsyncWriteStream.html [*AsyncWriteStream]]]
+[def __CompletionHandler__ [@http://www.boost.org/doc/html/boost_asio/reference/CompletionHandler.html [*CompletionHandler]]]
+[def __ConstBufferSequence__ [@http://www.boost.org/doc/html/boost_asio/reference/ConstBufferSequence.html [*ConstBufferSequence]]]
+[def __Handler__ [@http://www.boost.org/doc/html/boost_asio/reference/Handler.html [*Handler]]]
+[def __MutableBufferSequence__ [@http://www.boost.org/doc/html/boost_asio/reference/MutableBufferSequence.html [*MutableBufferSequence]]]
+[def __SyncReadStream__ [@http://www.boost.org/doc/html/boost_asio/reference/SyncReadStream.html [*SyncReadStream]]]
+[def __SyncWriteStream__ [@http://www.boost.org/doc/html/boost_asio/reference/SyncWriteStream.html [*SyncWriteStream]]]
[def __Body__ [link beast.ref.Body [*`Body`]]]
[def __DynamicBuffer__ [link beast.ref.DynamicBuffer [*DynamicBuffer]]]
@@ -62,6 +65,11 @@ provides implementations of the HTTP and WebSocket protocols.
][
An introduction with features, requirements, and credits.
]]
+ [[
+ [link beast.core Core Concepts]
+ ][
+ Library-wide concepts, classes, functions, and traits.
+ ]]
[[
[link beast.http Using HTTP]
][
@@ -96,6 +104,7 @@ provides implementations of the HTTP and WebSocket protocols.
]
[include overview.qbk]
+[include core.qbk]
[include http.qbk]
[include websocket.qbk]
[include examples.qbk]
diff --git a/doc/overview.qbk b/doc/overview.qbk
index 4dfc9525..5afc49e3 100644
--- a/doc/overview.qbk
+++ b/doc/overview.qbk
@@ -43,7 +43,7 @@ Beast requires:
* [*Boost.] Beast is built on Boost, especially Boost.Asio.
* [*OpenSSL.] If using TLS/Secure sockets (optional).
-[note Tested compilers: msvc-14+, gcc 5+, clang 3.6+]
+[note Tested compilers: msvc-14+, gcc 4.8+, clang 3.6+]
The library is [*header-only]. It is not necessary to add any .cpp files,
or to add commands to your build script for building Beast. To link your
diff --git a/doc/quickref.xml b/doc/quickref.xml
index 0f1fdf5c..ad6de992 100644
--- a/doc/quickref.xml
+++ b/doc/quickref.xml
@@ -29,7 +29,7 @@
Classes
- http__basic_dynamic_body
+ basic_dynamic_bodybasic_fieldsbasic_parserdynamic_body
@@ -159,7 +159,6 @@
buffers_adapterconsuming_buffersbuffered_read_stream
- errcerror_categoryerror_codeerror_condition
@@ -187,23 +186,35 @@
system_categoryto_static_string
+ Constants
+
+ errc
+ Type Traits
- is_AsyncReadStream
- is_AsyncWriteStream
- is_AsyncStream
- is_CompletionHandler
+ has_get_io_service
+ is_async_read_stream
+ is_async_write_stream
+ is_async_stream
+ is_completion_handleris_const_buffer_sequenceis_dynamic_bufferis_mutable_buffer_sequence
- is_SyncReadStream
- is_SyncStream
- is_SyncWriteStream
+ is_sync_read_stream
+ is_sync_stream
+ is_sync_write_stream
+ Helpers
+
+ allocate
+ deallocate
+ invoke
+ is_continuation
+ ConceptsAsyncStream
diff --git a/doc/types/BufferSequence.qbk b/doc/types/BufferSequence.qbk
index c008457b..9e493932 100644
--- a/doc/types/BufferSequence.qbk
+++ b/doc/types/BufferSequence.qbk
@@ -9,7 +9,7 @@
A `BufferSequence` is a type meeting either of the following requirements:
-* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*`ConstBufferSequence`]]
-* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*`MutableBufferSequence`]]
+* __ConstBufferSequence__
+* __MutableBufferSequence__
[endsect]
diff --git a/doc/types/DynamicBuffer.qbk b/doc/types/DynamicBuffer.qbk
index 0a4ef154..3ba090d4 100644
--- a/doc/types/DynamicBuffer.qbk
+++ b/doc/types/DynamicBuffer.qbk
@@ -37,8 +37,8 @@ In the table below:
* `a` denotes a value of type `X`.
* `c` denotes a (possibly const) value of type `X`.
* `n` denotes a value of type `std::size_t`.
-* `T` denotes a type meeting the requirements for [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ConstBufferSequence.html `ConstBufferSequence`].
-* `U` denotes a type meeting the requirements for [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/MutableBufferSequence.html `MutableBufferSequence`].
+* `T` denotes a type meeting the requirements for __ConstBufferSequence__.
+* `U` denotes a type meeting the requirements for __MutableBufferSequence__.
[table DynamicBuffer requirements
[[operation] [type] [semantics, pre/post-conditions]]
diff --git a/doc/types/Streams.qbk b/doc/types/Streams.qbk
index 67d60375..f8c1c7a7 100644
--- a/doc/types/Streams.qbk
+++ b/doc/types/Streams.qbk
@@ -21,14 +21,14 @@ A type modeling [*`Stream`] meets either or both of the following requirements:
A type modeling [*`AsyncStream`] meets the following requirements:
-* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/AsyncReadStream.html [*`AsyncReadStream`]]
-* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/AsyncWriteStream.html [*`AsyncWriteStream`]]
+* __AsyncReadStream__
+* __AsyncWriteStream__
[heading:SyncStream SyncStream]
A type modeling [*`SyncStream`] meets the following requirements:
-* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/SyncReadStream.html [*`SyncReadStream`]]
-* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/SyncWriteStream.html [*`SyncWriteStream`]]
+* __SyncReadStream__
+* __SyncWriteStream__
[endsect]
diff --git a/doc/websocket.qbk b/doc/websocket.qbk
index cfa78321..e22c01df 100644
--- a/doc/websocket.qbk
+++ b/doc/websocket.qbk
@@ -12,13 +12,10 @@
CreationMaking connectionsHandshaking
+ AcceptingMessages
- FramesControl Frames
- Buffers
- Asynchronous interface
- The io_service
- Thread Safety
+ Notes
''']
@@ -30,9 +27,8 @@ 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.WebSocket provides developers with a robust WebSocket implementation
-built on Boost.Asio with a consistent asynchronous model using a modern
-C++ approach.
+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]
@@ -48,11 +44,11 @@ The WebSocket protocol is described fully in
[section:creation Creation]
The interface to Beast's WebSocket implementation is a single template
-class [link beast.ref.websocket__stream `beast::websocket::stream`] which
+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
+of [link beast.ref.streams.SyncStream [*SyncStream]] if synchronous
operations are performed, or
-[link beast.ref.streams.AsyncStream [*`AsyncStream`]] if asynchronous
+[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:
@@ -85,7 +81,7 @@ beast::websocket::stream
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;
+ boost::asio::ip::tcp::socket sock;
...
beast::websocket::stream ws{std::move(sock)};
```
@@ -140,6 +136,22 @@ void do_accept(boost::asio::ip::tcp::acceptor& acceptor)
}
```
+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.
]
@@ -154,17 +166,103 @@ 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 URI of the resource to request.
-[link beast.ref.websocket__stream.handshake `handshake`] is used to send the
-request with the required host and resource strings.
+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("ws.example.com:80", "/cgi-bin/bitcoin-prices");
+ ws.handshake("localhost", "/");
```
-The [link beast.ref.websocket__stream `stream`] automatically
+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
@@ -179,10 +277,113 @@ similarly. If the handshake fails, an error is returned or exception thrown:
ws.accept();
```
-Servers that can handshake in multiple protocols may have already read data
-on the connection, or might have already received an entire HTTP request
-containing the upgrade request. Overloads of `accept` allow callers to
-pass in this additional buffered handshake data.
+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)
{
@@ -195,93 +396,6 @@ void do_accept(boost::asio::ip::tcp::socket& sock)
}
```
-Alternatively, the caller can pass an entire HTTP request if it was
-obtained elsewhere:
-```
-void do_accept(boost::asio::ip::tcp::socket& sock)
-{
- boost::asio::streambuf sb;
- beast::http::request request;
- beast::http::read(sock, sb, request);
- if(beast::http::is_upgrade(request))
- {
- websocket::stream ws{sock};
- ws.accept(request);
- ...
- }
-}
-```
-
-[heading Decorators]
-
-Often, callers may need the handshake HTTP request and response objects
-generated by the implementation to contain additional HTTP fields. Some
-examples include:
-
-* Setting the "User-Agent" or "Server" fields to a custom value
-* Announcing a subprotocol by setting the "Sec-WebSocket-Protocol" field
-* Adding fields used for HTTP's Basic Authentication
-
-To achieve this, the websocket interface provides the functions
-[link beast.ref.websocket__stream.handshake_ex `handshake_ex`],
-[link beast.ref.websocket__stream.async_handshake_ex `async_handshake_ex`],
-[link beast.ref.websocket__stream.accept_ex `accept_ex`], and
-[link beast.ref.websocket__stream.async_accept_ex `async_accept_ex`]
-which allow passing of an additional decorator parameter. This parameter is
-a function object to invoke which takes a writable reference to the handshake
-message. The function is called after the implementation creates the message,
-providing an opportunity for further modification. For client handshakes
-using
-[link beast.ref.websocket__stream.handshake_ex `handshake_ex`] or
-[link beast.ref.websocket__stream.async_handshake_ex `async_handshake_ex`],
-the message is passed as a reference to
-[link beast.ref.websocket__request_type `request_type`]. Here is an example
-that sets the user agent for a client request:
-
-```
- ws.handshake_ex("ws.example.com:80", "/",
- [](request_type& req)
- {
- req.fields.insert("User-Agent", "Beast Example");
- });
-```
-
-For server handshakes using
-[link beast.ref.websocket__stream.accept_ex `accept_ex`], and
-[link beast.ref.websocket__stream.async_accept_ex `async_accept_ex`] the function
-object receives the message as a reference to
-[link beast.ref.websocket__response_type `response_type`].
-Here is an example that sets the server field:
-
-```
- ws.accept_ex(
- [](response_type& res)
- {
- res.fields.insert("Server", "Beast Example");
- });
-```
-
-[heading Response 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,
-the functions
-[link beast.ref.websocket__stream.handshake `handshake`],
-[link beast.ref.websocket__stream.handshake_ex `handshake_ex`] or
-[link beast.ref.websocket__stream.async_handshake `async_handshake`],
-[link beast.ref.websocket__stream.async_handshake_ex `async_handshake_ex`],
-have an additional set of overloads which store the received HTTP message
-in the callers parameter, which must be a reference to type
-[link beast.ref.websocket__response_type `response_type`], like this:
-
-```
- response_type res;
- ws.handshake(res, "ws.example.com:80", "/");
- // res now contains the complete HTTP response.
-```
-
[endsect]
@@ -310,11 +424,7 @@ void echo(beast::websocket::stream& ws)
to perform other operations.
]
-[endsect]
-
-
-
-[section:frames Frames]
+[heading Frames]
Some use-cases make it impractical or impossible to buffer the entire
message ahead of time:
@@ -470,12 +580,12 @@ the fragments:
-[section:buffers Buffers]
+[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 [link beast.ref.DynamicBuffer [*`DynamicBuffer`]]. This concept is modeled on
-[@http://www.boost.org/doc/libs/1_64_0/doc/html/boost_asio/reference/basic_streambuf.html `boost::asio::basic_streambuf`].
+[@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
@@ -483,11 +593,7 @@ 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.
-[endsect]
-
-
-
-[section:async Asynchronous interface]
+[heading Asynchronous Operations]
Asynchronous versions are available for all functions:
```
@@ -513,24 +619,16 @@ void echo(websocket::stream& ws,
}
```
-[endsect]
-
-
-
-[section:io_service The io_service]
+[heading The io_service]
The creation and operation of the
-[@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/io_service.html `boost::asio::io_service`]
+[@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
+environments where threads are unavailable. Beast WebSocket itself does not
use or require threads.
-[endsect]
-
-
-
-[section:threads Thread Safety]
+[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
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index edb8e333..ad53efe0 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -5,6 +5,17 @@ GroupSources(include/beast beast)
GroupSources(examples "/")
+add_executable (echo-op
+ ${BEAST_INCLUDES}
+ echo_op.cpp
+)
+
+if (NOT WIN32)
+ target_link_libraries(echo-op ${Boost_LIBRARIES} Threads::Threads)
+else()
+ target_link_libraries(echo-op ${Boost_LIBRARIES})
+endif()
+
add_executable (http-crawl
${BEAST_INCLUDES}
${EXTRAS_INCLUDES}
diff --git a/examples/Jamfile.v2 b/examples/Jamfile
similarity index 93%
rename from examples/Jamfile.v2
rename to examples/Jamfile
index c80330b6..d4291648 100644
--- a/examples/Jamfile.v2
+++ b/examples/Jamfile
@@ -5,6 +5,10 @@
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#
+exe echo-op :
+ echo_op.cpp
+ ;
+
exe http-crawl :
http_crawl.cpp
urls_large_data.cpp
diff --git a/examples/echo_op.cpp b/examples/echo_op.cpp
new file mode 100644
index 00000000..67919cc7
--- /dev/null
+++ b/examples/echo_op.cpp
@@ -0,0 +1,230 @@
+//
+// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#include
+#include
+#include
+#include
+#include
+
+// 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 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;
+ return boost::asio::async_write(p.stream, p.buffer.data(), std::move(*this));
+
+ case 2:
+ 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;
+}
+
+// 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
+ // instead of letting the compiler generate several pages of hard
+ // to read error messages.
+ //
+ 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 BEAST_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();
+}
+
+int main()
+{
+ using address_type = boost::asio::ip::address;
+ using socket_type = boost::asio::ip::tcp::socket;
+ using endpoint_type = boost::asio::ip::tcp::endpoint;
+
+ // Create a listening socket, accept a connection, perform
+ // the echo, and then shut everything down and exit.
+ boost::asio::io_service ios;
+ socket_type sock{ios};
+ {
+ boost::asio::ip::tcp::acceptor acceptor{ios};
+ endpoint_type ep{address_type::from_string("0.0.0.0"), 0};
+ acceptor.open(ep.protocol());
+ acceptor.bind(ep);
+ acceptor.listen();
+ acceptor.accept(sock);
+ }
+ async_echo(sock,
+ [&](beast::error_code ec)
+ {
+ });
+ ios.run();
+ return 0;
+}
diff --git a/include/beast/core.hpp b/include/beast/core.hpp
index e069656d..ba7711c7 100644
--- a/include/beast/core.hpp
+++ b/include/beast/core.hpp
@@ -13,14 +13,12 @@
#include
#include
#include
-#include
#include
#include
#include
#include
#include
#include
-#include
#include
#include
#include
@@ -28,6 +26,6 @@
#include
#include
#include
-#include
+#include
#endif
diff --git a/include/beast/core/async_result.hpp b/include/beast/core/async_result.hpp
index 28889a27..31bd0cc8 100644
--- a/include/beast/core/async_result.hpp
+++ b/include/beast/core/async_result.hpp
@@ -9,8 +9,7 @@
#define BEAST_ASYNC_COMPLETION_HPP
#include
-#include
-#include
+#include
#include
#include
#include
@@ -18,11 +17,11 @@
namespace beast {
-/** An interface for customising the behaviour of an initiating function.
+/** An interface for customising the behaviour of an asynchronous initiation function.
- The async_result class is used for determining:
+ This class is used for determining:
- @li the concrete completion handler type to be called at the end of the
+ @li The concrete completion handler type to be called at the end of the
asynchronous operation;
@li the initiating function return type; and
@@ -91,12 +90,6 @@ public:
allows customization of the return type of the initiating function, and the
function signature of the final handler.
- @tparam CompletionToken A completion handler, or a user defined type
- with specializations for customizing the return type (for example,
- `boost::asio::use_future` or `boost::asio::yield_context`).
-
- @tparam Signature The callable signature of the final completion handler.
-
Example:
@code
...
@@ -150,7 +143,7 @@ struct async_completion
, result(completion_handler)
{
// CompletionToken is not invokable with the given signature
- static_assert(is_CompletionHandler<
+ static_assert(is_completion_handler<
completion_handler_type, Signature>::value,
"CompletionToken requirements not met: signature mismatch");
}
@@ -167,37 +160,11 @@ struct async_completion
CompletionToken>::type, Signature> result;
};
-/** Convert a completion token to the correct completion handler type.
-
- This helps asynchronous initiation functions to meet the
- requirements of the Extensible Asynchronous Model.
-
- @tparam CompletionToken Specifies the model used to obtain the result of
- the asynchronous operation.
-
- @tparam Signature The call signature for the completion handler type invoked
- on completion of the asynchronous operation.
-
- @see @ref async_completion, @ref async_return_type
-*/
template
using handler_type = typename beast::async_result<
typename std::decay::type,
Signature>::completion_handler_type;
-/** Determine the return value of an asynchronous initiation function
-
- This helps asynchronous initiation functions to meet the
- requirements of the Extensible Asynchronous Model.
-
- @tparam CompletionToken Specifies the model used to obtain the result of
- the asynchronous operation.
-
- @tparam Signature The call signature for the completion handler type invoked
- on completion of the asynchronous operation.
-
- @see @ref async_completion, @ref handler_type
-*/
template
using async_return_type = typename beast::async_result<
typename std::decay::type,
diff --git a/include/beast/core/bind_handler.hpp b/include/beast/core/bind_handler.hpp
index 18c26520..a4a30280 100644
--- a/include/beast/core/bind_handler.hpp
+++ b/include/beast/core/bind_handler.hpp
@@ -9,7 +9,7 @@
#define BEAST_BIND_HANDLER_HPP
#include
-#include
+#include
#include
#include
#include
@@ -24,24 +24,23 @@ namespace beast {
the returned handler, which provides the same `io_service`
execution guarantees as the original handler.
- Unlike `io_service::wrap`, the returned handler can be used in
- a subsequent call to `io_service::post` instead of
- `io_service::dispatch`, to ensure that the handler will not be
- invoked immediately by the calling function.
+ Unlike `boost::asio::io_service::wrap`, the returned handler can
+ be used in a subsequent call to `boost::asio::io_service::post`
+ instead of `boost::asio::io_service::dispatch`, to ensure that
+ the handler will not be invoked immediately by the calling
+ function.
Example:
@code
-
template
void
- do_cancel(AsyncReadStream& stream, ReadHandler&& handler)
+ signal_aborted(AsyncReadStream& stream, ReadHandler&& handler)
{
stream.get_io_service().post(
bind_handler(std::forward(handler),
boost::asio::error::operation_aborted, 0));
}
-
@endcode
@param handler The handler to wrap.
@@ -58,7 +57,7 @@ detail::bound_handler<
#endif
bind_handler(Handler&& handler, Args&&... args)
{
- static_assert(is_CompletionHandler<
+ static_assert(is_completion_handler<
Handler, void(Args...)>::value,
"Handler requirements not met");
return detail::bound_handler
diff --git a/include/beast/core/buffer_concepts.hpp b/include/beast/core/buffer_concepts.hpp
deleted file mode 100644
index 948b118b..00000000
--- a/include/beast/core/buffer_concepts.hpp
+++ /dev/null
@@ -1,55 +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)
-//
-
-#ifndef BEAST_BUFFER_CONCEPTS_HPP
-#define BEAST_BUFFER_CONCEPTS_HPP
-
-#include
-#include
-#include
-#include
-
-namespace beast {
-
-/// Determine if `T` meets the requirements of @b ConstBufferSequence.
-template
-#if BEAST_DOXYGEN
-struct is_const_buffer_sequence : std::integral_constant
-#else
-struct is_const_buffer_sequence :
- detail::is_buffer_sequence::type
-#endif
-{
-};
-
-/// Determine if `T` meets the requirements of @b DynamicBuffer.
-template
-#if BEAST_DOXYGEN
-struct is_dynamic_buffer : std::integral_constant
-#else
-struct is_dynamic_buffer :
- detail::is_dynamic_buffer::type
-#endif
-{
-};
-
-/// Determine if `T` meets the requirements of @b MutableBufferSequence.
-template
-#if BEAST_DOXYGEN
-struct is_mutable_buffer_sequence : std::integral_constant
-#else
-struct is_mutable_buffer_sequence :
- detail::is_buffer_sequence::type
-#endif
-{
-};
-
-} // beast
-
-#endif
diff --git a/include/beast/core/buffered_read_stream.hpp b/include/beast/core/buffered_read_stream.hpp
index fe380c71..2e2ff68d 100644
--- a/include/beast/core/buffered_read_stream.hpp
+++ b/include/beast/core/buffered_read_stream.hpp
@@ -10,11 +10,9 @@
#include
#include
-#include
#include
-#include
#include
-#include
+#include
#include
#include
#include
@@ -26,7 +24,7 @@ namespace beast {
This wraps a @b Stream implementation so that calls to write are
passed through to the underlying stream, while calls to read will
- first consume the input sequence stored in a @b `DynamicBuffer` which
+ first consume the input sequence stored in a @b DynamicBuffer which
is part of the object.
The use-case for this class is different than that of the
@@ -292,7 +290,7 @@ public:
std::size_t
write_some(ConstBufferSequence const& buffers)
{
- static_assert(is_SyncWriteStream::value,
+ static_assert(is_sync_write_stream::value,
"SyncWriteStream requirements not met");
return next_layer_.write_some(buffers);
}
@@ -314,7 +312,7 @@ public:
write_some(ConstBufferSequence const& buffers,
error_code& ec)
{
- static_assert(is_SyncWriteStream::value,
+ static_assert(is_sync_write_stream::value,
"SyncWriteStream requirements not met");
return next_layer_.write_some(buffers, ec);
}
diff --git a/include/beast/core/buffers_adapter.hpp b/include/beast/core/buffers_adapter.hpp
index 032a946b..2cb8eccc 100644
--- a/include/beast/core/buffers_adapter.hpp
+++ b/include/beast/core/buffers_adapter.hpp
@@ -9,16 +9,16 @@
#define BEAST_BUFFERS_ADAPTER_HPP
#include
-#include
+#include
#include
#include
namespace beast {
-/** Adapts a @b `MutableBufferSequence` into a @b `DynamicBuffer`.
+/** Adapts a @b MutableBufferSequence into a @b DynamicBuffer.
- This class wraps a @b `MutableBufferSequence` to meet the requirements
- of @b `DynamicBuffer`. Upon construction the input and output sequences are
+ This class wraps a @b MutableBufferSequence to meet the requirements
+ of @b DynamicBuffer. Upon construction the input and output sequences are
empty. A copy of the mutable buffer sequence object is stored; however,
ownership of the underlying memory is not transferred. The caller is
responsible for making sure that referenced memory remains valid
diff --git a/include/beast/core/detail/buffer_cat.hpp b/include/beast/core/detail/buffer_cat.hpp
index 60d840c2..d441f0e4 100644
--- a/include/beast/core/detail/buffer_cat.hpp
+++ b/include/beast/core/detail/buffer_cat.hpp
@@ -8,8 +8,7 @@
#ifndef BEAST_DETAIL_BUFFER_CAT_HPP
#define BEAST_DETAIL_BUFFER_CAT_HPP
-#include
-#include
+#include
#include
#include
#include
diff --git a/include/beast/core/detail/buffer_concepts.hpp b/include/beast/core/detail/buffer_concepts.hpp
deleted file mode 100644
index 6a4e830f..00000000
--- a/include/beast/core/detail/buffer_concepts.hpp
+++ /dev/null
@@ -1,180 +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)
-//
-
-#ifndef BEAST_DETAIL_BUFFER_CONCEPTS_HPP
-#define BEAST_DETAIL_BUFFER_CONCEPTS_HPP
-
-#include
-#include
-#include
-
-namespace beast {
-namespace detail {
-
-// Types that meet the requirements,
-// for use with std::declval only.
-template
-struct BufferSequence
-{
- using value_type = BufferType;
- using const_iterator = BufferType const*;
- ~BufferSequence();
- BufferSequence(BufferSequence const&) = default;
- const_iterator begin() const noexcept;
- const_iterator end() const noexcept;
-};
-using ConstBufferSequence =
- BufferSequence;
-using MutableBufferSequence =
- BufferSequence;
-
-template
-class is_buffer_sequence
-{
- template >
- static R check1(int);
- template
- static std::false_type check1(...);
- using type1 = decltype(check1(0));
-
- template::iterator_category>>
- #else
- // workaround:
- // boost::asio::detail::consuming_buffers::const_iterator
- // is not bidirectional
- std::forward_iterator_tag,
- typename std::iterator_traits<
- typename U::const_iterator>::iterator_category>>
- #endif
- static R check2(int);
- template
- static std::false_type check2(...);
- using type2 = decltype(check2(0));
-
- template().begin()),
- typename U::const_iterator>::type>
- static R check3(int);
- template
- static std::false_type check3(...);
- using type3 = decltype(check3(0));
-
- template().end()),
- typename U::const_iterator>::type>
- static R check4(int);
- template
- static std::false_type check4(...);
- using type4 = decltype(check4(0));
-
-public:
- using type = std::integral_constant::value &&
- std::is_destructible::value &&
- type1::value && type2::value &&
- type3::value && type4::value>;
-};
-
-template
-struct is_all_ConstBufferSequence
- : std::integral_constant::type::value &&
- is_all_ConstBufferSequence::value>
-{
-};
-
-template
-struct is_all_ConstBufferSequence
- : is_buffer_sequence::type
-{
-};
-
-template
-class is_dynamic_buffer
-{
- // size()
- template().size()), std::size_t>>
- static R check1(int);
- template
- static std::false_type check1(...);
- using type1 = decltype(check1(0));
-
- // max_size()
- template().max_size()), std::size_t>>
- static R check2(int);
- template
- static std::false_type check2(...);
- using type2 = decltype(check2(0));
-
- // capacity()
- template().capacity()), std::size_t>>
- static R check3(int);
- template
- static std::false_type check3(...);
- using type3 = decltype(check3(0));
-
- // data()
- template().data()),
- boost::asio::const_buffer>::type::value>>
- static R check4(int);
- template
- static std::false_type check4(...);
- using type4 = decltype(check4(0));
-
- // prepare()
- template().prepare(1)),
- boost::asio::mutable_buffer>::type::value>>
- static R check5(int);
- template
- static std::false_type check5(...);
- using type5 = decltype(check5(0));
-
- // commit()
- template().commit(1), std::true_type{})>
- static R check6(int);
- template
- static std::false_type check6(...);
- using type6 = decltype(check6(0));
-
- // consume
- template().consume(1), std::true_type{})>
- static R check7(int);
- template
- static std::false_type check7(...);
- using type7 = decltype(check7(0));
-
-public:
- using type = std::integral_constant;
-};
-
-} // detail
-} // beast
-
-#endif
diff --git a/include/beast/core/detail/stream_concepts.hpp b/include/beast/core/detail/stream_concepts.hpp
deleted file mode 100644
index c51991eb..00000000
--- a/include/beast/core/detail/stream_concepts.hpp
+++ /dev/null
@@ -1,134 +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)
-//
-
-#ifndef BEAST_DETAIL_STREAM_CONCEPTS_HPP
-#define BEAST_DETAIL_STREAM_CONCEPTS_HPP
-
-#include
-#include
-#include
-#include
-#include
-
-namespace beast {
-namespace detail {
-
-// Types that meet the requirements,
-// for use with std::declval only.
-struct StreamHandler
-{
- StreamHandler(StreamHandler const&) = default;
- void operator()(error_code ec, std::size_t);
-};
-using ReadHandler = StreamHandler;
-using WriteHandler = StreamHandler;
-
-template
-class has_get_io_service
-{
- template().get_io_service()),
- boost::asio::io_service&>>
- static R check(int);
- template
- static std::false_type check(...);
-public:
- using type = decltype(check(0));
-};
-
-template
-class is_AsyncReadStream
-{
- template().async_read_some(
- std::declval(),
- std::declval()),
- std::true_type{})>
- static R check(int);
- template
- static std::false_type check(...);
- using type1 = decltype(check(0));
-public:
- using type = std::integral_constant::type::value>;
-};
-
-template
-class is_AsyncWriteStream
-{
- template().async_write_some(
- std::declval(),
- std::declval()),
- std::true_type{})>
- static R check(int);
- template
- static std::false_type check(...);
- using type1 = decltype(check(0));
-public:
- using type = std::integral_constant::type::value>;
-};
-
-template
-class is_SyncReadStream
-{
- template().read_some(
- std::declval())),
- std::size_t>>
- static R check1(int);
- template
- static std::false_type check1(...);
- using type1 = decltype(check1(0));
-
- template().read_some(
- std::declval(),
- std::declval())), std::size_t>>
- static R check2(int);
- template
- static std::false_type check2(...);
- using type2 = decltype(check2(0));
-
-public:
- using type = std::integral_constant;
-};
-
-template
-class is_SyncWriteStream
-{
- template().write_some(
- std::declval())),
- std::size_t>>
- static R check1(int);
- template
- static std::false_type check1(...);
- using type1 = decltype(check1(0));
-
- template().write_some(
- std::declval(),
- std::declval())), std::size_t>>
- static R check2(int);
- template
- static std::false_type check2(...);
- using type2 = decltype(check2(0));
-
-public:
- using type = std::integral_constant;
-};
-
-} // detail
-} // beast
-
-#endif
diff --git a/include/beast/core/detail/sync_ostream.hpp b/include/beast/core/detail/sync_ostream.hpp
index aee673c6..40dbde38 100644
--- a/include/beast/core/detail/sync_ostream.hpp
+++ b/include/beast/core/detail/sync_ostream.hpp
@@ -8,8 +8,8 @@
#ifndef BEAST_DETAIL_SYNC_OSTREAM_HPP
#define BEAST_DETAIL_SYNC_OSTREAM_HPP
-#include
#include
+#include
#include
#include
diff --git a/include/beast/core/detail/type_traits.hpp b/include/beast/core/detail/type_traits.hpp
index f5b50c81..d3f24919 100644
--- a/include/beast/core/detail/type_traits.hpp
+++ b/include/beast/core/detail/type_traits.hpp
@@ -8,6 +8,10 @@
#ifndef BEAST_DETAIL_TYPE_TRAITS_HPP
#define BEAST_DETAIL_TYPE_TRAITS_HPP
+#include
+#include
+#include
+#include
#include
#include
#include
@@ -16,6 +20,10 @@
namespace beast {
namespace detail {
+//
+// utilities
+//
+
template
struct make_void
{
@@ -160,6 +168,288 @@ struct get_lowest_layer
has_lowest_layer::value>::type;
};
+//------------------------------------------------------------------------------
+
+//
+// buffer concepts
+//
+
+// Types that meet the requirements,
+// for use with std::declval only.
+template
+struct BufferSequence
+{
+ using value_type = BufferType;
+ using const_iterator = BufferType const*;
+ ~BufferSequence();
+ BufferSequence(BufferSequence const&) = default;
+ const_iterator begin() const noexcept;
+ const_iterator end() const noexcept;
+};
+using ConstBufferSequence =
+ BufferSequence;
+using MutableBufferSequence =
+ BufferSequence;
+
+template
+class is_buffer_sequence
+{
+ template >
+ static R check1(int);
+ template
+ static std::false_type check1(...);
+ using type1 = decltype(check1(0));
+
+ template::iterator_category>>
+ #else
+ // workaround:
+ // boost::asio::detail::consuming_buffers::const_iterator
+ // is not bidirectional
+ std::forward_iterator_tag,
+ typename std::iterator_traits<
+ typename U::const_iterator>::iterator_category>>
+ #endif
+ static R check2(int);
+ template
+ static std::false_type check2(...);
+ using type2 = decltype(check2(0));
+
+ template().begin()),
+ typename U::const_iterator>::type>
+ static R check3(int);
+ template
+ static std::false_type check3(...);
+ using type3 = decltype(check3