From 51f11f090269f4a5ecf79464a6c9eaab70c77012 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Wed, 10 May 2017 12:03:00 -0700 Subject: [PATCH] Concept revision and documentation (API Change): The concept type traits are renamed for consistency, and consolidated into a single header file A new section, Core Concepts, is added to the documentation describing all of the core utility classes and functions. This also includes a complete explanation and sample program describing how to write asynchronous initiation functions and their associated composed operations. --- CHANGELOG.md | 1 + doc/core.qbk | 611 ++++++++++++++++++ doc/design.qbk | 6 +- doc/examples.qbk | 9 + doc/images/message.png | Bin 40410 -> 40207 bytes doc/master.qbk | 31 +- doc/overview.qbk | 2 +- doc/quickref.xml | 29 +- doc/types/BufferSequence.qbk | 4 +- doc/types/DynamicBuffer.qbk | 4 +- doc/types/Streams.qbk | 8 +- doc/websocket.qbk | 362 +++++++---- examples/CMakeLists.txt | 11 + examples/{Jamfile.v2 => Jamfile} | 4 + examples/echo_op.cpp | 230 +++++++ include/beast/core.hpp | 4 +- include/beast/core/async_result.hpp | 43 +- include/beast/core/bind_handler.hpp | 17 +- include/beast/core/buffer_cat.hpp | 4 +- include/beast/core/buffer_concepts.hpp | 55 -- include/beast/core/buffered_read_stream.hpp | 10 +- include/beast/core/buffers_adapter.hpp | 8 +- include/beast/core/detail/buffer_cat.hpp | 3 +- include/beast/core/detail/buffer_concepts.hpp | 180 ------ include/beast/core/detail/stream_concepts.hpp | 134 ---- include/beast/core/detail/sync_ostream.hpp | 2 +- include/beast/core/detail/type_traits.hpp | 290 +++++++++ include/beast/core/handler_concepts.hpp | 28 - include/beast/core/handler_ptr.hpp | 5 + .../beast/core/impl/buffered_read_stream.ipp | 12 +- include/beast/core/impl/consuming_buffers.ipp | 2 +- include/beast/core/multi_buffer.hpp | 12 +- include/beast/core/ostream.hpp | 15 +- include/beast/core/static_buffer.hpp | 18 +- include/beast/core/stream_concepts.hpp | 77 --- include/beast/core/type_traits.hpp | 123 ++++ include/beast/http/concepts.hpp | 3 +- include/beast/http/dynamic_body.hpp | 6 +- include/beast/http/impl/async_read.ipp | 8 +- include/beast/http/impl/basic_parser.ipp | 3 +- include/beast/http/impl/read.ipp | 14 +- include/beast/http/impl/write.ipp | 15 +- include/beast/http/read.hpp | 12 +- include/beast/http/string_body.hpp | 2 +- include/beast/http/write.hpp | 12 +- .../beast/websocket/detail/utf8_checker.hpp | 14 +- include/beast/websocket/impl/accept.ipp | 48 +- include/beast/websocket/impl/close.ipp | 8 +- include/beast/websocket/impl/handshake.ipp | 26 +- include/beast/websocket/impl/ping.ipp | 6 +- include/beast/websocket/impl/read.ipp | 15 +- include/beast/websocket/impl/ssl.ipp | 4 +- include/beast/websocket/impl/stream.ipp | 5 +- include/beast/websocket/impl/teardown.ipp | 4 +- include/beast/websocket/impl/write.ipp | 15 +- include/beast/websocket/rfc6455.hpp | 15 + include/beast/websocket/stream.hpp | 10 +- test/Jamfile | 5 +- test/core/CMakeLists.txt | 7 +- test/core/buffer_concepts.cpp | 25 - test/core/buffer_test.hpp | 2 +- test/core/handler_concepts.cpp | 23 - test/core/multi_buffer.cpp | 2 +- test/core/stream_concepts.cpp | 30 - test/core/type_traits.cpp | 71 +- test/http/nodejs_parser.hpp | 2 +- 66 files changed, 1822 insertions(+), 944 deletions(-) create mode 100644 doc/core.qbk rename examples/{Jamfile.v2 => Jamfile} (93%) create mode 100644 examples/echo_op.cpp delete mode 100644 include/beast/core/buffer_concepts.hpp delete mode 100644 include/beast/core/detail/buffer_concepts.hpp delete mode 100644 include/beast/core/detail/stream_concepts.hpp delete mode 100644 include/beast/core/handler_concepts.hpp delete mode 100644 include/beast/core/stream_concepts.hpp create mode 100644 include/beast/core/type_traits.hpp delete mode 100644 test/core/buffer_concepts.cpp delete mode 100644 test/core/handler_concepts.cpp delete mode 100644 test/core/stream_concepts.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bc4d506..70ba7d54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ Version 41 * Trim Appveyor matrix rows +* Concept revision and documentation API Changes diff --git a/doc/core.qbk b/doc/core.qbk new file mode 100644 index 00000000..45b6dec5 --- /dev/null +++ b/doc/core.qbk @@ -0,0 +1,611 @@ +[/ + Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +] + +[section:core Core Concepts] + +[block ''' + + Concepts + DynamicBuffer + Buffer Algorithms + Asynchronous Model + Tutorial + +'''] + +In addition to network protocols, Beast provides users with robust concepts, +implementations, and algorithms which can be used to design higher level +abstractions that interoperate with Boost.Asio. In the sections that follow +are descriptions of these concepts, followed by algorithms using the concepts, +and then concluding with a tutorial on developing composed asynchronous +operations which are compatible with the Extensible Asynchronous Model provided +in Boost.Asio, __N3747__, and __N4588__. + + + +[section:concepts Concepts] + +Beast provides template metaprogramming functions to allow users to perform +concept checks on parameters, preventing undefined or illegal use. These trait +traits are used in the implementation of the library and part of its public +interface so users may build on the library using the same features. Use of +concept checks benefit users by providing accurate, concise compilation error +diagnostics. + +[table Type Traits +[[Name][Description]] +[[ + [link beast.ref.has_get_io_service `has_get_io_service`] +][ + Determine if the `get_io_service` member function is present with the + correct signature. +]] +[[ + [link beast.ref.is_async_read_stream `is_async_read_stream`] +][ + Determine if a type meets the requirements of __AsyncReadStream__. +]] +[[ + [link beast.ref.is_async_stream `is_async_stream`] +][ + Determine if a type meets the requirements of both __AsyncReadStream__ + and __AsyncWriteStream__. +]] +[[ + [link beast.ref.is_async_write_stream `is_async_write_stream`] +][ + Determine if a type meets the requirements of __AsyncWriteStream__. +]] +[[ + [link beast.ref.is_completion_handler `is_completion_handler`] +][ + Determine if a type meets the requirements of __CompletionHandler__, + and is callable with a specified signature. +]] +[[ + [link beast.ref.is_const_buffer_sequence `is_const_buffer_sequence`] +][ + Determine if a type meets the requirements of __ConstBufferSequence__. +]] +[[ + [link beast.ref.is_dynamic_buffer `is_dynamic_buffer`] +][ + Determine if a type meets the requirements of __DynamicBuffer__. +]] +[[ + [link beast.ref.is_mutable_buffer_sequence `is_mutable_buffer_sequence`] +][ + Determine if a type meets the requirements of __MutableBufferSequence__. +]] +[[ + [link beast.ref.is_sync_read_stream `is_sync_read_stream`] +][ + Determine if a type meets the requirements of __SyncReadStream__. +]] +[[ + [link beast.ref.is_sync_stream `is_sync_stream`] +][ + Determine if a type meets the requirements of both __SyncReadStream__ + and __SyncWriteStream__. +]] +[[ + [link beast.ref.is_sync_write_stream `is_sync_write_stream`] +][ + Determine if a type meets the requirements of __SyncWriteStream__. +]] +] + +[endsect] + + + + +[section:dynamic_buffer DynamicBuffer] + +The networking technical specification described in __N4588__ provides a +concept called a __DynamicBuffer__. Instances of this concept define +an output area expressed as a __MutableBufferSequence__ and an input area +expressed as a __ConstBufferSequence__. Octets may be moved from the output +area to the input area through a structured interface, and later consumed +from the input area when the data is no longer needed. Beast adopts this +concept by providing a concise definition taken directly from the technical +specification and using it as the interface for operations which require +dynamically sized buffers. Several classes are provided which implement the +dynamic buffer concept using various strategies: + +[table Dynamic buffer implementations +[[Name][Description]] +[[ + [link beast.ref.multi_buffer `multi_buffer`] + [link beast.ref.basic_multi_buffer `basic_multi_buffer`] +][ + Uses a sequence of one or more character arrays of varying sizes. + Additional character array objects are appended to the sequence to + accommodate changes in the size of the character sequence. The basic + container supports the standard allocator model. +]] +[[ + [link beast.ref.flat_buffer `flat_buffer`] + [link beast.ref.basic_flat_buffer `basic_flat_buffer`] +][ + Guarantees that input and output areas are buffer sequences with + length one. Upon construction an optional upper limit to the total + size of the input and output areas may be set. The basic container + supports the standard allocator model. +]] +[[ + [link beast.ref.static_buffer `static_buffer`] + [link beast.ref.static_buffer `static_buffer_n`] +][ + Provides the facilities of a dynamic buffer, subject to an upper + limit placed on the total size of the input and output areas defined + by a constexpr template parameter. The storage for the sequences are + kept in the class; the implementation does not perform heap allocations. +]] +[[ + [link beast.ref.buffers_adapter `buffers_adapter`] +][ + This wrapper adapts any __MutableBufferSequence__ into a + __DynamicBuffer__ with an upper limit on the total size of the input and + output areas equal to the size of the underlying mutable buffer sequence. + The implementation does not perform heap allocations. +]] +] + +[endsect] + + + +[section:algorithms Buffer Algorithms] + +Beast provides a collection of algorithms to work on __ConstBufferSequence__ +or __MutableBufferSequence__ objects. These algorithms allow composition of +new algorithms which work on any objects meeting the buffer sequence +requirements, in an efficient way: no memory allocations are performed; +instead, the algorithms implement lightweight iterators over the existing +underlying memory, whose lifetime is retained by the caller. + +[table Buffer algorithms +[[Name][Description]] +[[ + [link beast.ref.buffer_cat `buffer_cat`] +][ + The functions returns a new buffer sequence which, when iterated, + traverses the sequence which would be formed if all of the input buffer + sequences were concatenated. This powerful function allows multiple calls + to a stream's `write_some` function to be combined into one, eliminating + expensive system calls. +]] +[[ + [link beast.ref.consuming_buffers `consuming_buffers`] +][ + This class wraps the underlying memory of an existing buffer sequence + and presents a suffix of the original sequence. The length of the suffix + may be progressively shortened. This lets callers work with sequential + increments of a buffer sequence. +]] +[[ + [link beast.ref.prepare_buffer `prepare_buffer`] + [link beast.ref.prepare_buffers `prepare_buffers`] +][ + These functions return a new buffer sequence which wraps the underlying + memory of an existing buffer sequence, but with a smaller size. This + lets callers work with a prefix of a buffer sequence. +]] +] + +[endsect] + + + +[section:async Asynchronous Model] + +Asynchronous operations are started by calling a free function or member +function known as an ['asynchronous initiation function]. The initiation +function accepts parameters specific to the operation as well as a "completion +token." This token is either a completion handler, or another type allowing for +customization of how the result of the asynchronous operation is conveyed to +callers. Boost.Asio allows the special completion tokens __use_future__ and +objects of type __yield_context__ to allow callers to specify the use of futures +and coroutines respectively. This system, where the return value and method of +indicating completion may be customize at the call site of the asynchronous +initiation function, is known as the ['Extensible Asynchronous Model] described +in __N3747__, and built-in to __N4588__. + +[note + A full explanation of completion handlers, the Extensible Asynchronous + Model and how Boost.Asio's asynchronous interfaces are used is beyond the + scope of this document. Readers should consult the Boost.Asio documentation + for a comprehensive treatment. +] + +Since Beast is low level, authors of libraries may wish to create higher level +interfaces using the primitives found in this library. Non-trivial applications +will want to provide their own asychronous initiation functions which perform +a series of other, intermediate asynchronous operations before invoking the +final completion handler. The set of intermediate actions produced by calling +an initiation function is known as a ['composed operation]. To ensure full +interoperability and well-defined behavior, Boost.Asio imposes requirements on +the implementation of composed operations. Beast provides a number of useful +classes and macros to facilitate the development of composed operations and +the associated asynchronous initiation functions used to launch them. + +[table Asynchronous Helpers +[[Name][Description]] +[[ + [link beast.ref.handler_type `handler_type`] +][ + This template alias converts a completion token and signature to the + correct completion handler type. It is used in the implementation of + asynchronous initiation functions to meet the requirements of the + Extensible Asynchronous Model. +]] +[[ + [link beast.ref.async_return_type `async_return_type`] +][ + This template alias determines the return value of an asynchronous + initiation function given the completion token and signature. It is used + by asynchronous initiation functions to meet the requirements of the + Extensible Asynchronous Model. +]] +[[ + [link beast.ref.async_completion `async_completion`] +][ + This class aggregates the completion handler customization point and + the asynchronous initiation function return value customization point + into a single object which exposes the appropriate output types for the + given input types, and also contains boilerplate that is necessary to + implement an initiation function using the Extensible Model. +]] +[[ + [link beast.ref.handler_alloc `handler_alloc`] +][ + This class meets the requirements of [*Allocator], and uses any custom + memory allocation and deallocation hooks associated with a given handler. + It is useful for when a composed operation requires temporary dynamic + allocations to achieve its result. Memory allocated using this allocator + must be freed before the final completion handler is invoked. +]] +[[ + [link beast.ref.handler_ptr `handler_ptr`] +][ + This is a smart pointer container used to manage the internal state of a + composed operation. It is useful when the state is non trivial. For example + when the state has non-copyable or expensive to copy types. The container + takes ownership of the final completion handler, and provides boilerplate + to invoke the final handler in a way that also deletes the internal state. + The internal state is allocated using the final completion handler's + associated allocator, benefiting from all handler memory management + optimizations transparently. +]] +[[ + [link beast.ref.bind_handler `bind_handler`] +][ + This function returns a new, nullary completion handler which when + invoked with no arguments invokes the original completion handler with a + list of bound arguments. The invocation is made from the same implicit + or explicit strand as that which would be used to invoke the original + handler. This is accomplished by using the correct overload of + `asio_handler_invoke` associated with the original completion handler. + +]] +] + +When implementing composed operations it is necessary to provide hooks for +the composed operation which forward the hook calls to the hooks associated +with the final completion handler. These calls must be made from a namespace +that does not include overloads of the hook functions, since they depend on +argument dependent lookup to resolve. Beast provides a set of public helper +functions to invoke these hooks. These hooks are all located in the +`beast_asio_helpers` namespace rather than the `beast` namespace: + +[table Handler Hooks +[[Name][Description]] +[[ + [link beast.ref.beast_asio_helpers__allocate `allocate`] +][ + Allocation function for a handler. Asynchronous operations may need to + allocate temporary objects. Since asynchronous operations have a handler + function object, these temporary objects can be said to be associated with + the handler. +]] +[[ + [link beast.ref.beast_asio_helpers__deallocate `deallocate`] +][ + Deallocation function for a handler. The implementation guarantees that + the deallocation will occur before the associated handler is invoked, which + means the memory is ready to be reused for any new asynchronous operations + started by the handler. +]] +[[ + [link beast.ref.beast_asio_helpers__invoke `invoke`] +][ + Invocation function for a handler. When asynchronous operations are + composed from other asynchronous operations, all intermediate handlers + should be invoked using the same method as the final handler. This is + required to ensure that user-defined objects are not accessed in a way + that may violate the guarantees. This hooking function ensures that the + invoked method used for the final handler is accessible at each + intermediate step. +]] +[[ + [link beast.ref.beast_asio_helpers__is_continuation `is_continuation`] +][ + Continuation function for a handler. Asynchronous operations may represent + a continuation of the asynchronous control flow associated with the current + handler. The implementation can use this knowledge to optimise scheduling + of the handler. +]] +] + +[endsect] + + + +[section:tutorial Tutorial] + +To illustrate the usage of the asynchronous helpers in the core section of +this library, we will develop a simple asynchronous composed operation called +[*echo]. This operation will read up to the first newline on a stream, and +then write the same line including the newline back on the stream. + +First we define the input parameters and results, then declare our initiation +function. For our echo operation the only inputs are the stream and the +completion token. The output is the error code which is usually included in +all completion handler signatures. + +``` + #include + #include + #include + #include + #include + + // Read a line and echo it back + // + template + beast::async_return_type + async_echo(AsyncStream& stream, CompletionToken&& token) +``` + +Now that we have a declaration, we will define the body of the function. We +want to achieve the following goals: perform static type checking on the input +parameters, set up the return value as per __N3747__, and launch the composed +operation by constructing the object and invoking it. + +``` + template + class echo_op; // This is our composed operation implementation + + // Read a line and echo it back + // + template + beast::async_return_type + async_echo(AsyncStream& stream, CompletionToken&& token) + { + // Make sure stream meets the requirements. We use static_assert + // instead of letting the compiler generate several pages of hard + // to read error messages. + // + static_assert(beast::is_async_stream::value, + "AsyncStream requirements not met"); + + // This helper manages some of the handler's lifetime and + // uses the result and handler specializations associated with + // the completion token to help customize the return value. + // + beast::async_completion init{token}; + + // Create the composed operation and launch it. This is a constructor + // call followed by invocation of operator(). We use BEAST_HANDLER_TYPE + // to convert the completion token into the correct handler type, + // allowing user defined specializations of the async result template + // to take effect. + // + echo_op>{ + stream, init.completion_handler}(beast::error_code{}, 0); + + // This hook lets the caller see a return value when appropriate. + // For example this might return std::future if + // CompletionToken is boost::asio::use_future, or this might + // return an error code if CompletionToken specifies a coroutine. + // + return init.result.get(); + } +``` + +The initiation function contains a few relatively simple parts. There is the +customization of the return value type, static type checking, building the +return value type using the helper, and creating and launching the composed +operation object. The [*`echo_op`] object does most of the work here, and has +a somewhat non-trivial structure. This structure is necessary to meet the +stringent requirements of composed operations (described in more detail in +the Boost.Asio documentation). We will touch on these requirements without +explaining them in depth. + +First we will create boilerplate which is present in all composed operations +written in this style: + +``` + // This composed operation reads a line of input and echoes it back. + // + template + class echo_op + { + // This holds all of the state information required by the operation. + struct state + { + // The stream to read and write to + AsyncStream& stream; + + // Indicates what step in the operation's state machine + // to perform next, starting from zero. + int step = 0; + + // The buffer used to hold the input and output data. + // Note that we use a custom allocator for performance, + // this allows the implementation of the io_service to + // make efficient re-use of memory allocated by composed + // operations during continuations. + // + boost::asio::basic_streambuf> buffer; + + // handler_ptr requires that the first parameter to the + // contained object constructor is a reference to the + // managed final completion handler. + // + explicit state(Handler& handler, AsyncStream& stream_) + : stream(stream_) + , buffer((std::numeric_limits::max)(), + beast::handler_alloc{handler}) + { + } + }; + + // This smart pointer container allocates our state using the + // memory allocation hooks associated with the final completion + // handler, manages the lifetime of that handler for us, and + // enforces the destroy-before-invocation requirement on memory + // allocated using the hooks. + // + beast::handler_ptr p_; + + public: + // Boost.Asio requires that handlers are CopyConstructible. + // In some cases, it takes advantage of handlers that are + // MoveConstructible. This operation supports both. + // + echo_op(echo_op&&) = default; + echo_op(echo_op const&) = default; + + // The constructor simply creates our state variables in + // the smart pointer container. + // + template + echo_op(AsyncStream& stream, DeducedHandler&& handler) + : p_(std::forward(handler), stream) + { + } + + // Determines if the next asynchronous operation represents a + // continuation of the asynchronous flow of control associated + // with the final handler. If we are past step one, it means + // we have performed an asynchronous operation therefore any + // subsequent operation would represent a continuation. + // Otherwise, we propagate the handler's associated value of + // is_continuation. Getting this right means the implementation + // may schedule the invokation of the invoked functions more + // efficiently. + // + friend bool asio_handler_is_continuation(echo_op* op) + { + // This next call is structured to permit argument + // dependent lookup to take effect. + using boost::asio::asio_handler_is_continuation; + + // Always use std::addressof to pass the pointer to the handler, + // otherwise an unwanted overload of operator& may be called instead. + return op->p_->step > 1 || + asio_handler_is_continuation(std::addressof(op->p_.handler())); + } + + // Handler hook forwarding. These free functions invoke the hooks + // associated with the final completion handler. In effect, they + // make the Asio implementation treat our composed operation the + // same way it would treat the final completion handler for the + // purpose of memory allocation and invocation. + // + // Our implementation just passes through the call to the hook + // associated with the final handler. + + friend void* asio_handler_allocate(std::size_t size, echo_op* op) + { + using boost::asio::asio_handler_allocate; + return asio_handler_allocate(size, std::addressof(op->p_.handler())); + } + + friend void asio_handler_deallocate(void* p, std::size_t size, echo_op* op) + { + using boost::asio::asio_handler_deallocate; + return asio_handler_deallocate(p, size, std::addressof(op->p_.handler())); + } + + template + friend void asio_handler_invoke(Function&& f, echo_op* op) + { + using boost::asio::asio_handler_invoke; + return asio_handler_invoke(f, std::addressof(op->p_.handler())); + } + + // Our main entry point. This will get called as our + // intermediate operations complete. Definition below. + // + void operator()(beast::error_code ec, std::size_t bytes_transferred); + }; +``` + +We have the common boilerplate for a composed operation and now we just need +to implement the function call operator. Our strategy is to make our composed +object meet the requirements of a completion handler by being copyable (also +movable), and by providing the function call operator with the correct +signature. Rather than using `std::bind` or `boost::bind`, which destroys +the type information and therefore breaks the allocation and invocation +hooks, we will simply pass `std::move(*this)` as the completion handler +parameter for any operations that we initiate. For the move to work correctly, +care must be taken to ensure that no access to data members are made after the +move takes place. Here is the implementation of the function call operator for +this echo operation: +``` + // We are callable with the signature void(error_code, bytes_transferred), + // allowing `*this` to be used as both a ReadHandler and a WriteHandler. + // + template + void echo_op:: + operator()(beast::error_code ec, std::size_t bytes_transferred) + { + // Store a reference to our state. The address of the state won't + // change, and this solves the problem where dereferencing the + // data member is undefined after a move. + auto& p = *p_; + + // Now perform the next step in the state machine + switch(ec ? 2 : p.step) + { + // initial entry + case 0: + // read up to the first newline + p.step = 1; + return boost::asio::async_read_until(p.stream, p.buffer, "\n", std::move(*this)); + + case 1: + // write everything back + p.step = 2; + return boost::asio::async_write(p.stream, p.buffer.data(), std::move(*this)); + + case 2: + break; + } + + // Invoke the final handler. If we wanted to pass any arguments + // which come from our state, they would have to be moved to the + // stack first, since the `handler_ptr` guarantees that the state + // is destroyed before + // the handler is invoked. + // + p_.invoke(ec); + return; + } +``` + +A complete, runnable version of this example may be found in the examples +directory. + +[endsect] + + + +[endsect] diff --git a/doc/design.qbk b/doc/design.qbk index f836bcf0..40310019 100644 --- a/doc/design.qbk +++ b/doc/design.qbk @@ -486,7 +486,7 @@ start. Other design goals: Beast doesn't allocate or make copies of buffers when sending data. The caller's buffers are sent in-place. You can use any object meeting the requirements of - [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html ConstBufferSequence], + [@http://www.boost.org/doc/html/boost_asio/reference/ConstBufferSequence.html ConstBufferSequence], permitting efficient scatter-gather I/O. The [*ConstBufferSequence] interface allows callers to send data from @@ -588,8 +588,8 @@ start. Other design goals: [table [ - [[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/async_connect.html Beast], - [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/basic_socket_acceptor/async_accept.html also]] + [[@http://www.boost.org/doc/html/boost_asio/reference/async_connect.html Beast], + [@http://www.boost.org/doc/html/boost_asio/reference/basic_socket_acceptor/async_accept.html also]] [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/asio/endpoint.hpp#L52 websocketpp]] ][ [``` diff --git a/doc/examples.qbk b/doc/examples.qbk index aecd3083..6383a998 100644 --- a/doc/examples.qbk +++ b/doc/examples.qbk @@ -127,6 +127,15 @@ type, in `file_body`. * [@examples/http_sync_server.hpp] * [@examples/http_server.cpp] +[heading Composed Operations] + +This program shows how to use Beast's core foundations to build a +composable asynchronous initiation function with associated composed +operation implementation. This is a complete, runnable version of +the example described in the Core Foundations document section. + +* [@examples/echo_op.cpp] + [heading Listings] These are stand-alone listings of the HTTP and WebSocket examples. diff --git a/doc/images/message.png b/doc/images/message.png index 3ea625090c9dd7c8538a4d46719a5727f9c767d9..7a85af1293bbcbb73e247e89a0f6a042b51c4118 100644 GIT binary patch delta 39568 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%0APo_VT-KrP^JsNp>3+ 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 Y!N>UW)BC|qwxBTeboFyt=akR{0Mtq5SpWb4 diff --git a/doc/master.qbk b/doc/master.qbk index 4b0200b8..8047a0e8 100644 --- a/doc/master.qbk +++ b/doc/master.qbk @@ -23,22 +23,25 @@ [template indexterm1[term1] ''''''[term1]''''''] [template indexterm2[term1 term2] ''''''[term1]''''''[term2]''''''] +[def __N3747__ [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3747.pdf [*N3747]]] [def __N4588__ [@http://cplusplus.github.io/networking-ts/draft.pdf [*N4588]]] [def __rfc6455__ [@https://tools.ietf.org/html/rfc6455 rfc6455]] [def __rfc7230__ [@https://tools.ietf.org/html/rfc7230 rfc7230]] -[def __asio_handler_invoke__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/asio_handler_invoke.html `asio_handler_invoke`]] -[def __asio_handler_allocate__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/asio_handler_allocate.html `asio_handler_allocate`]] -[def __void_or_deduced__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.return_type_of_an_initiating_function ['void-or-deduced]]] +[def __asio_handler_invoke__ [@http://www.boost.org/doc/html/boost_asio/reference/asio_handler_invoke.html `asio_handler_invoke`]] +[def __asio_handler_allocate__ [@http://www.boost.org/doc/html/boost_asio/reference/asio_handler_allocate.html `asio_handler_allocate`]] +[def __void_or_deduced__ [@http://www.boost.org/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.return_type_of_an_initiating_function ['void-or-deduced]]] +[def __use_future__ [@http://www.boost.org/doc/html/boost_asio/reference/use_future_t.html `boost::asio::use_future`]] +[def __yield_context__ [@http://www.boost.org/doc/html/boost_asio/reference/yield_context.html `boost::asio::yield_context`]] -[def __AsyncReadStream__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/AsyncReadStream.html [*AsyncReadStream]]] -[def __AsyncWriteStream__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/AsyncWriteStream.html [*AsyncWriteStream]]] -[def __CompletionHandler__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/CompletionHandler.html [*CompletionHandler]]] -[def __ConstBufferSequence__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*ConstBufferSequence]]] -[def __Handler__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/Handler.html [*Handler]]] -[def __MutableBufferSequence__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*MutableBufferSequence]]] -[def __SyncReadStream__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/SyncReadStream.html [*SyncReadStream]]] -[def __SyncWriteStream__ [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/SyncWriteStream.html [*SyncWriteStream]]] +[def __AsyncReadStream__ [@http://www.boost.org/doc/html/boost_asio/reference/AsyncReadStream.html [*AsyncReadStream]]] +[def __AsyncWriteStream__ [@http://www.boost.org/doc/html/boost_asio/reference/AsyncWriteStream.html [*AsyncWriteStream]]] +[def __CompletionHandler__ [@http://www.boost.org/doc/html/boost_asio/reference/CompletionHandler.html [*CompletionHandler]]] +[def __ConstBufferSequence__ [@http://www.boost.org/doc/html/boost_asio/reference/ConstBufferSequence.html [*ConstBufferSequence]]] +[def __Handler__ [@http://www.boost.org/doc/html/boost_asio/reference/Handler.html [*Handler]]] +[def __MutableBufferSequence__ [@http://www.boost.org/doc/html/boost_asio/reference/MutableBufferSequence.html [*MutableBufferSequence]]] +[def __SyncReadStream__ [@http://www.boost.org/doc/html/boost_asio/reference/SyncReadStream.html [*SyncReadStream]]] +[def __SyncWriteStream__ [@http://www.boost.org/doc/html/boost_asio/reference/SyncWriteStream.html [*SyncWriteStream]]] [def __Body__ [link beast.ref.Body [*`Body`]]] [def __DynamicBuffer__ [link beast.ref.DynamicBuffer [*DynamicBuffer]]] @@ -62,6 +65,11 @@ provides implementations of the HTTP and WebSocket protocols. ][ An introduction with features, requirements, and credits. ]] + [[ + [link beast.core Core Concepts] + ][ + Library-wide concepts, classes, functions, and traits. + ]] [[ [link beast.http Using HTTP] ][ @@ -96,6 +104,7 @@ provides implementations of the HTTP and WebSocket protocols. ] [include overview.qbk] +[include core.qbk] [include http.qbk] [include websocket.qbk] [include examples.qbk] diff --git a/doc/overview.qbk b/doc/overview.qbk index 4dfc9525..5afc49e3 100644 --- a/doc/overview.qbk +++ b/doc/overview.qbk @@ -43,7 +43,7 @@ Beast requires: * [*Boost.] Beast is built on Boost, especially Boost.Asio. * [*OpenSSL.] If using TLS/Secure sockets (optional). -[note Tested compilers: msvc-14+, gcc 5+, clang 3.6+] +[note Tested compilers: msvc-14+, gcc 4.8+, clang 3.6+] The library is [*header-only]. It is not necessary to add any .cpp files, or to add commands to your build script for building Beast. To link your diff --git a/doc/quickref.xml b/doc/quickref.xml index 0f1fdf5c..ad6de992 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -29,7 +29,7 @@ Classes - http__basic_dynamic_body + basic_dynamic_body basic_fields basic_parser dynamic_body @@ -159,7 +159,6 @@ buffers_adapter consuming_buffers buffered_read_stream - errc error_category error_code error_condition @@ -187,23 +186,35 @@ system_category to_static_string + Constants + + errc + Type Traits - is_AsyncReadStream - is_AsyncWriteStream - is_AsyncStream - is_CompletionHandler + has_get_io_service + is_async_read_stream + is_async_write_stream + is_async_stream + is_completion_handler is_const_buffer_sequence is_dynamic_buffer is_mutable_buffer_sequence - is_SyncReadStream - is_SyncStream - is_SyncWriteStream + is_sync_read_stream + is_sync_stream + is_sync_write_stream + Helpers + + allocate + deallocate + invoke + is_continuation + Concepts AsyncStream diff --git a/doc/types/BufferSequence.qbk b/doc/types/BufferSequence.qbk index c008457b..9e493932 100644 --- a/doc/types/BufferSequence.qbk +++ b/doc/types/BufferSequence.qbk @@ -9,7 +9,7 @@ A `BufferSequence` is a type meeting either of the following requirements: -* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*`ConstBufferSequence`]] -* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*`MutableBufferSequence`]] +* __ConstBufferSequence__ +* __MutableBufferSequence__ [endsect] diff --git a/doc/types/DynamicBuffer.qbk b/doc/types/DynamicBuffer.qbk index 0a4ef154..3ba090d4 100644 --- a/doc/types/DynamicBuffer.qbk +++ b/doc/types/DynamicBuffer.qbk @@ -37,8 +37,8 @@ In the table below: * `a` denotes a value of type `X`. * `c` denotes a (possibly const) value of type `X`. * `n` denotes a value of type `std::size_t`. -* `T` denotes a type meeting the requirements for [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ConstBufferSequence.html `ConstBufferSequence`]. -* `U` denotes a type meeting the requirements for [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/MutableBufferSequence.html `MutableBufferSequence`]. +* `T` denotes a type meeting the requirements for __ConstBufferSequence__. +* `U` denotes a type meeting the requirements for __MutableBufferSequence__. [table DynamicBuffer requirements [[operation] [type] [semantics, pre/post-conditions]] diff --git a/doc/types/Streams.qbk b/doc/types/Streams.qbk index 67d60375..f8c1c7a7 100644 --- a/doc/types/Streams.qbk +++ b/doc/types/Streams.qbk @@ -21,14 +21,14 @@ A type modeling [*`Stream`] meets either or both of the following requirements: A type modeling [*`AsyncStream`] meets the following requirements: -* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/AsyncReadStream.html [*`AsyncReadStream`]] -* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/AsyncWriteStream.html [*`AsyncWriteStream`]] +* __AsyncReadStream__ +* __AsyncWriteStream__ [heading:SyncStream SyncStream] A type modeling [*`SyncStream`] meets the following requirements: -* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/SyncReadStream.html [*`SyncReadStream`]] -* [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/SyncWriteStream.html [*`SyncWriteStream`]] +* __SyncReadStream__ +* __SyncWriteStream__ [endsect] diff --git a/doc/websocket.qbk b/doc/websocket.qbk index cfa78321..e22c01df 100644 --- a/doc/websocket.qbk +++ b/doc/websocket.qbk @@ -12,13 +12,10 @@ Creation Making connections Handshaking + Accepting Messages - Frames Control Frames - Buffers - Asynchronous interface - The io_service - Thread Safety + Notes '''] @@ -30,9 +27,8 @@ this technology is to provide a mechanism for browser-based applications that need two-way communication with servers that does not rely on opening multiple HTTP connections. -Beast.WebSocket provides developers with a robust WebSocket implementation -built on Boost.Asio with a consistent asynchronous model using a modern -C++ approach. +Beast provides developers with a robust WebSocket implementation built on +Boost.Asio with a consistent asynchronous model using a modern C++ approach. The WebSocket protocol is described fully in [@https://tools.ietf.org/html/rfc6455 rfc6455] @@ -48,11 +44,11 @@ The WebSocket protocol is described fully in [section:creation Creation] The interface to Beast's WebSocket implementation is a single template -class [link beast.ref.websocket__stream `beast::websocket::stream`] which +class [link beast.ref.websocket__stream `websocket::stream`] which wraps a "next layer" object. The next layer object must meet the requirements -of [link beast.ref.streams.SyncStream [*`SyncStream`]] if synchronous +of [link beast.ref.streams.SyncStream [*SyncStream]] if synchronous operations are performed, or -[link beast.ref.streams.AsyncStream [*`AsyncStream`]] if asynchronous +[link beast.ref.streams.AsyncStream [*AsyncStream]] if asynchronous operations are performed, or both. Arguments supplied during construction are passed to next layer's constructor. Here we declare a websocket stream over a TCP/IP socket with ownership of the socket: @@ -85,7 +81,7 @@ beast::websocket::stream For servers that can handshake in multiple protocols, it may be desired to wrap an object that already exists. This socket can be moved in: ``` - boost::asio::ip::tcp::socket&& sock; + boost::asio::ip::tcp::socket sock; ... beast::websocket::stream ws{std::move(sock)}; ``` @@ -140,6 +136,22 @@ void do_accept(boost::asio::ip::tcp::acceptor& acceptor) } ``` +When using SSL, which itself wraps a next layer object that is usualy a +TCP/IP socket, multiple calls to retrieve the next layer may be required: +``` + beast::websocket::stream> ws{ios, ctx}; + + // connect the underlying TCP/IP socket + ws.next_layer().next_layer().connect(ep); + + // perform SSL handshake + ws.next_layer().handshake(boost::asio::ssl::stream_base::client); + + // perform WebSocket handshake + ws.handshake("localhost", "/"); +``` + [note Examples use synchronous interfaces for clarity of exposition. ] @@ -154,17 +166,103 @@ A WebSocket session begins when one side sends the HTTP Upgrade request for websocket, and the other side sends an appropriate HTTP response indicating that the request was accepted and that the connection has been upgraded. The HTTP Upgrade request must include the Host HTTP field, -and the URI of the resource to request. -[link beast.ref.websocket__stream.handshake `handshake`] is used to send the -request with the required host and resource strings. +and the target of the resource to request. The stream member functions +[link beast.ref.websocket__stream.handshake `handshake`] and +[link beast.ref.websocket__stream.async_handshake `async_handshake`] +are used to send the request with the required host and target strings. ``` beast::websocket::stream ws{ios}; ... ws.set_option(beast::websocket::keep_alive(true)); - ws.handshake("ws.example.com:80", "/cgi-bin/bitcoin-prices"); + ws.handshake("localhost", "/"); ``` -The [link beast.ref.websocket__stream `stream`] automatically +The implementation will create and send an HTTP request that typically +looks like this: + +[table WebSocket Upgrade HTTP Request +[[Serialized Octets][Description]] +[[ + ``` + GET / HTTP/1.1 + Host: localhost + Upgrade: websocket + Connection: upgrade + Sec-WebSocket-Key: 2pGeTR0DsE4dfZs2pH+8MA== + Sec-WebSocket-Version: 13 + User-Agent: Beast + ``` +][ + The host and target parameters become part of the Host field + and request-target in the resulting HTTP request. The key is + generated by the implementation. Callers may add fields or + modify fields by providing a ['decorator], described below. +]]] + +[heading Decorators] + +If the caller wishes to add or modify fields, the member functions +[link beast.ref.websocket__stream.handshake_ex `handshake_ex`] and +[link beast.ref.websocket__stream.async_handshake_ex `async_handshake_ex`] +are provided which allow an additional function object, called a +['decorator], to be passed. The decorator is invoked to modify +the HTTP Upgrade request as needed. This example sets a subprotocol +on the request: +``` + void decorate(beast::websocket::request_type& req) + { + req.fields.insert("Sec-WebSocket-Protocol", "xmpp;ws-chat"); + } + ... + ws.handshake_ex("localhost", "/", &decorate); + +``` + +The HTTP Upgrade request produced by the previous call will look thusly: + +[table Decorated WebSocket Upgrade HTTP Request +[[Serialized Octets][Description]] +[[ + ``` + GET / HTTP/1.1 + Host: localhost + Upgrade: websocket + Connection: upgrade + Sec-WebSocket-Key: 2pGeTR0DsE4dfZs2pH+8MA== + Sec-WebSocket-Version: 13 + Sec-WebSocket-Protocol: xmpp;ws-chat + User-Agent: Beast + ``` +][ + Undefined behavior results if the decorator modifies the fields + specific to perform the WebSocket Upgrade , such as the Upgrade + and Connection fields. +]]] + +[heading Filtering] + +When a client receives an HTTP Upgrade response from the server indicating +a successful upgrade, the caller may wish to perform additional validation +on the received HTTP response message. For example, to check that the +response to a basic authentication challenge is valid. To achieve this, +overloads of the handshake member function allow the caller to store the +received HTTP message in an output reference argument as +[link beast.ref.websocket__response_type `response_type`] +as follows: +``` + beast::websocket::response_type res; + ws.handshake(res, "localhost", "/"); + if(! res.fields.exists("Sec-WebSocket-Protocol")) + throw std::invalid_argument("missing subprotocols"); +``` + +[endsect] + + + +[section:accepting Accepting] + +A [link beast.ref.websocket__stream `stream`] automatically handles receiving and processing the HTTP response to the handshake request. The call to handshake is successful if a HTTP response is received with the 101 "Switching Protocols" status code. On failure, an error is returned or an @@ -179,10 +277,113 @@ similarly. If the handshake fails, an error is returned or exception thrown: ws.accept(); ``` -Servers that can handshake in multiple protocols may have already read data -on the connection, or might have already received an entire HTTP request -containing the upgrade request. Overloads of `accept` allow callers to -pass in this additional buffered handshake data. +Successful WebSocket Upgrade responses generated by the implementation will +typically look like this: + +[table Decorated WebSocket Upgrade HTTP Request +[[Serialized Octets][Description]] +[[ + ``` + HTTP/1.1 101 Switching Protocols + Upgrade: websocket + Connection: upgrade + Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= + Server: Beast/40 + ``` +][ + The Sec-WebSocket-Accept field value is generated from the + request in a fashion specified by the WebSocket protocol. +]]] + +[heading Decorators] + +If the caller wishes to add or modify fields, the member functions +[link beast.ref.websocket__stream.accept_ex `accept_ex`] and +[link beast.ref.websocket__stream.async_accept_ex `async_accept_ex`] +are provided which allow an additional function object, called a +['decorator], to be passed. The decorator is invoked to modify +the HTTP Upgrade request as needed. This example sets the Server +field on the response: +``` + ws.accept_ex( + [](beast::websocket::response_type& res) + { + res.fields.insert("Server", "AcmeServer"); + }); + +``` + +The HTTP Upgrade response produced by the previous call will look thusly: + +[table Decorated WebSocket Upgrade HTTP Request +[[Serialized Octets][Description]] +[[ + ``` + HTTP/1.1 101 Switching Protocols + Upgrade: websocket + Connection: upgrade + Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= + Server: AcmeServer + ``` +][ + When the Upgrade request fails, the implementation will still invoke + the decorator to modify the response. In this case, the response + object will have a status code other than 101. + + Undefined behavior results when the upgrade request is successful + and the decorator modifies the fields specific to perform the + WebSocket Upgrade , such as the Upgrade and Connection fields. +]]] + +[heading Passing HTTP Requests] + +When implementing an HTTP server that also supports WebSocket, the +server usually reads the HTTP request from the client. To detect when +the incoming HTTP request is a WebSocket Upgrade request, the function +[link beast.ref.websocket__is_upgrade `is_upgrade`] may be used. + +Once the caller determines that the HTTP request is a WebSocket Upgrade, +additional overloads of +[link beast.ref.websocket__stream.accept `accept`], +[link beast.ref.websocket__stream.accept_ex `accept_ex`], +[link beast.ref.websocket__stream.async_accept `async_accept`], and +[link beast.ref.websocket__stream.async_accept_ex `async_accept_ex`] +are provided which receive the entire HTTP request header as an object +to perform the handshake. In this example, the request is first read +in using the HTTP algorithms, and then passed to a newly constructed +stream: +``` + void handle_connection(boost::asio::ip::tcp::socket& sock) + { + beast::flat_buffer buffer; + beast::http::request req; + beast::http::read(sock, buffer, req); + if(beast::websocket::is_upgrade(req)) + { + beast::websocket::stream ws{std::move(sock)}; + ws.accept(req); + } + } +``` + +[heading Buffered Handshakes] + +Sometimes a server implementation wishes to read octets on the stream +in order to route the incoming request. For example, a server may read +the first 6 octets after accepting an incoming connection to determine +if a TLS protocol is being negotiated, and choose a suitable implementation +at run-time. In the case where the server wishes to accept the incoming +request as an HTTP WebSocket Upgrade request, additional overloads of +[link beast.ref.websocket__stream.accept `accept`], +[link beast.ref.websocket__stream.accept_ex `accept_ex`], +[link beast.ref.websocket__stream.async_accept `async_accept`], and +[link beast.ref.websocket__stream.async_accept_ex `async_accept_ex`] +are provided which receive the additional buffered octects and consume +them as part of the handshake. + +In this example, the server reads the initial HTTP message into the +specified dynamic buffer as an octet sequence in the buffer's output +area, and later uses those octets to attempt an HTTP WebSocket Upgrade: ``` void do_accept(boost::asio::ip::tcp::socket& sock) { @@ -195,93 +396,6 @@ void do_accept(boost::asio::ip::tcp::socket& sock) } ``` -Alternatively, the caller can pass an entire HTTP request if it was -obtained elsewhere: -``` -void do_accept(boost::asio::ip::tcp::socket& sock) -{ - boost::asio::streambuf sb; - beast::http::request request; - beast::http::read(sock, sb, request); - if(beast::http::is_upgrade(request)) - { - websocket::stream ws{sock}; - ws.accept(request); - ... - } -} -``` - -[heading Decorators] - -Often, callers may need the handshake HTTP request and response objects -generated by the implementation to contain additional HTTP fields. Some -examples include: - -* Setting the "User-Agent" or "Server" fields to a custom value -* Announcing a subprotocol by setting the "Sec-WebSocket-Protocol" field -* Adding fields used for HTTP's Basic Authentication - -To achieve this, the websocket interface provides the functions -[link beast.ref.websocket__stream.handshake_ex `handshake_ex`], -[link beast.ref.websocket__stream.async_handshake_ex `async_handshake_ex`], -[link beast.ref.websocket__stream.accept_ex `accept_ex`], and -[link beast.ref.websocket__stream.async_accept_ex `async_accept_ex`] -which allow passing of an additional decorator parameter. This parameter is -a function object to invoke which takes a writable reference to the handshake -message. The function is called after the implementation creates the message, -providing an opportunity for further modification. For client handshakes -using -[link beast.ref.websocket__stream.handshake_ex `handshake_ex`] or -[link beast.ref.websocket__stream.async_handshake_ex `async_handshake_ex`], -the message is passed as a reference to -[link beast.ref.websocket__request_type `request_type`]. Here is an example -that sets the user agent for a client request: - -``` - ws.handshake_ex("ws.example.com:80", "/", - [](request_type& req) - { - req.fields.insert("User-Agent", "Beast Example"); - }); -``` - -For server handshakes using -[link beast.ref.websocket__stream.accept_ex `accept_ex`], and -[link beast.ref.websocket__stream.async_accept_ex `async_accept_ex`] the function -object receives the message as a reference to -[link beast.ref.websocket__response_type `response_type`]. -Here is an example that sets the server field: - -``` - ws.accept_ex( - [](response_type& res) - { - res.fields.insert("Server", "Beast Example"); - }); -``` - -[heading Response Filtering] - -When a client receives an HTTP Upgrade response from the server indicating -a successful upgrade, the caller may wish to perform additional validation -on the received HTTP response message. For example, to check that the -response to a basic authentication challenge is valid. To achieve this, -the functions -[link beast.ref.websocket__stream.handshake `handshake`], -[link beast.ref.websocket__stream.handshake_ex `handshake_ex`] or -[link beast.ref.websocket__stream.async_handshake `async_handshake`], -[link beast.ref.websocket__stream.async_handshake_ex `async_handshake_ex`], -have an additional set of overloads which store the received HTTP message -in the callers parameter, which must be a reference to type -[link beast.ref.websocket__response_type `response_type`], like this: - -``` - response_type res; - ws.handshake(res, "ws.example.com:80", "/"); - // res now contains the complete HTTP response. -``` - [endsect] @@ -310,11 +424,7 @@ void echo(beast::websocket::stream& ws) to perform other operations. ] -[endsect] - - - -[section:frames Frames] +[heading Frames] Some use-cases make it impractical or impossible to buffer the entire message ahead of time: @@ -470,12 +580,12 @@ the fragments: -[section:buffers Buffers] +[section:notes Notes] Because calls to read data may return a variable amount of bytes, the interface to calls that read data require an object that meets the requirements of [link beast.ref.DynamicBuffer [*`DynamicBuffer`]]. This concept is modeled on -[@http://www.boost.org/doc/libs/1_64_0/doc/html/boost_asio/reference/basic_streambuf.html `boost::asio::basic_streambuf`]. +[@http://www.boost.org/doc/html/boost_asio/reference/basic_streambuf.html `boost::asio::basic_streambuf`]. The implementation does not perform queueing or buffering of messages. If desired, these features should be provided by callers. The impact of this @@ -483,11 +593,7 @@ design is that library users are in full control of the allocation strategy used to store data and the back-pressure applied on the read and write side of the underlying TCP/IP connection. -[endsect] - - - -[section:async Asynchronous interface] +[heading Asynchronous Operations] Asynchronous versions are available for all functions: ``` @@ -513,24 +619,16 @@ void echo(websocket::stream& ws, } ``` -[endsect] - - - -[section:io_service The io_service] +[heading The io_service] The creation and operation of the -[@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/io_service.html `boost::asio::io_service`] +[@http://www.boost.org/doc/html/boost_asio/reference/io_service.html `boost::asio::io_service`] associated with the underlying stream is left to the callers, permitting any implementation strategy including one that does not require threads for -environments where threads are unavailable. Beast.WebSocket itself does not +environments where threads are unavailable. Beast WebSocket itself does not use or require threads. -[endsect] - - - -[section:threads Thread Safety] +[heading Thread Safety] Like a regular asio socket, a [link beast.ref.websocket__stream `stream`] is not thread safe. Callers are responsible for synchronizing operations on the diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index edb8e333..ad53efe0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -5,6 +5,17 @@ GroupSources(include/beast beast) GroupSources(examples "/") +add_executable (echo-op + ${BEAST_INCLUDES} + echo_op.cpp +) + +if (NOT WIN32) + target_link_libraries(echo-op ${Boost_LIBRARIES} Threads::Threads) +else() + target_link_libraries(echo-op ${Boost_LIBRARIES}) +endif() + add_executable (http-crawl ${BEAST_INCLUDES} ${EXTRAS_INCLUDES} diff --git a/examples/Jamfile.v2 b/examples/Jamfile similarity index 93% rename from examples/Jamfile.v2 rename to examples/Jamfile index c80330b6..d4291648 100644 --- a/examples/Jamfile.v2 +++ b/examples/Jamfile @@ -5,6 +5,10 @@ # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) # +exe echo-op : + echo_op.cpp + ; + exe http-crawl : http_crawl.cpp urls_large_data.cpp diff --git a/examples/echo_op.cpp b/examples/echo_op.cpp new file mode 100644 index 00000000..67919cc7 --- /dev/null +++ b/examples/echo_op.cpp @@ -0,0 +1,230 @@ +// +// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include +#include +#include + +// This composed operation reads a line of input and echoes it back. +// +template +class echo_op +{ + // This holds all of the state information required by the operation. + struct state + { + // The stream to read and write to + AsyncStream& stream; + + // Indicates what step in the operation's state machine + // to perform next, starting from zero. + int step = 0; + + // The buffer used to hold the input and output data. + // Note that we use a custom allocator for performance, + // this allows the implementation of the io_service to + // make efficient re-use of memory allocated by composed + // operations during continuations. + // + boost::asio::basic_streambuf> buffer; + + // handler_ptr requires that the first parameter to the + // contained object constructor is a reference to the + // managed final completion handler. + // + explicit state(Handler& handler, AsyncStream& stream_) + : stream(stream_) + , buffer((std::numeric_limits::max)(), + beast::handler_alloc{handler}) + { + } + }; + + // This smart pointer container allocates our state using the + // memory allocation hooks associated with the final completion + // handler, manages the lifetime of that handler for us, and + // enforces the destroy-before-invocation requirement on memory + // allocated using the hooks. + // + beast::handler_ptr p_; + +public: + // Boost.Asio requires that handlers are CopyConstructible. + // In some cases, it takes advantage of handlers that are + // MoveConstructible. This operation supports both. + // + echo_op(echo_op&&) = default; + echo_op(echo_op const&) = default; + + // The constructor simply creates our state variables in + // the smart pointer container. + // + template + echo_op(AsyncStream& stream, DeducedHandler&& handler) + : p_(std::forward(handler), stream) + { + } + + // Determines if the next asynchronous operation represents a + // continuation of the asynchronous flow of control associated + // with the final handler. If we are past step one, it means + // we have performed an asynchronous operation therefore any + // subsequent operation would represent a continuation. + // Otherwise, we propagate the handler's associated value of + // is_continuation. Getting this right means the implementation + // may schedule the invokation of the invoked functions more + // efficiently. + // + friend bool asio_handler_is_continuation(echo_op* op) + { + // This next call is structured to permit argument + // dependent lookup to take effect. + using boost::asio::asio_handler_is_continuation; + + // Always use std::addressof to pass the pointer to the handler, + // otherwise an unwanted overload of operator& may be called instead. + return op->p_->step > 1 || + asio_handler_is_continuation(std::addressof(op->p_.handler())); + } + + // Handler hook forwarding. These free functions invoke the hooks + // associated with the final completion handler. In effect, they + // make the Asio implementation treat our composed operation the + // same way it would treat the final completion handler for the + // purpose of memory allocation and invocation. + // + // Our implementation just passes through the call to the hook + // associated with the final handler. + + friend void* asio_handler_allocate(std::size_t size, echo_op* op) + { + using boost::asio::asio_handler_allocate; + return asio_handler_allocate(size, std::addressof(op->p_.handler())); + } + + friend void asio_handler_deallocate(void* p, std::size_t size, echo_op* op) + { + using boost::asio::asio_handler_deallocate; + return asio_handler_deallocate(p, size, std::addressof(op->p_.handler())); + } + + template + friend void asio_handler_invoke(Function&& f, echo_op* op) + { + using boost::asio::asio_handler_invoke; + return asio_handler_invoke(f, std::addressof(op->p_.handler())); + } + + // Our main entry point. This will get called as our + // intermediate operations complete. Definition below. + // + void operator()(beast::error_code ec, std::size_t bytes_transferred); +}; + +// We are callable with the signature void(error_code, bytes_transferred), +// allowing `*this` to be used as both a ReadHandler and a WriteHandler. +// +template +void echo_op:: +operator()(beast::error_code ec, std::size_t bytes_transferred) +{ + // Store a reference to our state. The address of the state won't + // change, and this solves the problem where dereferencing the + // data member is undefined after a move. + auto& p = *p_; + + // Now perform the next step in the state machine + switch(ec ? 2 : p.step) + { + // initial entry + case 0: + // read up to the first newline + p.step = 1; + return boost::asio::async_read_until(p.stream, p.buffer, "\n", std::move(*this)); + + case 1: + // write everything back + p.step = 2; + return boost::asio::async_write(p.stream, p.buffer.data(), std::move(*this)); + + case 2: + break; + } + + // Invoke the final handler. If we wanted to pass any arguments + // which come from our state, they would have to be moved to the + // stack first, since the `handler_ptr` guarantees that the state + // is destroyed before + // the handler is invoked. + // + p_.invoke(ec); + return; +} + +// Read a line and echo it back +// +template +beast::async_return_type +async_echo(AsyncStream& stream, CompletionToken&& token) +{ + // Make sure stream meets the requirements. We use static_assert + // instead of letting the compiler generate several pages of hard + // to read error messages. + // + static_assert(beast::is_async_stream::value, + "AsyncStream requirements not met"); + + // This helper manages some of the handler's lifetime and + // uses the result and handler specializations associated with + // the completion token to help customize the return value. + // + beast::async_completion init{token}; + + // Create the composed operation and launch it. This is a constructor + // call followed by invocation of operator(). We use BEAST_HANDLER_TYPE + // to convert the completion token into the correct handler type, + // allowing user defined specializations of the async result template + // to take effect. + // + echo_op>{ + stream, init.completion_handler}(beast::error_code{}, 0); + + // This hook lets the caller see a return value when appropriate. + // For example this might return std::future if + // CompletionToken is boost::asio::use_future, or this might + // return an error code if CompletionToken specifies a coroutine. + // + return init.result.get(); +} + +int main() +{ + using address_type = boost::asio::ip::address; + using socket_type = boost::asio::ip::tcp::socket; + using endpoint_type = boost::asio::ip::tcp::endpoint; + + // Create a listening socket, accept a connection, perform + // the echo, and then shut everything down and exit. + boost::asio::io_service ios; + socket_type sock{ios}; + { + boost::asio::ip::tcp::acceptor acceptor{ios}; + endpoint_type ep{address_type::from_string("0.0.0.0"), 0}; + acceptor.open(ep.protocol()); + acceptor.bind(ep); + acceptor.listen(); + acceptor.accept(sock); + } + async_echo(sock, + [&](beast::error_code ec) + { + }); + ios.run(); + return 0; +} diff --git a/include/beast/core.hpp b/include/beast/core.hpp index e069656d..ba7711c7 100644 --- a/include/beast/core.hpp +++ b/include/beast/core.hpp @@ -13,14 +13,12 @@ #include #include #include -#include #include #include #include #include #include #include -#include #include #include #include @@ -28,6 +26,6 @@ #include #include #include -#include +#include #endif diff --git a/include/beast/core/async_result.hpp b/include/beast/core/async_result.hpp index 28889a27..31bd0cc8 100644 --- a/include/beast/core/async_result.hpp +++ b/include/beast/core/async_result.hpp @@ -9,8 +9,7 @@ #define BEAST_ASYNC_COMPLETION_HPP #include -#include -#include +#include #include #include #include @@ -18,11 +17,11 @@ namespace beast { -/** An interface for customising the behaviour of an initiating function. +/** An interface for customising the behaviour of an asynchronous initiation function. - The async_result class is used for determining: + This class is used for determining: - @li the concrete completion handler type to be called at the end of the + @li The concrete completion handler type to be called at the end of the asynchronous operation; @li the initiating function return type; and @@ -91,12 +90,6 @@ public: allows customization of the return type of the initiating function, and the function signature of the final handler. - @tparam CompletionToken A completion handler, or a user defined type - with specializations for customizing the return type (for example, - `boost::asio::use_future` or `boost::asio::yield_context`). - - @tparam Signature The callable signature of the final completion handler. - Example: @code ... @@ -150,7 +143,7 @@ struct async_completion , result(completion_handler) { // CompletionToken is not invokable with the given signature - static_assert(is_CompletionHandler< + static_assert(is_completion_handler< completion_handler_type, Signature>::value, "CompletionToken requirements not met: signature mismatch"); } @@ -167,37 +160,11 @@ struct async_completion CompletionToken>::type, Signature> result; }; -/** Convert a completion token to the correct completion handler type. - - This helps asynchronous initiation functions to meet the - requirements of the Extensible Asynchronous Model. - - @tparam CompletionToken Specifies the model used to obtain the result of - the asynchronous operation. - - @tparam Signature The call signature for the completion handler type invoked - on completion of the asynchronous operation. - - @see @ref async_completion, @ref async_return_type -*/ template using handler_type = typename beast::async_result< typename std::decay::type, Signature>::completion_handler_type; -/** Determine the return value of an asynchronous initiation function - - This helps asynchronous initiation functions to meet the - requirements of the Extensible Asynchronous Model. - - @tparam CompletionToken Specifies the model used to obtain the result of - the asynchronous operation. - - @tparam Signature The call signature for the completion handler type invoked - on completion of the asynchronous operation. - - @see @ref async_completion, @ref handler_type -*/ template using async_return_type = typename beast::async_result< typename std::decay::type, diff --git a/include/beast/core/bind_handler.hpp b/include/beast/core/bind_handler.hpp index 18c26520..a4a30280 100644 --- a/include/beast/core/bind_handler.hpp +++ b/include/beast/core/bind_handler.hpp @@ -9,7 +9,7 @@ #define BEAST_BIND_HANDLER_HPP #include -#include +#include #include #include #include @@ -24,24 +24,23 @@ namespace beast { the returned handler, which provides the same `io_service` execution guarantees as the original handler. - Unlike `io_service::wrap`, the returned handler can be used in - a subsequent call to `io_service::post` instead of - `io_service::dispatch`, to ensure that the handler will not be - invoked immediately by the calling function. + Unlike `boost::asio::io_service::wrap`, the returned handler can + be used in a subsequent call to `boost::asio::io_service::post` + instead of `boost::asio::io_service::dispatch`, to ensure that + the handler will not be invoked immediately by the calling + function. Example: @code - template void - do_cancel(AsyncReadStream& stream, ReadHandler&& handler) + signal_aborted(AsyncReadStream& stream, ReadHandler&& handler) { stream.get_io_service().post( bind_handler(std::forward(handler), boost::asio::error::operation_aborted, 0)); } - @endcode @param handler The handler to wrap. @@ -58,7 +57,7 @@ detail::bound_handler< #endif bind_handler(Handler&& handler, Args&&... args) { - static_assert(is_CompletionHandler< + static_assert(is_completion_handler< Handler, void(Args...)>::value, "Handler requirements not met"); return detail::bound_handler diff --git a/include/beast/core/buffer_concepts.hpp b/include/beast/core/buffer_concepts.hpp deleted file mode 100644 index 948b118b..00000000 --- a/include/beast/core/buffer_concepts.hpp +++ /dev/null @@ -1,55 +0,0 @@ -// -// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BEAST_BUFFER_CONCEPTS_HPP -#define BEAST_BUFFER_CONCEPTS_HPP - -#include -#include -#include -#include - -namespace beast { - -/// Determine if `T` meets the requirements of @b ConstBufferSequence. -template -#if BEAST_DOXYGEN -struct is_const_buffer_sequence : std::integral_constant -#else -struct is_const_buffer_sequence : - detail::is_buffer_sequence::type -#endif -{ -}; - -/// Determine if `T` meets the requirements of @b DynamicBuffer. -template -#if BEAST_DOXYGEN -struct is_dynamic_buffer : std::integral_constant -#else -struct is_dynamic_buffer : - detail::is_dynamic_buffer::type -#endif -{ -}; - -/// Determine if `T` meets the requirements of @b MutableBufferSequence. -template -#if BEAST_DOXYGEN -struct is_mutable_buffer_sequence : std::integral_constant -#else -struct is_mutable_buffer_sequence : - detail::is_buffer_sequence::type -#endif -{ -}; - -} // beast - -#endif diff --git a/include/beast/core/buffered_read_stream.hpp b/include/beast/core/buffered_read_stream.hpp index fe380c71..2e2ff68d 100644 --- a/include/beast/core/buffered_read_stream.hpp +++ b/include/beast/core/buffered_read_stream.hpp @@ -10,11 +10,9 @@ #include #include -#include #include -#include #include -#include +#include #include #include #include @@ -26,7 +24,7 @@ namespace beast { This wraps a @b Stream implementation so that calls to write are passed through to the underlying stream, while calls to read will - first consume the input sequence stored in a @b `DynamicBuffer` which + first consume the input sequence stored in a @b DynamicBuffer which is part of the object. The use-case for this class is different than that of the @@ -292,7 +290,7 @@ public: std::size_t write_some(ConstBufferSequence const& buffers) { - static_assert(is_SyncWriteStream::value, + static_assert(is_sync_write_stream::value, "SyncWriteStream requirements not met"); return next_layer_.write_some(buffers); } @@ -314,7 +312,7 @@ public: write_some(ConstBufferSequence const& buffers, error_code& ec) { - static_assert(is_SyncWriteStream::value, + static_assert(is_sync_write_stream::value, "SyncWriteStream requirements not met"); return next_layer_.write_some(buffers, ec); } diff --git a/include/beast/core/buffers_adapter.hpp b/include/beast/core/buffers_adapter.hpp index 032a946b..2cb8eccc 100644 --- a/include/beast/core/buffers_adapter.hpp +++ b/include/beast/core/buffers_adapter.hpp @@ -9,16 +9,16 @@ #define BEAST_BUFFERS_ADAPTER_HPP #include -#include +#include #include #include namespace beast { -/** Adapts a @b `MutableBufferSequence` into a @b `DynamicBuffer`. +/** Adapts a @b MutableBufferSequence into a @b DynamicBuffer. - This class wraps a @b `MutableBufferSequence` to meet the requirements - of @b `DynamicBuffer`. Upon construction the input and output sequences are + This class wraps a @b MutableBufferSequence to meet the requirements + of @b DynamicBuffer. Upon construction the input and output sequences are empty. A copy of the mutable buffer sequence object is stored; however, ownership of the underlying memory is not transferred. The caller is responsible for making sure that referenced memory remains valid diff --git a/include/beast/core/detail/buffer_cat.hpp b/include/beast/core/detail/buffer_cat.hpp index 60d840c2..d441f0e4 100644 --- a/include/beast/core/detail/buffer_cat.hpp +++ b/include/beast/core/detail/buffer_cat.hpp @@ -8,8 +8,7 @@ #ifndef BEAST_DETAIL_BUFFER_CAT_HPP #define BEAST_DETAIL_BUFFER_CAT_HPP -#include -#include +#include #include #include #include diff --git a/include/beast/core/detail/buffer_concepts.hpp b/include/beast/core/detail/buffer_concepts.hpp deleted file mode 100644 index 6a4e830f..00000000 --- a/include/beast/core/detail/buffer_concepts.hpp +++ /dev/null @@ -1,180 +0,0 @@ -// -// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BEAST_DETAIL_BUFFER_CONCEPTS_HPP -#define BEAST_DETAIL_BUFFER_CONCEPTS_HPP - -#include -#include -#include - -namespace beast { -namespace detail { - -// Types that meet the requirements, -// for use with std::declval only. -template -struct BufferSequence -{ - using value_type = BufferType; - using const_iterator = BufferType const*; - ~BufferSequence(); - BufferSequence(BufferSequence const&) = default; - const_iterator begin() const noexcept; - const_iterator end() const noexcept; -}; -using ConstBufferSequence = - BufferSequence; -using MutableBufferSequence = - BufferSequence; - -template -class is_buffer_sequence -{ - template > - static R check1(int); - template - static std::false_type check1(...); - using type1 = decltype(check1(0)); - - template::iterator_category>> - #else - // workaround: - // boost::asio::detail::consuming_buffers::const_iterator - // is not bidirectional - std::forward_iterator_tag, - typename std::iterator_traits< - typename U::const_iterator>::iterator_category>> - #endif - static R check2(int); - template - static std::false_type check2(...); - using type2 = decltype(check2(0)); - - template().begin()), - typename U::const_iterator>::type> - static R check3(int); - template - static std::false_type check3(...); - using type3 = decltype(check3(0)); - - template().end()), - typename U::const_iterator>::type> - static R check4(int); - template - static std::false_type check4(...); - using type4 = decltype(check4(0)); - -public: - using type = std::integral_constant::value && - std::is_destructible::value && - type1::value && type2::value && - type3::value && type4::value>; -}; - -template -struct is_all_ConstBufferSequence - : std::integral_constant::type::value && - is_all_ConstBufferSequence::value> -{ -}; - -template -struct is_all_ConstBufferSequence - : is_buffer_sequence::type -{ -}; - -template -class is_dynamic_buffer -{ - // size() - template().size()), std::size_t>> - static R check1(int); - template - static std::false_type check1(...); - using type1 = decltype(check1(0)); - - // max_size() - template().max_size()), std::size_t>> - static R check2(int); - template - static std::false_type check2(...); - using type2 = decltype(check2(0)); - - // capacity() - template().capacity()), std::size_t>> - static R check3(int); - template - static std::false_type check3(...); - using type3 = decltype(check3(0)); - - // data() - template().data()), - boost::asio::const_buffer>::type::value>> - static R check4(int); - template - static std::false_type check4(...); - using type4 = decltype(check4(0)); - - // prepare() - template().prepare(1)), - boost::asio::mutable_buffer>::type::value>> - static R check5(int); - template - static std::false_type check5(...); - using type5 = decltype(check5(0)); - - // commit() - template().commit(1), std::true_type{})> - static R check6(int); - template - static std::false_type check6(...); - using type6 = decltype(check6(0)); - - // consume - template().consume(1), std::true_type{})> - static R check7(int); - template - static std::false_type check7(...); - using type7 = decltype(check7(0)); - -public: - using type = std::integral_constant; -}; - -} // detail -} // beast - -#endif diff --git a/include/beast/core/detail/stream_concepts.hpp b/include/beast/core/detail/stream_concepts.hpp deleted file mode 100644 index c51991eb..00000000 --- a/include/beast/core/detail/stream_concepts.hpp +++ /dev/null @@ -1,134 +0,0 @@ -// -// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BEAST_DETAIL_STREAM_CONCEPTS_HPP -#define BEAST_DETAIL_STREAM_CONCEPTS_HPP - -#include -#include -#include -#include -#include - -namespace beast { -namespace detail { - -// Types that meet the requirements, -// for use with std::declval only. -struct StreamHandler -{ - StreamHandler(StreamHandler const&) = default; - void operator()(error_code ec, std::size_t); -}; -using ReadHandler = StreamHandler; -using WriteHandler = StreamHandler; - -template -class has_get_io_service -{ - template().get_io_service()), - boost::asio::io_service&>> - static R check(int); - template - static std::false_type check(...); -public: - using type = decltype(check(0)); -}; - -template -class is_AsyncReadStream -{ - template().async_read_some( - std::declval(), - std::declval()), - std::true_type{})> - static R check(int); - template - static std::false_type check(...); - using type1 = decltype(check(0)); -public: - using type = std::integral_constant::type::value>; -}; - -template -class is_AsyncWriteStream -{ - template().async_write_some( - std::declval(), - std::declval()), - std::true_type{})> - static R check(int); - template - static std::false_type check(...); - using type1 = decltype(check(0)); -public: - using type = std::integral_constant::type::value>; -}; - -template -class is_SyncReadStream -{ - template().read_some( - std::declval())), - std::size_t>> - static R check1(int); - template - static std::false_type check1(...); - using type1 = decltype(check1(0)); - - template().read_some( - std::declval(), - std::declval())), std::size_t>> - static R check2(int); - template - static std::false_type check2(...); - using type2 = decltype(check2(0)); - -public: - using type = std::integral_constant; -}; - -template -class is_SyncWriteStream -{ - template().write_some( - std::declval())), - std::size_t>> - static R check1(int); - template - static std::false_type check1(...); - using type1 = decltype(check1(0)); - - template().write_some( - std::declval(), - std::declval())), std::size_t>> - static R check2(int); - template - static std::false_type check2(...); - using type2 = decltype(check2(0)); - -public: - using type = std::integral_constant; -}; - -} // detail -} // beast - -#endif diff --git a/include/beast/core/detail/sync_ostream.hpp b/include/beast/core/detail/sync_ostream.hpp index aee673c6..40dbde38 100644 --- a/include/beast/core/detail/sync_ostream.hpp +++ b/include/beast/core/detail/sync_ostream.hpp @@ -8,8 +8,8 @@ #ifndef BEAST_DETAIL_SYNC_OSTREAM_HPP #define BEAST_DETAIL_SYNC_OSTREAM_HPP -#include #include +#include #include #include diff --git a/include/beast/core/detail/type_traits.hpp b/include/beast/core/detail/type_traits.hpp index f5b50c81..d3f24919 100644 --- a/include/beast/core/detail/type_traits.hpp +++ b/include/beast/core/detail/type_traits.hpp @@ -8,6 +8,10 @@ #ifndef BEAST_DETAIL_TYPE_TRAITS_HPP #define BEAST_DETAIL_TYPE_TRAITS_HPP +#include +#include +#include +#include #include #include #include @@ -16,6 +20,10 @@ namespace beast { namespace detail { +// +// utilities +// + template struct make_void { @@ -160,6 +168,288 @@ struct get_lowest_layer has_lowest_layer::value>::type; }; +//------------------------------------------------------------------------------ + +// +// buffer concepts +// + +// Types that meet the requirements, +// for use with std::declval only. +template +struct BufferSequence +{ + using value_type = BufferType; + using const_iterator = BufferType const*; + ~BufferSequence(); + BufferSequence(BufferSequence const&) = default; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; +}; +using ConstBufferSequence = + BufferSequence; +using MutableBufferSequence = + BufferSequence; + +template +class is_buffer_sequence +{ + template > + static R check1(int); + template + static std::false_type check1(...); + using type1 = decltype(check1(0)); + + template::iterator_category>> + #else + // workaround: + // boost::asio::detail::consuming_buffers::const_iterator + // is not bidirectional + std::forward_iterator_tag, + typename std::iterator_traits< + typename U::const_iterator>::iterator_category>> + #endif + static R check2(int); + template + static std::false_type check2(...); + using type2 = decltype(check2(0)); + + template().begin()), + typename U::const_iterator>::type> + static R check3(int); + template + static std::false_type check3(...); + using type3 = decltype(check3(0)); + + template().end()), + typename U::const_iterator>::type> + static R check4(int); + template + static std::false_type check4(...); + using type4 = decltype(check4(0)); + +public: + using type = std::integral_constant::value && + std::is_destructible::value && + type1::value && type2::value && + type3::value && type4::value>; +}; + +template +struct is_all_ConstBufferSequence + : std::integral_constant::type::value && + is_all_ConstBufferSequence::value> +{ +}; + +template +struct is_all_ConstBufferSequence + : is_buffer_sequence::type +{ +}; + +template +class is_dynamic_buffer +{ + // size() + template().size()), std::size_t>> + static R check1(int); + template + static std::false_type check1(...); + using type1 = decltype(check1(0)); + + // max_size() + template().max_size()), std::size_t>> + static R check2(int); + template + static std::false_type check2(...); + using type2 = decltype(check2(0)); + + // capacity() + template().capacity()), std::size_t>> + static R check3(int); + template + static std::false_type check3(...); + using type3 = decltype(check3(0)); + + // data() + template().data()), + boost::asio::const_buffer>::type::value>> + static R check4(int); + template + static std::false_type check4(...); + using type4 = decltype(check4(0)); + + // prepare() + template().prepare(1)), + boost::asio::mutable_buffer>::type::value>> + static R check5(int); + template + static std::false_type check5(...); + using type5 = decltype(check5(0)); + + // commit() + template().commit(1), std::true_type{})> + static R check6(int); + template + static std::false_type check6(...); + using type6 = decltype(check6(0)); + + // consume + template().consume(1), std::true_type{})> + static R check7(int); + template + static std::false_type check7(...); + using type7 = decltype(check7(0)); + +public: + using type = std::integral_constant; +}; + +//------------------------------------------------------------------------------ + +// +// stream concepts +// + +// Types that meet the requirements, +// for use with std::declval only. +struct StreamHandler +{ + StreamHandler(StreamHandler const&) = default; + void operator()(error_code ec, std::size_t); +}; +using ReadHandler = StreamHandler; +using WriteHandler = StreamHandler; + +template +class has_get_io_service +{ + template().get_io_service()), + boost::asio::io_service&>> + static R check(int); + template + static std::false_type check(...); +public: + using type = decltype(check(0)); +}; + +template +class is_async_read_stream +{ + template().async_read_some( + std::declval(), + std::declval()), + std::true_type{})> + static R check(int); + template + static std::false_type check(...); + using type1 = decltype(check(0)); +public: + using type = std::integral_constant::type::value>; +}; + +template +class is_async_write_stream +{ + template().async_write_some( + std::declval(), + std::declval()), + std::true_type{})> + static R check(int); + template + static std::false_type check(...); + using type1 = decltype(check(0)); +public: + using type = std::integral_constant::type::value>; +}; + +template +class is_sync_read_stream +{ + template().read_some( + std::declval())), + std::size_t>> + static R check1(int); + template + static std::false_type check1(...); + using type1 = decltype(check1(0)); + + template().read_some( + std::declval(), + std::declval())), std::size_t>> + static R check2(int); + template + static std::false_type check2(...); + using type2 = decltype(check2(0)); + +public: + using type = std::integral_constant; +}; + +template +class is_sync_write_stream +{ + template().write_some( + std::declval())), + std::size_t>> + static R check1(int); + template + static std::false_type check1(...); + using type1 = decltype(check1(0)); + + template().write_some( + std::declval(), + std::declval())), std::size_t>> + static R check2(int); + template + static std::false_type check2(...); + using type2 = decltype(check2(0)); + +public: + using type = std::integral_constant; +}; + } // detail } // beast diff --git a/include/beast/core/handler_concepts.hpp b/include/beast/core/handler_concepts.hpp deleted file mode 100644 index 0f535981..00000000 --- a/include/beast/core/handler_concepts.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// -// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BEAST_HANDLER_CONCEPTS_HPP -#define BEAST_HANDLER_CONCEPTS_HPP - -#include -#include -#include - -namespace beast { - -/// Determine if `T` meets the requirements of @b `CompletionHandler`. -template -#if BEAST_DOXYGEN -using is_CompletionHandler = std::integral_constant; -#else -using is_CompletionHandler = std::integral_constant::type>::value && - detail::is_invocable::value>; -#endif -} // beast - -#endif diff --git a/include/beast/core/handler_ptr.hpp b/include/beast/core/handler_ptr.hpp index 52650656..c288c4d1 100644 --- a/include/beast/core/handler_ptr.hpp +++ b/include/beast/core/handler_ptr.hpp @@ -191,6 +191,11 @@ public: deallocation-before-invocation Asio guarantee. All instances of @ref handler_ptr which refer to the same owned object will be reset, including this instance. + + @note Care must be taken when the arguments are themselves + stored in the owned object. Such arguments must first be + moved to the stack or elsewhere, and then passed, or else + undefined behavior will result. */ template void diff --git a/include/beast/core/impl/buffered_read_stream.ipp b/include/beast/core/impl/buffered_read_stream.ipp index f7c28915..4e00b240 100644 --- a/include/beast/core/impl/buffered_read_stream.ipp +++ b/include/beast/core/impl/buffered_read_stream.ipp @@ -10,9 +10,9 @@ #include #include -#include #include #include +#include namespace beast { @@ -164,12 +164,12 @@ async_write_some(ConstBufferSequence const& buffers, WriteHandler&& handler) -> async_return_type { - static_assert(is_AsyncWriteStream::value, + static_assert(is_async_write_stream::value, "AsyncWriteStream requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); - static_assert(is_CompletionHandler::value, "WriteHandler requirements not met"); return next_layer_.async_write_some(buffers, @@ -183,7 +183,7 @@ buffered_read_stream:: read_some( MutableBufferSequence const& buffers) { - static_assert(is_SyncReadStream::value, + static_assert(is_sync_read_stream::value, "SyncReadStream requirements not met"); static_assert(is_mutable_buffer_sequence< MutableBufferSequence>::value, @@ -202,7 +202,7 @@ buffered_read_stream:: read_some(MutableBufferSequence const& buffers, error_code& ec) { - static_assert(is_SyncReadStream::value, + static_assert(is_sync_read_stream::value, "SyncReadStream requirements not met"); static_assert(is_mutable_buffer_sequence< MutableBufferSequence>::value, @@ -232,7 +232,7 @@ async_read_some(MutableBufferSequence const& buffers, ReadHandler&& handler) -> async_return_type { - static_assert(is_AsyncReadStream::value, + static_assert(is_async_read_stream::value, "Stream requirements not met"); static_assert(is_mutable_buffer_sequence< MutableBufferSequence>::value, diff --git a/include/beast/core/impl/consuming_buffers.ipp b/include/beast/core/impl/consuming_buffers.ipp index 44b9157e..4cc90a9c 100644 --- a/include/beast/core/impl/consuming_buffers.ipp +++ b/include/beast/core/impl/consuming_buffers.ipp @@ -8,7 +8,7 @@ #ifndef BEAST_IMPL_CONSUMING_BUFFERS_IPP #define BEAST_IMPL_CONSUMING_BUFFERS_IPP -#include +#include #include #include #include diff --git a/include/beast/core/multi_buffer.hpp b/include/beast/core/multi_buffer.hpp index 55a2c04b..040fa69c 100644 --- a/include/beast/core/multi_buffer.hpp +++ b/include/beast/core/multi_buffer.hpp @@ -19,7 +19,7 @@ namespace beast { -/** A @b `DynamicBuffer` that uses multiple buffers internally. +/** A @b DynamicBuffer that uses multiple buffers internally. The implementation uses a sequence of one or more character arrays of varying sizes. Additional character array objects are appended to @@ -315,15 +315,7 @@ private: debug_check() const; }; -/** A @b `DynamicBuffer` that uses multiple buffers internally. - - The implementation uses a sequence of one or more character arrays - of varying sizes. Additional character array objects are appended to - the sequence to accommodate changes in the size of the character - sequence. - - @note Meets the requirements of @b `DynamicBuffer`. -*/ +/// A typical multi buffer using multi_buffer = basic_multi_buffer>; } // beast diff --git a/include/beast/core/ostream.hpp b/include/beast/core/ostream.hpp index 42dfb66a..b2efbb05 100644 --- a/include/beast/core/ostream.hpp +++ b/include/beast/core/ostream.hpp @@ -9,7 +9,7 @@ #define BEAST_WRITE_OSTREAM_HPP #include -#include +#include #include #include #include @@ -35,6 +35,8 @@ namespace beast { @param b An object meeting the requirements of @b ConstBufferSequence to be streamed. The implementation will make a copy of this object. + Ownership of the underlying memory is not transferred, the application + is still responsible for managing its lifetime. */ template #if BEAST_DOXYGEN @@ -68,11 +70,12 @@ buffers(ConstBufferSequence const& b) @param buffer An object meeting the requirements of @b DynamicBuffer into which the formatted output will be placed. - @return An object derived from `std::ostream` which directs output - into the specified dynamic buffer. Ownership of the dynamic buffer - is not transferred. The caller is responsible for ensuring the - dynamic buffer is not destroyed for the lifetime of the output - stream. + @return An object derived from `std::ostream` which redirects output + The wrapped dynamic buffer is not modified, a copy is made instead. + Ownership of the underlying memory is not transferred, the application + is still responsible for managing its lifetime. The caller is + responsible for ensuring the dynamic buffer is not destroyed for the + lifetime of the output stream. */ template #if BEAST_DOXYGEN diff --git a/include/beast/core/static_buffer.hpp b/include/beast/core/static_buffer.hpp index d6dfe58b..51fc458d 100644 --- a/include/beast/core/static_buffer.hpp +++ b/include/beast/core/static_buffer.hpp @@ -25,6 +25,11 @@ namespace beast { of template functions receiving static stream buffer arguments in a deduced context, the signature of the receiving function should use @ref static_buffer. + + When used with @ref static_buffer_n this implements a dynamic + buffer using no memory allocations. + + @see @ref static_buffer_n */ class static_buffer { @@ -141,11 +146,15 @@ protected: /** A @b DynamicBuffer with a fixed size internal buffer. + This implements a dynamic buffer using no memory allocations. + @tparam N The number of bytes in the internal buffer. @note To reduce the number of template instantiations when passing objects of this type in a deduced context, the signature of the - receiving function should use `static_buffer` instead. + receiving function should use @ref static_buffer instead. + + @see @ref static_buffer */ template class static_buffer_n @@ -179,9 +188,10 @@ public: /** Reset the static buffer. - Postconditions: - The input sequence and output sequence are empty, - `max_size()` returns `N`. + @par Effects + + The input sequence and output sequence are empty, + @ref max_size returns `N`. */ void reset() diff --git a/include/beast/core/stream_concepts.hpp b/include/beast/core/stream_concepts.hpp deleted file mode 100644 index f3609e4b..00000000 --- a/include/beast/core/stream_concepts.hpp +++ /dev/null @@ -1,77 +0,0 @@ -// -// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BEAST_STREAM_CONCEPTS_HPP -#define BEAST_STREAM_CONCEPTS_HPP - -#include -#include -#include - -namespace beast { - -/// Determine if `T` has the `get_io_service` member. -template -#if BEAST_DOXYGEN -struct has_get_io_service : std::integral_constant{}; -#else -using has_get_io_service = typename detail::has_get_io_service::type; -#endif - -/// Determine if `T` meets the requirements of @b `AsyncReadStream`. -template -#if BEAST_DOXYGEN -struct is_AsyncReadStream : std::integral_constant{}; -#else -using is_AsyncReadStream = typename detail::is_AsyncReadStream::type; -#endif - -/// Determine if `T` meets the requirements of @b `AsyncWriteStream`. -template -#if BEAST_DOXYGEN -struct is_AsyncWriteStream : std::integral_constant{}; -#else -using is_AsyncWriteStream = typename detail::is_AsyncWriteStream::type; -#endif - -/// Determine if `T` meets the requirements of @b `SyncReadStream`. -template -#if BEAST_DOXYGEN -struct is_SyncReadStream : std::integral_constant{}; -#else -using is_SyncReadStream = typename detail::is_SyncReadStream::type; -#endif - -/// Determine if `T` meets the requirements of @b `SyncWriterStream`. -template -#if BEAST_DOXYGEN -struct is_SyncWriteStream : std::integral_constant{}; -#else -using is_SyncWriteStream = typename detail::is_SyncWriteStream::type; -#endif - -/// Determine if `T` meets the requirements of @b `AsyncStream`. -template -#if BEAST_DOXYGEN -struct is_AsyncStream : std::integral_constant{}; -#else -using is_AsyncStream = std::integral_constant::value && is_AsyncWriteStream::value>; -#endif - -/// Determine if `T` meets the requirements of @b `SyncStream`. -template -#if BEAST_DOXYGEN -struct is_SyncStream : std::integral_constant{}; -#else -using is_SyncStream = std::integral_constant::value && is_SyncWriteStream::value>; -#endif - -} // beast - -#endif diff --git a/include/beast/core/type_traits.hpp b/include/beast/core/type_traits.hpp new file mode 100644 index 00000000..005934ce --- /dev/null +++ b/include/beast/core/type_traits.hpp @@ -0,0 +1,123 @@ +// +// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BEAST_TYPE_TRAITS_HPP +#define BEAST_TYPE_TRAITS_HPP + +#include +#include +#include +#include + +namespace beast { + +/// Determine if `T` meets the requirements of @b ConstBufferSequence. +template +#if BEAST_DOXYGEN +struct is_const_buffer_sequence : std::integral_constant +#else +struct is_const_buffer_sequence : + detail::is_buffer_sequence::type +#endif +{ +}; + +/// Determine if `T` meets the requirements of @b DynamicBuffer. +template +#if BEAST_DOXYGEN +struct is_dynamic_buffer : std::integral_constant +#else +struct is_dynamic_buffer : + detail::is_dynamic_buffer::type +#endif +{ +}; + +/// Determine if `T` meets the requirements of @b MutableBufferSequence. +template +#if BEAST_DOXYGEN +struct is_mutable_buffer_sequence : std::integral_constant +#else +struct is_mutable_buffer_sequence : + detail::is_buffer_sequence::type +#endif +{ +}; + +/// Determine if `T` has the `get_io_service` member. +template +#if BEAST_DOXYGEN +struct has_get_io_service : std::integral_constant{}; +#else +using has_get_io_service = typename detail::has_get_io_service::type; +#endif + +/// Determine if `T` meets the requirements of @b AsyncReadStream. +template +#if BEAST_DOXYGEN +struct is_async_read_stream : std::integral_constant{}; +#else +using is_async_read_stream = typename detail::is_async_read_stream::type; +#endif + +/// Determine if `T` meets the requirements of @b AsyncWriteStream. +template +#if BEAST_DOXYGEN +struct is_async_write_stream : std::integral_constant{}; +#else +using is_async_write_stream = typename detail::is_async_write_stream::type; +#endif + +/// Determine if `T` meets the requirements of @b SyncReadStream. +template +#if BEAST_DOXYGEN +struct is_sync_read_stream : std::integral_constant{}; +#else +using is_sync_read_stream = typename detail::is_sync_read_stream::type; +#endif + +/// Determine if `T` meets the requirements of @b SyncWriterStream. +template +#if BEAST_DOXYGEN +struct is_sync_write_stream : std::integral_constant{}; +#else +using is_sync_write_stream = typename detail::is_sync_write_stream::type; +#endif + +/// Determine if `T` meets the requirements of @b AsyncStream. +template +#if BEAST_DOXYGEN +struct is_async_stream : std::integral_constant{}; +#else +using is_async_stream = std::integral_constant::value && is_async_write_stream::value>; +#endif + +/// Determine if `T` meets the requirements of @b SyncStream. +template +#if BEAST_DOXYGEN +struct is_sync_stream : std::integral_constant{}; +#else +using is_sync_stream = std::integral_constant::value && is_sync_write_stream::value>; +#endif + +/// Determine if `T` meets the requirements of @b CompletionHandler. +template +#if BEAST_DOXYGEN +using is_completion_handler = std::integral_constant; +#else +using is_completion_handler = std::integral_constant::type>::value && + detail::is_invocable::value>; +#endif + +} // beast + +#endif diff --git a/include/beast/http/concepts.hpp b/include/beast/http/concepts.hpp index f0f6a754..c7a14c12 100644 --- a/include/beast/http/concepts.hpp +++ b/include/beast/http/concepts.hpp @@ -10,8 +10,7 @@ #include #include -#include -#include +#include #include #include #include diff --git a/include/beast/http/dynamic_body.hpp b/include/beast/http/dynamic_body.hpp index 452b763a..3c247143 100644 --- a/include/beast/http/dynamic_body.hpp +++ b/include/beast/http/dynamic_body.hpp @@ -16,9 +16,9 @@ namespace beast { namespace http { -/** A message body represented by a @b `DynamicBuffer` +/** A message body represented by a @b DynamicBuffer - Meets the requirements of @b `Body`. + Meets the requirements of @b Body. */ template struct basic_dynamic_body @@ -113,7 +113,7 @@ private: /** A dynamic message body represented by a @ref multi_buffer - Meets the requirements of @b `Body`. + Meets the requirements of @b Body. */ using dynamic_body = basic_dynamic_body; diff --git a/include/beast/http/impl/async_read.ipp b/include/beast/http/impl/async_read.ipp index 26d34dde..b095e2cc 100644 --- a/include/beast/http/impl/async_read.ipp +++ b/include/beast/http/impl/async_read.ipp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include @@ -645,7 +645,7 @@ async_read_some( basic_parser& parser, ReadHandler&& handler) { - static_assert(is_AsyncReadStream::value, + static_assert(is_async_read_stream::value, "AsyncReadStream requirements not met"); static_assert(is_dynamic_buffer::value, "DynamicBuffer requirements not met"); @@ -667,7 +667,7 @@ async_read( basic_parser& parser, ReadHandler&& handler) { - static_assert(is_AsyncReadStream::value, + static_assert(is_async_read_stream::value, "AsyncReadStream requirements not met"); static_assert(is_dynamic_buffer::value, "DynamicBuffer requirements not met"); @@ -694,7 +694,7 @@ async_read( message& msg, ReadHandler&& handler) { - static_assert(is_AsyncReadStream::value, + static_assert(is_async_read_stream::value, "AsyncReadStream requirements not met"); static_assert(is_dynamic_buffer::value, "DynamicBuffer requirements not met"); diff --git a/include/beast/http/impl/basic_parser.ipp b/include/beast/http/impl/basic_parser.ipp index 76e9a3a0..f6e81d9b 100644 --- a/include/beast/http/impl/basic_parser.ipp +++ b/include/beast/http/impl/basic_parser.ipp @@ -8,10 +8,9 @@ #ifndef BEAST_HTTP_IMPL_BASIC_PARSER_IPP #define BEAST_HTTP_IMPL_BASIC_PARSER_IPP -#include +#include #include #include -#include #include #include #include diff --git a/include/beast/http/impl/read.ipp b/include/beast/http/impl/read.ipp index 725a2e58..f971bf04 100644 --- a/include/beast/http/impl/read.ipp +++ b/include/beast/http/impl/read.ipp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include @@ -192,7 +192,7 @@ read_some( DynamicBuffer& dynabuf, basic_parser& parser) { - static_assert(is_SyncReadStream::value, + static_assert(is_sync_read_stream::value, "SyncReadStream requirements not met"); static_assert(is_dynamic_buffer::value, "DynamicBuffer requirements not met"); @@ -216,7 +216,7 @@ read_some( basic_parser& parser, error_code& ec) { - static_assert(is_SyncReadStream::value, + static_assert(is_sync_read_stream::value, "SyncReadStream requirements not met"); static_assert(is_dynamic_buffer::value, "DynamicBuffer requirements not met"); @@ -234,7 +234,7 @@ read( DynamicBuffer& dynabuf, basic_parser& parser) { - static_assert(is_SyncReadStream::value, + static_assert(is_sync_read_stream::value, "SyncReadStream requirements not met"); static_assert(is_dynamic_buffer::value, "DynamicBuffer requirements not met"); @@ -256,7 +256,7 @@ read( basic_parser& parser, error_code& ec) { - static_assert(is_SyncReadStream::value, + static_assert(is_sync_read_stream::value, "SyncReadStream requirements not met"); static_assert(is_dynamic_buffer::value, "DynamicBuffer requirements not met"); @@ -282,7 +282,7 @@ read( DynamicBuffer& dynabuf, message& msg) { - static_assert(is_SyncReadStream::value, + static_assert(is_sync_read_stream::value, "SyncReadStream requirements not met"); static_assert(is_dynamic_buffer::value, "DynamicBuffer requirements not met"); @@ -310,7 +310,7 @@ read( message& msg, error_code& ec) { - static_assert(is_SyncReadStream::value, + static_assert(is_sync_read_stream::value, "SyncReadStream requirements not met"); static_assert(is_dynamic_buffer::value, "DynamicBuffer requirements not met"); diff --git a/include/beast/http/impl/write.ipp b/include/beast/http/impl/write.ipp index 0d253439..ea14dbbf 100644 --- a/include/beast/http/impl/write.ipp +++ b/include/beast/http/impl/write.ipp @@ -12,12 +12,11 @@ #include #include #include -#include #include #include #include -#include #include +#include #include #include #include @@ -183,7 +182,7 @@ void write(SyncWriteStream& stream, header const& msg) { - static_assert(is_SyncWriteStream::value, + static_assert(is_sync_write_stream::value, "SyncWriteStream requirements not met"); error_code ec; write(stream, msg, ec); @@ -198,7 +197,7 @@ write(SyncWriteStream& stream, header const& msg, error_code& ec) { - static_assert(is_SyncWriteStream::value, + static_assert(is_sync_write_stream::value, "SyncWriteStream requirements not met"); multi_buffer b; { @@ -219,7 +218,7 @@ async_write(AsyncWriteStream& stream, header const& msg, WriteHandler&& handler) { - static_assert(is_AsyncWriteStream::value, + static_assert(is_async_write_stream::value, "AsyncWriteStream requirements not met"); async_completion init{handler}; @@ -565,7 +564,7 @@ void write(SyncWriteStream& stream, message const& msg) { - static_assert(is_SyncWriteStream::value, + static_assert(is_sync_write_stream::value, "SyncWriteStream requirements not met"); static_assert(is_Body::value, "Body requirements not met"); @@ -587,7 +586,7 @@ write(SyncWriteStream& stream, message const& msg, error_code& ec) { - static_assert(is_SyncWriteStream::value, + static_assert(is_sync_write_stream::value, "SyncWriteStream requirements not met"); static_assert(is_Body::value, "Body requirements not met"); @@ -647,7 +646,7 @@ async_write(AsyncWriteStream& stream, message const& msg, WriteHandler&& handler) { - static_assert(is_AsyncWriteStream::value, + static_assert(is_async_write_stream::value, "AsyncWriteStream requirements not met"); static_assert(is_Body::value, "Body requirements not met"); diff --git a/include/beast/http/read.hpp b/include/beast/http/read.hpp index 0750fcb7..16a7dd1e 100644 --- a/include/beast/http/read.hpp +++ b/include/beast/http/read.hpp @@ -374,9 +374,9 @@ async_read( @ref error::end_of_stream is indicated. @param stream The stream from which the data is to be read. - The type must support the @b `SyncReadStream` concept. + The type must support the @b SyncReadStream concept. - @param dynabuf A @b `DynamicBuffer` holding additional bytes + @param dynabuf A @b DynamicBuffer holding additional bytes read by the implementation from the stream. This is both an input and an output parameter; on entry, any data in the dynamic buffer's input sequence will be given to the parser @@ -420,9 +420,9 @@ read( @ref error::end_of_stream is indicated. @param stream The stream from which the data is to be read. - The type must support the @b `SyncReadStream` concept. + The type must support the @b SyncReadStream concept. - @param dynabuf A @b `DynamicBuffer` holding additional bytes + @param dynabuf A @b DynamicBuffer holding additional bytes read by the implementation from the stream. This is both an input and an output parameter; on entry, any data in the dynamic buffer's input sequence will be given to the parser @@ -470,9 +470,9 @@ read( @ref error::end_of_stream is indicated. @param stream The stream to read the message from. - The type must support the @b `AsyncReadStream` concept. + The type must support the @b AsyncReadStream concept. - @param dynabuf A @b `DynamicBuffer` holding additional bytes + @param dynabuf A @b DynamicBuffer holding additional bytes read by the implementation from the stream. This is both an input and an output parameter; on entry, any data in the dynamic buffer's input sequence will be given to the parser diff --git a/include/beast/http/string_body.hpp b/include/beast/http/string_body.hpp index 34274864..bd7aeee3 100644 --- a/include/beast/http/string_body.hpp +++ b/include/beast/http/string_body.hpp @@ -21,7 +21,7 @@ namespace http { /** A Body represented by a std::string. - Meets the requirements of @b `Body`. + Meets the requirements of @b Body. */ struct string_body { diff --git a/include/beast/http/write.hpp b/include/beast/http/write.hpp index de816b54..e7304084 100644 --- a/include/beast/http/write.hpp +++ b/include/beast/http/write.hpp @@ -36,7 +36,7 @@ namespace http { this function will not return `boost::asio::error::eof`. @param stream The stream to which the data is to be written. - The type must support the @b `SyncWriteStream` concept. + The type must support the @b SyncWriteStream concept. @param msg The header to write. @@ -66,7 +66,7 @@ write(SyncWriteStream& stream, this function will not return `boost::asio::error::eof`. @param stream The stream to which the data is to be written. - The type must support the @b `SyncWriteStream` concept. + The type must support the @b SyncWriteStream concept. @param msg The header to write. @@ -101,7 +101,7 @@ write(SyncWriteStream& stream, this function will not return `boost::asio::error::eof`. @param stream The stream to which the data is to be written. - The type must support the @b `AsyncWriteStream` concept. + The type must support the @b AsyncWriteStream concept. @param msg The header to write. The object must remain valid at least until the completion handler is called; ownership is @@ -148,7 +148,7 @@ async_write(AsyncWriteStream& stream, function will be `boost::asio::error::eof`. @param stream The stream to which the data is to be written. - The type must support the @b `SyncWriteStream` concept. + The type must support the @b SyncWriteStream concept. @param msg The message to write. @@ -179,7 +179,7 @@ write(SyncWriteStream& stream, function will be `boost::asio::error::eof`. @param stream The stream to which the data is to be written. - The type must support the @b `SyncWriteStream` concept. + The type must support the @b SyncWriteStream concept. @param msg The message to write. @@ -216,7 +216,7 @@ write(SyncWriteStream& stream, the error set to `boost::asio::error::eof`. @param stream The stream to which the data is to be written. - The type must support the @b `AsyncWriteStream` concept. + The type must support the @b AsyncWriteStream concept. @param msg The message to write. The object must remain valid at least until the completion handler is called; ownership is diff --git a/include/beast/websocket/detail/utf8_checker.hpp b/include/beast/websocket/detail/utf8_checker.hpp index a7f20d8f..39e50a46 100644 --- a/include/beast/websocket/detail/utf8_checker.hpp +++ b/include/beast/websocket/detail/utf8_checker.hpp @@ -8,9 +8,9 @@ #ifndef BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_HPP #define BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_HPP +#include #include #include -#include #include #include @@ -105,7 +105,8 @@ public: template void -utf8_checker_t<_>::reset() +utf8_checker_t<_>:: +reset() { need_ = 0; p_ = have_; @@ -113,7 +114,8 @@ utf8_checker_t<_>::reset() template bool -utf8_checker_t<_>::finish() +utf8_checker_t<_>:: +finish() { auto const success = need_ == 0; reset(); @@ -123,7 +125,8 @@ utf8_checker_t<_>::finish() template template bool -utf8_checker_t<_>::write(ConstBufferSequence const& bs) +utf8_checker_t<_>:: +write(ConstBufferSequence const& bs) { static_assert(is_const_buffer_sequence::value, "ConstBufferSequence requirements not met"); @@ -138,7 +141,8 @@ utf8_checker_t<_>::write(ConstBufferSequence const& bs) template bool -utf8_checker_t<_>::write(std::uint8_t const* in, std::size_t size) +utf8_checker_t<_>:: +write(std::uint8_t const* in, std::size_t size) { auto const valid = [](std::uint8_t const*& in) diff --git a/include/beast/websocket/impl/accept.ipp b/include/beast/websocket/impl/accept.ipp index 43b7fae4..8a6d4a50 100644 --- a/include/beast/websocket/impl/accept.ipp +++ b/include/beast/websocket/impl/accept.ipp @@ -301,7 +301,7 @@ void stream:: accept() { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); error_code ec; accept(ec); @@ -315,7 +315,7 @@ void stream:: accept_ex(ResponseDecorator const& decorator) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(detail::is_ResponseDecorator< ResponseDecorator>::value, @@ -331,7 +331,7 @@ void stream:: accept(error_code& ec) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); reset(); do_accept(&default_decorate_res, ec); @@ -343,7 +343,7 @@ void stream:: accept_ex(ResponseDecorator const& decorator, error_code& ec) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(detail::is_ResponseDecorator< ResponseDecorator>::value, @@ -358,7 +358,7 @@ void stream:: accept(ConstBufferSequence const& buffers) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, @@ -377,7 +377,7 @@ stream:: accept_ex(ConstBufferSequence const& buffers, ResponseDecorator const &decorator) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, @@ -397,7 +397,7 @@ void stream:: accept(ConstBufferSequence const& buffers, error_code& ec) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, @@ -419,7 +419,7 @@ stream:: accept_ex(ConstBufferSequence const& buffers, ResponseDecorator const& decorator, error_code& ec) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, @@ -442,7 +442,7 @@ void stream:: accept(http::header const& req) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); error_code ec; accept(req, ec); @@ -457,7 +457,7 @@ stream:: accept_ex(http::header const& req, ResponseDecorator const& decorator) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(detail::is_ResponseDecorator< ResponseDecorator>::value, @@ -475,7 +475,7 @@ stream:: accept(http::header const& req, error_code& ec) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); reset(); do_accept(req, &default_decorate_res, ec); @@ -488,7 +488,7 @@ stream:: accept_ex(http::header const& req, ResponseDecorator const& decorator, error_code& ec) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(detail::is_ResponseDecorator< ResponseDecorator>::value, @@ -504,7 +504,7 @@ stream:: accept(http::header const& req, ConstBufferSequence const& buffers) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, @@ -524,7 +524,7 @@ accept_ex(http::header const& req, ConstBufferSequence const& buffers, ResponseDecorator const& decorator) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, @@ -545,7 +545,7 @@ stream:: accept(http::header const& req, ConstBufferSequence const& buffers, error_code& ec) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, @@ -569,7 +569,7 @@ accept_ex(http::header const& req, ResponseDecorator const& decorator, error_code& ec) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, @@ -595,7 +595,7 @@ async_return_type< stream:: async_accept(AcceptHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); async_completion init{handler}; @@ -614,7 +614,7 @@ stream:: async_accept_ex(ResponseDecorator const& decorator, AcceptHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); static_assert(detail::is_ResponseDecorator< ResponseDecorator>::value, @@ -636,7 +636,7 @@ stream:: async_accept(ConstBufferSequence const& buffers, AcceptHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, @@ -660,7 +660,7 @@ async_accept_ex(ConstBufferSequence const& buffers, ResponseDecorator const& decorator, AcceptHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, @@ -685,7 +685,7 @@ stream:: async_accept(http::header const& req, AcceptHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); async_completion init{handler}; @@ -707,7 +707,7 @@ stream:: async_accept_ex(http::header const& req, ResponseDecorator const& decorator, AcceptHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); static_assert(detail::is_ResponseDecorator< ResponseDecorator>::value, @@ -733,7 +733,7 @@ async_accept(http::header const& req, ConstBufferSequence const& buffers, AcceptHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, @@ -760,7 +760,7 @@ async_accept_ex(http::header const& req, ResponseDecorator const& decorator, AcceptHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, diff --git a/include/beast/websocket/impl/close.ipp b/include/beast/websocket/impl/close.ipp index ff631bde..1974dcf6 100644 --- a/include/beast/websocket/impl/close.ipp +++ b/include/beast/websocket/impl/close.ipp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include namespace beast { @@ -202,7 +202,7 @@ async_return_type< stream:: async_close(close_reason const& cr, CloseHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements not met"); async_completion init{handler}; @@ -217,7 +217,7 @@ void stream:: close(close_reason const& cr) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); error_code ec; close(cr, ec); @@ -230,7 +230,7 @@ void stream:: close(close_reason const& cr, error_code& ec) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); BOOST_ASSERT(! wr_close_); wr_close_ = true; diff --git a/include/beast/websocket/impl/handshake.ipp b/include/beast/websocket/impl/handshake.ipp index f336598a..2653dcbe 100644 --- a/include/beast/websocket/impl/handshake.ipp +++ b/include/beast/websocket/impl/handshake.ipp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include @@ -167,7 +167,7 @@ async_handshake(string_view const& host, string_view const& target, HandshakeHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements not met"); async_completion init{handler}; @@ -188,7 +188,7 @@ async_handshake(response_type& res, string_view const& target, HandshakeHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements not met"); async_completion init{handler}; @@ -209,7 +209,7 @@ async_handshake_ex(string_view const& host, RequestDecorator const& decorator, HandshakeHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements not met"); static_assert(detail::is_RequestDecorator< RequestDecorator>::value, @@ -234,7 +234,7 @@ async_handshake_ex(response_type& res, RequestDecorator const& decorator, HandshakeHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements not met"); static_assert(detail::is_RequestDecorator< RequestDecorator>::value, @@ -254,7 +254,7 @@ stream:: handshake(string_view const& host, string_view const& target) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); error_code ec; handshake( @@ -270,7 +270,7 @@ handshake(response_type& res, string_view const& host, string_view const& target) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); error_code ec; handshake(res, host, target, ec); @@ -286,7 +286,7 @@ handshake_ex(string_view const& host, string_view const& target, RequestDecorator const& decorator) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(detail::is_RequestDecorator< RequestDecorator>::value, @@ -306,7 +306,7 @@ handshake_ex(response_type& res, string_view const& target, RequestDecorator const& decorator) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(detail::is_RequestDecorator< RequestDecorator>::value, @@ -323,7 +323,7 @@ stream:: handshake(string_view const& host, string_view const& target, error_code& ec) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); do_handshake(nullptr, host, target, &default_decorate_req, ec); @@ -337,7 +337,7 @@ handshake(response_type& res, string_view const& target, error_code& ec) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); do_handshake(&res, host, target, &default_decorate_req, ec); @@ -352,7 +352,7 @@ handshake_ex(string_view const& host, RequestDecorator const& decorator, error_code& ec) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(detail::is_RequestDecorator< RequestDecorator>::value, @@ -371,7 +371,7 @@ handshake_ex(response_type& res, RequestDecorator const& decorator, error_code& ec) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(detail::is_RequestDecorator< RequestDecorator>::value, diff --git a/include/beast/websocket/impl/ping.ipp b/include/beast/websocket/impl/ping.ipp index 49e8a6eb..e39b51c0 100644 --- a/include/beast/websocket/impl/ping.ipp +++ b/include/beast/websocket/impl/ping.ipp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include @@ -200,7 +200,7 @@ async_return_type< stream:: async_ping(ping_data const& payload, WriteHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); async_completion init{handler}; @@ -217,7 +217,7 @@ async_return_type< stream:: async_pong(ping_data const& payload, WriteHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); async_completion init{handler}; diff --git a/include/beast/websocket/impl/read.ipp b/include/beast/websocket/impl/read.ipp index 07a7ebc0..74527318 100644 --- a/include/beast/websocket/impl/read.ipp +++ b/include/beast/websocket/impl/read.ipp @@ -9,12 +9,11 @@ #define BEAST_WEBSOCKET_IMPL_READ_IPP #include -#include #include #include #include #include -#include +#include #include #include #include @@ -692,7 +691,7 @@ stream:: async_read_frame(frame_info& fi, DynamicBuffer& dynabuf, ReadHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); static_assert(beast::is_dynamic_buffer::value, "DynamicBuffer requirements not met"); @@ -710,7 +709,7 @@ void stream:: read_frame(frame_info& fi, DynamicBuffer& dynabuf) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(beast::is_dynamic_buffer::value, "DynamicBuffer requirements not met"); @@ -726,7 +725,7 @@ void stream:: read_frame(frame_info& fi, DynamicBuffer& dynabuf, error_code& ec) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(beast::is_dynamic_buffer::value, "DynamicBuffer requirements not met"); @@ -1104,7 +1103,7 @@ stream:: async_read(opcode& op, DynamicBuffer& dynabuf, ReadHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); static_assert(beast::is_dynamic_buffer::value, "DynamicBuffer requirements not met"); @@ -1122,7 +1121,7 @@ void stream:: read(opcode& op, DynamicBuffer& dynabuf) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(beast::is_dynamic_buffer::value, "DynamicBuffer requirements not met"); @@ -1138,7 +1137,7 @@ void stream:: read(opcode& op, DynamicBuffer& dynabuf, error_code& ec) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(beast::is_dynamic_buffer::value, "DynamicBuffer requirements not met"); diff --git a/include/beast/websocket/impl/ssl.ipp b/include/beast/websocket/impl/ssl.ipp index 1058fa0f..37245db2 100644 --- a/include/beast/websocket/impl/ssl.ipp +++ b/include/beast/websocket/impl/ssl.ipp @@ -10,8 +10,8 @@ #include #include -#include #include +#include namespace beast { namespace websocket { @@ -139,7 +139,7 @@ async_teardown(teardown_tag, boost::asio::ssl::stream& stream, TeardownHandler&& handler) { - static_assert(beast::is_CompletionHandler< + static_assert(beast::is_completion_handler< TeardownHandler, void(error_code)>::value, "TeardownHandler requirements not met"); detail::teardown_ssl_op #include #include -#include #include #include #include -#include +#include #include #include #include @@ -29,6 +28,8 @@ #include #include +#include + namespace beast { namespace websocket { diff --git a/include/beast/websocket/impl/teardown.ipp b/include/beast/websocket/impl/teardown.ipp index 19726892..98ed8beb 100644 --- a/include/beast/websocket/impl/teardown.ipp +++ b/include/beast/websocket/impl/teardown.ipp @@ -9,9 +9,9 @@ #define BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP #include -#include #include #include +#include #include namespace beast { @@ -152,7 +152,7 @@ async_teardown(teardown_tag, boost::asio::ip::tcp::socket& socket, TeardownHandler&& handler) { - static_assert(beast::is_CompletionHandler< + static_assert(beast::is_completion_handler< TeardownHandler, void(error_code)>::value, "TeardownHandler requirements not met"); detail::teardown_tcp_op #include -#include #include #include #include #include #include -#include +#include #include #include #include @@ -551,7 +550,7 @@ stream:: async_write_frame(bool fin, ConstBufferSequence const& bs, WriteHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements not met"); static_assert(beast::is_const_buffer_sequence< ConstBufferSequence>::value, @@ -570,7 +569,7 @@ void stream:: write_frame(bool fin, ConstBufferSequence const& buffers) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(beast::is_const_buffer_sequence< ConstBufferSequence>::value, @@ -588,7 +587,7 @@ stream:: write_frame(bool fin, ConstBufferSequence const& buffers, error_code& ec) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(beast::is_const_buffer_sequence< ConstBufferSequence>::value, @@ -900,7 +899,7 @@ async_return_type< stream:: async_write(ConstBufferSequence const& bs, WriteHandler&& handler) { - static_assert(is_AsyncStream::value, + static_assert(is_async_stream::value, "AsyncStream requirements not met"); static_assert(beast::is_const_buffer_sequence< ConstBufferSequence>::value, @@ -919,7 +918,7 @@ void stream:: write(ConstBufferSequence const& buffers) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(beast::is_const_buffer_sequence< ConstBufferSequence>::value, @@ -936,7 +935,7 @@ void stream:: write(ConstBufferSequence const& buffers, error_code& ec) { - static_assert(is_SyncStream::value, + static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(beast::is_const_buffer_sequence< ConstBufferSequence>::value, diff --git a/include/beast/websocket/rfc6455.hpp b/include/beast/websocket/rfc6455.hpp index 6813ed3f..c68af13a 100644 --- a/include/beast/websocket/rfc6455.hpp +++ b/include/beast/websocket/rfc6455.hpp @@ -30,6 +30,21 @@ namespace websocket { the request should be routed to an instance of @ref websocket::stream. + @par Example + @code + void handle_connection(boost::asio::ip::tcp::socket& sock) + { + beast::flat_buffer buffer; + beast::http::request req; + beast::http::read(sock, buffer, req); + if(beast::websocket::is_upgrade(req)) + { + beast::websocket::stream ws{std::move(sock)}; + ws.accept(req); + } + } + @endcode + @param req The HTTP Request object to check. @return `true` if the request is a WebSocket Upgrade. diff --git a/include/beast/websocket/stream.hpp b/include/beast/websocket/stream.hpp index f2ba705d..bcaead58 100644 --- a/include/beast/websocket/stream.hpp +++ b/include/beast/websocket/stream.hpp @@ -81,17 +81,17 @@ struct frame_info @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. + operations, the type must support the @b SyncStream concept. For asynchronous operations, the type must support the - @b `AsyncStream` concept. + @b AsyncStream concept. @note A stream object must not be moved or destroyed while there are pending asynchronous operations associated with it. @par Concepts - @b `AsyncStream`, - @b `DynamicBuffer`, - @b `SyncStream` + @b AsyncStream, + @b DynamicBuffer, + @b SyncStream */ template class stream : public detail::stream_base diff --git a/test/Jamfile b/test/Jamfile index 8feacdb3..27d45218 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -19,7 +19,6 @@ unit-test core-tests : core/async_result.cpp core/bind_handler.cpp core/buffer_cat.cpp - core/buffer_concepts.cpp core/buffered_read_stream.cpp core/buffers_adapter.cpp core/clamp.cpp @@ -27,19 +26,17 @@ unit-test core-tests : core/error.cpp core/flat_buffer.cpp core/handler_alloc.cpp - core/handler_concepts.cpp core/handler_ptr.cpp core/multi_buffer.cpp core/ostream.cpp core/prepare_buffer.cpp core/static_buffer.cpp core/static_string.cpp - core/stream_concepts.cpp core/string_view.cpp + core/type_traits.cpp core/base64.cpp core/empty_base_optimization.cpp core/sha1.cpp - core/type_traits.cpp ; unit-test http-tests : diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt index 525988ea..d442c35d 100644 --- a/test/core/CMakeLists.txt +++ b/test/core/CMakeLists.txt @@ -12,7 +12,6 @@ add_executable (core-tests async_result.cpp bind_handler.cpp buffer_cat.cpp - buffer_concepts.cpp buffers_adapter.cpp clamp.cpp consuming_buffers.cpp @@ -20,23 +19,19 @@ add_executable (core-tests error.cpp flat_buffer.cpp handler_alloc.cpp - handler_concepts.cpp handler_ptr.cpp multi_buffer.cpp ostream.cpp prepare_buffer.cpp static_buffer.cpp static_string.cpp - stream_concepts.cpp string_view.cpp + type_traits.cpp base64.cpp empty_base_optimization.cpp sha1.cpp - type_traits.cpp ) if (NOT WIN32) target_link_libraries(core-tests ${Boost_LIBRARIES} Threads::Threads) -else() - target_link_libraries(core-tests ${Boost_LIBRARIES}) endif() diff --git a/test/core/buffer_concepts.cpp b/test/core/buffer_concepts.cpp deleted file mode 100644 index 52c7de1f..00000000 --- a/test/core/buffer_concepts.cpp +++ /dev/null @@ -1,25 +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) -// - -// Test that header file is self-contained. -#include - -namespace beast { - -namespace { -struct T -{ -}; -} - -static_assert(is_const_buffer_sequence::value, ""); -static_assert(! is_const_buffer_sequence::value, ""); - -static_assert(is_mutable_buffer_sequence::value, ""); -static_assert(! is_mutable_buffer_sequence::value, ""); - -} // beast diff --git a/test/core/buffer_test.hpp b/test/core/buffer_test.hpp index f703530e..2ff098b7 100644 --- a/test/core/buffer_test.hpp +++ b/test/core/buffer_test.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_TEST_BUFFER_TEST_HPP #define BEAST_TEST_BUFFER_TEST_HPP -#include +#include #include #include #include diff --git a/test/core/handler_concepts.cpp b/test/core/handler_concepts.cpp deleted file mode 100644 index ba3c3a55..00000000 --- a/test/core/handler_concepts.cpp +++ /dev/null @@ -1,23 +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) -// - -// Test that header file is self-contained. -#include - -namespace beast { - -namespace { -struct T -{ - void operator()(int); -}; -} - -static_assert(is_CompletionHandler::value, ""); -static_assert(! is_CompletionHandler::value, ""); - -} // beast diff --git a/test/core/multi_buffer.cpp b/test/core/multi_buffer.cpp index 44fc4de5..6a3bb6d9 100644 --- a/test/core/multi_buffer.cpp +++ b/test/core/multi_buffer.cpp @@ -9,8 +9,8 @@ #include #include "buffer_test.hpp" -#include #include +#include #include #include #include diff --git a/test/core/stream_concepts.cpp b/test/core/stream_concepts.cpp deleted file mode 100644 index f9d59040..00000000 --- a/test/core/stream_concepts.cpp +++ /dev/null @@ -1,30 +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) -// - -// Test that header file is self-contained. -#include -#include - -namespace beast { - -using stream_type = boost::asio::ip::tcp::socket; - -static_assert(has_get_io_service::value, ""); -static_assert(is_AsyncReadStream::value, ""); -static_assert(is_AsyncWriteStream::value, ""); -static_assert(is_AsyncStream::value, ""); -static_assert(is_SyncReadStream::value, ""); -static_assert(is_SyncWriteStream::value, ""); -static_assert(is_SyncStream::value, ""); - -static_assert(! has_get_io_service::value, ""); -static_assert(! is_AsyncReadStream::value, ""); -static_assert(! is_AsyncWriteStream::value, ""); -static_assert(! is_SyncReadStream::value, ""); -static_assert(! is_SyncWriteStream::value, ""); - -} // beast diff --git a/test/core/type_traits.cpp b/test/core/type_traits.cpp index 2c0054ed..c439ff65 100644 --- a/test/core/type_traits.cpp +++ b/test/core/type_traits.cpp @@ -6,9 +6,12 @@ // // Test that header file is self-contained. -#include +#include + +#include namespace beast { + namespace detail { namespace { @@ -60,13 +63,8 @@ static_assert(! is_invocable< // get_lowest_layer // -struct F1 -{ -}; - -struct F2 -{ -}; +struct F1 {}; +struct F2 {}; template struct F3 @@ -120,4 +118,61 @@ static_assert(std::is_same< } // (anonymous) } // detail + +// +// buffer concepts +// + +namespace { + +struct T {}; + +static_assert(is_const_buffer_sequence::value, ""); +static_assert(! is_const_buffer_sequence::value, ""); + +static_assert(is_mutable_buffer_sequence::value, ""); +static_assert(! is_mutable_buffer_sequence::value, ""); + +} // (anonymous) + +// +// handler concepts +// + +namespace { + +struct H +{ + void operator()(int); +}; + +} // anonymous + +static_assert(is_completion_handler::value, ""); +static_assert(! is_completion_handler::value, ""); + +// +// stream concepts +// + +//namespace { + +using stream_type = boost::asio::ip::tcp::socket; + +static_assert(has_get_io_service::value, ""); +static_assert(is_async_read_stream::value, ""); +static_assert(is_async_write_stream::value, ""); +static_assert(is_async_stream::value, ""); +static_assert(is_sync_read_stream::value, ""); +static_assert(is_sync_write_stream::value, ""); +static_assert(is_sync_stream::value, ""); + +static_assert(! has_get_io_service::value, ""); +static_assert(! is_async_read_stream::value, ""); +static_assert(! is_async_write_stream::value, ""); +static_assert(! is_sync_read_stream::value, ""); +static_assert(! is_sync_write_stream::value, ""); + +//} // (anonymous) + } // beast diff --git a/test/http/nodejs_parser.hpp b/test/http/nodejs_parser.hpp index 97317f79..8ee0764b 100644 --- a/test/http/nodejs_parser.hpp +++ b/test/http/nodejs_parser.hpp @@ -12,8 +12,8 @@ #include #include -#include #include +#include #include #include #include