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 lowest_layer_type metafunction
* 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 __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/doc/http_examples.hpp]
[import ../../example/echo-op/echo_op.cpp]
@@ -121,18 +127,20 @@
[import ../../test/doc/exemplars.cpp]
[import ../../test/doc/core_snippets.cpp]
[import ../../test/doc/core_3_layers.cpp]
[import ../../test/doc/http_snippets.cpp]
[import ../../test/doc/websocket_snippets.cpp]
[include 01_intro.qbk]
[include 02_examples.qbk]
[include 03_core.qbk]
[include 04_http.qbk]
[include 05_http_examples.qbk]
[include 06_websocket.qbk]
[include 07_concepts.qbk]
[include 08_design.qbk]
[import ../../test/doc/core_1_refresher.cpp]
[import ../../test/doc/core_3_layers.cpp]
[include 01_intro/0_intro.qbk]
[include 02_examples/0_examples.qbk]
[include 03_core/0_core.qbk]
[include 04_http/0_http.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]
[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]
This library depends and builds on the networking facilities destined to become
part of the official C++ standard library. The latest draft of the proposal to
add these networking facilities is called the
[@http://cplusplus.github.io/networking-ts/draft.pdf Networking Technical Specification].
This specification is projected to become official no sooner than the year
2023. There are three implementations of this specification, which differ
cosmetically but otherwise use the same function signatures and type
declarations: the reference networking-ts implementation, the Boost.Asio
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:
This library uses the
[@http://cplusplus.github.io/networking-ts/draft.pdf Networking Technical Specification],
scheduled to become an official part of C++ no sooner than the year
2023. Three implementations exist, with cosmetic differences but
otherwise using the same function signatures and type declarations:
Boost.Asio, stand-alone Asio, and networking-ts-impl. This table shows
how a variable of the each library's `io_context` type is declared by
including the appropriate header and using a suitable namespace alias:
[table Networking Implementations
[[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
collectively as "networking." The Boost.Asio and Asio flavors of networking
provide additional functionality which is not currently proposed for C++ (but
will likely appear in a future specification). Examples of functionality
in Boost.Asio not present in the proposed networking draft include support for:
collectively as [*Networking] (or just ['networking]). The Boost.Asio and
Asio flavors of Networking provide additional features not currently proposed
for C++, but likely to appear in a future specification, such as:
* [@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/signal_set.html POSIX signals] (e.g. SIGINT, SIGABORT)
* [@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`
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.
While this library offers performant implementations of the HTTP and
WebSocket network protocols, it depends on the networking interface
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
To further ease of use, this 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
networking.
@@ -92,12 +89,12 @@ effect:
[snippet_core_1a]
[snippet_core_1b]
[include 03_core/1_refresher.qbk]
[include 03_core/2_streams.qbk]
[include 03_core/3_layers.qbk]
[include 03_core/4_buffers.qbk]
[include 03_core/5_files.qbk]
[include 03_core/6_composed.qbk]
[include 03_core/7_detect_ssl.qbk]
[include 1_refresher.qbk]
[include 2_streams.qbk]
[include 3_layers.qbk]
[include 4_buffers.qbk]
[include 5_files.qbk]
[include 6_composed.qbk]
[include 7_detect_ssl.qbk]
[endsect]

View File

@@ -9,8 +9,8 @@
[section:asio_refresher Refresher]
To use Beast effectively, a prior understanding of networking is required.
This section reviews networking concepts as a reminder and guide for further
To use Beast effectively, a prior understanding of Networking is required.
This section reviews these concepts as a reminder and guide for further
learning.
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/Winsock ['Windows Sockets 2]] ("Winsock").
C++ Networking, represented by __NetTS__ and __Asio__, provides another layer
of abstraction with features such as:
Networking in C++, represented by __Asio__,
[@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]
A
[@https://en.wikipedia.org/wiki/Data_buffer ['buffer]]
holds a contiguous sequence of bytes used when reading or writing data with
objects that perform I/O.
The networking types __const_buffer__ and __mutable_buffer__ represent
these memory regions as type-safe pointer/size pairs, as shown below:
```
net::const_buffer cb(string_view("Hello, world!", 13));
assert(string_view(reinterpret_cast<char const*>(cb.data()), cb.size()) == "Hello, world!");
holds a contiguous sequence of bytes used when performing I/O.
The types
[@boost:/doc/html/boost_asio/reference/const_buffer.html `net::const_buffer`]
and
[@boost:/doc/html/boost_asio/reference/mutable_buffer.html `net::mutable_buffer`]
represent these memory regions as type-safe pointer/size pairs:
char storage[13];
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!");
```
[code_core_1_refresher_1s]
[tip
Networking uses custom buffer types because `span<byte>` does too much.
It not only type-erases the original pointer but also recasts it to a
pointer-to-byte. The operating system doesn't care about this, but if
a user wants to send 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 networking implementations to provide
targeted features such as
`const_buffer` and `mutable_buffer` are preferred over `std::span<byte>`
and `span<byte const>` because
[@https://en.cppreference.com/w/cpp/container/span `std::span`]
does too much. It not only
type-erases the original pointer but also recasts it to a pointer-to-byte.
The operating system doesn't care about this, but if a user wants to send
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]]
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
__ConstBufferSequence__ and __MutableBufferSequence__ describe bidirectional
ranges whose value type is convertible to `const_buffer` and
`mutable_buffer` respectively. Buffer sequences may be used to transact
in multiple buffers in a single function call, a technique sometimes
referred to as
`mutable_buffer` respectively. These sequences allow transacting with
multiple buffers in a single function call, a technique called
[@https://en.wikipedia.org/wiki/Vectored_I/O ['scatter/gather I/O]].
Buffers and sequences are non-owning; copies produce shallow references and
not duplicates of the underlying memory. Each of these statements declares
Buffers and buffer sequences are non-owning; copies produce shallow references
and not duplicates of the underlying memory. Each of these statements declares
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
that supports increasing and decreasing the size of the managed buffer
sequence. Beast and networking use dynamic buffers when the amount of
storage required to perform an operation is not known ahead of time,
such as when reading a complete HTTP message.
[code_core_1_refresher_2s]
The functions
[@boost:/doc/html/boost_asio/reference/buffer_size.html `net::buffer_size`] and
[@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]
Synchronous input and output is accomplished through blocking function
calls that provide the complete results of the operation upon returning.
Such operations typically cannot be canceled and do not have a method for
setting a timeout. The __SyncReadStream__ and __SyncWriteStream__ concepts
define requirements for
calls that return with the result of the operation. Such operations typically
cannot be canceled and do not have a method for setting a timeout. The
__SyncReadStream__ and __SyncWriteStream__ concepts define requirements for
['synchronous streams]:
a portable I/O abstraction that exchanges data using buffer sequences
to represent bytes and either `error_code` or an exception to report
any failures.
a portable I/O abstraction that transfers data using buffer sequences to
represent bytes and either `error_code` or an exception to report any
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
['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
shows an algorithm which writes text and uses exceptions to indicate errors:
```
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);
}
```
[code_core_1_refresher_4]
The same algorithm may be expressed using error codes instead of exceptions:
```
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);
}
```
[code_core_1_refresher_5]
[/-----------------------------------------------------------------------------]
[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]],
which starts the operation and returns to the caller immediately. This
['outstanding]
asynchronous operation continues to make progress concurrently without
blocking. When the externally observable side effects are fully established,
a movable function object known as a
asynchronous operation proceeds concurrently without blocking the caller.
When the externally observable side effects are fully established, a movable
function object known as a
[@boost:/doc/html/boost_asio/reference/CompletionHandler.html ['completion handler]]
provided in the initiating function call is queued for execution with the
results, which may include the error code and other specific information.
@@ -174,32 +178,42 @@ An asynchronous operation is said to be
['completed]
after the completion handler is queued. The code that follows shows how some
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
operation is complete:
```
net::async_write(sock, net::const_buffer(string_view("Hello, world!")),
[](error_code ec, std::size_t bytes_transferred)
{
if(! ec)
assert(bytes_transferred == 13);
else
std::cerr << "Error: " << ec.message() << "\n";
});
```
[code_core_1_refresher_3s]
Every completion handler (also referred to as a
[@https://en.wikipedia.org/wiki/Continuation ['continuation]])
has both an
[@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
[@boost:/doc/html/boost_asio/reference/associated_executor.html ['associated executor]].
The allocator may be used to obtain temporary storage (which [*must] be
deallocated before the completion handler is invoked), while 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.
[@boost:/doc/html/boost_asio/reference/associated_executor.html ['associated executor]]
returned by
[@boost:/doc/html/boost_asio/reference/get_associated_executor.html `net::get_associated_executor`].
These associations may be specified intrusively:
[code_core_1_refresher_6]
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
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
both. This example shows an algorithm which writes some text to an
asynchronous stream:
```
template <class AsyncWriteStream, class WriteHandler>
void async_hello (AsyncWriteStream& stream, WriteHandler&& handler)
{
net::async_write (stream,
net::buffer(string_view("Hello, world!")),
std::forward<Handler>(handler));
}
```
[code_core_1_refresher_7]
[/-----------------------------------------------------------------------------]
[heading Concurrency]
@@ -245,42 +254,57 @@ without explicit locking by requiring all access to I/O objects to be
performed within a
[@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
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:
[heading Universal Model]
* 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 system
for customizing the return type of initiating functions and obtaining the
actual completion handler from a completion token is known as the
[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3747.pdf ['Universal Model for Asynchronous Operations]] (N3747).
This generic example shows the signature for an initiating function to
write some text to a stream:
```
template <class AsyncWriteStream, class WriteHandler>
auto async_hello (AsyncWriteStream& stream, WriteHandler&& handler);
```
The signature for the initiating function includes a
['completion token],
which is a generalization of completion handlers permitting user-defined
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:
]
The type of the function's return value is determined by the
[@boost:/doc/html/boost_asio/reference/async_result.html `net::async_result`]
customization point, which comes with specializations for common library
types such as `std::future` and may also be specialized for user-defined
types. The universal model also provides the
[@boost:/doc/html/boost_asio/reference/async_completion.html `net::async_completion`]
customization point for transforming
the `handler` argument (called a
[@boost:/doc/html/boost_asio/reference/asynchronous_operations/completion_token.html ['CompletionToken]]
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.
For example, when using `net::use_future` the internal handler will deliver
the result by calling `std::promise::set_value` on the promise object
returned by the initiating function.
[/-----------------------------------------------------------------------------]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -20,6 +20,7 @@ alias run-tests :
[ compile http_snippets.cpp ]
[ compile websocket_snippets.cpp ]
[ run core_examples.cpp $(TEST_MAIN) ]
[ run core_1_refresher.cpp $(TEST_MAIN) ]
[ run core_3_layers.cpp $(TEST_MAIN) ]
[ run http_examples.cpp $(TEST_MAIN) ]
;
@@ -27,6 +28,7 @@ alias run-tests :
exe fat-tests :
$(TEST_MAIN)
core_examples.cpp
core_1_refresher.cpp
core_3_layers.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 beast {
namespace {
void
snippets()
core_3_layers_snippets()
{
#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_write_stream<counted_stream<test::stream>>::value);
} // (anon)
struct core_3_layers_test
: public beast::unit_test::suite
{
@@ -246,7 +242,7 @@ struct core_3_layers_test
void
run() override
{
BEAST_EXPECT(&snippets);
BEAST_EXPECT(&core_3_layers_snippets);
BEAST_EXPECT(&set_non_blocking<net::ip::tcp::socket>);
BEAST_EXPECT(&counted_stream<test::stream>::get_executor);