diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cf696ec..e0e6c9f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 -------------------------------------------------------------------------------- diff --git a/doc/qbk/00_main.qbk b/doc/qbk/00_main.qbk index 44a39749..b08d8085 100644 --- a/doc/qbk/00_main.qbk +++ b/doc/qbk/00_main.qbk @@ -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] diff --git a/doc/qbk/01_intro.qbk b/doc/qbk/01_intro/0_intro.qbk similarity index 99% rename from doc/qbk/01_intro.qbk rename to doc/qbk/01_intro/0_intro.qbk index 28faddd6..70ac6bfe 100644 --- a/doc/qbk/01_intro.qbk +++ b/doc/qbk/01_intro/0_intro.qbk @@ -137,4 +137,4 @@ for his generous participation and source code contributions. -[include 01_intro/1_reports.qbk] +[include 1_reports.qbk] diff --git a/doc/qbk/02_examples.qbk b/doc/qbk/02_examples/0_examples.qbk similarity index 100% rename from doc/qbk/02_examples.qbk rename to doc/qbk/02_examples/0_examples.qbk diff --git a/doc/qbk/03_core.qbk b/doc/qbk/03_core/0_core.qbk similarity index 52% rename from doc/qbk/03_core.qbk rename to doc/qbk/03_core/0_core.qbk index dd33983c..28cf9869 100644 --- a/doc/qbk/03_core.qbk +++ b/doc/qbk/03_core/0_core.qbk @@ -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] diff --git a/doc/qbk/03_core/1_refresher.qbk b/doc/qbk/03_core/1_refresher.qbk index ce5efd9e..348b5786 100644 --- a/doc/qbk/03_core/1_refresher.qbk +++ b/doc/qbk/03_core/1_refresher.qbk @@ -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(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(mb.data()), mb.size()) == "Hello, world!"); -``` +[code_core_1_refresher_1s] [tip - Networking uses custom buffer types because `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 networking implementations to provide - targeted features such as + `const_buffer` and `mutable_buffer` are preferred over `std::span` + and `span` 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 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 - 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 - 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` 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` 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 - void async_hello (AsyncWriteStream& stream, WriteHandler&& handler) - { - net::async_write (stream, - net::buffer(string_view("Hello, world!")), - std::forward(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` 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 - 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. [/-----------------------------------------------------------------------------] diff --git a/doc/qbk/04_http.qbk b/doc/qbk/04_http/0_http.qbk similarity index 95% rename from doc/qbk/04_http.qbk rename to doc/qbk/04_http/0_http.qbk index 9da67ca2..bf976c65 100644 --- a/doc/qbk/04_http.qbk +++ b/doc/qbk/04_http/0_http.qbk @@ -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] diff --git a/doc/qbk/05_http_examples.qbk b/doc/qbk/05_http_examples/0_http_examples.qbk similarity index 100% rename from doc/qbk/05_http_examples.qbk rename to doc/qbk/05_http_examples/0_http_examples.qbk diff --git a/doc/qbk/06_websocket.qbk b/doc/qbk/06_websocket/0_websocket.qbk similarity index 80% rename from doc/qbk/06_websocket.qbk rename to doc/qbk/06_websocket/0_websocket.qbk index 5d00e645..825ee7b1 100644 --- a/doc/qbk/06_websocket.qbk +++ b/doc/qbk/06_websocket/0_websocket.qbk @@ -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] diff --git a/doc/qbk/07_concepts.qbk b/doc/qbk/07_concepts/0_concepts.qbk similarity index 55% rename from doc/qbk/07_concepts.qbk rename to doc/qbk/07_concepts/0_concepts.qbk index 18dcc93f..11c9c4db 100644 --- a/doc/qbk/07_concepts.qbk +++ b/doc/qbk/07_concepts/0_concepts.qbk @@ -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] diff --git a/doc/qbk/08_design.qbk b/doc/qbk/08_design/0_design.qbk similarity index 94% rename from doc/qbk/08_design.qbk rename to doc/qbk/08_design/0_design.qbk index f8070fd8..4586e101 100644 --- a/doc/qbk/08_design.qbk +++ b/doc/qbk/08_design/0_design.qbk @@ -66,9 +66,9 @@ interfaces of Beast (which have since changed). '''] -[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] diff --git a/test/doc/CMakeLists.txt b/test/doc/CMakeLists.txt index 4c253993..e7b932ad 100644 --- a/test/doc/CMakeLists.txt +++ b/test/doc/CMakeLists.txt @@ -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 diff --git a/test/doc/Jamfile b/test/doc/Jamfile index 9d6d139b..5332ea26 100644 --- a/test/doc/Jamfile +++ b/test/doc/Jamfile @@ -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 ; diff --git a/test/doc/core_1_refresher.cpp b/test/doc/core_1_refresher.cpp new file mode 100644 index 00000000..7ffe3e2c --- /dev/null +++ b/test/doc/core_1_refresher.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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( + 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( + 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 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 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 +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::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(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 +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::max(512, // under 512 is too little, + buffer.capacity() - buffer.size()), + std::min(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 + std::size_t read_some(MutableBufferSequence const& buffers); + + // Returns the number of bytes read successfully, sets the error code if a failure occurs + template + 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 + std::size_t write_some(ConstBufferSequence const& buffers); + + // Returns the number of bytes written successfully, sets the error code if a failure occurs + template + std::size_t write_some(ConstBufferSequence const& buffers, error_code& ec); +}; +//] +template +std::size_t sync_read_stream::read_some(MutableBufferSequence const&) +{ + return 0; +} +template +std::size_t sync_read_stream::read_some(MutableBufferSequence const&, error_code&) +{ + return 0; +} +template +std::size_t sync_write_stream::write_some(ConstBufferSequence const&) +{ + return 0; +} +template +std::size_t sync_write_stream::write_some(ConstBufferSequence const&, error_code&) +{ + return 0; +} +BOOST_STATIC_ASSERT(is_sync_read_stream::value); +BOOST_STATIC_ASSERT(is_sync_write_stream::value); + +//[code_core_1_refresher_4 +template +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 +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; + 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 +void async_hello (AsyncWriteStream& stream, WriteHandler&& handler) +{ + net::async_write (stream, + net::buffer("Hello, world!", 13), + std::forward(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::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))); + + BEAST_EXPECT(static_cast< + std::size_t(*)(net::ip::tcp::socket&, flat_buffer&)>( + &read_line)); + + 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)); + + BEAST_EXPECT(static_cast< + void(*)(test::stream&, error_code&)>( + &hello)); + + handler h; + h.get_allocator(); + h.get_executor(); + + BEAST_EXPECT((&async_hello)); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,doc,core_1_refresher); + +} // beast +} // boost diff --git a/test/doc/core_3_layers.cpp b/test/doc/core_3_layers.cpp index b63403cc..c1181766 100644 --- a/test/doc/core_3_layers.cpp +++ b/test/doc/core_3_layers.cpp @@ -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>::value); BOOST_STATIC_ASSERT(is_async_read_stream>::value); BOOST_STATIC_ASSERT(is_async_write_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); BEAST_EXPECT(&counted_stream::get_executor);