This commit is contained in:
Vinnie Falco
2019-02-01 04:16:05 -08:00
parent 49deee4a69
commit 74f02f8ac5
15 changed files with 597 additions and 220 deletions

View File

@@ -3,6 +3,7 @@ Version 208:
* Add get_lowest_layer free function * Add get_lowest_layer free function
* Add lowest_layer_type metafunction * Add lowest_layer_type metafunction
* Add close_socket, beast_close_socket customization * Add close_socket, beast_close_socket customization
* Doc work
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@@ -111,6 +111,12 @@
[def __flat_static_buffer_base__ [link beast.ref.boost__beast__flat_static_buffer_base `flat_static_buffer_base`]] [def __flat_static_buffer_base__ [link beast.ref.boost__beast__flat_static_buffer_base `flat_static_buffer_base`]]
[def __websocket_stream__ [link beast.ref.boost__beast__websocket__stream `websocket::stream`]] [def __websocket_stream__ [link beast.ref.boost__beast__websocket__stream `websocket::stream`]]
[/
VFALCO unfortunately quickbook wants relative paths and we have no
variables so all of the .cpp and .hpp files are placed here to keep
the paths close to absolute.
]
[import ../../example/common/detect_ssl.hpp] [import ../../example/common/detect_ssl.hpp]
[import ../../example/doc/http_examples.hpp] [import ../../example/doc/http_examples.hpp]
[import ../../example/echo-op/echo_op.cpp] [import ../../example/echo-op/echo_op.cpp]
@@ -121,18 +127,20 @@
[import ../../test/doc/exemplars.cpp] [import ../../test/doc/exemplars.cpp]
[import ../../test/doc/core_snippets.cpp] [import ../../test/doc/core_snippets.cpp]
[import ../../test/doc/core_3_layers.cpp]
[import ../../test/doc/http_snippets.cpp] [import ../../test/doc/http_snippets.cpp]
[import ../../test/doc/websocket_snippets.cpp] [import ../../test/doc/websocket_snippets.cpp]
[include 01_intro.qbk] [import ../../test/doc/core_1_refresher.cpp]
[include 02_examples.qbk] [import ../../test/doc/core_3_layers.cpp]
[include 03_core.qbk]
[include 04_http.qbk] [include 01_intro/0_intro.qbk]
[include 05_http_examples.qbk] [include 02_examples/0_examples.qbk]
[include 06_websocket.qbk] [include 03_core/0_core.qbk]
[include 07_concepts.qbk] [include 04_http/0_http.qbk]
[include 08_design.qbk] [include 05_http_examples/0_http_examples.qbk]
[include 06_websocket/0_websocket.qbk]
[include 07_concepts/0_concepts.qbk]
[include 08_design/0_design.qbk]
[include 09_releases.qbk] [include 09_releases.qbk]
[section:quickref Reference] [section:quickref Reference]

View File

@@ -137,4 +137,4 @@ for his generous participation and source code contributions.
[include 01_intro/1_reports.qbk] [include 1_reports.qbk]

View File

