diff --git a/CHANGELOG.md b/CHANGELOG.md
index 309f8352..244c69f1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@ Version 45
* Better test::enable_yield_to
* Add test::pipe
* Fix header::reason
+* Documentation work
--------------------------------------------------------------------------------
diff --git a/doc/design.qbk b/doc/design.qbk
index 438770e8..055dcf43 100644
--- a/doc/design.qbk
+++ b/doc/design.qbk
@@ -9,9 +9,10 @@
[block '''
- HTTP FAQ
- WebSocket FAQ
- Comparison to Zaphoyd Studios WebSocket++
+ HTTP Message Container
+ HTTP Comparison to Other Libraries
+ Comparison to Zaphoyd Studios WebSocket++
+ Boost Formal Review FAQ
''']
@@ -46,27 +47,6 @@ of other packages offering similar functionality. In this section and the
FAQs that follow we attempt to answer those questions that are also applicable
to Beast.
-[variablelist
-[[
- "I would also like to see instances of this library being used
- in production. That would give some evidence that the design
- works in practice."
-][
- Beast.HTTP and Beast.WebSocket are production ready and currently
- running on public servers receiving traffic and handling millions of
- dollars worth of financial transactions daily. The servers run [*rippled],
- open source software ([@https://github.com/ripple/rippled repository])
- implementing the
- [@https://ripple.com/files/ripple_consensus_whitepaper.pdf [*Ripple Consensus Protocol]],
- technology provided by [@http://ripple.com Ripple].
-]]
-
-]
-
-
-
-[section:http HTTP FAQ]
-
For HTTP we model the message to maximize flexibility of implementation
strategies while allowing familiar verbs such as [*`read`] and [*`write`].
The HTTP interface is further driven by the needs of the WebSocket module,
@@ -79,575 +59,9 @@ start. Other design goals:
* Allow for customizations, if the user needs it.
-[variablelist
-
-[[
- "Some more advanced examples, e.g. including TLS with client/server
- certificates would help."
-][
- The HTTP interface doesn't try to reinvent the wheel, it just uses
- the `boost::asio::ip::tcp::socket` or `boost::asio::ssl::stream` that
- you set up beforehand. Callers use the interfaces already existing
- on those objects to make outgoing connections, accept incoming connections,
- or establish TLS sessions with certificates. We find the available Asio
- examples for performing these tasks sufficient.
-]]
-
-[[
- "A built-in HTTP router?"
-][
- We presume this means a facility to match expressions against the URI
- in HTTP requests, and dispatch them to calling code. The authors feel
- that this is a responsibility of higher level code. Beast does
- not try to offer a web server.
-]]
-
-[[
- "HTTP Cookies? Forms/File Uploads?"
-][
- Cookies, or managing these types of HTTP headers in general, is the
- responsibility of higher levels. Beast.HTTP just tries to get complete
- messages to and from the calling code. It deals in the HTTP headers just
- enough to process the message body and leaves the rest to callers. However,
- for forms and file uploads the symmetric interface of the message class
- allows HTTP requests to include arbitrary body types including those needed
- to upload a file or fill out a form.
-]]
-
-[[
- "...supporting TLS (is this a feature? If not this would be a show-stopper),
- etc."
-][
- Beast does not provide direct facilities for implementing TLS connections;
- however, the interfaces already existing on the `boost::asio::ssl::stream`
- are available and can be used to establish secure connections. Then,
- functions like `http::read` or `http::async_write` can work with those
- encrypted connections with no problem.
-]]
-
-[[
- "There should also be more examples of how to integrate the http service
- with getting files from the file system, generating responses CGI-style"
-][
- The design goal for the library is to not try to invent a web server.
- We feel that there is a strong need for a basic implementation that
- models the HTTP message and provides functions to send and receive them
- over Asio. Such an implementation should serve as a building block upon
- which higher abstractions such as the aforementioned HTTP service or
- cgi-gateway can be built.
-
- One of the example programs implements a simple HTTP server that
- delivers files from the filesystem.
-]]
-
-[[
- "You should send a 100-continue to ask for the rest of the body if required."
-][
- The Beast interface supporst this functionality (by allowing this
- special case of partial message parsing and serialization). Specifically,
- it lets callers read the request up to just before the body,
- and let callers write the request up to just before the body. However,
- making use of this behavior is up to callers (since Beast is low level).
-]]
-
-[[
- "What about HTTP/2?"
-][
- Many reviewers feel that HTTP/2 support is an essential feature of
- a HTTP library. The authors agree that HTTP/2 is important but also
- feel that the most sensible implementation is one that does not re-use
- the same network reading and writing interface for 2 as that for 1.0
- and 1.1.
-
- The Beast HTTP message model is suitable for HTTP/2 and can be re-used.
- The IETF HTTP Working Group adopted message compatiblity with HTTP/1.x
- as an explicit goal. A parser can simply emit full headers after
- decoding the compressed HTTP/2 headers. The stream ID is not logically
- part of the message but rather message metadata and should be
- communicated out-of-band (see below). HTTP/2 sessions begin with a
- traditional HTTP/1.1 Upgrade similar in fashion to the WebSocket
- upgrade. An HTTP/2 implementation can use existing Beast.HTTP primitives
- to perform this handshake.
-
- Free functions for HTTP/2 sessions are not possible because of the
- requirement to maintain per-session state. For example, to decode the
- compressed headers. Or to remember and respect the remote peer's window
- settings. The authors propose that a HTTP/2 implementation be written
- as a separate class template, similar to the `websocket::stream` but with
- additional interfaces to support version 2 features. We feel that
- Beast.HTTP offers enough useful functionality to justify inclusion,
- so that developers can take advantage of it right away instead of
- waiting.
-]]
-
-]
-
-[endsect]
-
-
-
-[section:websocket WebSocket FAQ]
-
-[variablelist
-
-[[
- What about message compression?
-][
- Beast WebSocket supports the permessage-deflate extension described in
- [@https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-00 draft-ietf-hybi-permessage-compression-00].
- The library comes with a header-only, C++11 port of ZLib's "deflate" codec
- used in the implementation of the permessage-deflate extension.
-]]
-
-[[
- Where is the TLS/SSL interface?
-][
- The `websocket::stream` wraps the socket or stream that you provide
- (for example, a `boost::asio::ip::tcp::socket` or a
- `boost::asio::ssl::stream`). You establish your TLS connection using the
- interface on `ssl::stream` like shown in all of the Asio examples, then
- construct your `websocket::stream` around it. It works perfectly fine;
- Beast.WebSocket doesn't try to reinvent the wheel or put a fresh coat of
- interface paint on the `ssl::stream`.
-
- The WebSocket implementation [*does] provide support for shutting down
- the TLS connection through the use of the ADL compile-time virtual functions
- [link beast.ref.websocket__teardown `teardown`] and
- [link beast.ref.websocket__async_teardown `async_teardown`]. These will
- properly close the connection as per rfc6455 and overloads are available
- for TLS streams. Callers may provide their own overloads of these functions
- for user-defined next layer types.
-]]
-
-]
-
-[endsect]
-
-
-
-[section:websocketpp Comparison to Zaphoyd Studios WebSocket++]
-
-[variablelist
-
-[[
- How does this compare to [@https://www.zaphoyd.com/websocketpp websocketpp],
- an alternate header-only WebSocket implementation?
-][
- [variablelist
-
- [[1. Synchronous Interface][
-
- Beast offers full support for WebSockets using a synchronous interface. It
- uses the same style of interfaces found in Boost.Asio: versions that throw
- exceptions, or versions that return the error code in a reference parameter:
-
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L774 Beast]]
- [websocketpp]
- ][
- [```
- template
- void
- read(opcode& op, DynamicBuffer& dynabuf)
- ```]
- [
- //
- ]
- ]]]]
-
- [[2. Connection Model][
-
- websocketpp supports multiple transports by utilizing a trait, the `config::transport_type`
- ([@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/asio/connection.hpp#L60 asio transport example])
- To get an idea of the complexity involved with implementing a transport,
- compare the asio transport to the
- [@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L59 `iostream` transport]
- (a layer that allows websocket communication over a `std::iostream`).
-
- In contrast, Beast abstracts the transport by defining just one [*`NextLayer`]
- template argument The type requirements for [*`NextLayer`] are
- already familiar to users as they are documented in Asio:
- __AsyncReadStream__, __AsyncWriteStream__, __SyncReadStream__, __SyncWriteStream__.
-
- The type requirements for instantiating `beast::websocket::stream` versus
- `websocketpp::connection` with user defined types are vastly reduced
- (18 functions versus 2). Note that websocketpp connections are passed by
- `shared_ptr`. Beast does not use `shared_ptr` anywhere in its public interface.
- A `beast::websocket::stream` is constructible and movable in a manner identical
- to a `boost::asio::ip::tcp::socket`. Callers can put such objects in a
- `shared_ptr` if they want to, but there is no requirement to do so.
-
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp Beast]]
- [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L234 websocketpp]]
- ][
- [```
- template
- class stream
- {
- NextLayer next_layer_;
- ...
- }
- ```]
- [```
- template
- class connection
- : public config::transport_type::transport_con_type
- , public config::connection_base
- {
- public:
- typedef lib::shared_ptr ptr;
- ...
- }
- ```]
- ]]]]
-
- [[3. Client and Server Role][
-
- websocketpp provides multi-role support through a hierarchy of
- different classes. A `beast::websocket::stream` is role-agnostic, it
- offers member functions to perform both client and server handshakes
- in the same class. The same types are used for client and server
- streams.
-
- [table
- [
- [Beast]
- [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/roles/server_endpoint.hpp#L39 websocketpp],
- [@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/roles/client_endpoint.hpp#L42 also]]
- ][
- [
- //
- ]
- [```
- template
- class client : public endpoint,config>;
- template
- class server : public endpoint,config>;
- ```]
- ]]]]
-
- [[4. Thread Safety][
-
- websocketpp uses mutexes to protect shared data from concurrent
- access. In contrast, Beast does not use mutexes anywhere in its
- implementation. Instead, it follows the Asio pattern. Calls to
- asynchronous initiation functions use the same method to invoke
- intermediate handlers as the method used to invoke the final handler,
- through the __asio_handler_invoke__ mechanism.
-
- The only requirement in Beast is that calls to asynchronous initiation
- functions are made from the same implicit or explicit strand. For
- example, if the `io_service` associated with a `beast::websocket::stream`
- is single threaded, this counts as an implicit strand and no performance
- costs associated with mutexes are incurred.
-
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/impl/read_frame_op.ipp#L118 Beast]]
- [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L706 websocketpp]]
- ][
- [```
- template
- friend
- void asio_handler_invoke(Function&& f, read_frame_op* op)
- {
- return boost_asio_handler_invoke_helpers::invoke(f, op->d_->h);
- }
- ```]
- [```
- mutex_type m_read_mutex;
- ```]
- ]]]]
-
- [[5. Callback Model][
-
- websocketpp requires a one-time call to set the handler for each event
- in its interface (for example, upon message receipt). The handler is
- represented by a `std::function` equivalent. Its important to recognize
- that the websocketpp interface performs type-erasure on this handler.
-
- In comparison, Beast handlers are specified in a manner identical to
- Boost.Asio. They are function objects which can be copied or moved but
- most importantly they are not type erased. The compiler can see
- through the type directly to the implementation, permitting
- optimization. Furthermore, Beast follows the Asio rules for treatment
- of handlers. It respects any allocation, continuation, or invocation
- customizations associated with the handler through the use of argument
- dependent lookup overloads of functions such as `asio_handler_allocate`.
-
- The Beast completion handler is provided at the call site. For each
- call to an asynchronous initiation function, it is guaranteed that
- there will be exactly one final call to the handler. This functions
- exactly the same way as the asynchronous initiation functions found in
- Boost.Asio, allowing the composition of higher level abstractions.
-
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L834 Beast]]
- [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L281 websocketpp],
- [@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L473 also]]
- ][
- [```
- template
- typename async_completion::result_type
- async_read(opcode& op, DynamicBuffer& dynabuf, ReadHandler&& handler);
- ```]
- [```
- typedef lib::function message_handler;
- void set_message_handler(message_handler h);
- ```]
- ]]]]
-
- [[6. Extensible Asynchronous Model][
-
- Beast fully supports the
- [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf Extensible Asynchronous Model]
- developed by Christopher Kohlhoff, author of Boost.Asio (see Section 8).
-
- Beast websocket asynchronous interfaces may be used seamlessly with
- `std::future` stackful/stackless coroutines, or user defined customizations.
-
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/impl/stream.ipp#L378 Beast]]
- [websocketpp]
- ][
- [```
- beast::async_completion completion(handler);
- read_op{
- completion.handler, *this, op, buffer};
- return completion.result.get();
- ```]
- [
- //
- ]
- ]]]]
-
- [[7. Message Buffering][
-
- websocketpp defines a message buffer, passed in arguments by
- `shared_ptr`, and an associated message manager which permits
- aggregation and reuse of memory. The implementation of
- `websocketpp::message` uses a `std::string` to hold the payload. If an
- incoming message is broken up into multiple frames, the string may be
- reallocated for each continuation frame. The `std::string` always uses
- the standard allocator, it is not possible to customize the choice of
- allocator.
-
- Beast allows callers to specify the object for receiving the message
- or frame data, which is of any type meeting the requirements of
- __DynamicBuffer__ (modeled after `boost::asio::streambuf`).
-
- Beast comes with the class __basic_multi_buffer__, an efficient
- implementation of the __DynamicBuffer__ concept which makes use of multiple
- allocated octet arrays. If an incoming message is broken up into
- multiple pieces, no reallocation occurs. Instead, new allocations are
- appended to the sequence when existing allocations are filled. Beast
- does not impose any particular memory management model on callers. The
- __basic_multi_buffer__ provided by beast supports standard allocators through
- a template argument. Use the __DynamicBuffer__ that comes with beast,
- customize the allocator if you desire, or provide your own type that
- meets the requirements.
-
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L774 Beast]]
- [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/message_buffer/message.hpp#L78 websocketpp]]
- ][
- [```
- template
- read(opcode& op, DynamicBuffer& dynabuf);
- ```]
- [```
- template class con_msg_manager>
- class message {
- public:
- typedef lib::shared_ptr ptr;
- ...
- std::string m_payload;
- ...
- };
- ```]
- ]]]]
-
- [[8. Sending Messages][
-
- When sending a message, websocketpp requires that the payload is
- packaged in a `websocketpp::message` object using `std::string` as the
- storage, or it requires a copy of the caller provided buffer by
- constructing a new message object. Messages are placed onto an
- outgoing queue. An asynchronous write operation runs in the background
- to clear the queue. No user facing handler can be registered to be
- notified when messages or frames have completed sending.
-
- 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/html/boost_asio/reference/ConstBufferSequence.html ConstBufferSequence],
- permitting efficient scatter-gather I/O.
-
- The [*ConstBufferSequence] interface allows callers to send data from
- memory-mapped regions (not possible in websocketpp). Callers can also
- use the same buffers to send data to multiple streams, for example
- broadcasting common subscription data to many clients at once. For
- each call to `async_write` the completion handler is called once when
- the data finishes sending, in a manner identical to `boost::asio::async_write`.
-
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L1048 Beast]]
- [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L672 websocketpp]]
- ][
- [```
- template
- void
- write(ConstBufferSequence const& buffers);
- ```]
- [```
- lib::error_code send(std::string const & payload,
- frame::opcode::value op = frame::opcode::text);
- ...
- lib::error_code send(message_ptr msg);
- ```]
- ]]]]
-
- [[9. Streaming Messages][
-
- websocketpp requires that the entire message fit into memory, and that
- the size is known ahead of time.
-
- Beast allows callers to compose messages in individual frames. This is
- useful when the size of the data is not known ahead of time or if it
- is not desired to buffer the entire message in memory at once before
- sending it. For example, sending periodic output of a database query
- running on a coroutine. Or sending the contents of a file in pieces,
- without bringing it all into memory.
-
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L1151 Beast]]
- [websocketpp]
- ][
- [```
- template
- void
- write_frame(bool fin,
- ConstBufferSequence const& buffers);
- ```]
- [
- //
- ]
- ]]]]
-
- [[10. Flow Control][
-
- The websocketpp read implementation continuously reads asynchronously
- from the network and buffers message data. To prevent unbounded growth
- and leverage TCP/IP's flow control mechanism, callers can periodically
- turn this 'read pump' off and back on.
-
- In contrast a `beast::websocket::stream` does not independently begin
- background activity, nor does it buffer messages. It receives data only
- when there is a call to an asynchronous initiation function (for
- example `beast::websocket::stream::async_read`) with an associated handler.
- Applications do not need to implement explicit logic to regulate the
- flow of data. Instead, they follow the traditional model of issuing a
- read, receiving a read completion, processing the message, then
- issuing a new read and repeating the process.
-
- [table
- [
- [Beast]
- [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L728 websocketpp]]
- ][
- [
- //
- ]
- [```
- lib::error_code pause_reading();
- lib::error_code resume_reading();
- ```]
- ]]]]
-
- [[11. Connection Establishment][
-
- websocketpp offers the `endpoint` class which can handle binding and
- listening to a port, and spawning connection objects.
-
- Beast does not reinvent the wheel here, callers use the interfaces
- already in `boost::asio` for receiving incoming connections resolving
- host names, or establishing outgoing connections. After the socket (or
- `boost::asio::ssl::stream`) is connected, the `beast::websocket::stream`
- is constructed around it and the WebSocket handshake can be performed.
-
- Beast users are free to implement their own "connection manager", but
- there is no requirement to do so.
-
- [table
- [
- [[@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]]
- ][
- [```
- #include
- ```]
- [```
- template
- class endpoint : public config::socket_type;
- ```]
- ]]]]
-
- [[12. WebSocket Handshaking][
-
- Callers invoke `beast::websocket::accept` to perform the WebSocket
- handshake, but there is no requirement to use this function. Advanced
- users can perform the WebSocket handshake themselves. Beast WebSocket
- provides the tools for composing the request or response, and the
- Beast HTTP interface provides the container and algorithms for sending
- and receiving HTTP/1 messages including the necessary HTTP Upgrade
- request for establishing the WebSocket session.
-
- Beast allows the caller to pass the incoming HTTP Upgrade request for
- the cases where the caller has already received an HTTP message.
- This flexibility permits novel and robust implementations. For example,
- a listening socket that can handshake in multiple protocols on the
- same port.
-
- Sometimes callers want to read some bytes on the socket before reading
- the WebSocket HTTP Upgrade request. Beast allows these already-received
- bytes to be supplied to an overload of the accepting function to permit
- sophisticated features. For example, a listening socket that can
- accept both regular WebSocket and Secure WebSocket (SSL) connections.
-
- [table
- [
- [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L501 Beast],
- [@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L401 also]]
- [websocketpp]
- ][
- [```
- template
- void
- accept(ConstBufferSequence const& buffers);
-
- template
- void
- accept(http::request_v1 const& request);
- ```]
- [
- //
- ]
- ]]]]
-
- ]
-]]
-
-]
-
-[endsect]
+[include design/http_message.qbk]
+[include design/http_comparison.qbk]
+[include design/websocket_zaphoyd.qbk]
+[include design/review.qbk]
[endsect]
diff --git a/doc/design/http_comparison.qbk b/doc/design/http_comparison.qbk
new file mode 100644
index 00000000..f063bf4f
--- /dev/null
+++ b/doc/design/http_comparison.qbk
@@ -0,0 +1,454 @@
+[/
+ 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:http_comparison HTTP Comparison to Other Libraries]
+
+There are a few C++ published libraries which implement some of the HTTP
+protocol. We analyze the message model chosen by those libraries and discuss
+the advantages and disadvantages relative to Beast.
+
+The general strategy used by the author to evaluate external libraries is
+as follows:
+
+* Review the message model. Can it represent a complete request or
+ response? What level of allocator support is present? How much
+ customization is possible?
+
+* Review the stream abstraction. This is the type of object, such as
+ a socket, which may be used to parse or serialize (i.e. read and write).
+ Can user defined types be specified? What's the level of conformance to
+ to Asio or Networking-TS concepts?
+
+* Check treatment of buffers. Does the library manage the buffers or
+ or can users provide their own buffers?
+
+* How does the library handle corner cases such as trailers,
+ Expect: 100-continue, or deferred commitment of the body type?
+
+[note
+ Declarations examples from external libraries have been edited:
+ portions have been removed for simplification.
+]
+
+
+
+[heading cpp-netlib]
+
+[@https://github.com/cpp-netlib/cpp-netlib/tree/092cd570fb179d029d1865aade9f25aae90d97b9 [*cpp-netlib]]
+is a network programming library previously intended for Boost but not
+having gone through formal review. As of this writing it still uses the
+Boost name, namespace, and directory structure although the project states
+that Boost acceptance is no longer a goal. The library is based on Boost.Asio
+and bills itself as ['"a collection of network related routines/implementations
+geared towards providing a robust cross-platform networking library"]. It
+cites ['"Common Message Type"] as a feature. As of the branch previous
+linked, it uses these declarations:
+```
+template
+struct basic_message {
+ public:
+ typedef Tag tag;
+
+ typedef typename headers_container::type headers_container_type;
+ typedef typename headers_container_type::value_type header_type;
+ typedef typename string::type string_type;
+
+ headers_container_type& headers() { return headers_; }
+ headers_container_type const& headers() const { return headers_; }
+
+ string_type& body() { return body_; }
+ string_type const& body() const { return body_; }
+
+ string_type& source() { return source_; }
+ string_type const& source() const { return source_; }
+
+ string_type& destination() { return destination_; }
+ string_type const& destination() const { return destination_; }
+
+ private:
+ friend struct detail::directive_base;
+ friend struct detail::wrapper_base >;
+
+ mutable headers_container_type headers_;
+ mutable string_type body_;
+ mutable string_type source_;
+ mutable string_type destination_;
+};
+```
+
+This container is the base class template used to represent HTTP messages.
+It uses a "tag" type style specializations for a variety of trait classes,
+allowing for customization of the various parts of the message. For example,
+a user specializes `headers_container` to determine what container type
+holds the header fields. We note some problems with the container declaration:
+
+* The header and body containers may only be default-constructed.
+
+* No stateful allocator support.
+
+* There is no way to defer the commitment of the type for `body_` to
+ after the headers are read in.
+
+* The message model includes a "source" and "destination." This is
+ extraneous metadata associated with the connection which is not part
+ of the HTTP protocol specification and belongs elsewhere.
+
+* The use of `string_type` (a customization point) for source,
+ destination, and body suggests that `string_type` models a
+ [*ForwardRange] whose `value_type` is `char`. This representation
+ is less than ideal, considering that the library is built on
+ Boost.Asio. Adapting a __DynamicBuffer__ to the required forward
+ range destroys information conveyed by the __ConstBufferSequence__
+ and __MutableBufferSequence__ used in dynamic buffers. The consequence
+ is that cpp-netlib implementations will be less efficient than an
+ equivalent __N4588__ conforming implementation.
+
+* The library uses specializatons of `string` to change the type
+ of string used everywhere, including the body, field name and value
+ pairs, and extraneous metadata such as source and destination. The
+ user may only choose a single type: field name, field values, and
+ the body container will all use the same string type. This limits
+ utility of the customization point. The library's use of the string
+ trait is limited to selecting between `std::string` and `std::wstring`.
+ We do not find this use-case compelling given the limitations.
+
+* The specialized trait classes generate a proliferation of small
+ additional framework types. To specialize traits, users need to exit
+ their namespace and intrude into the `boost::network::http` namespace.
+ The way the traits are used in the library limits the usefulness
+ of the traits to trivial purpose.
+
+* The `string customization point constrains user defined body types
+ to few possible strategies. There is no way to represent an HTTP message
+ body as a filename with accompanying algorithms to store or retrieve data
+ from the file system.
+
+The design of the message container in this library is cumbersome
+with its system of customization using trait specializations. The
+use of these customizations is extremely limited due to the way they
+are used in the container declaration, making the design overly
+complex without corresponding benefit.
+
+
+
+[heading Boost.HTTP]
+
+[@https://github.com/BoostGSoC14/boost.http/tree/45fc1aa828a9e3810b8d87e669b7f60ec100bff4 [*boost.http]]
+is a library resulting from the 2014 Google Summer of Code. It was submitted
+for a Boost formal review and rejected in 2015. It is based on Boost.Asio,
+and development on the library has continued to the present. As of the branch
+previously linked, it uses these message declarations:
+```
+template
+struct basic_message
+{
+ typedef Headers headers_type;
+ typedef Body body_type;
+
+ headers_type &headers();
+
+ const headers_type &headers() const;
+
+ body_type &body();
+
+ const body_type &body() const;
+
+ headers_type &trailers();
+
+ const headers_type &trailers() const;
+
+private:
+ headers_type headers_;
+ body_type body_;
+ headers_type trailers_;
+};
+
+typedef basic_message> message;
+
+template
+struct is_message>: public std::true_type {};
+```
+
+* This container cannot model a complete message. The ['start-line] items
+ (method and target for requests, reason-phrase for responses) are
+ communicated out of band, as is the ['http-version]. A function that
+ operates on the message including the start line requires additional
+ parameters. This is evident in one of the
+ [@https://github.com/BoostGSoC14/boost.http/blob/45fc1aa828a9e3810b8d87e669b7f60ec100bff4/example/basic_router.cpp#L81 example programs].
+ The `500` and `"OK"` arguments represent the response ['status-code] and
+ ['reason-phrase] respectively:
+ ```
+ ...
+ http::message reply;
+ ...
+ self->socket.async_write_response(500, string_ref("OK"), reply, yield);
+ ```
+
+* `headers_`, `body_`, and `trailers_` may only be default-constructed,
+ since there are no explicitly declared constructors.
+
+* There is no way to defer the commitment of the [*Body] type to after
+ the headers are read in. This is related to the previous limitation
+ on default-construction.
+
+* No stateful allocator support. This follows from the previous limitation
+ on default-construction. Buffers for start-line strings must be
+ managed externally from the message object since they are not members.
+
+* The trailers are stored in a separate object. Aside from the combinatorial
+ explosion of the number of additional constructors necessary to fully
+ support arbitrary forwarded parameter lists for each of the headers, body,
+ and trailers members, the requirement to know in advance whether a
+ particular HTTP field will be located in the headers or the trailers
+ poses an unnecessary complication for general purpose functions that
+ operate on messages.
+
+* The declarations imply that `std::vector` is a model of [*Body].
+ More formally, that a body is represented by the [*ForwardRange]
+ concept whose `value_type` is an 8-bit integer. This representation
+ is less than ideal, considering that the library is built on
+ Boost.Asio. Adapting a __DynamicBuffer__ to the required forward range
+ destroys information conveyed by the __ConstBufferSequence__ and
+ __MutableBufferSequence__ used in dynamic buffers. The consequence is
+ that Boost.HTTP implementations will be less efficient when dealing
+ with body containers than an equivalent __N4588__ conforming
+ implementation.
+
+* The [*Body] customization point constrains user defined types to
+ very limited implementation strategies. For example, there is no way
+ to represent an HTTP message body as a filename with accompanying
+ algorithms to store or retrieve data from the file system.
+
+This representation addresses a narrow range of use cases. It has
+limited potential for customization and performance. It is more difficult
+to use because it excludes the start line fields from the model.
+
+
+
+[heading C++ REST SDK (cpprestsdk)]
+
+[@https://github.com/Microsoft/cpprestsdk/tree/381f5aa92d0dfb59e37c0c47b4d3771d8024e09a [*cpprestsdk]]
+is a Microsoft project which ['"...aims to help C++ developers connect to and
+interact with services"]. It offers the most functionality of the libraries
+reviewed here, including support for Websocket services using its websocket++
+dependency. It can use native APIs such as HTTP.SYS when building Windows
+based applications, and it can use Boost.Asio. The WebSocket module uses
+Boost.Asio exclusively.
+
+As cpprestsdk is developed by a large corporation, it contains quite a bit
+of functionality and necessarily has more interfaces. We will break down
+the interfaces used to model messages into more manageable pieces. This
+is the container used to store the HTTP header fields:
+```
+class http_headers
+{
+public:
+ ...
+
+private:
+ std::map m_headers;
+};
+```
+
+This declaration is quite bare-bones. We note the typical problems of
+most field containers:
+
+* The container may only be default-constructed.
+
+* No support for allocators, stateful or otherwise.
+
+* There are no customization points at all.
+
+Now we analyze the structure of
+the larger message container. The library uses a handle/body idiom. There
+are two public message container interfaces, one for requests (`http_request`)
+and one for responses (`http_response`). Each interface maintains a private
+shared pointer to an implementation class. Public member function calls
+are routed to the internal implementation. This is the first implementation
+class, which forms the base class for both the request and response
+implementations:
+```
+namespace details {
+
+class http_msg_base
+{
+public:
+ http_headers &headers() { return m_headers; }
+
+ _ASYNCRTIMP void set_body(const concurrency::streams::istream &instream, const utf8string &contentType);
+
+ /// Set the stream through which the message body could be read
+ void set_instream(const concurrency::streams::istream &instream) { m_inStream = instream; }
+
+ /// Set the stream through which the message body could be written
+ void set_outstream(const concurrency::streams::ostream &outstream, bool is_default) { m_outStream = outstream; m_default_outstream = is_default; }
+
+ const pplx::task_completion_event & _get_data_available() const { return m_data_available; }
+
+protected:
+ /// Stream to read the message body.
+ concurrency::streams::istream m_inStream;
+
+ /// stream to write the msg body
+ concurrency::streams::ostream m_outStream;
+
+ http_headers m_headers;
+ bool m_default_outstream;
+
+ /// The TCE is used to signal the availability of the message body.
+ pplx::task_completion_event m_data_available;
+};
+```
+
+To understand these declarations we need to first understand that cpprestsdk
+uses the asynchronous model defined by Microsoft's
+[@https://msdn.microsoft.com/en-us/library/dd504870.aspx [*Concurrency Runtime]].
+Identifiers from the [@https://msdn.microsoft.com/en-us/library/jj987780.aspx [*`pplx` namespace]]
+define common asynchronous patterns such as tasks and events. The
+`concurrency::streams::istream` parameter and `m_data_available` data member
+indicates a lack of separation of concerns. The representation of HTTP messages
+should not be conflated with the asynchronous model used to serialize or
+parse those messages in the message declarations.
+
+The next declaration forms the complete implementation class referenced by the
+handle in the public interface (which follows after):
+```
+/// Internal representation of an HTTP request message.
+class _http_request final : public http::details::http_msg_base, public std::enable_shared_from_this<_http_request>
+{
+public:
+ _ASYNCRTIMP _http_request(http::method mtd);
+
+ _ASYNCRTIMP _http_request(std::unique_ptr server_context);
+
+ http::method &method() { return m_method; }
+
+ const pplx::cancellation_token &cancellation_token() const { return m_cancellationToken; }
+
+ _ASYNCRTIMP pplx::task reply(const http_response &response);
+
+private:
+
+ // Actual initiates sending the response, without checking if a response has already been sent.
+ pplx::task _reply_impl(http_response response);
+
+ http::method m_method;
+
+ std::shared_ptr m_progress_handler;
+};
+
+} // namespace details
+```
+
+As before, we note that the implementation class for HTTP requests concerns
+itself more with the mechanics of sending the message asynchronously than
+it does with actually modeling the HTTP message as described in __rfc7230__:
+
+* The constructor accepting `std::unique_ptrmethod(); }
+
+ void set_method(const http::method &method) const { _m_impl->method() = method; }
+
+ /// Extract the body of the request message as a string value, checking that the content type is a MIME text type.
+ /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
+ pplx::task extract_string(bool ignore_content_type = false)
+ {
+ auto impl = _m_impl;
+ return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->extract_string(ignore_content_type); });
+ }
+
+ /// Extracts the body of the request message into a json value, checking that the content type is application/json.
+ /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out.
+ pplx::task extract_json(bool ignore_content_type = false) const
+ {
+ auto impl = _m_impl;
+ return pplx::create_task(_m_impl->_get_data_available()).then([impl, ignore_content_type](utility::size64_t) { return impl->_extract_json(ignore_content_type); });
+ }
+
+ /// Sets the body of the message to the contents of a byte vector. If the 'Content-Type'
+ void set_body(const std::vector &body_data);
+
+ /// Defines a stream that will be relied on to provide the body of the HTTP message when it is
+ /// sent.
+ void set_body(const concurrency::streams::istream &stream, const utility::string_t &content_type = _XPLATSTR("application/octet-stream"));
+
+ /// Defines a stream that will be relied on to hold the body of the HTTP response message that
+ /// results from the request.
+ void set_response_stream(const concurrency::streams::ostream &stream);
+ {
+ return _m_impl->set_response_stream(stream);
+ }
+
+ /// Defines a callback function that will be invoked for every chunk of data uploaded or downloaded
+ /// as part of the request.
+ void set_progress_handler(const progress_handler &handler);
+
+private:
+ friend class http::details::_http_request;
+ friend class http::client::http_client;
+
+ std::shared_ptr _m_impl;
+};
+```
+
+It is clear from this declaration that the goal of the message model in
+this library is driven by its use-case (interacting with REST servers)
+and not to model HTTP messages generally. We note problems similar to
+the other declarations:
+
+* There are no compile-time customization points at all. The only
+ customization is in the `concurrency::streams::istream` and
+ `concurrency::streams::ostream` reference parameters. Presumably,
+ these are abstract interfaces which may be subclassed by users
+ to achieve custom behaviors.
+
+* The extraction of the body is conflated with the asynchronous model.
+
+* No way to define an allocator for the container used when extracting
+ the body.
+
+* A body can only be extracted once, limiting the use of this container
+ when using a functional programming style.
+
+* Setting the body requires either a vector or a `concurrency::streams::istream`.
+ No user defined types are possible.
+
+* The HTTP request container conflates HTTP response behavior (see the
+ `set_response_stream` member). Again this is likely purpose-driven but
+ the lack of separation of concerns limits this library to only the
+ uses explicitly envisioned by the authors.
+
+The general theme of the HTTP message model in cpprestsdk is "no user
+definable customizations". There is no allocator support, and no
+separation of concerns. It is designed to perform a specific set of
+behaviors. In other words, it does not follow the open/closed principle.
+
+Tasks in the Concurrency Runtime operate in a fashion similar to
+`std::future`, but with some improvements such as continuations which
+are not yet in the C++ standard. The costs of using a task based
+asynchronous interface instead of completion handlers is well
+documented: synchronization points along the call chain of composed
+task operations which cannot be optimized away. See:
+[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3747.pdf
+[*A Universal Model for Asynchronous Operations]] (Kohlhoff).
+
+[endsect]
diff --git a/doc/design/http_message.qbk b/doc/design/http_message.qbk
new file mode 100644
index 00000000..d7e5226d
--- /dev/null
+++ b/doc/design/http_message.qbk
@@ -0,0 +1,284 @@
+[/
+ 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:http_message HTTP Message Container]
+
+In this section we describe the problem of modeling HTTP messages and explain
+how the library arrived at its solution, with a discussion of the benefits
+and drawbacks of the design choices. The goal for creating a message model
+is to create a container with value semantics, possibly movable and/or
+copyable, that completely describes the message. More formally, the container
+has all of the information necessary for the serialization algorithms to
+represent the entire message as a series of octets, and that the parsing
+algorithms store all of the captured information about an entire message
+in the container.
+
+In addition to capturing the entirety of a message, we would like the message
+container to contain enough customization points to permit the following:
+allocator support, user-defined containers to represent header fields, and
+user-defined containers to represent the body. And finally, because requests
+and responses have slightly different information in the ['start-line], we
+would like the containers for requests and responses to be represented by
+different types.
+
+Here is our first attempt at declaring some message containers:
+
+[table
+[[
+```
+/// An HTTP request
+template
+struct request
+{
+ int version;
+ std::string method;
+ std::string target;
+ Fields fields;
+ typename Body::value_type body;
+};
+```
+][
+```
+/// An HTTP response
+template
+struct response
+{
+ int version;
+ int status;
+ std::string reason;
+ Fields fields;
+ typename Body::value_type body;
+};
+```
+]]
+]
+
+Here we have accomplished a few things. Request and response objects are
+different types. The user can choose the container used to represent the
+fields. And the user can choose the [*Body] type, which is a concept
+defining not only the type of `body` member but also the algorithms used
+to transfer information in and out of that member when performing
+serialization and parsing.
+
+However, a problem arises. How do we write a function which can accept
+an object that is either a request or a response? As written, the only
+ovious solution is to make the message a template type. Additional traits
+classes would then be needed to make sure that the passed object has a
+valid type which meets the requirements. We can avoid those complexities
+by renaming the containers and making them partial specializations of a
+single class, like this:
+
+```
+/// An HTTP message
+template
+struct message;
+
+/// An HTTP request
+template
+struct message
+{
+ int version;
+ std::string method;
+ std::string target;
+ Fields fields;
+ typename Body::value_type body;
+};
+
+/// An HTTP response
+template
+struct response
+{
+ int version;
+ int status;
+ std::string reason;
+ Fields fields;
+ typename Body::value_type body;
+};
+```
+
+Now we can declare a function which takes any message as a parameter:
+```
+template
+void f(message& msg);
+```
+
+This function can manipulate the fields common to requests and responses.
+If it needs to access the other fields, it can do so using tag dispatch
+with an object of type `std::integral_constant`.
+
+Often, in non-trivial HTTP applications, we want to read the HTTP header
+and examine its contents before choosing a top for [*Body]. To accomplish
+this, there needs to be a way to model the header portion of a message.
+And we'd like to do this in a way that allows functions which take the
+header as a parameter, to also accept a type representing the whole
+message (the function will see just the header part). This suggests
+inheritance:
+```
+/// An HTTP message header
+template
+struct header;
+
+/// An HTTP request header
+template
+struct header
+{
+ int version;
+ std::string method;
+ std::string target;
+ Fields fields;
+};
+
+/// An HTTP response header
+template
+struct header
+{
+ int version;
+ int status;
+ std::string reason;
+ Fields fields;
+};
+
+/// An HTTP message
+template
+struct message : header
+{
+ typename Body::value_type body;
+
+ /// Construct from a `header`
+ message(header&& h);
+};
+
+```
+
+Note that the `message` class now has a constructor allowing messages
+to be constructed from a similarly typed `header`. This handles the case
+where the user already has the header and wants to make a commitment to the
+type for [*Body]. This also lets us declare a function accepting any header:
+```
+template
+void f(header& msg);
+```
+
+Until now we have not given significant consideration to the constructors
+of the `message` class. But to achieve all our goals we will need to make
+sure that there are enough constructor overloads to not only provide for
+the special copy and move members if the instantiated types support it,
+but also allow the fields container and body container to be constructed
+with arbitrary variadic lists of parameters. This allows those members
+to properly support allocators.
+
+The solution used in the library is to treat the message like a `std::pair`
+for the purposes of construction, except that instead of `first` and `last`
+we have `fields` and `body`. This means that single-argument constructors
+for those fields should be accessible as they are with `std::pair`, and
+that a mechanism identical to the pair's use of `std::piecewise_construct`
+should be provided. Those constructors are too complex to repeat here, but
+interested readers can view the declarations in the corresponding header
+file.
+
+There is now significant progress with our message container but a stumbling
+block remains. The presence of `std::string` members is an obstacle to our
+goal of making messages allocator-aware. One obvious solution is to add an
+allocator to the template parameter list of the header and message classes,
+and allow the string based members to construct with instances of those
+allocators. This is unsatisfying because of the combinatorial explosion of
+constructor variations needed to support the scheme. It also means that
+request messages could have [*four] different allocators: two for the fields
+and body, and two for the method and target strings. A better solution is
+needed.
+
+To make allocators workable, we make a simple change to the interface and
+then engineer a clever concession. First, the interface change:
+```
+/// An HTTP request header
+template
+struct header
+{
+ int version;
+ string_view method() const;
+ void method(string_view);
+ string_view target(); const;
+ void target(string_view);
+ Fields fields;
+};
+
+/// An HTTP response header
+template
+struct header
+{
+ int version;
+ int status;
+ string_view reason() const;
+ void reason(string_view);
+ Fields fields;
+};
+```
+
+This approach replaces public data members with traditional accessors
+using non-owning references to string buffers. Now we make a concession:
+management of the corresponding string is delegated to the [*Fields]
+container, which can already be allocator aware and constructed with the
+necessary allocator parameter via the provided constructor overloads for
+`message`. The delegation implementation looks like this (only the
+response header specialization is shown):
+```
+/// An HTTP response header
+template
+struct header
+{
+ int version;
+ int status;
+
+ auto
+ reason() const -> decltype(std::declval().reason()) const
+ {
+ return fields.reason();
+ }
+
+ template
+ void
+ reason(Value&& value)
+ {
+ fields.reason(std::forward(value));
+ }
+
+ Fields fields;
+```
+
+An advantage of this technique is that user-provided implementations of
+[*Fields] may use arbitrary representation strategies. For example, instead
+of explicitly storing the method as a string, store it as an enumerated
+integer with a lookup table of static strings for known message types.
+
+Now that we've accomplished our initial goals and more, there is one small
+quality of life improvement to make. Users will choose different types for
+`Body` far more often than they will for `Fields`. Thus, we swap the order
+of these types and provide a default:
+```
+/// An HTTP header
+template
+struct header;
+
+/// An HTTP message
+template
+struct message;
+```
+
+This container is also capable of representing complete HTTP/2 messages.
+Not because it was explicitly designed for, but because the IETF wanted to
+preserve message compatibility with HTTP/1. Aside from version specific
+fields such as Connection, the contents of HTTP/1 and HTTP/2 messages are
+identical even though their serialized representation is considerably
+different. The message model presented in this library is ready for HTTP/2.
+
+In conclusion, this representation for the message container is well thought
+out, provides comprehensive flexibility, and avoids the necessity of defining
+additional traits classes. User declarations of functions that accept headers
+or messages as parameters are easy to write in a variety of ways to accomplish
+different results, without forcing cumbersome SFINAE declarations everywhere.
+
+[endsect]
diff --git a/doc/review.qbk b/doc/design/review.qbk
similarity index 51%
rename from doc/review.qbk
rename to doc/design/review.qbk
index 652835e1..bc234493 100644
--- a/doc/review.qbk
+++ b/doc/design/review.qbk
@@ -5,7 +5,7 @@
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
-[section:review For Reviewers]
+[section:review Boost Formal Review FAQ]
To set realistic expectations and prevent a litany of duplicate review
statements, these notes address the most common questions and comments
@@ -58,6 +58,12 @@ about Beast and other HTTP libraries that have gone through formal review.
[[
"There's no HTTP/2 support yet!"
][
+ Many reviewers feel that HTTP/2 support is an essential feature of
+ a HTTP library. The authors agree that HTTP/2 is important but also
+ feel that the most sensible implementation is one that does not re-use
+ the same network reading and writing interface for 2 as that for 1.0
+ and 1.1.
+
The Beast HTTP message model was designed with the new protocol
in mind and should be evaluated in that context. There are plans
to add HTTP/2 in the future, but there is no rush to do so.
@@ -66,6 +72,16 @@ about Beast and other HTTP libraries that have gone through formal review.
It is the author's position that there is sufficient value in
Beast's HTTP/1-only implementation that the lack of HTTP/2
should not be a barrier to acceptance.
+
+ The Beast HTTP message model is suitable for HTTP/2 and can be re-used.
+ The IETF HTTP Working Group adopted message compatiblity with HTTP/1.x
+ as an explicit goal. A parser can simply emit full headers after
+ decoding the compressed HTTP/2 headers. The stream ID is not logically
+ part of the message but rather message metadata and should be
+ communicated out-of-band (see below). HTTP/2 sessions begin with a
+ traditional HTTP/1.1 Upgrade similar in fashion to the WebSocket
+ upgrade. An HTTP/2 implementation can use existing Beast.HTTP primitives
+ to perform this handshake.
]]
[[
"This should work with standalone-Asio!"
@@ -106,7 +122,7 @@ about Beast and other HTTP libraries that have gone through formal review.
is lifted from another extremely popular project which has performance
as a design goal (see https://github.com/h2o/picohttpparser).
- From: http://www.boost.org/development/requirements.html
+ From: [@http://www.boost.org/development/requirements.html]
"Aim first for clarity and correctness; optimization should
be only a secondary concern in most Boost libraries."
@@ -143,6 +159,121 @@ about Beast and other HTTP libraries that have gone through formal review.
+[[
+ "Some more advanced examples, e.g. including TLS with client/server
+ certificates would help."
+][
+ The HTTP interface doesn't try to reinvent the wheel, it just uses
+ the `boost::asio::ip::tcp::socket` or `boost::asio::ssl::stream` that
+ you set up beforehand. Callers use the interfaces already existing
+ on those objects to make outgoing connections, accept incoming connections,
+ or establish TLS sessions with certificates. We find the available Asio
+ examples for performing these tasks sufficient.
+]]
+
+[[
+ "A built-in HTTP router?"
+][
+ We presume this means a facility to match expressions against the URI
+ in HTTP requests, and dispatch them to calling code. The authors feel
+ that this is a responsibility of higher level code. Beast does
+ not try to offer a web server.
+]]
+
+[[
+ "HTTP Cookies? Forms/File Uploads?"
+][
+ Cookies, or managing these types of HTTP headers in general, is the
+ responsibility of higher levels. Beast.HTTP just tries to get complete
+ messages to and from the calling code. It deals in the HTTP headers just
+ enough to process the message body and leaves the rest to callers. However,
+ for forms and file uploads the symmetric interface of the message class
+ allows HTTP requests to include arbitrary body types including those needed
+ to upload a file or fill out a form.
+]]
+
+[[
+ "...supporting TLS (is this a feature? If not this would be a show-stopper),
+ etc."
+][
+ Beast does not provide direct facilities for implementing TLS connections;
+ however, the interfaces already existing on the `boost::asio::ssl::stream`
+ are available and can be used to establish secure connections. Then,
+ functions like `http::read` or `http::async_write` can work with those
+ encrypted connections with no problem.
+]]
+
+[[
+ "There should also be more examples of how to integrate the http service
+ with getting files from the file system, generating responses CGI-style"
+][
+ The design goal for the library is to not try to invent a web server.
+ We feel that there is a strong need for a basic implementation that
+ models the HTTP message and provides functions to send and receive them
+ over Asio. Such an implementation should serve as a building block upon
+ which higher abstractions such as the aforementioned HTTP service or
+ cgi-gateway can be built.
+
+ One of the example programs implements a simple HTTP server that
+ delivers files from the filesystem.
+]]
+
+[[
+ "You should send a 100-continue to ask for the rest of the body if required."
+][
+ The Beast interface supporst this functionality (by allowing this
+ special case of "split" message parsing and serialization). Specifically,
+ it lets callers read the request up to just before the body,
+ and let callers write the request up to just before the body. However,
+ making use of this behavior is up to callers (since Beast is low level).
+]]
+
+
+
+[[
+ "I would also like to see instances of this library being used
+ in production. That would give some evidence that the design
+ works in practice."
+][
+ Beast.HTTP and Beast.WebSocket are production ready and currently
+ running on public servers receiving traffic and handling millions of
+ dollars worth of financial transactions daily. The servers run [*rippled],
+ open source software ([@https://github.com/ripple/rippled repository])
+ implementing the
+ [@https://ripple.com/files/ripple_consensus_whitepaper.pdf [*Ripple Consensus Protocol]],
+ technology provided by [@http://ripple.com Ripple].
+]]
+
+
+
+[[
+ What about WebSocket message compression?
+][
+ Beast WebSocket supports the permessage-deflate extension described in
+ [@https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-00 draft-ietf-hybi-permessage-compression-00].
+ The library comes with a header-only, C++11 port of ZLib's "deflate" codec
+ used in the implementation of the permessage-deflate extension.
+]]
+[[
+ Where is the WebSocket TLS/SSL interface?
+][
+ The `websocket::stream` wraps the socket or stream that you provide
+ (for example, a `boost::asio::ip::tcp::socket` or a
+ `boost::asio::ssl::stream`). You establish your TLS connection using the
+ interface on `ssl::stream` like shown in all of the Asio examples, then
+ construct your `websocket::stream` around it. It works perfectly fine;
+ Beast.WebSocket doesn't try to reinvent the wheel or put a fresh coat of
+ interface paint on the `ssl::stream`.
+
+ The WebSocket implementation [*does] provide support for shutting down
+ the TLS connection through the use of the ADL compile-time virtual functions
+ [link beast.ref.websocket__teardown `teardown`] and
+ [link beast.ref.websocket__async_teardown `async_teardown`]. These will
+ properly close the connection as per rfc6455 and overloads are available
+ for TLS streams. Callers may provide their own overloads of these functions
+ for user-defined next layer types.
+]]
+
]
[endsect]
diff --git a/doc/design/websocket_zaphoyd.qbk b/doc/design/websocket_zaphoyd.qbk
new file mode 100644
index 00000000..5868651e
--- /dev/null
+++ b/doc/design/websocket_zaphoyd.qbk
@@ -0,0 +1,431 @@
+[/
+ 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:websocket_zaphoyd Comparison to Zaphoyd Studios WebSocket++]
+
+[variablelist
+
+[[
+ How does this compare to [@https://www.zaphoyd.com/websocketpp websocketpp],
+ an alternate header-only WebSocket implementation?
+][
+ [variablelist
+
+ [[1. Synchronous Interface][
+
+ Beast offers full support for WebSockets using a synchronous interface. It
+ uses the same style of interfaces found in Boost.Asio: versions that throw
+ exceptions, or versions that return the error code in a reference parameter:
+
+ [table
+ [
+ [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L774 Beast]]
+ [websocketpp]
+ ][
+ [```
+ template
+ void
+ read(opcode& op, DynamicBuffer& dynabuf)
+ ```]
+ [
+ //
+ ]
+ ]]]]
+
+ [[2. Connection Model][
+
+ websocketpp supports multiple transports by utilizing a trait, the `config::transport_type`
+ ([@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/asio/connection.hpp#L60 asio transport example])
+ To get an idea of the complexity involved with implementing a transport,
+ compare the asio transport to the
+ [@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L59 `iostream` transport]
+ (a layer that allows websocket communication over a `std::iostream`).
+
+ In contrast, Beast abstracts the transport by defining just one [*`NextLayer`]
+ template argument The type requirements for [*`NextLayer`] are
+ already familiar to users as they are documented in Asio:
+ __AsyncReadStream__, __AsyncWriteStream__, __SyncReadStream__, __SyncWriteStream__.
+
+ The type requirements for instantiating `beast::websocket::stream` versus
+ `websocketpp::connection` with user defined types are vastly reduced
+ (18 functions versus 2). Note that websocketpp connections are passed by
+ `shared_ptr`. Beast does not use `shared_ptr` anywhere in its public interface.
+ A `beast::websocket::stream` is constructible and movable in a manner identical
+ to a `boost::asio::ip::tcp::socket`. Callers can put such objects in a
+ `shared_ptr` if they want to, but there is no requirement to do so.
+
+ [table
+ [
+ [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp Beast]]
+ [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L234 websocketpp]]
+ ][
+ [```
+ template
+ class stream
+ {
+ NextLayer next_layer_;
+ ...
+ }
+ ```]
+ [```
+ template
+ class connection
+ : public config::transport_type::transport_con_type
+ , public config::connection_base
+ {
+ public:
+ typedef lib::shared_ptr ptr;
+ ...
+ }
+ ```]
+ ]]]]
+
+ [[3. Client and Server Role][
+
+ websocketpp provides multi-role support through a hierarchy of
+ different classes. A `beast::websocket::stream` is role-agnostic, it
+ offers member functions to perform both client and server handshakes
+ in the same class. The same types are used for client and server
+ streams.
+
+ [table
+ [
+ [Beast]
+ [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/roles/server_endpoint.hpp#L39 websocketpp],
+ [@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/roles/client_endpoint.hpp#L42 also]]
+ ][
+ [
+ //
+ ]
+ [```
+ template
+ class client : public endpoint,config>;
+ template
+ class server : public endpoint,config>;
+ ```]
+ ]]]]
+
+ [[4. Thread Safety][
+
+ websocketpp uses mutexes to protect shared data from concurrent
+ access. In contrast, Beast does not use mutexes anywhere in its
+ implementation. Instead, it follows the Asio pattern. Calls to
+ asynchronous initiation functions use the same method to invoke
+ intermediate handlers as the method used to invoke the final handler,
+ through the __asio_handler_invoke__ mechanism.
+
+ The only requirement in Beast is that calls to asynchronous initiation
+ functions are made from the same implicit or explicit strand. For
+ example, if the `io_service` associated with a `beast::websocket::stream`
+ is single threaded, this counts as an implicit strand and no performance
+ costs associated with mutexes are incurred.
+
+ [table
+ [
+ [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/impl/read_frame_op.ipp#L118 Beast]]
+ [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/transport/iostream/connection.hpp#L706 websocketpp]]
+ ][
+ [```
+ template
+ friend
+ void asio_handler_invoke(Function&& f, read_frame_op* op)
+ {
+ return boost_asio_handler_invoke_helpers::invoke(f, op->d_->h);
+ }
+ ```]
+ [```
+ mutex_type m_read_mutex;
+ ```]
+ ]]]]
+
+ [[5. Callback Model][
+
+ websocketpp requires a one-time call to set the handler for each event
+ in its interface (for example, upon message receipt). The handler is
+ represented by a `std::function` equivalent. Its important to recognize
+ that the websocketpp interface performs type-erasure on this handler.
+
+ In comparison, Beast handlers are specified in a manner identical to
+ Boost.Asio. They are function objects which can be copied or moved but
+ most importantly they are not type erased. The compiler can see
+ through the type directly to the implementation, permitting
+ optimization. Furthermore, Beast follows the Asio rules for treatment
+ of handlers. It respects any allocation, continuation, or invocation
+ customizations associated with the handler through the use of argument
+ dependent lookup overloads of functions such as `asio_handler_allocate`.
+
+ The Beast completion handler is provided at the call site. For each
+ call to an asynchronous initiation function, it is guaranteed that
+ there will be exactly one final call to the handler. This functions
+ exactly the same way as the asynchronous initiation functions found in
+ Boost.Asio, allowing the composition of higher level abstractions.
+
+ [table
+ [
+ [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L834 Beast]]
+ [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L281 websocketpp],
+ [@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L473 also]]
+ ][
+ [```
+ template
+ typename async_completion::result_type
+ async_read(opcode& op, DynamicBuffer& dynabuf, ReadHandler&& handler);
+ ```]
+ [```
+ typedef lib::function message_handler;
+ void set_message_handler(message_handler h);
+ ```]
+ ]]]]
+
+ [[6. Extensible Asynchronous Model][
+
+ Beast fully supports the
+ [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf Extensible Asynchronous Model]
+ developed by Christopher Kohlhoff, author of Boost.Asio (see Section 8).
+
+ Beast websocket asynchronous interfaces may be used seamlessly with
+ `std::future` stackful/stackless coroutines, or user defined customizations.
+
+ [table
+ [
+ [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/impl/stream.ipp#L378 Beast]]
+ [websocketpp]
+ ][
+ [```
+ beast::async_completion completion(handler);
+ read_op{
+ completion.handler, *this, op, buffer};
+ return completion.result.get();
+ ```]
+ [
+ //
+ ]
+ ]]]]
+
+ [[7. Message Buffering][
+
+ websocketpp defines a message buffer, passed in arguments by
+ `shared_ptr`, and an associated message manager which permits
+ aggregation and reuse of memory. The implementation of
+ `websocketpp::message` uses a `std::string` to hold the payload. If an
+ incoming message is broken up into multiple frames, the string may be
+ reallocated for each continuation frame. The `std::string` always uses
+ the standard allocator, it is not possible to customize the choice of
+ allocator.
+
+ Beast allows callers to specify the object for receiving the message
+ or frame data, which is of any type meeting the requirements of
+ __DynamicBuffer__ (modeled after `boost::asio::streambuf`).
+
+ Beast comes with the class __basic_multi_buffer__, an efficient
+ implementation of the __DynamicBuffer__ concept which makes use of multiple
+ allocated octet arrays. If an incoming message is broken up into
+ multiple pieces, no reallocation occurs. Instead, new allocations are
+ appended to the sequence when existing allocations are filled. Beast
+ does not impose any particular memory management model on callers. The
+ __basic_multi_buffer__ provided by beast supports standard allocators through
+ a template argument. Use the __DynamicBuffer__ that comes with beast,
+ customize the allocator if you desire, or provide your own type that
+ meets the requirements.
+
+ [table
+ [
+ [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L774 Beast]]
+ [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/message_buffer/message.hpp#L78 websocketpp]]
+ ][
+ [```
+ template
+ read(opcode& op, DynamicBuffer& dynabuf);
+ ```]
+ [```
+ template class con_msg_manager>
+ class message {
+ public:
+ typedef lib::shared_ptr ptr;
+ ...
+ std::string m_payload;
+ ...
+ };
+ ```]
+ ]]]]
+
+ [[8. Sending Messages][
+
+ When sending a message, websocketpp requires that the payload is
+ packaged in a `websocketpp::message` object using `std::string` as the
+ storage, or it requires a copy of the caller provided buffer by
+ constructing a new message object. Messages are placed onto an
+ outgoing queue. An asynchronous write operation runs in the background
+ to clear the queue. No user facing handler can be registered to be
+ notified when messages or frames have completed sending.
+
+ 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/html/boost_asio/reference/ConstBufferSequence.html ConstBufferSequence],
+ permitting efficient scatter-gather I/O.
+
+ The [*ConstBufferSequence] interface allows callers to send data from
+ memory-mapped regions (not possible in websocketpp). Callers can also
+ use the same buffers to send data to multiple streams, for example
+ broadcasting common subscription data to many clients at once. For
+ each call to `async_write` the completion handler is called once when
+ the data finishes sending, in a manner identical to `boost::asio::async_write`.
+
+ [table
+ [
+ [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L1048 Beast]]
+ [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L672 websocketpp]]
+ ][
+ [```
+ template
+ void
+ write(ConstBufferSequence const& buffers);
+ ```]
+ [```
+ lib::error_code send(std::string const & payload,
+ frame::opcode::value op = frame::opcode::text);
+ ...
+ lib::error_code send(message_ptr msg);
+ ```]
+ ]]]]
+
+ [[9. Streaming Messages][
+
+ websocketpp requires that the entire message fit into memory, and that
+ the size is known ahead of time.
+
+ Beast allows callers to compose messages in individual frames. This is
+ useful when the size of the data is not known ahead of time or if it
+ is not desired to buffer the entire message in memory at once before
+ sending it. For example, sending periodic output of a database query
+ running on a coroutine. Or sending the contents of a file in pieces,
+ without bringing it all into memory.
+
+ [table
+ [
+ [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L1151 Beast]]
+ [websocketpp]
+ ][
+ [```
+ template
+ void
+ write_frame(bool fin,
+ ConstBufferSequence const& buffers);
+ ```]
+ [
+ //
+ ]
+ ]]]]
+
+ [[10. Flow Control][
+
+ The websocketpp read implementation continuously reads asynchronously
+ from the network and buffers message data. To prevent unbounded growth
+ and leverage TCP/IP's flow control mechanism, callers can periodically
+ turn this 'read pump' off and back on.
+
+ In contrast a `beast::websocket::stream` does not independently begin
+ background activity, nor does it buffer messages. It receives data only
+ when there is a call to an asynchronous initiation function (for
+ example `beast::websocket::stream::async_read`) with an associated handler.
+ Applications do not need to implement explicit logic to regulate the
+ flow of data. Instead, they follow the traditional model of issuing a
+ read, receiving a read completion, processing the message, then
+ issuing a new read and repeating the process.
+
+ [table
+ [
+ [Beast]
+ [[@https://github.com/zaphoyd/websocketpp/blob/378437aecdcb1dfe62096ffd5d944bf1f640ccc3/websocketpp/connection.hpp#L728 websocketpp]]
+ ][
+ [
+ //
+ ]
+ [```
+ lib::error_code pause_reading();
+ lib::error_code resume_reading();
+ ```]
+ ]]]]
+
+ [[11. Connection Establishment][
+
+ websocketpp offers the `endpoint` class which can handle binding and
+ listening to a port, and spawning connection objects.
+
+ Beast does not reinvent the wheel here, callers use the interfaces
+ already in `boost::asio` for receiving incoming connections resolving
+ host names, or establishing outgoing connections. After the socket (or
+ `boost::asio::ssl::stream`) is connected, the `beast::websocket::stream`
+ is constructed around it and the WebSocket handshake can be performed.
+
+ Beast users are free to implement their own "connection manager", but
+ there is no requirement to do so.
+
+ [table
+ [
+ [[@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]]
+ ][
+ [```
+ #include
+ ```]
+ [```
+ template
+ class endpoint : public config::socket_type;
+ ```]
+ ]]]]
+
+ [[12. WebSocket Handshaking][
+
+ Callers invoke `beast::websocket::accept` to perform the WebSocket
+ handshake, but there is no requirement to use this function. Advanced
+ users can perform the WebSocket handshake themselves. Beast WebSocket
+ provides the tools for composing the request or response, and the
+ Beast HTTP interface provides the container and algorithms for sending
+ and receiving HTTP/1 messages including the necessary HTTP Upgrade
+ request for establishing the WebSocket session.
+
+ Beast allows the caller to pass the incoming HTTP Upgrade request for
+ the cases where the caller has already received an HTTP message.
+ This flexibility permits novel and robust implementations. For example,
+ a listening socket that can handshake in multiple protocols on the
+ same port.
+
+ Sometimes callers want to read some bytes on the socket before reading
+ the WebSocket HTTP Upgrade request. Beast allows these already-received
+ bytes to be supplied to an overload of the accepting function to permit
+ sophisticated features. For example, a listening socket that can
+ accept both regular WebSocket and Secure WebSocket (SSL) connections.
+
+ [table
+ [
+ [[@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L501 Beast],
+ [@https://github.com/vinniefalco/Beast/blob/6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99/include/beast/websocket/stream.hpp#L401 also]]
+ [websocketpp]
+ ][
+ [```
+ template
+ void
+ accept(ConstBufferSequence const& buffers);
+
+ template
+ void
+ accept(http::request_v1 const& request);
+ ```]
+ [
+ //
+ ]
+ ]]]]
+
+ ]
+]]
+
+]
+
+[endsect]
diff --git a/doc/master.qbk b/doc/master.qbk
index e564998f..6d3bcfef 100644
--- a/doc/master.qbk
+++ b/doc/master.qbk
@@ -88,13 +88,8 @@ provides implementations of the HTTP and WebSocket protocols.
[[
[link beast.design Design]
][
- Design rationale, answers to questions, and
- other library comparisons.
- ]]
- [[
- [link beast.review For Reviewers]
- ][
- Participants in Beast's formal review should read this first.
+ Design rationale, answers to questions, library comparisons,
+ and special considerations for Boost Formal Review participants.
]]
[[
[link beast.ref Reference]
@@ -114,7 +109,6 @@ provides implementations of the HTTP and WebSocket protocols.
[include websocket.qbk]
[include examples.qbk]
[include design.qbk]
-[include review.qbk]
[section:ref Reference]
[xinclude quickref.xml]