diff --git a/CHANGELOG.md b/CHANGELOG.md index 518768be..1fb02ee9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ WebSocket: * Call do_fail from read_some * eof on accept returns error::closed * Fix stream::read_size_hint calculation +* Documentation API Changes: diff --git a/doc/qbk/06_websocket.qbk b/doc/qbk/06_websocket.qbk index 71bc0be2..a5413a8d 100644 --- a/doc/qbk/06_websocket.qbk +++ b/doc/qbk/06_websocket.qbk @@ -35,6 +35,7 @@ Boost.Asio with a consistent asynchronous model using a modern C++ approach. [include 06_websocket/4_server.qbk] [include 06_websocket/5_messages.qbk] [include 06_websocket/6_control.qbk] -[include 06_websocket/7_notes.qbk] +[include 06_websocket/7_teardown.qbk] +[include 06_websocket/8_notes.qbk] [endsect] diff --git a/doc/qbk/06_websocket/1_streams.qbk b/doc/qbk/06_websocket/1_streams.qbk index 420839a8..670fdec5 100644 --- a/doc/qbk/06_websocket/1_streams.qbk +++ b/doc/qbk/06_websocket/1_streams.qbk @@ -35,7 +35,7 @@ and `ssl::context` arguments are forwarded to the wrapped stream's constructor: [important Code which declares websocket stream objects using Asio SSL types - must include the file [include_file beast/websocket/ssl.hpp]. + must include the file [include_file boost/beast/websocket/ssl.hpp]. ] [heading Non-owning References] diff --git a/doc/qbk/06_websocket/4_server.qbk b/doc/qbk/06_websocket/4_server.qbk index d0db473a..e0f344fc 100644 --- a/doc/qbk/06_websocket/4_server.qbk +++ b/doc/qbk/06_websocket/4_server.qbk @@ -13,9 +13,11 @@ A [link beast.ref.boost__beast__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 exception is thrown. Depending on the keep alive -setting, the connection may remain open for a subsequent handshake attempt. +is received with the +[@https://tools.ietf.org/html/rfc6455#section-4.2.2 ['101 Switching Protocols]] +status code. On failure, an error is returned or an exception is thrown. +Depending on the keep alive setting, the connection may remain open for +a subsequent handshake attempt. Performing a handshake for an incoming websocket upgrade request operates similarly. If the handshake fails, an error is returned or exception thrown: @@ -36,8 +38,10 @@ typically look like this: Server: Beast/40 ``` ][ - The Sec-WebSocket-Accept field value is generated from the - request in a fashion specified by the WebSocket protocol. + The + [@https://tools.ietf.org/html/rfc6455#section-11.3.3 ['Sec-WebSocket-Accept]] + field value is generated from the request in a fashion specified by + the WebSocket protocol. ]]] [heading Decorators] @@ -115,4 +119,13 @@ area, and later uses those octets to attempt an HTTP WebSocket Upgrade: [ws_snippet_14] +The implementation uses a fixed-size storage area to hold buffers passed +using these functions. If an application is reaching the limit of the +internal buffer size, then the websocket stream may be instantiated with +the next layer type of +[link beast.ref.boost__beast__buffered_read_stream `buffered_read_stream`] +to wrap the underlying stream. The buffered handshake data may be first +placed into the buffered read stream, which uses a dynamically sized +buffer. + [endsect] diff --git a/doc/qbk/06_websocket/5_messages.qbk b/doc/qbk/06_websocket/5_messages.qbk index d8d36497..7cb64718 100644 --- a/doc/qbk/06_websocket/5_messages.qbk +++ b/doc/qbk/06_websocket/5_messages.qbk @@ -9,6 +9,51 @@ [section Send and Receive Messages] +Interfaces for transacting messages are structured into a couple of +layers. The highest layer provides ease of use, while lower layers provide +additional control and flexibility. The layers are arranged thusly: + +[table +[[Level][Read/Write What][Description]] +[ + [[*2]] + [ + message + ][ + At the top layer, these functions allow for an entire + message to be sent or received. They are designed for + ease of use: + [link beast.ref.boost__beast__websocket__stream.read.overload2 `read`], + [link beast.ref.boost__beast__websocket__stream.write.overload2 `write`], + [link beast.ref.boost__beast__websocket__stream.async_read `async_read`], and + [link beast.ref.boost__beast__websocket__stream.async_write `async_write`]. + ] +][ + [[*1]] + [ + ['partial] + ][ + These read functions enable partial message data to be + received into a __DynamicBuffer__. They can be configured + to perform bounded work: + [link beast.ref.boost__beast__websocket__stream.read_some.overload2 `read_some`], and + [link beast.ref.boost__beast__websocket__stream.async_read_some.overload1 `async_read_some`]. + ] +][ + [[*0]] + [ + ['partial] + ][ + At the lowest level these read and write functions enable + partial message data to be transacted using a constant or + mutable buffer sequence: + [link beast.ref.boost__beast__websocket__stream.read_some.overload4 `read_some`], + [link beast.ref.boost__beast__websocket__stream.write_some.overload2 `write_some`], + [link beast.ref.boost__beast__websocket__stream.async_read_some.overload2 `async_read_some`], and + [link beast.ref.boost__beast__websocket__stream.async_write_some `async_write_some`]. + ] +]] + After the WebSocket handshake is accomplished, callers may send and receive messages using the message oriented interface. This interface requires that all of the buffers representing the message are known ahead of time: diff --git a/doc/qbk/06_websocket/6_control.qbk b/doc/qbk/06_websocket/6_control.qbk index 6ac648ed..b38fe6b4 100644 --- a/doc/qbk/06_websocket/6_control.qbk +++ b/doc/qbk/06_websocket/6_control.qbk @@ -80,22 +80,26 @@ a stream read operation must be active. [heading Close Frames] -The WebSocket protocol defines a procedure and control message for initiating -a close of the session. Handling of close initiated by the remote end of the -connection is performed automatically. To manually initiate a close, use -the -[link beast.ref.boost__beast__websocket__stream.close `close`] function: +The WebSocket protocol defines a procedure and control message for +initiating a close of the session. In this procedure, a host requests +the close by sending a +[@https://tools.ietf.org/html/rfc6455#section-5.5.1 ['close frame]]. +To request a close use a close function such as +[link beast.ref.boost__beast__websocket__stream.close.overload2 `close`] or +[link beast.ref.boost__beast__websocket__stream.async_close `async_close`]: [ws_snippet_18] -When the remote peer initiates a close by sending a close frame, Beast -will handle it for you by causing the next read to return `error::closed`. -When this error code is delivered, it indicates to the application that -the WebSocket connection has been closed cleanly, and that the TCP/IP -connection has been closed. After initiating a close, it is necessary to -continue reading messages until receiving the error `error::closed`. This -is because the remote peer may still be sending message and control frames -before it receives and responds to the close frame. +The close function will send a close frame, read and discard incoming +message data until receiving a close frame, and then shut down the +underlying connection before returning. + +When a close frame is received by during a read operation, the implementation +will automatically respond with a close frame and then shut down the +underlying connection before returning. In this case, the read operation +will complete with the code +[link beast.ref.boost__beast__websocket__error `error::closed`]. +This indicates to the caller that the connection has been closed cleanly. [important To receive the @@ -105,10 +109,10 @@ before it receives and responds to the close frame. [heading Auto-fragment] -To ensure timely delivery of control frames, large messages can be broken up -into smaller sized frames. The automatic fragment option turns on this -feature, and the write buffer size option determines the maximum size of -the fragments: +To ensure timely delivery of control frames, large outgoing messages can +be broken up into smaller sized frames. The automatic fragment option +turns on this feature, and the write buffer size option determines the +maximum size of the fragments: [ws_snippet_19] diff --git a/doc/qbk/06_websocket/7_teardown.qbk b/doc/qbk/06_websocket/7_teardown.qbk new file mode 100644 index 00000000..7492d201 --- /dev/null +++ b/doc/qbk/06_websocket/7_teardown.qbk @@ -0,0 +1,66 @@ +[/ + Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + Official repository: https://github.com/boostorg/beast +] + +[section Teardown] + +The WebSocket protocol requirements described in rfc6455 section 7.1.1 +outline an operation described as +[@https://tools.ietf.org/html/rfc6455#section-7.1.1 ['Close the WebSocket Connection]]. +This operation cleanly discards bytes remaining at receiving endpoints +and also closes the underlying TCP/IP connection. Orderly shutdowns are +always preferred; for TLS or SSL streams, a protocol-level shutdown is +desired. This presents a small issue for the +[link beast.ref.boost__beast__websocket__stream `stream`] +implementation: the stream's `NextLayer` template type requires only +__SyncStream__ or __AsyncStream__, but those concepts do not support +the operations to shut down the connection. + +To enable the implementation to perform the shutdown components of the +close operation, the library exposes two customization points expressed +as free functions associated with the next layer type: + +* [link beast.ref.boost__beast__websocket__teardown `teardown`]: Overloads + of this function drain and shut down a stream synchronously. + +* [link beast.ref.boost__beast__websocket__teardown `async_teardown`]: + Overloads of this function drain and shut down a stream asynchronously. + +The implementation provides suitable overloads of the teardown +customization points when websocket streams are instantiated using the +Asio types __socket__ or __ssl_stream__ for the next layer. In this +case no user action is required. However, when the websocket stream is +instantiated for a user-defined type, compile errors will result if the +customization points are not provided for the user defined type. +Furthermore, user-defined types that wrap one of the Asio objects +mentioned earlier may wish to invoke a teardown customization point +for the wrapped object. This is how those tasks are accomplished. + +[heading User-defined Teardown] + +To provide overloads of teardown for a user-defined type, simply declare +the two free functions with the correct signature, accepting a reference +to the user-defined type as the stream parameter: + +[ws_snippet_22] + +When the implementation invokes the asynchronous teardown function, it +always uses an invokable completion handler. It is not necessary +to specify the return type customization when creating user-defined +overloads of `async_teardown`. + +[heading Invoking Teardown] + +To invoke the customization point, first bring the default implementation +into scope with a `using` statement. Then call the customization point +without namespace qualification, allowing argument-dependent lookup to +take effect: + +[ws_snippet_23] + +[endsect] diff --git a/doc/qbk/06_websocket/7_notes.qbk b/doc/qbk/06_websocket/8_notes.qbk similarity index 100% rename from doc/qbk/06_websocket/7_notes.qbk rename to doc/qbk/06_websocket/8_notes.qbk diff --git a/include/boost/beast/http/basic_parser.hpp b/include/boost/beast/http/basic_parser.hpp index 3315be7d..01bf6ebb 100644 --- a/include/boost/beast/http/basic_parser.hpp +++ b/include/boost/beast/http/basic_parser.hpp @@ -38,12 +38,11 @@ namespace http { fields. The parser is optimized for the case where the input buffer sequence consists of a single contiguous buffer. The - @ref beast::flat_buffer class is provided, which guarantees + @ref flat_buffer class is provided, which guarantees that the input sequence of the stream buffer will be represented by exactly one contiguous buffer. To ensure the optimum performance - of the parser, use @ref beast::flat_buffer with HTTP algorithms - such as @ref beast::http::read, @ref beast::http::read_some, - @ref beast::http::async_read, and @ref beast::http::async_read_some. + of the parser, use @ref flat_buffer with HTTP algorithms + such as @ref read, @ref read_some, @ref async_read, and @ref async_read_some. Alternatively, the caller may use custom techniques to ensure that the structured portion of the HTTP message (header or chunk header) is contained in a linear buffer. diff --git a/test/doc/websocket_snippets.cpp b/test/doc/websocket_snippets.cpp index 751d099d..1f093b78 100644 --- a/test/doc/websocket_snippets.cpp +++ b/test/doc/websocket_snippets.cpp @@ -232,6 +232,68 @@ void echo(stream& ws, //] #endif +//[ws_snippet_22 + +struct custom_stream +{ + friend + void + teardown( + role_type role, + custom_stream& stream, + error_code& ec); + + template + friend + void + async_teardown( + role_type role, + custom_stream& stream, + TeardownHandler&& handler); +}; + +//] + +//[ws_snippet_23 + +template +struct custom_wrapper +{ + NextLayer next_layer; + + template + explicit + custom_wrapper(Args&&... args) + : next_layer(std::forward(args)...) + { + } + + friend + void + teardown( + role_type role, + custom_stream& stream, + error_code& ec) + { + using boost::beast::websocket::teardown; + teardown(role, stream.next_layer, ec); + } + + template + friend + void + async_teardown( + role_type role, + custom_stream& stream, + TeardownHandler&& handler) + { + using boost::beast::websocket::async_teardown; + async_teardown(role, stream.next_layer, std::forward(handler)); + } +}; + +//] + } // doc_ws_snippets //------------------------------------------------------------------------------