@@ -9,17 +9,14 @@
[section:using_io Using Networking] [section:using_io Using Networking]
This library depends and builds on the networking facilities destined to become This library uses the
part of the official C++ standard library. The latest draft of the proposal to [@http://cplusplus.github.io/networking-ts/draft.pdf Networking Technical Specification],
add these networking facilities is called the scheduled to become an official part of C++ no sooner than the year
[@http://cplusplus.github.io/networking-ts/draft.pdf Networking Technical Specification]. 2023. Three implementations exist, with cosmetic differences but
This specification is projected to become official no sooner than the year otherwise using the same function signatures and type declarations:
2023. There are three implementations of this specification, which differ Boost.Asio, stand-alone Asio, and networking-ts-impl. This table shows
cosmetically but otherwise use the same function signatures and type how a variable of the each library's `io_context` type is declared by
declarations: the reference networking-ts implementation, the Boost.Asio including the appropriate header and using a suitable namespace alias:
implementation, and the stand-alone Asio implementation. The following
table illustrates shows how an I/O context variable is declared by including
the appropriate header and using a suitable namespace alias:
[table Networking Implementations [table Networking Implementations
[[Name][Namespace and Header Example ]] [[Name][Namespace and Header Example ]]
@@ -54,31 +51,31 @@ the appropriate header and using a suitable namespace alias:
] ]
This document refers to the three implementations above interchangeably and This document refers to the three implementations above interchangeably and
collectively as "networking." The Boost.Asio and Asio flavors of networking collectively as [*Networking] (or just ['networking]). The Boost.Asio and
provide additional functionality which is not currently proposed for C++ (but Asio flavors of Networking provide additional features not currently proposed
will likely appear in a future specification). Examples of functionality for C++, but likely to appear in a future specification, such as:
in Boost.Asio not present in the proposed networking draft include support for:
* [@boost:/doc/html/boost_asio/reference/serial_port.html Serial ports] * [@boost:/doc/html/boost_asio/reference/serial_port.html Serial ports]
* [@boost:/doc/html/boost_asio/reference/local__stream_protocol.html UNIX domain sockets] * [@boost:/doc/html/boost_asio/reference/local__stream_protocol.html UNIX domain sockets]
* [@boost:/doc/html/boost_asio/reference/signal_set.html POSIX signals] (e.g. SIGINT, SIGABORT) * [@boost:/doc/html/boost_asio/reference/signal_set.html POSIX signals] (e.g. SIGINT, SIGABORT)
* [@boost:/doc/html/boost_asio/reference/ssl__stream.html TLS streams] (such as OpenSSL) * [@boost:/doc/html/boost_asio/reference/ssl__stream.html TLS streams] (such as OpenSSL)
Boost.Beast depends specifically on the Boost.Asio flavor of Networking,
although this may change in the future.
While this library offers performant implementations of the HTTP and
WebSocket network protocols, it depends on the networking interfaces
to perform general tasks such as performing domain name resolution
(DNS lookup), establishing outgoing connections, and accepting incoming
connections. Callers are responsible for interacting with networking
to initialize objects to the correct state where they are usable by
this library.
In this documentation, the example code, and the implementation, the `net` In this documentation, the example code, and the implementation, the `net`
namespace is used to qualify networking identifiers. For Boost.Beast, namespace is used to qualify Networking identifiers. For Boost.Beast,
`net` will be an alias for the `boost::asio` namespace. `net` will be an alias for the `boost::asio` namespace.
While this library offers performant implementations of the HTTP and To further ease of use, this library provides an extensive collection
WebSocket network protocols, it depends on the networking interface of types and algorithms. This section of the documentation explains these types and algorithms, provides examples
to perform tasks which are not specific to the protocol. Examples of
these tasks include performing domain name resolution (DNS lookup),
establishing outgoing connections, and accepting incoming connections.
Callers are responsible for interacting with networking to initialize
objects to the correct state where they are usable by this library.
To facilitiate interacting with networking, the library provides an
extensive collection of types and algorithms. This section of the
documentation explains these types and algorithms, provides examples
of usage, and also provides refreshers and tutorials for working with of usage, and also provides refreshers and tutorials for working with
networking. networking.
@@ -92,12 +89,12 @@ effect:
[snippet_core_1a] [snippet_core_1a]
[snippet_core_1b] [snippet_core_1b]
[include 03_core/1_refresher.qbk] [include 1_refresher.qbk]
[include 03_core/2_streams.qbk] [include 2_streams.qbk]
[include 03_core/3_layers.qbk] [include 3_layers.qbk]
[include 03_core/4_buffers.qbk] [include 4_buffers.qbk]
[include 03_core/5_files.qbk] [include 5_files.qbk]
[include 03_core/6_composed.qbk] [include 6_composed.qbk]
[include 03_core/7_detect_ssl.qbk] [include 7_detect_ssl.qbk]
[endsect] [endsect]

View File

@@ -9,8 +9,8 @@
[section:asio_refresher Refresher] [section:asio_refresher Refresher]
To use Beast effectively, a prior understanding of networking is required. To use Beast effectively, a prior understanding of Networking is required.
This section reviews networking concepts as a reminder and guide for further This section reviews these concepts as a reminder and guide for further
learning. learning.
A A
@@ -44,46 +44,37 @@ interact with networking using various flavors of interfaces such as
[@https://en.wikipedia.org/wiki/Berkeley_sockets ['Berkeley sockets]] or [@https://en.wikipedia.org/wiki/Berkeley_sockets ['Berkeley sockets]] or
[@https://en.wikipedia.org/wiki/Winsock ['Windows Sockets 2]] ("Winsock"). [@https://en.wikipedia.org/wiki/Winsock ['Windows Sockets 2]] ("Winsock").
C++ Networking, represented by __NetTS__ and __Asio__, provides another layer Networking in C++, represented by __Asio__,
of abstraction with features such as: [@https://think-async.com/Asio/ Asio], and
__NetTS__, provides a layer of abstraction to interact portably with the
operating system facilities for not just networking but general
[@https://en.wikipedia.org/wiki/Input/output ['input/output]] ("I/O").
* Deadline timers [/-----------------------------------------------------------------------------]
* Buffer sequences
* Stream concepts
* Asynchronous I/O
These concepts enable generic programming so that higher levels of abstraction
may be composed to arbitrary degree. In fact, the interfaces and concepts
offered by networking are best described as providing support for general
[@https://en.wikipedia.org/wiki/Input/output ['input/output]] ("I/O")
algorithms, including networking.
[heading Buffers] [heading Buffers]
A A
[@https://en.wikipedia.org/wiki/Data_buffer ['buffer]] [@https://en.wikipedia.org/wiki/Data_buffer ['buffer]]
holds a contiguous sequence of bytes used when reading or writing data with holds a contiguous sequence of bytes used when performing I/O.
objects that perform I/O. The types
The networking types __const_buffer__ and __mutable_buffer__ represent [@boost:/doc/html/boost_asio/reference/const_buffer.html `net::const_buffer`]
these memory regions as type-safe pointer/size pairs, as shown below: and
``` [@boost:/doc/html/boost_asio/reference/mutable_buffer.html `net::mutable_buffer`]
net::const_buffer cb(string_view("Hello, world!", 13)); represent these memory regions as type-safe pointer/size pairs:
assert(string_view(reinterpret_cast<char const*>(cb.data()), cb.size()) == "Hello, world!");
char storage[13]; [code_core_1_refresher_1s]
net::mutable_buffer mb(bytes, sizeof(storage));
std::memcpy(mb.data(), cb.data(), mb.size());
assert(string_view(reinterpret_cast<char const*>(mb.data()), mb.size()) == "Hello, world!");
```
[tip [tip
Networking uses custom buffer types because `span<byte>` does too much. `const_buffer` and `mutable_buffer` are preferred over `std::span<byte>`
It not only type-erases the original pointer but also recasts it to a and `span<byte const>` because
pointer-to-byte. The operating system doesn't care about this, but if [@https://en.cppreference.com/w/cpp/container/span `std::span`]
a user wants to send and receive an array of some other type, presenting does too much. It not only
it as an array of bytes which supports bitwise operations is unnecessary. type-erases the original pointer but also recasts it to a pointer-to-byte.
Custom buffer types also enable networking implementations to provide The operating system doesn't care about this, but if a user wants to send
targeted features such as and receive an array of some other type, presenting it as an array of bytes
which supports bitwise operations is unnecessary. Custom buffer types also
enable implementations to provide targeted features such as
[@boost:/doc/html/boost_asio/overview/core/buffers.html#boost_asio.overview.core.buffers.buffer_debugging ['buffer debugging]] [@boost:/doc/html/boost_asio/overview/core/buffers.html#boost_asio.overview.core.buffers.buffer_debugging ['buffer debugging]]
without changing the more general vocabulary types. without changing the more general vocabulary types.
] ]
@@ -91,36 +82,71 @@ these memory regions as type-safe pointer/size pairs, as shown below:
The concepts The concepts
__ConstBufferSequence__ and __MutableBufferSequence__ describe bidirectional __ConstBufferSequence__ and __MutableBufferSequence__ describe bidirectional
ranges whose value type is convertible to `const_buffer` and ranges whose value type is convertible to `const_buffer` and
`mutable_buffer` respectively. Buffer sequences may be used to transact `mutable_buffer` respectively. These sequences allow transacting with
in multiple buffers in a single function call, a technique sometimes multiple buffers in a single function call, a technique called
referred to as
[@https://en.wikipedia.org/wiki/Vectored_I/O ['scatter/gather I/O]]. [@https://en.wikipedia.org/wiki/Vectored_I/O ['scatter/gather I/O]].
Buffers and sequences are non-owning; copies produce shallow references and Buffers and buffer sequences are non-owning; copies produce shallow references
not duplicates of the underlying memory. Each of these statements declares and not duplicates of the underlying memory. Each of these statements declares
a buffer sequence: a buffer sequence:
```
net::const_buffer b1;
net::mutable_buffer b2;
std::array<net::const_buffer, 3> b3;
```
The __DynamicBuffer__ concept defines a buffer container with an interface [code_core_1_refresher_2s]
that supports increasing and decreasing the size of the managed buffer
sequence. Beast and networking use dynamic buffers when the amount of The functions
storage required to perform an operation is not known ahead of time, [@boost:/doc/html/boost_asio/reference/buffer_size.html `net::buffer_size`] and
such as when reading a complete HTTP message. [@boost:/doc/html/boost_asio/reference/buffer_copy.html `net::buffer_copy`]
determine the total number of bytes in a buffer sequence, and transfer some
or all of bytes from one buffer sequence to another respectively. The
function `buffer_size` is a customization point: user defined overloads
in foreign namespaces are possible, and callers should invoke `buffer_size`
without namespace qualification. The functions
[@boost:/doc/html/boost_asio/reference/buffer_sequence_begin.html `net::buffer_sequence_begin`] and
[@boost:/doc/html/boost_asio/reference/buffer_sequence_end.html `net::buffer_sequence_end`]
are used to obtain a pair of iterators for traversing the sequence.
Beast provides a set of buffer sequence types and algorithms such as
[link beast.ref.boost__beast__buffers_cat `buffers_cat`],
[link beast.ref.boost__beast__buffers_front `buffers_front`],
[link beast.ref.boost__beast__buffers_prefix `buffers_prefix`],
[link beast.ref.boost__beast__buffers_range `buffers_range`], and
[link beast.ref.boost__beast__buffers_suffix `buffers_suffix`].
This example returns the bytes in a buffer sequence as a string:
[code_core_1_refresher_1]
The __DynamicBuffer__ concept defines a resizable buffer sequence interface.
Algorithms may be expressed in terms of dynamic buffers when the memory
requirements are not known ahead of time, for example when reading an
HTTP message from a stream.
Beast provides a well-rounded collection of dynamic buffer types such as
[link beast.ref.boost__beast__buffers_adaptor `buffers_adaptor`],
[link beast.ref.boost__beast__flat_buffer `flat_buffer`],
[link beast.ref.boost__beast__multi_buffer `multi_buffer`], and
[link beast.ref.boost__beast__static_buffer `static_buffer`].
In the following function, the contents of a stream are read into a dynamic
buffer until it contains a newline character, using
[@boost:/doc/html/boost_asio/reference/buffers_iterator.html `net::buffers_iterator`]
to treat the contents of the buffer as a range of characters:
[code_core_1_refresher_2]
[/-----------------------------------------------------------------------------]
[heading Synchronous I/O] [heading Synchronous I/O]
Synchronous input and output is accomplished through blocking function Synchronous input and output is accomplished through blocking function
calls that provide the complete results of the operation upon returning. calls that return with the result of the operation. Such operations typically
Such operations typically cannot be canceled and do not have a method for cannot be canceled and do not have a method for setting a timeout. The
setting a timeout. The __SyncReadStream__ and __SyncWriteStream__ concepts __SyncReadStream__ and __SyncWriteStream__ concepts define requirements for
define requirements for
['synchronous streams]: ['synchronous streams]:
a portable I/O abstraction that exchanges data using buffer sequences a portable I/O abstraction that transfers data using buffer sequences to
to represent bytes and either `error_code` or an exception to report represent bytes and either `error_code` or an exception to report any
any failures. failures.
[@boost:/doc/html/boost_asio/reference/basic_stream_socket.html ['net::basic_stream_socket]]
is a synchronous stream commonly used to form TCP/IP connections.
User-defined types which meet the requirements are possible:
[code_core_1_refresher_3]
A A
['synchronous stream algorithm] ['synchronous stream algorithm]
@@ -128,35 +154,13 @@ is written as a function template accepting a stream object meeting the
named requirements for synchronous reading, writing, or both. This example named requirements for synchronous reading, writing, or both. This example
shows an algorithm which writes text and uses exceptions to indicate errors: shows an algorithm which writes text and uses exceptions to indicate errors:
``` [code_core_1_refresher_4]
template <class SyncWriteStream>
void hello (SyncWriteStream& stream)
{
net::const_buffer cb(string_view("Hello, world!"));
do
{
auto bytes_transferred = stream.write_some(cb); // may throw
cb += bytes_transferred; // adjust the pointer and size
}
while (cb.size() > 0);
}
```
The same algorithm may be expressed using error codes instead of exceptions: The same algorithm may be expressed using error codes instead of exceptions:
``` [code_core_1_refresher_5]
template <class SyncWriteStream>
void hello (SyncWriteStream& stream, error_code& ec) [/-----------------------------------------------------------------------------]
{
net::const_buffer cb(string_view("Hello, world!"));
do
{
auto bytes_transferred = stream.write_some(cb, ec);
cb += bytes_transferred; // adjust the pointer and size
}
while (cb.size() > 0 && ! ec);
}
```
[heading Asynchronous I/O] [heading Asynchronous I/O]
@@ -164,9 +168,9 @@ An asynchronous operation begins with a call to an
[@boost:/doc/html/boost_asio/reference/asynchronous_operations.html ['initiating function]], [@boost:/doc/html/boost_asio/reference/asynchronous_operations.html ['initiating function]],
which starts the operation and returns to the caller immediately. This which starts the operation and returns to the caller immediately. This
['outstanding] ['outstanding]
asynchronous operation continues to make progress concurrently without asynchronous operation proceeds concurrently without blocking the caller.
blocking. When the externally observable side effects are fully established, When the externally observable side effects are fully established, a movable
a movable function object known as a function object known as a
[@boost:/doc/html/boost_asio/reference/CompletionHandler.html ['completion handler]] [@boost:/doc/html/boost_asio/reference/CompletionHandler.html ['completion handler]]
provided in the initiating function call is queued for execution with the provided in the initiating function call is queued for execution with the
results, which may include the error code and other specific information. results, which may include the error code and other specific information.
@@ -174,32 +178,42 @@ An asynchronous operation is said to be
['completed] ['completed]
after the completion handler is queued. The code that follows shows how some after the completion handler is queued. The code that follows shows how some
text may be written to a text may be written to a
[@boost:/doc/html/boost_asio/reference/ip__tcp/socket.html `socket`] [@boost:/doc/html/boost_asio/reference/ip__tcp/socket.html `net::socket`]
asynchronously, invoking a lambda when the asynchronously, invoking a lambda when the
operation is complete: operation is complete:
```
net::async_write(sock, net::const_buffer(string_view("Hello, world!")), [code_core_1_refresher_3s]
[](error_code ec, std::size_t bytes_transferred)
{
if(! ec)
assert(bytes_transferred == 13);
else
std::cerr << "Error: " << ec.message() << "\n";
});
```
Every completion handler (also referred to as a Every completion handler (also referred to as a
[@https://en.wikipedia.org/wiki/Continuation ['continuation]]) [@https://en.wikipedia.org/wiki/Continuation ['continuation]])
has both an has both an
[@boost:/doc/html/boost_asio/overview/core/allocation.html ['associated allocator]] [@boost:/doc/html/boost_asio/overview/core/allocation.html ['associated allocator]]
returned by
[@boost:/doc/html/boost_asio/reference/get_associated_allocator.html `net::get_associated_allocator`],
and an and an
[@boost:/doc/html/boost_asio/reference/associated_executor.html ['associated executor]]. [@boost:/doc/html/boost_asio/reference/associated_executor.html ['associated executor]]
The allocator may be used to obtain temporary storage (which [*must] be returned by
deallocated before the completion handler is invoked), while the executor [@boost:/doc/html/boost_asio/reference/get_associated_executor.html `net::get_associated_executor`].
is a cheaply copyable object providing the algorithm used to invoke the These associations may be specified intrusively:
completion handler. Unless customized by the caller, a completion handler
defaults to using `std::allocator<void>` and the executor of the [code_core_1_refresher_6]
corresponding I/O object.
Or these associations may be specified non-intrusively, by specializing
the class templates
[@boost:/doc/html/boost_asio/reference/associated_allocator.html `net::associated_allocator`]
and
[@boost:/doc/html/boost_asio/reference/associated_executor.html `net::associated_executor`].
The function
[@boost:/doc/html/boost_asio/reference/bind_executor.html `net::bind_executor`]
may be used when the caller wants to change the executor of a completion
handler.
The allocator is used by the implementation to obtain any temporary storage
necessary to perform the operation. Temporary allocations are always freed
before the completion handler is invoked. The executor is a cheaply copyable
object providing the algorithm used to invoke the completion handler. Unless
customized by the caller, a completion handler defaults to using
`std::allocator<void>` and the executor of the corresponding I/O object.
Networking prescribes facilities to determine the context in which Networking prescribes facilities to determine the context in which
handlers run. Every I/O object refers to an __ExecutionContext__ for handlers run. Every I/O object refers to an __ExecutionContext__ for
@@ -218,15 +232,10 @@ is written as a templated initiating function template accepting a stream
object meeting the named requirements for asynchronous reading, writing, or object meeting the named requirements for asynchronous reading, writing, or
both. This example shows an algorithm which writes some text to an both. This example shows an algorithm which writes some text to an
asynchronous stream: asynchronous stream:
```
template <class AsyncWriteStream, class WriteHandler> [code_core_1_refresher_7]
void async_hello (AsyncWriteStream& stream, WriteHandler&& handler)
{ [/-----------------------------------------------------------------------------]
net::async_write (stream,
net::buffer(string_view("Hello, world!")),
std::forward<Handler>(handler));
}
```
[heading Concurrency] [heading Concurrency]
@@ -245,42 +254,57 @@ without explicit locking by requiring all access to I/O objects to be
performed within a performed within a
[@boost:/doc/html/boost_asio/overview/core/strands.html ['strand]]. [@boost:/doc/html/boost_asio/overview/core/strands.html ['strand]].
[heading Asynchronous Model] [/-----------------------------------------------------------------------------]
Completion handlers are native to networking but cause an inversion of the [heading Universal Model]
flow of control. Alternatives to using completion handlers include futures,
fibers, coroutines, or user-defined types. Networking supports these
alternatives with a feature that provides these hooks for customizing
initiating functions:
* Converting a custom ['CompletionToken] to a "real" handler type Because completion handlers cause an inversion of the flow of control,
sometimes other methods of attaching a continuation are desired. Networking
provides the
[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3747.pdf ['Universal Model for Asynchronous Operations]],
providing a customizable means for transforming the signature of the initiating
function to use other types of objects and methods in place of a completion
handler callback. For example to call to write a string to a socket
asynchronously, using a `std::future` to receive the number of bytes transferred
thusly looks like this:
* Creating the initiating function's result [code_core_1_refresher_4s]
This functionality is enabled by passing the variable
[@boost:/doc/html/boost_asio/reference/use_future.html `net::use_future`]
(of type
[@boost:/doc/html/boost_asio/reference/use_future_t.html `net::use_future_t<>`])
in place of the completion handler. The same `async_write` function overload
can work with a
[@https://en.wikipedia.org/wiki/Fiber_(computer_science) ['fiber]]
launched with
[@boost:/doc/html/boost_asio/reference/spawn/overload1.html `net::spawn`]:
[code_core_1_refresher_5s]
In both of these cases, an object with a specific type is used in place of
the completion handler, and the return value of the initiating function
is transformed from `void` to `std::future<std::size_t>` or `std::size_t`.
The return type transformation is supported by customization points in the
initiating function signature. Here is the signature for `net::async_write`:
[code_core_1_refresher_8]
[/ The type of the function's return value is determined by the
The system [@boost:/doc/html/boost_asio/reference/async_result.html `net::async_result`]
for customizing the return type of initiating functions and obtaining the customization point, which comes with specializations for common library
actual completion handler from a completion token is known as the types such as `std::future` and may also be specialized for user-defined
[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3747.pdf ['Universal Model for Asynchronous Operations]] (N3747). types. The universal model also provides the
This generic example shows the signature for an initiating function to [@boost:/doc/html/boost_asio/reference/async_completion.html `net::async_completion`]
write some text to a stream: customization point for transforming
``` the `handler` argument (called a
template <class AsyncWriteStream, class WriteHandler> [@boost:/doc/html/boost_asio/reference/asynchronous_operations/completion_token.html ['CompletionToken]]
auto async_hello (AsyncWriteStream& stream, WriteHandler&& handler); in this context) into an underlying completion handler to be invoked when the
``` operation is complete. This transformed, internal handler is responsible for
the finalizing step that delivers the result of the operation to the caller.
The signature for the initiating function includes a For example, when using `net::use_future` the internal handler will deliver
['completion token], the result by calling `std::promise::set_value` on the promise object
which is a generalization of completion handlers permitting user-defined returned by the initiating function.
types such as futures or coroutines to be substituted as the mechanism by
which the results of the asynchronous operation are delivered. The following
statements all call the same function to asynchronously read data from a
stream, but use a different method for receiving the results:
]
[/-----------------------------------------------------------------------------] [/-----------------------------------------------------------------------------]

View File

@@ -198,15 +198,15 @@ parts of the implementation. The layers are arranged thusly:
[http_snippet_1] [http_snippet_1]
] ]
[include 04_http/01_primer.qbk] [include 01_primer.qbk]
[include 04_http/02_message.qbk] [include 02_message.qbk]
[include 04_http/03_streams.qbk] [include 03_streams.qbk]
[include 04_http/04_serializer_streams.qbk] [include 04_serializer_streams.qbk]
[include 04_http/05_parser_streams.qbk] [include 05_parser_streams.qbk]
[include 04_http/06_serializer_buffers.qbk] [include 06_serializer_buffers.qbk]
[include 04_http/07_parser_buffers.qbk] [include 07_parser_buffers.qbk]
[include 04_http/08_chunked_encoding.qbk] [include 08_chunked_encoding.qbk]
[include 04_http/09_custom_body.qbk] [include 09_custom_body.qbk]
[include 04_http/10_custom_parsers.qbk] [include 10_custom_parsers.qbk]
[endsect] [endsect]

View File

@@ -29,13 +29,13 @@ Boost.Asio with a consistent asynchronous model using a modern C++ approach.
[ws_snippet_1] [ws_snippet_1]
] ]
[include 06_websocket/01_streams.qbk] [include 01_streams.qbk]
[include 06_websocket/02_connect.qbk] [include 02_connect.qbk]
[include 06_websocket/03_client.qbk] [include 03_client.qbk]
[include 06_websocket/04_server.qbk] [include 04_server.qbk]
[include 06_websocket/05_messages.qbk] [include 05_messages.qbk]
[include 06_websocket/06_control.qbk] [include 06_control.qbk]
[include 06_websocket/07_teardown.qbk] [include 07_teardown.qbk]
[include 06_websocket/08_notes.qbk] [include 08_notes.qbk]
[endsect] [endsect]

View File

@@ -11,14 +11,14 @@
This section describes all of the concepts defined by the library. This section describes all of the concepts defined by the library.
[include 07_concepts/Body.qbk] [include Body.qbk]
[include 07_concepts/BodyReader.qbk] [include BodyReader.qbk]
[include 07_concepts/BodyWriter.qbk] [include BodyWriter.qbk]
[include 07_concepts/BufferSequence.qbk] [include BufferSequence.qbk]
[include 07_concepts/DynamicBuffer.qbk] [include DynamicBuffer.qbk]
[include 07_concepts/Fields.qbk] [include Fields.qbk]
[include 07_concepts/FieldsWriter.qbk] [include FieldsWriter.qbk]
[include 07_concepts/File.qbk] [include File.qbk]
[include 07_concepts/Streams.qbk] [include Streams.qbk]
[endsect] [endsect]

View File

@@ -66,9 +66,9 @@ interfaces of Beast (which have since changed).
</mediaobject> </mediaobject>
'''] ''']
[include 08_design/1_http_message.qbk] [include 1_http_message.qbk]
[include 08_design/2_http_comparison.qbk] [include 2_http_comparison.qbk]
[include 08_design/3_websocket_zaphoyd.qbk] [include 3_websocket_zaphoyd.qbk]
[include 08_design/4_faq.qbk] [include 4_faq.qbk]
[endsect] [endsect]

View File

@@ -17,9 +17,11 @@ add_executable (tests-doc
${EXTRAS_FILES} ${EXTRAS_FILES}
${TEST_MAIN} ${TEST_MAIN}
Jamfile Jamfile
snippets.hpp
snippets.ipp snippets.ipp
core_examples.cpp core_examples.cpp
core_snippets.cpp core_snippets.cpp
core_1_refresher.cpp
core_3_layers.cpp core_3_layers.cpp
http_examples.cpp http_examples.cpp
http_snippets.cpp http_snippets.cpp

View File

@@ -20,6 +20,7 @@ alias run-tests :
[ compile http_snippets.cpp ] [ compile http_snippets.cpp ]
[ compile websocket_snippets.cpp ] [ compile websocket_snippets.cpp ]
[ run core_examples.cpp $(TEST_MAIN) ] [ run core_examples.cpp $(TEST_MAIN) ]
[ run core_1_refresher.cpp $(TEST_MAIN) ]
[ run core_3_layers.cpp $(TEST_MAIN) ] [ run core_3_layers.cpp $(TEST_MAIN) ]
[ run http_examples.cpp $(TEST_MAIN) ] [ run http_examples.cpp $(TEST_MAIN) ]
; ;
@@ -27,6 +28,7 @@ alias run-tests :
exe fat-tests : exe fat-tests :
$(TEST_MAIN) $(TEST_MAIN)
core_examples.cpp core_examples.cpp
core_1_refresher.cpp
core_3_layers.cpp core_3_layers.cpp
http_examples.cpp http_examples.cpp
; ;

View File

@@ -0,0 +1,347 @@
//
// Copyright (c) 2016-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)
//
// Official repository: https://github.com/boostorg/beast
//
#include "snippets.hpp"
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/beast/_experimental/test/stream.hpp>
#include <boost/beast/core/flat_buffer.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/buffers_iterator.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/use_future.hpp>
#include <boost/asio/write.hpp>
#include <algorithm>
#include <assert.h>
namespace boost {
namespace beast {
namespace {
void
snippets()
{
#include "snippets.ipp"
{
//[code_core_1_refresher_1s
net::const_buffer cb("Hello, world!", 13);
assert(string_view(reinterpret_cast<char const*>(
cb.data()), cb.size()) == "Hello, world!");
char storage[13];
net::mutable_buffer mb(storage, sizeof(storage));
std::memcpy(mb.data(), cb.data(), mb.size());
assert(string_view(reinterpret_cast<char const*>(
mb.data()), mb.size()) == "Hello, world!");
//]
}
{
//[code_core_1_refresher_2s
net::const_buffer b1; // a ConstBufferSequence by definition
net::mutable_buffer b2; // a MutableBufferSequence by definition
std::array<net::const_buffer, 3> b3; // A ConstBufferSequence by named requirements
//]
}
{
//[code_core_1_refresher_3s
// initiate an asynchronous write operation
net::async_write(sock, net::const_buffer("Hello, world!", 13),
[](error_code ec, std::size_t bytes_transferred)
{
// this lambda is invoked when the write operation completes
if(! ec)
assert(bytes_transferred == 13);
else
std::cerr << "Error: " << ec.message() << "\n";
});
// meanwhile, the operation is outstanding and execution continues from here
//]
}
{
//[code_core_1_refresher_4s
std::future<std::size_t> f = net::async_write(sock,
net::const_buffer("Hello, world!", 13), net::use_future);
//]
}
{
//[code_core_1_refresher_5s
net::spawn(
[&sock](net::yield_context yield)
{
std::size_t bytes_transferred = net::async_write(sock,
net::const_buffer("Hello, world!", 13), yield);
(void)bytes_transferred;
});
//]
}
}
//[code_core_1_refresher_1
template <class ConstBufferSequence>
std::string string_from_buffers (ConstBufferSequence const& buffers)
{
// check that the type meets the requirements using the provided type traits
static_assert(
net::is_const_buffer_sequence<ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
// optimization: reserve all the space for the string first
std::string result;
using net::buffer_size; // buffer_size is a customization point,
result.reserve(buffer_size(buffers)); // called without namespace qualification
// iterate over each buffer in the sequence and append it to the string
for(auto it = net::buffer_sequence_begin(buffers); // returns an iterator to beginning of the sequence
it != net::buffer_sequence_end(buffers);) // returns a past-the-end iterator to the sequence
{
// A buffer sequence iterator's value_type is always convertible to net::const_buffer
net::const_buffer buffer = *it++;
// A cast is always required to out-out of type-safety
result.append(static_cast<char const*>(buffer.data()), buffer.size());
}
return result;
}
//]
//[code_core_1_refresher_2
// Read a line ending in '\n' from a socket, returning
// the number of characters up to but not including the newline
template <class DynamicBuffer>
std::size_t read_line(net::ip::tcp::socket& sock, DynamicBuffer& buffer)
{
// this alias keeps things readable
using range = net::buffers_iterator<
typename DynamicBuffer::const_buffers_type>;
for(;;)
{
// get iterators representing the range of characters in the buffer
auto begin = range::begin(buffer.data());
auto end = range::end(buffer.data());
// search for "\n" and return if found
auto pos = std::find(begin, end, '\n');
if(pos != range::end(buffer.data()))
return std::distance(begin, end);
// Determine the number of bytes to read,
// using available capacity in the buffer first.
std::size_t bytes_to_read = std::min<std::size_t>(
std::max<std::size_t>(512, // under 512 is too little,
buffer.capacity() - buffer.size()),
std::min<std::size_t>(65536, // and over 65536 is too much.
buffer.max_size() - buffer.size()));
// Read up to bytes_to_read bytes into the dynamic buffer
buffer.commit(sock.read_some(buffer.prepare(bytes_to_read)));
}
}
//]
//[code_core_1_refresher_3
// Meets the requirements of SyncReadStream
struct sync_read_stream
{
// Returns the number of bytes read upon success, otherwise throws an exception
template <class MutableBufferSequence>
std::size_t read_some(MutableBufferSequence const& buffers);
// Returns the number of bytes read successfully, sets the error code if a failure occurs
template <class MutableBufferSequence>
std::size_t read_some(MutableBufferSequence const& buffers, error_code& ec);
};
// Meets the requirements of SyncWriteStream
struct sync_write_stream
{
// Returns the number of bytes written upon success, otherwise throws an exception
template <class ConstBufferSequence>
std::size_t write_some(ConstBufferSequence const& buffers);
// Returns the number of bytes written successfully, sets the error code if a failure occurs
template <class ConstBufferSequence>
std::size_t write_some(ConstBufferSequence const& buffers, error_code& ec);
};
//]
template<class MutableBufferSequence>
std::size_t sync_read_stream::read_some(MutableBufferSequence const&)
{
return 0;
}
template<class MutableBufferSequence>
std::size_t sync_read_stream::read_some(MutableBufferSequence const&, error_code&)
{
return 0;
}
template<class ConstBufferSequence>
std::size_t sync_write_stream::write_some(ConstBufferSequence const&)
{
return 0;
}
template<class ConstBufferSequence>
std::size_t sync_write_stream::write_some(ConstBufferSequence const&, error_code&)
{
return 0;
}
BOOST_STATIC_ASSERT(is_sync_read_stream<sync_read_stream>::value);
BOOST_STATIC_ASSERT(is_sync_write_stream<sync_write_stream>::value);
//[code_core_1_refresher_4
template <class SyncWriteStream>
void hello (SyncWriteStream& stream)
{
net::const_buffer cb("Hello, world!", 13);
do
{
auto bytes_transferred = stream.write_some(cb); // may throw
cb += bytes_transferred; // adjust the pointer and size
}
while (cb.size() > 0);
}
//]
//[code_core_1_refresher_5
template <class SyncWriteStream>
void hello (SyncWriteStream& stream, error_code& ec)
{
net::const_buffer cb("Hello, world!", 13);
do
{
auto bytes_transferred = stream.write_some(cb, ec);
cb += bytes_transferred; // adjust the pointer and size
}
while (cb.size() > 0 && ! ec);
}
//]
//[code_core_1_refresher_6
// Intrusively specify an associated allocator and executor
struct handler
{
using allocator_type = std::allocator<char>;
allocator_type get_allocator() const noexcept;
using executor_type = net::io_context::executor_type;
executor_type get_executor() const noexcept;
void operator()(error_code, std::size_t);
};
//]
inline auto handler::get_allocator() const noexcept ->
allocator_type
{
return {};
}
inline auto handler::get_executor() const noexcept ->
executor_type
{
static net::io_context ioc;
return ioc.get_executor();
}
inline void handler::operator()(error_code, std::size_t)
{
}
//[code_core_1_refresher_7
template <class AsyncWriteStream, class WriteHandler>
void async_hello (AsyncWriteStream& stream, WriteHandler&& handler)
{
net::async_write (stream,
net::buffer("Hello, world!", 13),
std::forward<WriteHandler>(handler));
}
//]
//[code_core_1_refresher_8
template<
class AsyncWriteStream,
class ConstBufferSequence,
class WriteHandler>
auto
async_write(
AsyncWriteStream& stream,
ConstBufferSequence const& buffers,
WriteHandler&& handler) ->
typename net::async_result< // return-type customization point
typename std::decay<WriteHandler>::type, // type used to specialize async_result
void(error_code, std::size_t) // signature of the corresponding completion handler
>::return_type
{
net::async_completion<
WriteHandler, // completion handler customization point
void(error_code, std::size_t) // signature of the corresponding completion handler
> init(handler); // variable which holds the corresponding completion handler
(void)init.completion_handler; // the underlying completion handler used for the operation
// ...launch the operation (omitted for clarity)
return init.result.get();
}
//]
} // (anon)
struct core_1_refresher_test
: public beast::unit_test::suite
{
void
run() override
{
BEAST_EXPECT(&snippets);
BEAST_EXPECT((static_cast<
std::string(*)(net::const_buffer const&)>(
&string_from_buffers<net::const_buffer>)));
BEAST_EXPECT(static_cast<
std::size_t(*)(net::ip::tcp::socket&, flat_buffer&)>(
&read_line<flat_buffer>));
BEAST_EXPECT(static_cast<
std::size_t(sync_read_stream::*)(
net::mutable_buffer const&)>(
&sync_read_stream::read_some));
BEAST_EXPECT(static_cast<
std::size_t(sync_read_stream::*)(
net::mutable_buffer const&, error_code&)>(
&sync_read_stream::read_some));
BEAST_EXPECT(static_cast<
std::size_t(sync_write_stream::*)(
net::const_buffer const&)>(
&sync_write_stream::write_some));
BEAST_EXPECT(static_cast<
std::size_t(sync_write_stream::*)(
net::const_buffer const&, error_code&)>(
&sync_write_stream::write_some));
BEAST_EXPECT(static_cast<
void(*)(test::stream&)>(
&hello<test::stream>));
BEAST_EXPECT(static_cast<
void(*)(test::stream&, error_code&)>(
&hello<test::stream>));
handler h;
h.get_allocator();
h.get_executor();
BEAST_EXPECT((&async_hello<test::stream, handler>));
}
};
BEAST_DEFINE_TESTSUITE(beast,doc,core_1_refresher);
} // beast
} // boost

View File

@@ -22,10 +22,8 @@
namespace boost { namespace boost {
namespace beast { namespace beast {
namespace {
void void
snippets() core_3_layers_snippets()
{ {
#include "snippets.ipp" #include "snippets.ipp"
{ {
@@ -231,8 +229,6 @@ BOOST_STATIC_ASSERT(is_sync_write_stream<counted_stream<test::stream>>::value);
BOOST_STATIC_ASSERT(is_async_read_stream<counted_stream<test::stream>>::value); BOOST_STATIC_ASSERT(is_async_read_stream<counted_stream<test::stream>>::value);
BOOST_STATIC_ASSERT(is_async_write_stream<counted_stream<test::stream>>::value); BOOST_STATIC_ASSERT(is_async_write_stream<counted_stream<test::stream>>::value);
} // (anon)
struct core_3_layers_test struct core_3_layers_test
: public beast::unit_test::suite : public beast::unit_test::suite
{ {
@@ -246,7 +242,7 @@ struct core_3_layers_test
void void
run() override run() override
{ {
BEAST_EXPECT(&snippets); BEAST_EXPECT(&core_3_layers_snippets);
BEAST_EXPECT(&set_non_blocking<net::ip::tcp::socket>); BEAST_EXPECT(&set_non_blocking<net::ip::tcp::socket>);
BEAST_EXPECT(&counted_stream<test::stream>::get_executor); BEAST_EXPECT(&counted_stream<test::stream>::get_executor);