diff --git a/doc/0_main.qbk b/doc/0_main.qbk index 61b23914..bf0676b2 100644 --- a/doc/0_main.qbk +++ b/doc/0_main.qbk @@ -88,7 +88,6 @@ [include 1_overview.qbk] [include 2_examples.qbk] [include 3_0_core.qbk] -[include 4_0_network.qbk] [include 5_00_http.qbk] [include 6_0_http_examples.qbk] [include 7_0_websocket.qbk] diff --git a/doc/1_overview.qbk b/doc/1_overview.qbk index ccae7a2b..6d022224 100644 --- a/doc/1_overview.qbk +++ b/doc/1_overview.qbk @@ -7,15 +7,10 @@ [section:overview Introduction] -[important - Beast is a cross-platform, header-only C++11 library for - [*low-level HTTP/1, WebSocket, and network protocol] programming - using the consistent asynchronous model of __Asio__. Beast is - not an HTTP client or HTTP server, but it can be used to build - those things. It is a foundation for writing interoperable - libraries by providing HTTP vocabulary types and algorithms. The - examples show how client and server applications might be built. -] +Beast is a C++ header-only library serving as a foundation for writing +interoperable networking libraries by providing [*low-level HTTP/1, +WebSocket, and networking protocol] vocabulary types and algorithms +using the consistent asynchronous model of __Asio__. This library is designed for: @@ -30,46 +25,36 @@ This library is designed for: * [*Basis for Further Abstraction.] Components are well-suited for building upon. -[heading Audience] - -Beast is for network programmers who have some familiarity with -__Asio__. In particular, users who wish to write asynchronous programs -with Beast should already know how to use Asio sockets and streams, -and should know how to create concurrent network programs using -Asio callbacks or coroutines. +Beast is not an HTTP client or HTTP server, but it can be used to build +those things. [heading Motivation] -An absence of high quality C++ network protocol libraries has led -to a patchwork of open-source solutions lacking suitable conciseness -and expressive power for standardization. Part of this problem is a -lack of common C++ networking interfaces. This is changing soon with -the Networking Technical Specification (__N4588__): a uniform interface -on track to become standardized. This technical specification is modeled -closely after Boost.Asio. +Beast empowers users to create their own libraries, clients, and servers +using HTTP/1 and WebSocket. Code will be easier and faster to implement, +understand, and maintain, because Beast takes care of the low-level +protocol details. The HTTP and WebSocket protocols drive most of the World Wide Web. Every web browser implements these protocols to load webpages and to enable client side programs (often written in JavaScript) to communicate interactively. C++ benefits greatly from having a standardized implementation of these protocols. -[note - The Beast roadmap includes a port to the networking - interface based on __N4588__. -] - [heading Requirements] +This library is for programmers familiar with __Asio__. Users who +wish to use asynchronous interfaces should already know how to +create concurrent network programs using callbacks or coroutines. + Beast requires: * [*C++11:] Robust support for most language features. -* [*Boost:] Boost.Asio and some other parts of Boost. +* [*Boost:] Beast only works with Boost, not stand-alone Asio * [*OpenSSL:] Optional, for using TLS/Secure sockets. -[note Supported compilers: msvc-14+, gcc 4.8+, clang 3.6+] +Supported compilers: msvc-14+, gcc 4.8+, clang 3.6+ -This library is [*header-only]. To link a program using Beast -successfully, add the +Sources are [*header-only]. To link a program using Beast successfully, add the [@http://www.boost.org/libs/system/doc/reference.html Boost.System] library to the list of linked libraries. If you use coroutines you'll also need the @@ -78,13 +63,6 @@ library. Please visit the [@http://www.boost.org/doc/ Boost documentation] for instructions on how to do this for your particular build system. -[note - Beast does not compile using the - [@https://github.com/chriskohlhoff/asio stand-alone Asio], - since it relies on other Boost parts. There are no immediate - plans to offer a version that works with stand-alone Asio. -] - [heading Credits] Boost.Asio is the inspiration behind which all of the interfaces and diff --git a/doc/3_0_core.qbk b/doc/3_0_core.qbk index 7e74b6d9..035fed75 100644 --- a/doc/3_0_core.qbk +++ b/doc/3_0_core.qbk @@ -26,6 +26,7 @@ with descriptions. [include 3_1_asio.qbk] [include 3_2_streams.qbk] [include 3_3_buffers.qbk] -[include 3_4_async.qbk] +[include 3_4_composed.qbk] +[include 3_5_detect_ssl.qbk] [endsect] diff --git a/doc/3_1_asio.qbk b/doc/3_1_asio.qbk index fb70c99a..41a01f70 100644 --- a/doc/3_1_asio.qbk +++ b/doc/3_1_asio.qbk @@ -15,13 +15,6 @@ left to the interfaces already existing on the underlying streams. ] -Library stream algorithms require a __socket__, __ssl_stream__, or other -__Stream__ object that has already established communication with an -endpoint. This example is provided as a reminder of how to work with -sockets: - -[snippet_core_2] - Throughout this documentation identifiers with the following names have special meaning: @@ -59,4 +52,11 @@ special meaning: ]] ] +Library stream algorithms require a __socket__, __ssl_stream__, or other +__Stream__ object that has already established communication with an +endpoint. This example is provided as a reminder of how to work with +sockets: + +[snippet_core_2] + [endsect] diff --git a/doc/3_2_streams.qbk b/doc/3_2_streams.qbk index ed52112d..7b6d49d1 100644 --- a/doc/3_2_streams.qbk +++ b/doc/3_2_streams.qbk @@ -5,7 +5,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ] -[section:streams Using Streams] +[section:streams Stream Types] A __Stream__ is a communication channel where data is transferred as an ordered sequence of octet buffers. Streams are either synchronous diff --git a/doc/3_3_buffers.qbk b/doc/3_3_buffers.qbk index 0548ef2e..ddf11fa4 100644 --- a/doc/3_3_buffers.qbk +++ b/doc/3_3_buffers.qbk @@ -5,7 +5,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ] -[section:buffers Using Buffers] +[section:buffers Buffer Types] __Asio__ provides the __ConstBufferSequence__ and __MutableBufferSequence__ concepts, whose models provide ranges of buffers, as well as the __streambuf__ diff --git a/doc/3_4_async.qbk b/doc/3_4_composed.qbk similarity index 65% rename from doc/3_4_async.qbk rename to doc/3_4_composed.qbk index ef282b5e..ff2c3768 100644 --- a/doc/3_4_async.qbk +++ b/doc/3_4_composed.qbk @@ -5,7 +5,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ] -[section:async Asynchronous I/O] +[section:composed Composed Operations] Asynchronous operations are started by calling a free function or member function known as an ['asynchronous initiation function]. The initiation @@ -101,4 +101,56 @@ available: ]] ] +[heading Example: Asynchronous Echo] + +Here we develop an asynchronous composed operation called [*echo]. +This operation will read up to the first newline on a stream, and +then write the same line including the newline back on the stream. +The implementation performs both reading and writing, and has a +non-trivially-copyable state. +First we define the input parameters and results, then declare our +initiation function. For our echo operation the only inputs are the +stream and the completion token. The output is the error code which +is usually included in all completion handler signatures. + +[core_sample_echo_op_1] + +Now that we have a declaration, we will define the body of the function. +We want to achieve the following goals: perform static type checking on +the input parameters, set up the return value as per __N3747__, and launch +the composed operation by constructing the object and invoking it. + +[core_sample_echo_op_2] + +The initiation function contains a few relatively simple parts. There is +the customization of the return value type, static type checking, building +the return value type using the helper, and creating and launching the +composed operation object. The [*`echo_op`] object does most of the work +here, and has a somewhat non-trivial structure. This structure is necessary +to meet the stringent requirements of composed operations (described in more +detail in the __Asio__ documentation). We will touch on these requirements +without explaining them in depth. + +Here is the boilerplate present in all composed operations written +in this style: + +[core_sample_echo_op_3] + +We have the common boilerplate for a composed operation and now we just need +to implement the function call operator. Our strategy is to make our composed +object meet the requirements of a completion handler by being copyable (also +movable), and by providing the function call operator with the correct +signature. Rather than using `std::bind` or `boost::bind`, which destroys +the type information and therefore breaks the allocation and invocation +hooks, we will simply pass `std::move(*this)` as the completion handler +parameter for any operations that we initiate. For the move to work correctly, +care must be taken to ensure that no access to data members are made after the +move takes place. Here is the implementation of the function call operator for +this echo operation: + +[core_sample_echo_op_4] + +A complete, runnable version of this example may be found in the examples +directory. + [endsect] diff --git a/doc/4_1_detect_tls.qbk b/doc/3_5_detect_ssl.qbk similarity index 92% rename from doc/4_1_detect_tls.qbk rename to doc/3_5_detect_ssl.qbk index 1b775685..afe75fda 100644 --- a/doc/4_1_detect_tls.qbk +++ b/doc/3_5_detect_ssl.qbk @@ -5,13 +5,13 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ] -[section:detect_tls TLS/SSL Detector Composed Operation] +[section:detect_tls Example: Detect SSL] In this example we will build a simple function to detect the presence -of the TLS handshake given an input buffer sequence. Then we build on +of the SSL handshake given an input buffer sequence. Then we build on the example by adding synchronous stream algorithms. Finally, we implemement an asynchronous detection function using a composed operation. -This SSL detector may be used to allow a server to accept both TLS and +This SSL detector may be used to allow a server to accept both SSL/TLS and unencrypted connections at the same port. Here is the declaration for a function to detect the SSL client handshake. diff --git a/doc/4_0_network.qbk b/doc/4_0_network.qbk deleted file mode 100644 index 8274c964..00000000 --- a/doc/4_0_network.qbk +++ /dev/null @@ -1,17 +0,0 @@ -[/ - Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) - - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -] - -[section:network Network Examples] - -These examples in this section are working functions that may be found -in the examples directory. They demonstrate the usage of the library -for a variety of scenarios. - -[include 4_1_detect_tls.qbk] -[include 4_2_echo.qbk] - -[endsect] diff --git a/doc/4_2_echo.qbk b/doc/4_2_echo.qbk deleted file mode 100644 index f9d51a95..00000000 --- a/doc/4_2_echo.qbk +++ /dev/null @@ -1,61 +0,0 @@ -[/ - Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) - - Distributed under the Boost Software License, Version 1.0. (See accompanying - file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -] - -[section:echo Echo Composed Operation] - -Here we developed a more advanced asynchronous composed operation called -[*echo]. This operation will read up to the first newline on a stream, and -then write the same line including the newline back on the stream. The -implementation performs both reading and writing, and has a -non-trivially-copyable state. - -First we define the input parameters and results, then declare our initiation -function. For our echo operation the only inputs are the stream and the -completion token. The output is the error code which is usually included in -all completion handler signatures. - -[core_sample_echo_op_1] - -Now that we have a declaration, we will define the body of the function. We -want to achieve the following goals: perform static type checking on the input -parameters, set up the return value as per __N3747__, and launch the composed -operation by constructing the object and invoking it. - -[core_sample_echo_op_2] - -The initiation function contains a few relatively simple parts. There is the -customization of the return value type, static type checking, building the -return value type using the helper, and creating and launching the composed -operation object. The [*`echo_op`] object does most of the work here, and has -a somewhat non-trivial structure. This structure is necessary to meet the -stringent requirements of composed operations (described in more detail in -the __Asio__ documentation). We will touch on these requirements without -explaining them in depth. - -First we will create boilerplate which is present in all composed operations -written in this style: - -[core_sample_echo_op_3] - -We have the common boilerplate for a composed operation and now we just need -to implement the function call operator. Our strategy is to make our composed -object meet the requirements of a completion handler by being copyable (also -movable), and by providing the function call operator with the correct -signature. Rather than using `std::bind` or `boost::bind`, which destroys -the type information and therefore breaks the allocation and invocation -hooks, we will simply pass `std::move(*this)` as the completion handler -parameter for any operations that we initiate. For the move to work correctly, -care must be taken to ensure that no access to data members are made after the -move takes place. Here is the implementation of the function call operator for -this echo operation: - -[core_sample_echo_op_4] - -A complete, runnable version of this example may be found in the examples -directory. - -[endsect] diff --git a/doc/5_06_serializer_buffers.qbk b/doc/5_06_serializer_buffers.qbk index 78c2dc55..be9823ca 100644 --- a/doc/5_06_serializer_buffers.qbk +++ b/doc/5_06_serializer_buffers.qbk @@ -9,7 +9,7 @@ An instance of __serializer__ can be invoked directly, without using the provided stream operations. This could be useful for implementing -algorithms on streams whose interface does not conform to __Stream__. +algorithms on objects whose interface does not conform to __Stream__. For example, a [@https://github.com/libuv/libuv *libuv* socket]. The serializer interface is interactive; the caller invokes it repeatedly diff --git a/doc/5_07_parser_buffers.qbk b/doc/5_07_parser_buffers.qbk index 8d19e5fb..d5243fe7 100644 --- a/doc/5_07_parser_buffers.qbk +++ b/doc/5_07_parser_buffers.qbk @@ -9,7 +9,7 @@ A subclass of __basic_parser__ can be invoked directly, without using the provided stream operations. This could be useful for implementing -algorithms on objects whose interface does not conform to any __Stream__. +algorithms on objects whose interface does not conform to __Stream__. For example, a [@http://zeromq.org/ *ZeroMQ* socket]. The basic parser interface is interactive; the caller invokes the function diff --git a/examples/doc_core_samples.hpp b/examples/doc_core_samples.hpp index 8a1b700a..e51ebb7c 100644 --- a/examples/doc_core_samples.hpp +++ b/examples/doc_core_samples.hpp @@ -147,12 +147,18 @@ detect_ssl( // The algorithm should never need more than 4 bytes BOOST_ASSERT(buffer.size() < 4); - // We need more bytes, but no more than four total. - buffer.commit(stream.read_some(buffer.prepare(4 - buffer.size()), ec)); + // Create up to 4 bytes of space in the buffer's output area. + auto const mutable_buffer = buffer.prepare(4 - buffer.size()); + + // Try to fill our buffer by reading from the stream + std::size_t const bytes_transferred = stream.read_some(mutable_buffer, ec); // Check for an error if(ec) break; + + // Commit what we read into the buffer's input area. + buffer.commit(bytes_transferred); } // error diff --git a/examples/echo_op.cpp b/examples/echo_op.cpp index 3d740fdf..56bc3268 100644 --- a/examples/echo_op.cpp +++ b/examples/echo_op.cpp @@ -13,11 +13,51 @@ //[core_sample_echo_op_1 -// Read a line and echo it back -// -template -beast::async_return_type -async_echo(AsyncStream& stream, CompletionToken&& token); +/** Asynchronously read a line and echo it back. + + This function is used to asynchronously read a line ending + in a carriage-return linefeed ("CRLF") from the stream, + and then write it back. The function call always returns + immediately. The asynchronous operation will continue until + one of the following conditions is true: + + @li A line was read in and sent back on the stream + + @li An error occurs. + + This operation is implemented in terms of one or more calls to + the stream's `async_read_some` and `async_write_some` functions, + and is known as a composed operation. The program must + ensure that the stream performs no other operations until this + operation completes. The implementation may read additional octets + that lie past the end of the line being read. These octets are + silently discarded. + + @param The stream to operate on. The type must meet the + requirements of @b AsyncReadStream and @AsyncWriteStream + + @param token The completion token to use. If this is a + completion handler, copies will be made as required. + The signature of the handler must be: + @code + void handler( + error_code& ec // result of operation + ); + @endcode + Regardless of whether the asynchronous operation completes + immediately or not, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `boost::asio::io_service::post`. +*/ +template< + class AsyncStream, + class CompletionToken> +beast::async_return_type< /*< The [link beast.ref.async_return_type `async_return_type`] customizes the return value based on the completion token >*/ + CompletionToken, + void(beast::error_code)> /*< This is the signature for the completion handler >*/ +async_echo( + AsyncStream& stream, + CompletionToken&& token); //] diff --git a/include/beast/websocket/stream.hpp b/include/beast/websocket/stream.hpp index d6985dde..9fe4d2fb 100644 --- a/include/beast/websocket/stream.hpp +++ b/include/beast/websocket/stream.hpp @@ -42,10 +42,6 @@ using response_type = http::response; The @ref stream class template provides asynchronous and blocking message-oriented functionality necessary for clients and servers to utilize the WebSocket protocol. - - @par Thread Safety - @e Distinct @e objects: Safe.@n - @e Shared @e objects: Unsafe. For asynchronous operations, the application must ensure that they are are all performed within the same implicit @@ -56,6 +52,12 @@ using response_type = http::response; To use the @ref stream template with an `ip::tcp::socket`, you would write: + @tparam NextLayer The type representing the next layer, to which + data will be read and written during operations. For synchronous + operations, the type must support the @b SyncStream concept. + For asynchronous operations, the type must support the + @b AsyncStream concept. + @code websocket::stream ws{io_service}; @endcode @@ -65,11 +67,9 @@ using response_type = http::response; websocket::stream ws{sock}; @endcode - @tparam NextLayer The type representing the next layer, to which - data will be read and written during operations. For synchronous - operations, the type must support the @b SyncStream concept. - For asynchronous operations, the type must support the - @b AsyncStream concept. + @par Thread Safety + @e Distinct @e objects: Safe.@n + @e Shared @e objects: Unsafe. @note A stream object must not be moved or destroyed while there are pending asynchronous operations associated with it. @@ -95,7 +95,7 @@ public: using lowest_layer_type = typename get_lowest_layer::type; - /** Move-construct a stream. + /** Move constructor If @c NextLayer is move constructible, this function will move-construct a new stream from the existing stream. @@ -105,17 +105,17 @@ public: */ stream(stream&&) = default; - /** Move assignment. + /** Move assignment - If `NextLayer` is move constructible, this function - will move-construct a new stream from the existing stream. + If `NextLayer` is move assignable, this function + will move-assign a new stream from the existing stream. @note The behavior of move assignment on or from streams with active or pending operations is undefined. */ stream& operator=(stream&&) = default; - /** Construct a WebSocket stream. + /** Constructor This constructor creates a websocket stream and initializes the next layer object. @@ -130,22 +130,21 @@ public: explicit stream(Args&&... args); - /** Destructor. + /** Destructor @note A stream object must not be destroyed while there are pending asynchronous operations associated with it. */ ~stream() = default; - /** Get the io_service associated with the stream. + /** Return the `io_service` associated with the stream - This function may be used to obtain the io_service object + This function may be used to obtain the `io_service` object that the stream uses to dispatch handlers for asynchronous operations. @return A reference to the io_service object that the stream - will use to dispatch handlers. Ownership is not transferred - to the caller. + will use to dispatch handlers. */ boost::asio::io_service& get_io_service() @@ -153,13 +152,13 @@ public: return stream_.get_io_service(); } - /** Get a reference to the next layer. + /** Get a reference to the next layer This function returns a reference to the next layer in a stack of stream layers. @return A reference to the next layer in the stack of - stream layers. Ownership is not transferred to the caller. + stream layers. */ next_layer_type& next_layer() @@ -167,13 +166,13 @@ public: return stream_.next_layer(); } - /** Get a reference to the next layer. + /** Get a reference to the next layer This function returns a reference to the next layer in a stack of stream layers. @return A reference to the next layer in the stack of - stream layers. Ownership is not transferred to the caller. + stream layers. */ next_layer_type const& next_layer() const @@ -181,13 +180,13 @@ public: return stream_.next_layer(); } - /** Get a reference to the lowest layer. + /** Get a reference to the lowest layer This function returns a reference to the lowest layer in a stack of stream layers. @return A reference to the lowest layer in the stack of - stream layers. Ownership is not transferred to the caller. + stream layers. */ lowest_layer_type& lowest_layer() @@ -195,7 +194,7 @@ public: return stream_.lowest_layer(); } - /** Get a reference to the lowest layer. + /** Get a reference to the lowest layer This function returns a reference to the lowest layer in a stack of stream layers. @@ -2571,9 +2570,11 @@ public: This call is implemented in terms of one or more calls to the stream's `read_some` and `write_some` operations. - Upon a success, op is set to either binary or text depending on - the message type, and the input area of the stream buffer will - hold all the message payload bytes (which may be zero in length). + Upon a success, the input area of the stream buffer will + hold the received message payload bytes (which may be zero + in length). The functions @ref got_binary and @ref got_text + may be used to query the stream and determine the type + of the last received message. During reads, the implementation handles control frames as follows: @@ -2608,9 +2609,11 @@ public: This call is implemented in terms of one or more calls to the stream's `read_some` and `write_some` operations. - Upon a success, op is set to either binary or text depending on - the message type, and the input area of the stream buffer will - hold all the message payload bytes (which may be zero in length). + Upon a success, the input area of the stream buffer will + hold the received message payload bytes (which may be zero + in length). The functions @ref got_binary and @ref got_text + may be used to query the stream and determine the type + of the last received message. During reads, the implementation handles control frames as follows: @@ -2650,9 +2653,11 @@ public: ensure that the stream performs no other reads until this operation completes. - Upon a success, op is set to either binary or text depending on - the message type, and the input area of the stream buffer will - hold all the message payload bytes (which may be zero in length). + Upon a success, the input area of the stream buffer will + hold the received message payload bytes (which may be zero + in length). The functions @ref got_binary and @ref got_text + may be used to query the stream and determine the type + of the last received message. During reads, the implementation handles control frames as follows: diff --git a/test/core/doc_snippets.cpp b/test/core/doc_snippets.cpp index 9a38c1ce..db025f52 100644 --- a/test/core/doc_snippets.cpp +++ b/test/core/doc_snippets.cpp @@ -37,7 +37,7 @@ boost::asio::ip::tcp::socket sock{ios}; { //[snippet_core_2 -auto host = "www.example.com"; +char const* const host = "www.example.com"; boost::asio::ip::tcp::resolver r{ios}; boost::asio::ip::tcp::socket stream{ios}; boost::asio::connect(stream, r.resolve(