From 3f50efa1383c173c3817613e585da0f02c597e5d Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sun, 17 Feb 2019 16:03:09 -0800 Subject: [PATCH] WebSocket Decorator is a socket option (API Change): This changes the interface used to apply a decorator to the HTTP request or response messages used to perform the WebSocket handshake as follows: * Add the `stream_base::decorator` option object * Add `stream::set_option` overload to set the decorator from the option * The decorator applies to all client and server handshakes performed on the stream after the option is set. * Overloads of the following functions which accept a Decorator are deprecated: - accept, accept_ex - handshake, handshake_ex - async_accept, async_accept_ex - async_handshake, async_handshake_ex Actions Required: * Code which passes decorator to any `websocket::stream` member function should call `stream::set_option` instead with a newly constructed `stream_base::decorator` object containing the decorator. Alternatively, the macro `BOOST_BEAST_ALLOW_DEPRECATED` may be defined to 1. --- doc/qbk/06_websocket/03_client.qbk | 66 +- doc/qbk/06_websocket/04_server.qbk | 80 +- doc/qbk/06_websocket/05_decorator.qbk | 79 ++ .../{05_messages.qbk => 06_messages.qbk} | 0 .../{06_control.qbk => 07_control.qbk} | 0 .../{07_teardown.qbk => 08_teardown.qbk} | 0 .../{08_notes.qbk => 09_notes.qbk} | 0 doc/qbk/06_websocket/0_websocket.qbk | 9 +- doc/qbk/main.qbk | 1 + .../server-flex/advanced_server_flex.cpp | 10 + example/advanced/server/advanced_server.cpp | 18 +- .../async-ssl/websocket_client_async_ssl.cpp | 9 + .../client/async/websocket_client_async.cpp | 9 + .../coro-ssl/websocket_client_coro_ssl.cpp | 9 + .../client/coro/websocket_client_coro.cpp | 9 + .../sync-ssl/websocket_client_sync_ssl.cpp | 9 + .../client/sync/websocket_client_sync.cpp | 9 + .../async-ssl/websocket_server_async_ssl.cpp | 9 + .../server/async/websocket_server_async.cpp | 18 +- .../server/chat-multi/websocket_session.cpp | 4 - .../server/chat-multi/websocket_session.hpp | 14 + .../coro-ssl/websocket_server_coro_ssl.cpp | 9 + .../server/coro/websocket_server_coro.cpp | 9 + .../server/fast/websocket_server_fast.cpp | 39 +- .../websocket_server_stackless_ssl.cpp | 9 + .../stackless/websocket_server_stackless.cpp | 9 + .../sync-ssl/websocket_server_sync_ssl.cpp | 9 + .../server/sync/websocket_server_sync.cpp | 9 + .../boost/beast/core/flat_static_buffer.hpp | 2 +- .../beast/websocket/detail/decorator.hpp | 91 +- include/boost/beast/websocket/impl/accept.hpp | 281 ++-- .../boost/beast/websocket/impl/handshake.hpp | 209 +-- include/boost/beast/websocket/impl/stream.hpp | 10 + .../beast/websocket/impl/stream_impl.hpp | 4 +- include/boost/beast/websocket/stream.hpp | 1153 ++--------------- include/boost/beast/websocket/stream_base.hpp | 20 + test/beast/websocket/_detail_decorator.cpp | 10 +- test/beast/websocket/stream.cpp | 14 + test/doc/CMakeLists.txt | 1 + test/doc/Jamfile | 2 + test/doc/websocket_3_handshake.cpp | 236 ++++ 41 files changed, 1097 insertions(+), 1391 deletions(-) create mode 100644 doc/qbk/06_websocket/05_decorator.qbk rename doc/qbk/06_websocket/{05_messages.qbk => 06_messages.qbk} (100%) rename doc/qbk/06_websocket/{06_control.qbk => 07_control.qbk} (100%) rename doc/qbk/06_websocket/{07_teardown.qbk => 08_teardown.qbk} (100%) rename doc/qbk/06_websocket/{08_notes.qbk => 09_notes.qbk} (100%) create mode 100644 test/doc/websocket_3_handshake.cpp diff --git a/doc/qbk/06_websocket/03_client.qbk b/doc/qbk/06_websocket/03_client.qbk index e5574a4c..1f2038ea 100644 --- a/doc/qbk/06_websocket/03_client.qbk +++ b/doc/qbk/06_websocket/03_client.qbk @@ -9,7 +9,7 @@ [section Handshaking (Clients)] -A WebSocket session begins when a client sends the HTTP/1 +A WebSocket session begins when a client sends the HTTP/1.1 [@https://tools.ietf.org/html/rfc7230#section-6.7 Upgrade] request for [@https://tools.ietf.org/html/rfc6455#section-1.3 websocket], @@ -19,69 +19,39 @@ The Upgrade request must include the [@https://tools.ietf.org/html/rfc7230#section-5.4 Host] field, and the [@https://tools.ietf.org/html/rfc7230#section-5.3 target] -of the resource to request. The stream member functions -[link beast.ref.boost__beast__websocket__stream.handshake.overload1 `handshake`] and -[link beast.ref.boost__beast__websocket__stream.async_handshake.overload1 `async_handshake`] -are used to send the request with the required host and target strings. +of the resource to request. +A typical HTTP Upgrade request created and sent by the implementation +will look like this: -[ws_snippet_8] - -The implementation will create and send a request that typically -looks like this: - -[table WebSocket Upgrade HTTP Request +[table WebSocket HTTP Upgrade Request [[Serialized Octets][Description]] [[ ``` GET / HTTP/1.1 - Host: localhost + Host: www.example.com Upgrade: websocket Connection: upgrade Sec-WebSocket-Key: 2pGeTR0DsE4dfZs2pH+8MA== Sec-WebSocket-Version: 13 - User-Agent: Beast + User-Agent: Boost.Beast/216 ``` ][ 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. + modify fields by providing a ['decorator], described later. ]]] -[heading Decorators] +The +[link beast.ref.boost__beast__websocket__stream `stream`] +member functions +[link beast.ref.boost__beast__websocket__stream.handshake `handshake`] and +[link beast.ref.boost__beast__websocket__stream.async_handshake `async_handshake`] +are used to send the request with the required host and target strings. The +code below sends the WebSocket HTTP Upgrade request, then reads and processes +the response: -If the caller wishes to add or modify fields, the member functions -[link beast.ref.boost__beast__websocket__stream.handshake_ex `handshake_ex`] and -[link beast.ref.boost__beast__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: - -[ws_snippet_9] - -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] +[code_websocket_3_client_1] When a client receives an HTTP Upgrade response from the server indicating a successful upgrade, the caller may wish to perform additional validation @@ -92,6 +62,6 @@ received HTTP message in an output reference argument of type [link beast.ref.boost__beast__websocket__response_type `response_type`] as follows: -[ws_snippet_10] +[code_websocket_3_client_2] [endsect] diff --git a/doc/qbk/06_websocket/04_server.qbk b/doc/qbk/06_websocket/04_server.qbk index e0f344fc..fe232ee2 100644 --- a/doc/qbk/06_websocket/04_server.qbk +++ b/doc/qbk/06_websocket/04_server.qbk @@ -18,16 +18,10 @@ is received with the 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. +A typical HTTP Upgrade response created and sent by the implementation +upon receiving an upgrade request handshake will look like this: -Performing a handshake for an incoming websocket upgrade request operates -similarly. If the handshake fails, an error is returned or exception thrown: - -[ws_snippet_11] - -Successful WebSocket Upgrade responses generated by the implementation will -typically look like this: - -[table Decorated WebSocket Upgrade HTTP Request +[table WebSocket Upgrade HTTP Response [[Serialized Octets][Description]] [[ ``` @@ -35,7 +29,7 @@ typically look like this: Upgrade: websocket Connection: upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= - Server: Beast/40 + Server: Boost.Beast/216 ``` ][ The @@ -44,39 +38,18 @@ typically look like this: the WebSocket protocol. ]]] -[heading Decorators] +The +[link beast.ref.boost__beast__websocket__stream `stream`] +member functions +[link beast.ref.boost__beast__websocket__stream.accept `accept`] and +[link beast.ref.boost__beast__websocket__stream.async_accept `async_accept`] +are used to read the WebSocket HTTP Upgrade request handshake from a stream +already connected to an incoming peer, and then send the WebSocket HTTP +Upgrade response, as shown: -If the caller wishes to add or modify fields, the member functions -[link beast.ref.boost__beast__websocket__stream.accept_ex `accept_ex`] and -[link beast.ref.boost__beast__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: +[code_websocket_3_server_1] -[ws_snippet_12] -The HTTP Upgrade response produced by the previous call looks 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: 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] @@ -87,16 +60,19 @@ the incoming HTTP request is a WebSocket Upgrade request, the function Once the caller determines that the HTTP request is a WebSocket Upgrade, additional overloads of -[link beast.ref.boost__beast__websocket__stream.accept `accept`], -[link beast.ref.boost__beast__websocket__stream.accept_ex `accept_ex`], -[link beast.ref.boost__beast__websocket__stream.async_accept `async_accept`], and -[link beast.ref.boost__beast__websocket__stream.async_accept_ex `async_accept_ex`] +[link beast.ref.boost__beast__websocket__stream.accept `accept`] and +[link beast.ref.boost__beast__websocket__stream.async_accept `async_accept`] 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 +to perform the handshake. By reading the request manually, the program +can handle normal HTTP requests as well as upgrades. The program may +also enforce policies based on the HTTP fields, such as Basic +Authentication. In this example, the request is first read in +using the HTTP algorithms, and then passed to a newly constructed stream: -[ws_snippet_13] +[code_websocket_3_server_1b] + + [heading Buffered Handshakes] @@ -106,10 +82,8 @@ 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.boost__beast__websocket__stream.accept `accept`], -[link beast.ref.boost__beast__websocket__stream.accept_ex `accept_ex`], -[link beast.ref.boost__beast__websocket__stream.async_accept `async_accept`], and -[link beast.ref.boost__beast__websocket__stream.async_accept_ex `async_accept_ex`] +[link beast.ref.boost__beast__websocket__stream.accept `accept`] and +[link beast.ref.boost__beast__websocket__stream.async_accept `async_accept`] are provided which receive the additional buffered octets and consume them as part of the handshake. @@ -117,7 +91,7 @@ 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: -[ws_snippet_14] +[code_websocket_3_server_2] The implementation uses a fixed-size storage area to hold buffers passed using these functions. If an application is reaching the limit of the @@ -128,4 +102,6 @@ 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/05_decorator.qbk b/doc/qbk/06_websocket/05_decorator.qbk new file mode 100644 index 00000000..d0dd8217 --- /dev/null +++ b/doc/qbk/06_websocket/05_decorator.qbk @@ -0,0 +1,79 @@ +[/ + 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:decorator Custom HTTP Fields] + +For programs which need to modify either the outgoing WebSocket HTTP Upgrade +request, the outgoing WebSocket HTTP Upgrade response, or both, the stream +supports an optional property called a ['decorator]. This is a function +pointer or callable object which is invoked before the implementation +sends an HTTP message. The decorator receives a modifiable reference to +the message, allowing for modifications. The interface to this system +uses: + +[table WebSocket Decorator Interface +[[Name][Description]] +[[ + `request_type` +][ + This is the type of the object passed to the decorator to + represent HTTP Upgrade requests. +]] +[[ + `response_type` +][ + This is the type of the object passed to the decorator to + represent HTTP Upgrade response. +]] +[[ + [link beast.ref.boost__beast__websocket__stream_base__decorator `stream_base::decorator`] +][ + Objects of this type are used to hold a decorator to be + set on the stream using `set_option`. +]] +[[ + [link beast.ref.boost__beast__websocket__stream.set_option `stream::set_option`] +][ + This function is used to set a `stream_base::decorator` on the stream. +]] +] + +This declares a normal function which decorates outgoing HTTP requests: + +[code_websocket_3_decorator_1b] + +If a decorator is used, it must be set on the stream before any handshaking +takes place. This sets the decorator on the stream, to be used for all +subsequent calls to accept or handshake: + +[code_websocket_3_decorator_1] + +Alternatively, a function object may be used. Small function objects will +not incur a memory allocation. The follow code declares and sets a function +object as a decorator: + +[code_websocket_3_decorator_2] + +A lambda may be used in place of a named function object: + +[code_websocket_3_decorator_3] + +It also possible for a single decorator to handle both requests and +responses, if it is overloaded for both types either as a generic +lambda (C++14 and later) or as a class as shown here: + +[code_websocket_3_decorator_4] + +[important + Undefined behavior results if the decorator modifies the fields + specific to perform the WebSocket Upgrade, such as the Upgrade + or Connection fields. +] + +[endsect] diff --git a/doc/qbk/06_websocket/05_messages.qbk b/doc/qbk/06_websocket/06_messages.qbk similarity index 100% rename from doc/qbk/06_websocket/05_messages.qbk rename to doc/qbk/06_websocket/06_messages.qbk diff --git a/doc/qbk/06_websocket/06_control.qbk b/doc/qbk/06_websocket/07_control.qbk similarity index 100% rename from doc/qbk/06_websocket/06_control.qbk rename to doc/qbk/06_websocket/07_control.qbk diff --git a/doc/qbk/06_websocket/07_teardown.qbk b/doc/qbk/06_websocket/08_teardown.qbk similarity index 100% rename from doc/qbk/06_websocket/07_teardown.qbk rename to doc/qbk/06_websocket/08_teardown.qbk diff --git a/doc/qbk/06_websocket/08_notes.qbk b/doc/qbk/06_websocket/09_notes.qbk similarity index 100% rename from doc/qbk/06_websocket/08_notes.qbk rename to doc/qbk/06_websocket/09_notes.qbk diff --git a/doc/qbk/06_websocket/0_websocket.qbk b/doc/qbk/06_websocket/0_websocket.qbk index 825ee7b1..87deb65e 100644 --- a/doc/qbk/06_websocket/0_websocket.qbk +++ b/doc/qbk/06_websocket/0_websocket.qbk @@ -33,9 +33,10 @@ Boost.Asio with a consistent asynchronous model using a modern C++ approach. [include 02_connect.qbk] [include 03_client.qbk] [include 04_server.qbk] -[include 05_messages.qbk] -[include 06_control.qbk] -[include 07_teardown.qbk] -[include 08_notes.qbk] +[include 05_decorator.qbk] +[include 06_messages.qbk] +[include 07_control.qbk] +[include 08_teardown.qbk] +[include 09_notes.qbk] [endsect] diff --git a/doc/qbk/main.qbk b/doc/qbk/main.qbk index 27cc3ef5..2f6a91c5 100644 --- a/doc/qbk/main.qbk +++ b/doc/qbk/main.qbk @@ -132,6 +132,7 @@ [import ../../test/doc/core_1_refresher.cpp] [import ../../test/doc/core_3_layers.cpp] +[import ../../test/doc/websocket_3_handshake.cpp] [section:quickref Reference] ''' diff --git a/example/advanced/server-flex/advanced_server_flex.cpp b/example/advanced/server-flex/advanced_server_flex.cpp index 32bb8383..3a596959 100644 --- a/example/advanced/server-flex/advanced_server_flex.cpp +++ b/example/advanced/server-flex/advanced_server_flex.cpp @@ -251,6 +251,16 @@ public: websocket::stream_base::suggested_settings( websocket::role_type::server)); + // Set a decorator to change the Server of the handshake + derived().ws().set_option( + websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " advanced-server-flex"); + })); + // Accept the websocket handshake derived().ws().async_accept( req, diff --git a/example/advanced/server/advanced_server.cpp b/example/advanced/server/advanced_server.cpp index f8f6f922..15ff7d02 100644 --- a/example/advanced/server/advanced_server.cpp +++ b/example/advanced/server/advanced_server.cpp @@ -229,10 +229,6 @@ public: websocket_session(tcp::socket socket) : ws_(std::move(socket)) { - // Set suggested timeout settings for the websocket - ws_.set_option( - websocket::stream_base::suggested_settings( - websocket::role_type::server)); } // Start the asynchronous accept operation @@ -240,6 +236,20 @@ public: void do_accept(http::request> req) { + // Set suggested timeout settings for the websocket + ws_.set_option( + websocket::stream_base::suggested_settings( + websocket::role_type::server)); + + // Set a decorator to change the Server of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " advanced-server"); + })); + // Accept the websocket handshake ws_.async_accept( req, diff --git a/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp b/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp index 603de9a3..7921545f 100644 --- a/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp +++ b/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp @@ -132,6 +132,15 @@ public: websocket::stream_base::suggested_settings( websocket::role_type::client)); + // Set a decorator to change the User-Agent of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::request_type& req) + { + req.set(http::field::user_agent, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-client-async-ssl"); + })); + // Perform the websocket handshake ws_.async_handshake(host_, "/", beast::bind_front_handler( diff --git a/example/websocket/client/async/websocket_client_async.cpp b/example/websocket/client/async/websocket_client_async.cpp index 3e35e2cb..3026dfb1 100644 --- a/example/websocket/client/async/websocket_client_async.cpp +++ b/example/websocket/client/async/websocket_client_async.cpp @@ -110,6 +110,15 @@ public: websocket::stream_base::suggested_settings( websocket::role_type::client)); + // Set a decorator to change the User-Agent of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::request_type& req) + { + req.set(http::field::user_agent, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-client-async"); + })); + // Perform the websocket handshake ws_.async_handshake(host_, "/", beast::bind_front_handler( diff --git a/example/websocket/client/coro-ssl/websocket_client_coro_ssl.cpp b/example/websocket/client/coro-ssl/websocket_client_coro_ssl.cpp index 16de37ad..b0d16ca5 100644 --- a/example/websocket/client/coro-ssl/websocket_client_coro_ssl.cpp +++ b/example/websocket/client/coro-ssl/websocket_client_coro_ssl.cpp @@ -74,6 +74,15 @@ do_session( // Set a timeout on the operation beast::get_lowest_layer(ws).expires_after(std::chrono::seconds(30)); + // Set a decorator to change the User-Agent of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::request_type& req) + { + req.set(http::field::user_agent, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-client-coro"); + })); + // Perform the SSL handshake ws.next_layer().async_handshake(ssl::stream_base::client, yield[ec]); if(ec) diff --git a/example/websocket/client/coro/websocket_client_coro.cpp b/example/websocket/client/coro/websocket_client_coro.cpp index 4f1e164e..0b48a2ae 100644 --- a/example/websocket/client/coro/websocket_client_coro.cpp +++ b/example/websocket/client/coro/websocket_client_coro.cpp @@ -74,6 +74,15 @@ do_session( websocket::stream_base::suggested_settings( websocket::role_type::client)); + // Set a decorator to change the User-Agent of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::request_type& req) + { + req.set(http::field::user_agent, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-client-coro"); + })); + // Perform the websocket handshake ws.async_handshake(host, "/", yield[ec]); if(ec) diff --git a/example/websocket/client/sync-ssl/websocket_client_sync_ssl.cpp b/example/websocket/client/sync-ssl/websocket_client_sync_ssl.cpp index 60a56d19..8bcd6194 100644 --- a/example/websocket/client/sync-ssl/websocket_client_sync_ssl.cpp +++ b/example/websocket/client/sync-ssl/websocket_client_sync_ssl.cpp @@ -73,6 +73,15 @@ int main(int argc, char** argv) // Perform the SSL handshake ws.next_layer().handshake(ssl::stream_base::client); + // Set a decorator to change the User-Agent of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::request_type& req) + { + req.set(http::field::user_agent, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-client-coro"); + })); + // Perform the websocket handshake ws.handshake(host, "/"); diff --git a/example/websocket/client/sync/websocket_client_sync.cpp b/example/websocket/client/sync/websocket_client_sync.cpp index 3821618f..7eeb08e4 100644 --- a/example/websocket/client/sync/websocket_client_sync.cpp +++ b/example/websocket/client/sync/websocket_client_sync.cpp @@ -60,6 +60,15 @@ int main(int argc, char** argv) // Make the connection on the IP address we get from a lookup net::connect(ws.next_layer(), results.begin(), results.end()); + // Set a decorator to change the User-Agent of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::request_type& req) + { + req.set(http::field::user_agent, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-client-coro"); + })); + // Perform the websocket handshake ws.handshake(host, "/"); diff --git a/example/websocket/server/async-ssl/websocket_server_async_ssl.cpp b/example/websocket/server/async-ssl/websocket_server_async_ssl.cpp index 21f46b10..6c94e1d5 100644 --- a/example/websocket/server/async-ssl/websocket_server_async_ssl.cpp +++ b/example/websocket/server/async-ssl/websocket_server_async_ssl.cpp @@ -89,6 +89,15 @@ public: websocket::stream_base::suggested_settings( websocket::role_type::server)); + // Set a decorator to change the Server of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-server-async-ssl"); + })); + // Accept the websocket handshake ws_.async_accept( beast::bind_front_handler( diff --git a/example/websocket/server/async/websocket_server_async.cpp b/example/websocket/server/async/websocket_server_async.cpp index 4cbdc728..57cb6b45 100644 --- a/example/websocket/server/async/websocket_server_async.cpp +++ b/example/websocket/server/async/websocket_server_async.cpp @@ -52,16 +52,26 @@ public: session(tcp::socket socket) : ws_(std::move(socket)) { - // Set suggested timeout settings for the websocket - ws_.set_option( - websocket::stream_base::suggested_settings( - websocket::role_type::server)); } // Start the asynchronous operation void run() { + // Set suggested timeout settings for the websocket + ws_.set_option( + websocket::stream_base::suggested_settings( + websocket::role_type::server)); + + // Set a decorator to change the Server of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-server-async"); + })); + // Accept the websocket handshake ws_.async_accept( beast::bind_front_handler( diff --git a/example/websocket/server/chat-multi/websocket_session.cpp b/example/websocket/server/chat-multi/websocket_session.cpp index 960b1ab6..70af3eba 100644 --- a/example/websocket/server/chat-multi/websocket_session.cpp +++ b/example/websocket/server/chat-multi/websocket_session.cpp @@ -17,10 +17,6 @@ websocket_session( : ws_(std::move(socket)) , state_(state) { - // Set suggested timeout settings for the websocket - ws_.set_option( - websocket::stream_base::suggested_settings( - websocket::role_type::server)); } websocket_session:: diff --git a/example/websocket/server/chat-multi/websocket_session.hpp b/example/websocket/server/chat-multi/websocket_session.hpp index f10e35c0..a3604ccd 100644 --- a/example/websocket/server/chat-multi/websocket_session.hpp +++ b/example/websocket/server/chat-multi/websocket_session.hpp @@ -57,6 +57,20 @@ void websocket_session:: run(http::request> req) { + // Set suggested timeout settings for the websocket + ws_.set_option( + websocket::stream_base::suggested_settings( + websocket::role_type::server)); + + // Set a decorator to change the Server of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-chat-multi"); + })); + // Accept the websocket handshake ws_.async_accept( req, diff --git a/example/websocket/server/coro-ssl/websocket_server_coro_ssl.cpp b/example/websocket/server/coro-ssl/websocket_server_coro_ssl.cpp index 541750db..55854343 100644 --- a/example/websocket/server/coro-ssl/websocket_server_coro_ssl.cpp +++ b/example/websocket/server/coro-ssl/websocket_server_coro_ssl.cpp @@ -75,6 +75,15 @@ do_session( websocket::stream_base::suggested_settings( websocket::role_type::server)); + // Set a decorator to change the Server of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-server-coro-ssl"); + })); + // Accept the websocket handshake ws.async_accept(yield[ec]); if(ec) diff --git a/example/websocket/server/coro/websocket_server_coro.cpp b/example/websocket/server/coro/websocket_server_coro.cpp index 4e94c8ac..35b83ccb 100644 --- a/example/websocket/server/coro/websocket_server_coro.cpp +++ b/example/websocket/server/coro/websocket_server_coro.cpp @@ -56,6 +56,15 @@ do_session(ws_type& ws, net::yield_context yield) websocket::stream_base::suggested_settings( websocket::role_type::server)); + // Set a decorator to change the Server of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-server-coro"); + })); + // Accept the websocket handshake ws.async_accept(yield[ec]); if(ec) diff --git a/example/websocket/server/fast/websocket_server_fast.cpp b/example/websocket/server/fast/websocket_server_fast.cpp index 1414d969..a0bd5f41 100644 --- a/example/websocket/server/fast/websocket_server_fast.cpp +++ b/example/websocket/server/fast/websocket_server_fast.cpp @@ -90,13 +90,15 @@ do_sync_session(ws_type& ws) setup_stream(ws); - ws.accept_ex( + // Set a decorator to change the Server of the handshake + ws.set_option(websocket::stream_base::decorator( [](websocket::response_type& res) { - res.set(http::field::server, - "Boost.Beast/" + std::to_string(BOOST_BEAST_VERSION) + "-Sync"); - }, - ec); + res.set(http::field::server, std::string( + BOOST_BEAST_VERSION_STRING) + "-Sync"); + })); + + ws.accept(ec); if(ec) return fail(ec, "accept"); @@ -158,13 +160,16 @@ public: void run() { - // Accept the websocket handshake - ws_.async_accept_ex( + // Set a decorator to change the Server of the handshake + ws_.set_option(websocket::stream_base::decorator( [](websocket::response_type& res) { - res.set(http::field::server, - "Boost.Beast/" + std::to_string(BOOST_BEAST_VERSION) + "-Async"); - }, + res.set(http::field::server, std::string( + BOOST_BEAST_VERSION_STRING) + "-Async"); + })); + + // Accept the websocket handshake + ws_.async_accept( beast::bind_front_handler( &async_session::on_accept, shared_from_this())); @@ -324,13 +329,15 @@ do_coro_session(ws_type& ws, net::yield_context yield) setup_stream(ws); - ws.async_accept_ex( - [&](websocket::response_type& res) + // Set a decorator to change the Server of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) { - res.set(http::field::server, - "Boost.Beast/" + std::to_string(BOOST_BEAST_VERSION) + "-Coro"); - }, - yield[ec]); + res.set(http::field::server, std::string( + BOOST_BEAST_VERSION_STRING) + "-Fiber"); + })); + + ws.async_accept(yield[ec]); if(ec) return fail(ec, "accept"); diff --git a/example/websocket/server/stackless-ssl/websocket_server_stackless_ssl.cpp b/example/websocket/server/stackless-ssl/websocket_server_stackless_ssl.cpp index 928c670d..436f48cc 100644 --- a/example/websocket/server/stackless-ssl/websocket_server_stackless_ssl.cpp +++ b/example/websocket/server/stackless-ssl/websocket_server_stackless_ssl.cpp @@ -102,6 +102,15 @@ public: websocket::stream_base::suggested_settings( websocket::role_type::server)); + // Set a decorator to change the Server of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-server-stackless-ssl"); + })); + // Accept the websocket handshake yield ws_.async_accept( std::bind( diff --git a/example/websocket/server/stackless/websocket_server_stackless.cpp b/example/websocket/server/stackless/websocket_server_stackless.cpp index 09d5a690..0bea203f 100644 --- a/example/websocket/server/stackless/websocket_server_stackless.cpp +++ b/example/websocket/server/stackless/websocket_server_stackless.cpp @@ -79,6 +79,15 @@ public: websocket::stream_base::suggested_settings( websocket::role_type::server)); + // Set a decorator to change the Server of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-server-stackless"); + })); + // Accept the websocket handshake yield ws_.async_accept( std::bind( diff --git a/example/websocket/server/sync-ssl/websocket_server_sync_ssl.cpp b/example/websocket/server/sync-ssl/websocket_server_sync_ssl.cpp index 976d7cf0..abd86669 100644 --- a/example/websocket/server/sync-ssl/websocket_server_sync_ssl.cpp +++ b/example/websocket/server/sync-ssl/websocket_server_sync_ssl.cpp @@ -48,6 +48,15 @@ do_session(tcp::socket& socket, ssl::context& ctx) // Perform the SSL handshake ws.next_layer().handshake(ssl::stream_base::server); + // Set a decorator to change the Server of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-server-sync-ssl"); + })); + // Accept the websocket handshake ws.accept(); diff --git a/example/websocket/server/sync/websocket_server_sync.cpp b/example/websocket/server/sync/websocket_server_sync.cpp index 0d50f438..5411feda 100644 --- a/example/websocket/server/sync/websocket_server_sync.cpp +++ b/example/websocket/server/sync/websocket_server_sync.cpp @@ -39,6 +39,15 @@ do_session(tcp::socket& socket) // Construct the stream by moving in the socket websocket::stream ws{std::move(socket)}; + // Set a decorator to change the Server of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-server-sync"); + })); + // Accept the websocket handshake ws.accept(); diff --git a/include/boost/beast/core/flat_static_buffer.hpp b/include/boost/beast/core/flat_static_buffer.hpp index 87c0d423..41400735 100644 --- a/include/boost/beast/core/flat_static_buffer.hpp +++ b/include/boost/beast/core/flat_static_buffer.hpp @@ -104,7 +104,7 @@ public: void reset() noexcept { - static_assert(sizeof(I) != 0, + static_assert(I != 0, BOOST_BEAST_DEPRECATION_STRING); } #endif diff --git a/include/boost/beast/websocket/detail/decorator.hpp b/include/boost/beast/websocket/detail/decorator.hpp index d399c8b3..46d781bb 100644 --- a/include/boost/beast/websocket/detail/decorator.hpp +++ b/include/boost/beast/websocket/detail/decorator.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include namespace boost { @@ -37,14 +38,12 @@ class decorator void(*)(), void(incomplete::*)(), decltype(std::bind( - std::declval(), - std::declval>())) + std::declval< + void(incomplete::*)(request_type&)>(), + std::shared_ptr{}, + std::placeholders::_1)) >(); - struct none - { - }; - struct base { virtual ~base() = default; @@ -62,6 +61,16 @@ class decorator invoke(response_type&) = 0; }; + using type = typename + std::aligned_storage::type; + + type buf_; + base* base_; + + struct none + { + }; + template struct is_req_op : std::false_type { @@ -143,13 +152,42 @@ class decorator } }; - using type = typename - std::aligned_storage::type; + void + destroy() + { + if(is_inline()) + base_->~base(); + else if(base_) + delete base_; + } - type buf_; - base* base_; + template + base* + construct(F&& f, std::true_type) + { + return ::new(&buf_) impl< + typename std::decay::type>( + std::forward(f)); + } + + template + base* + construct(F&& f, std::false_type) + { + return new impl< + typename std::decay::type>( + std::forward(f)); + } public: + decorator(decorator const&) = delete; + decorator& operator=(decorator const&) = delete; + + ~decorator() + { + destroy(); + } + decorator() : decorator(none{}) { @@ -171,25 +209,34 @@ public: impl(none{}); } - template + template::value>::type> explicit decorator(F&& f) - : base_(sizeof(F) <= sizeof(buf_) ? - ::new(&buf_) impl< - typename std::decay::type>( - std::forward(f)) : - new impl< - typename std::decay::type>( - std::forward(f))) + : base_(construct(std::forward(f), + std::integral_constant{})) { } - ~decorator() + decorator& + operator=(decorator&& other) { - if(is_inline()) - base_->~base(); + this->destroy(); + base_ = nullptr; + if(other.is_inline()) + { + base_ = other.base_->move(&buf_); + other.base_->~base(); + } else - delete base_; + { + base_ = other.base_; + } + other.base_ = ::new(&other.buf_) + impl(none{}); + return *this; } bool diff --git a/include/boost/beast/websocket/impl/accept.hpp b/include/boost/beast/websocket/impl/accept.hpp index ef0266ff..5ee270d1 100644 --- a/include/boost/beast/websocket/impl/accept.hpp +++ b/include/boost/beast/websocket/impl/accept.hpp @@ -75,12 +75,15 @@ build_response( error_code& result) { auto const decorate = - [&decorator](response_type& res) + [this, &decorator](response_type& res) { + decorator_opt(res); decorator(res); if(! res.count(http::field::server)) { - BOOST_STATIC_ASSERT(sizeof(BOOST_BEAST_VERSION_STRING) < 20); + // VFALCO this is weird.. + BOOST_STATIC_ASSERT(sizeof( + BOOST_BEAST_VERSION_STRING) < 20); static_string<20> s(BOOST_BEAST_VERSION_STRING); res.set(http::field::server, s); } @@ -395,23 +398,6 @@ accept() BOOST_THROW_EXCEPTION(system_error{ec}); } -template -template -void -stream:: -accept_ex(ResponseDecorator const& decorator) -{ - static_assert(is_sync_stream::value, - "SyncStream requirements not met"); - static_assert(detail::is_response_decorator< - ResponseDecorator>::value, - "ResponseDecorator requirements not met"); - error_code ec; - accept_ex(decorator, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); -} - template void stream:: @@ -424,22 +410,6 @@ accept(error_code& ec) &default_decorate_res, ec); } -template -template -void -stream:: -accept_ex(ResponseDecorator const& decorator, error_code& ec) -{ - static_assert(is_sync_stream::value, - "SyncStream requirements not met"); - static_assert(detail::is_response_decorator< - ResponseDecorator>::value, - "ResponseDecorator requirements not met"); - do_accept( - net::const_buffer{}, - decorator, ec); -} - template template typename std::enable_if -template< - class ConstBufferSequence, - class ResponseDecorator> -typename std::enable_if::value>::type -stream:: -accept_ex( - ConstBufferSequence const& buffers, - ResponseDecorator const &decorator) -{ - static_assert(is_sync_stream::value, - "SyncStream requirements not met"); - static_assert(net::is_const_buffer_sequence< - ConstBufferSequence>::value, - "ConstBufferSequence requirements not met"); - static_assert(detail::is_response_decorator< - ResponseDecorator>::value, - "ResponseDecorator requirements not met"); - error_code ec; - accept_ex(buffers, decorator, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); -} - template template typename std::enable_if -template< - class ConstBufferSequence, - class ResponseDecorator> -typename std::enable_if::value>::type -stream:: -accept_ex( - ConstBufferSequence const& buffers, - ResponseDecorator const& decorator, - error_code& ec) -{ - static_assert(is_sync_stream::value, - "SyncStream requirements not met"); - static_assert(net::is_const_buffer_sequence< - ConstBufferSequence>::value, - "ConstBufferSequence requirements not met"); - static_assert(net::is_const_buffer_sequence< - ConstBufferSequence>::value, - "ConstBufferSequence requirements not met"); - do_accept(buffers, decorator, ec); -} template template @@ -538,28 +460,6 @@ accept( BOOST_THROW_EXCEPTION(system_error{ec}); } -template -template< - class Body, class Allocator, - class ResponseDecorator> -void -stream:: -accept_ex( - http::request> const& req, - ResponseDecorator const& decorator) -{ - static_assert(is_sync_stream::value, - "SyncStream requirements not met"); - static_assert(detail::is_response_decorator< - ResponseDecorator>::value, - "ResponseDecorator requirements not met"); - error_code ec; - accept_ex(req, decorator, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); -} - template template void @@ -575,27 +475,6 @@ accept( do_accept(req, &default_decorate_res, ec); } -template -template< - class Body, class Allocator, - class ResponseDecorator> -void -stream:: -accept_ex( - http::request> const& req, - ResponseDecorator const& decorator, - error_code& ec) -{ - static_assert(is_sync_stream::value, - "SyncStream requirements not met"); - static_assert(detail::is_response_decorator< - ResponseDecorator>::value, - "ResponseDecorator requirements not met"); - impl_->reset(); - do_accept(req, decorator, ec); -} - //------------------------------------------------------------------------------ template @@ -769,6 +648,156 @@ async_accept_ex( return init.result.get(); } +//------------------------------------------------------------------------------ + +template +template +void +stream:: +accept_ex(ResponseDecorator const& decorator) +{ +#if ! BOOST_BEAST_ALLOW_DEPRECATED + static_assert(sizeof(ResponseDecorator) == 0, + BOOST_BEAST_DEPRECATION_STRING); +#endif + static_assert(is_sync_stream::value, + "SyncStream requirements not met"); + static_assert(detail::is_response_decorator< + ResponseDecorator>::value, + "ResponseDecorator requirements not met"); + error_code ec; + accept_ex(decorator, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); +} + +template +template +void +stream:: +accept_ex(ResponseDecorator const& decorator, error_code& ec) +{ +#if ! BOOST_BEAST_ALLOW_DEPRECATED + static_assert(sizeof(ResponseDecorator) == 0, + BOOST_BEAST_DEPRECATION_STRING); +#endif + static_assert(is_sync_stream::value, + "SyncStream requirements not met"); + static_assert(detail::is_response_decorator< + ResponseDecorator>::value, + "ResponseDecorator requirements not met"); + do_accept( + net::const_buffer{}, + decorator, ec); +} + +template +template< + class ConstBufferSequence, + class ResponseDecorator> +typename std::enable_if::value>::type +stream:: +accept_ex( + ConstBufferSequence const& buffers, + ResponseDecorator const &decorator) +{ +#if ! BOOST_BEAST_ALLOW_DEPRECATED + static_assert(sizeof(ResponseDecorator) == 0, + BOOST_BEAST_DEPRECATION_STRING); +#endif + static_assert(is_sync_stream::value, + "SyncStream requirements not met"); + static_assert(net::is_const_buffer_sequence< + ConstBufferSequence>::value, + "ConstBufferSequence requirements not met"); + static_assert(detail::is_response_decorator< + ResponseDecorator>::value, + "ResponseDecorator requirements not met"); + error_code ec; + accept_ex(buffers, decorator, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); +} + +template +template< + class ConstBufferSequence, + class ResponseDecorator> +typename std::enable_if::value>::type +stream:: +accept_ex( + ConstBufferSequence const& buffers, + ResponseDecorator const& decorator, + error_code& ec) +{ +#if ! BOOST_BEAST_ALLOW_DEPRECATED + static_assert(sizeof(ResponseDecorator) == 0, + BOOST_BEAST_DEPRECATION_STRING); +#endif + static_assert(is_sync_stream::value, + "SyncStream requirements not met"); + static_assert(net::is_const_buffer_sequence< + ConstBufferSequence>::value, + "ConstBufferSequence requirements not met"); + static_assert(net::is_const_buffer_sequence< + ConstBufferSequence>::value, + "ConstBufferSequence requirements not met"); + do_accept(buffers, decorator, ec); +} + +template +template< + class Body, class Allocator, + class ResponseDecorator> +void +stream:: +accept_ex( + http::request> const& req, + ResponseDecorator const& decorator) +{ +#if ! BOOST_BEAST_ALLOW_DEPRECATED + static_assert(sizeof(ResponseDecorator) == 0, + BOOST_BEAST_DEPRECATION_STRING); +#endif + static_assert(is_sync_stream::value, + "SyncStream requirements not met"); + static_assert(detail::is_response_decorator< + ResponseDecorator>::value, + "ResponseDecorator requirements not met"); + error_code ec; + accept_ex(req, decorator, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); +} + +template +template< + class Body, class Allocator, + class ResponseDecorator> +void +stream:: +accept_ex( + http::request> const& req, + ResponseDecorator const& decorator, + error_code& ec) +{ +#if ! BOOST_BEAST_ALLOW_DEPRECATED + static_assert(sizeof(ResponseDecorator) == 0, + BOOST_BEAST_DEPRECATION_STRING); +#endif + static_assert(is_sync_stream::value, + "SyncStream requirements not met"); + static_assert(detail::is_response_decorator< + ResponseDecorator>::value, + "ResponseDecorator requirements not met"); + impl_->reset(); + do_accept(req, decorator, ec); +} + } // websocket } // beast } // boost diff --git a/include/boost/beast/websocket/impl/handshake.hpp b/include/boost/beast/websocket/impl/handshake.hpp index cbb909c0..19431afc 100644 --- a/include/boost/beast/websocket/impl/handshake.hpp +++ b/include/boost/beast/websocket/impl/handshake.hpp @@ -195,57 +195,6 @@ async_handshake(response_type& res, return init.result.get(); } -template -template -BOOST_ASIO_INITFN_RESULT_TYPE( - HandshakeHandler, void(error_code)) -stream:: -async_handshake_ex(string_view host, - string_view target, - RequestDecorator const& decorator, - HandshakeHandler&& handler) -{ - static_assert(is_async_stream::value, - "AsyncStream requirements not met"); - static_assert(detail::is_request_decorator< - RequestDecorator>::value, - "RequestDecorator requirements not met"); - BOOST_BEAST_HANDLER_INIT( - HandshakeHandler, void(error_code)); - handshake_op( - std::move(init.completion_handler), - impl_, nullptr, host, target, - decorator)(); - return init.result.get(); -} - -template -template -BOOST_ASIO_INITFN_RESULT_TYPE( - HandshakeHandler, void(error_code)) -stream:: -async_handshake_ex(response_type& res, - string_view host, - string_view target, - RequestDecorator const& decorator, - HandshakeHandler&& handler) -{ - static_assert(is_async_stream::value, - "AsyncStream requirements not met"); - static_assert(detail::is_request_decorator< - RequestDecorator>::value, - "RequestDecorator requirements not met"); - BOOST_BEAST_HANDLER_INIT( - HandshakeHandler, void(error_code)); - handshake_op( - std::move(init.completion_handler), - impl_, &res, host, target, - decorator)(); - return init.result.get(); -} - template void stream:: @@ -276,45 +225,6 @@ handshake(response_type& res, BOOST_THROW_EXCEPTION(system_error{ec}); } -template -template -void -stream:: -handshake_ex(string_view host, - string_view target, - RequestDecorator const& decorator) -{ - static_assert(is_sync_stream::value, - "SyncStream requirements not met"); - static_assert(detail::is_request_decorator< - RequestDecorator>::value, - "RequestDecorator requirements not met"); - error_code ec; - handshake_ex(host, target, decorator, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); -} - -template -template -void -stream:: -handshake_ex(response_type& res, - string_view host, - string_view target, - RequestDecorator const& decorator) -{ - static_assert(is_sync_stream::value, - "SyncStream requirements not met"); - static_assert(detail::is_request_decorator< - RequestDecorator>::value, - "RequestDecorator requirements not met"); - error_code ec; - handshake_ex(res, host, target, decorator, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); -} - template void stream:: @@ -341,6 +251,55 @@ handshake(response_type& res, host, target, &default_decorate_req, ec); } +//------------------------------------------------------------------------------ + +template +template +void +stream:: +handshake_ex(string_view host, + string_view target, + RequestDecorator const& decorator) +{ +#if ! BOOST_BEAST_ALLOW_DEPRECATED + static_assert(sizeof(RequestDecorator) == 0, + BOOST_BEAST_DEPRECATION_STRING); +#endif + static_assert(is_sync_stream::value, + "SyncStream requirements not met"); + static_assert(detail::is_request_decorator< + RequestDecorator>::value, + "RequestDecorator requirements not met"); + error_code ec; + handshake_ex(host, target, decorator, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); +} + +template +template +void +stream:: +handshake_ex(response_type& res, + string_view host, + string_view target, + RequestDecorator const& decorator) +{ +#if ! BOOST_BEAST_ALLOW_DEPRECATED + static_assert(sizeof(RequestDecorator) == 0, + BOOST_BEAST_DEPRECATION_STRING); +#endif + static_assert(is_sync_stream::value, + "SyncStream requirements not met"); + static_assert(detail::is_request_decorator< + RequestDecorator>::value, + "RequestDecorator requirements not met"); + error_code ec; + handshake_ex(res, host, target, decorator, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); +} + template template void @@ -350,6 +309,10 @@ handshake_ex(string_view host, RequestDecorator const& decorator, error_code& ec) { +#if ! BOOST_BEAST_ALLOW_DEPRECATED + static_assert(sizeof(RequestDecorator) == 0, + BOOST_BEAST_DEPRECATION_STRING); +#endif static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(detail::is_request_decorator< @@ -369,6 +332,10 @@ handshake_ex(response_type& res, RequestDecorator const& decorator, error_code& ec) { +#if ! BOOST_BEAST_ALLOW_DEPRECATED + static_assert(sizeof(RequestDecorator) == 0, + BOOST_BEAST_DEPRECATION_STRING); +#endif static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(detail::is_request_decorator< @@ -378,6 +345,68 @@ handshake_ex(response_type& res, host, target, decorator, ec); } +template +template +BOOST_ASIO_INITFN_RESULT_TYPE( + HandshakeHandler, void(error_code)) +stream:: +async_handshake_ex(string_view host, + string_view target, + RequestDecorator const& decorator, + HandshakeHandler&& handler) +{ +#if ! BOOST_BEAST_ALLOW_DEPRECATED + static_assert(sizeof(RequestDecorator) == 0, + BOOST_BEAST_DEPRECATION_STRING); +#endif + static_assert(is_async_stream::value, + "AsyncStream requirements not met"); + static_assert(detail::is_request_decorator< + RequestDecorator>::value, + "RequestDecorator requirements not met"); + BOOST_BEAST_HANDLER_INIT( + HandshakeHandler, void(error_code)); + handshake_op( + std::move(init.completion_handler), + impl_, nullptr, host, target, + decorator)(); + return init.result.get(); +} + +template +template +BOOST_ASIO_INITFN_RESULT_TYPE( + HandshakeHandler, void(error_code)) +stream:: +async_handshake_ex(response_type& res, + string_view host, + string_view target, + RequestDecorator const& decorator, + HandshakeHandler&& handler) +{ +#if ! BOOST_BEAST_ALLOW_DEPRECATED + static_assert(sizeof(RequestDecorator) == 0, + BOOST_BEAST_DEPRECATION_STRING); +#endif + static_assert(is_async_stream::value, + "AsyncStream requirements not met"); + static_assert(detail::is_request_decorator< + RequestDecorator>::value, + "RequestDecorator requirements not met"); + BOOST_BEAST_HANDLER_INIT( + HandshakeHandler, void(error_code)); + handshake_op( + std::move(init.completion_handler), + impl_, &res, host, target, + decorator)(); + return init.result.get(); +} + + + + } // websocket } // beast } // boost diff --git a/include/boost/beast/websocket/impl/stream.hpp b/include/boost/beast/websocket/impl/stream.hpp index 2544a770..49289049 100644 --- a/include/boost/beast/websocket/impl/stream.hpp +++ b/include/boost/beast/websocket/impl/stream.hpp @@ -147,6 +147,16 @@ read_size_hint(DynamicBuffer& buffer) const // //------------------------------------------------------------------------------ +// decorator + +template +void +stream:: +set_option(decorator opt) +{ + impl_->decorator_opt = std::move(opt.d_); +} + // timeout template diff --git a/include/boost/beast/websocket/impl/stream_impl.hpp b/include/boost/beast/websocket/impl/stream_impl.hpp index b700a640..7dd1c914 100644 --- a/include/boost/beast/websocket/impl/stream_impl.hpp +++ b/include/boost/beast/websocket/impl/stream_impl.hpp @@ -96,7 +96,8 @@ struct stream::impl_type bool timed_out = false; int idle_counter = 0; - timeout timeout_opt; + detail::decorator decorator_opt; // Decorator for HTTP messages + timeout timeout_opt; // Timeout/idle settings template @@ -532,6 +533,7 @@ build_request( req.set(http::field::sec_websocket_key, key); req.set(http::field::sec_websocket_version, "13"); this->build_request_pmd(req); + decorator_opt(req); decorator(req); if(! req.count(http::field::user_agent)) req.set(http::field::user_agent, diff --git a/include/boost/beast/websocket/stream.hpp b/include/boost/beast/websocket/stream.hpp index 7c28c5c2..1066176b 100644 --- a/include/boost/beast/websocket/stream.hpp +++ b/include/boost/beast/websocket/stream.hpp @@ -353,9 +353,11 @@ public: template void - set_option(Option const& opt); + set_option(Option opt); #else + void set_option(decorator opt); + void get_option(timeout& opt); void set_option(timeout const& opt); #endif @@ -707,141 +709,6 @@ public: string_view host, string_view target); - /** Perform the WebSocket handshake in the client role. - - This function is used to perform the - WebSocket handshake, - required before messages can be sent and received. During the handshake, - the client sends the Websocket Upgrade HTTP request, and the server - replies with an HTTP response indicating the result of the handshake. - - The call blocks until one of the following conditions is true: - - @li The request is sent and the response is received. - - @li An error occurs. - - The algorithm, known as a composed operation, is implemented - in terms of calls to the next layer's `read_some` and `write_some` - functions. - - The handshake is successful if the received HTTP response - indicates the upgrade was accepted by the server, represented by a - status-code - of @ref beast::http::status::switching_protocols. - - @param host The name of the remote host. This is required by - the HTTP protocol to set the "Host" header field. - - @param target The request-target, in origin-form. The server may use the - target to distinguish different services on the same listening port. - - @param decorator A function object which will be called to modify - the HTTP request object generated by the implementation. This - could be used to set the User-Agent field, subprotocols, or other - application or HTTP specific fields. The function object will be - called with this equivalent signature: - @code - void decorator( - request_type& req - ); - @endcode - - @throws system_error Thrown on failure. - - @par Example - @code - ws.handshake("localhost", "/", - [](request_type& req) - { - req.set(field::user_agent, "Beast"); - }); - @endcode - - @see - @li Websocket Opening Handshake Client Requirements (RFC6455) - @li Host field (RFC7230) - @li request-target (RFC7230) - @li origin-form (RFC7230) - */ - template - void - handshake_ex( - string_view host, - string_view target, - RequestDecorator const& decorator); - - /** Perform the WebSocket handshake in the client role. - - This function is used to perform the - WebSocket handshake, - required before messages can be sent and received. During the handshake, - the client sends the Websocket Upgrade HTTP request, and the server - replies with an HTTP response indicating the result of the handshake. - - The call blocks until one of the following conditions is true: - - @li The request is sent and the response is received. - - @li An error occurs. - - The algorithm, known as a composed operation, is implemented - in terms of calls to the next layer's `read_some` and `write_some` - functions. - - The handshake is successful if the received HTTP response - indicates the upgrade was accepted by the server, represented by a - status-code - of @ref beast::http::status::switching_protocols. - - @param res The HTTP Upgrade response returned by the remote - endpoint. The caller may use the response to access any - additional information sent by the server. - - @param host The name of the remote host. This is required by - the HTTP protocol to set the "Host" header field. - - @param target The request-target, in origin-form. The server may use the - target to distinguish different services on the same listening port. - - @param decorator A function object which will be called to modify - the HTTP request object generated by the implementation. This - could be used to set the User-Agent field, subprotocols, or other - application or HTTP specific fields. The function object will be - called with this equivalent signature: - @code - void decorator( - request_type& req - ); - @endcode - - @throws system_error Thrown on failure. - - @par Example - @code - response_type res; - ws.handshake(res, "localhost", "/", - [](request_type& req) - { - req.set(field::user_agent, "Beast"); - }); - std::cout << res; - @endcode - - @see - @li Websocket Opening Handshake Client Requirements (RFC6455) - @li Host field (RFC7230) - @li request-target (RFC7230) - @li origin-form (RFC7230) - */ - template - void - handshake_ex( - response_type& res, - string_view host, - string_view target, - RequestDecorator const& decorator); - /** Perform the WebSocket handshake in the client role. This function is used to perform the @@ -948,148 +815,6 @@ public: string_view target, error_code& ec); - /** Perform the WebSocket handshake in the client role. - - This function is used to perform the - WebSocket handshake, - required before messages can be sent and received. During the handshake, - the client sends the Websocket Upgrade HTTP request, and the server - replies with an HTTP response indicating the result of the handshake. - - The call blocks until one of the following conditions is true: - - @li The request is sent and the response is received. - - @li An error occurs. - - The algorithm, known as a composed operation, is implemented - in terms of calls to the next layer's `read_some` and `write_some` - functions. - - The handshake is successful if the received HTTP response - indicates the upgrade was accepted by the server, represented by a - status-code - of @ref beast::http::status::switching_protocols. - - @param host The name of the remote host. This is required by - the HTTP protocol to set the "Host" header field. - - @param target The request-target, in origin-form. The server may use the - target to distinguish different services on the same listening port. - - @param decorator A function object which will be called to modify - the HTTP request object generated by the implementation. This - could be used to set the User-Agent field, subprotocols, or other - application or HTTP specific fields. The function object will be - called with this equivalent signature: - @code - void decorator( - request_type& req - ); - @endcode - - @param ec Set to indicate what error occurred, if any. - - @par Example - @code - error_code ec; - ws.handshake("localhost", "/", - [](request_type& req) - { - req.set(field::user_agent, "Beast"); - }, - ec); - @endcode - - @see - @li Websocket Opening Handshake Client Requirements (RFC6455) - @li Host field (RFC7230) - @li request-target (RFC7230) - @li origin-form (RFC7230) - */ - template - void - handshake_ex( - string_view host, - string_view target, - RequestDecorator const& decorator, - error_code& ec); - - /** Perform the WebSocket handshake in the client role. - - This function is used to perform the - WebSocket handshake, - required before messages can be sent and received. During the handshake, - the client sends the Websocket Upgrade HTTP request, and the server - replies with an HTTP response indicating the result of the handshake. - - The call blocks until one of the following conditions is true: - - @li The request is sent and the response is received. - - @li An error occurs. - - The algorithm, known as a composed operation, is implemented - in terms of calls to the next layer's `read_some` and `write_some` - functions. - - The handshake is successful if the received HTTP response - indicates the upgrade was accepted by the server, represented by a - status-code - of @ref beast::http::status::switching_protocols. - - @param res The HTTP Upgrade response returned by the remote - endpoint. The caller may use the response to access any - additional information sent by the server. - - @param host The name of the remote host. This is required by - the HTTP protocol to set the "Host" header field. - - @param target The request-target, in origin-form. The server may use the - target to distinguish different services on the same listening port. - - @param decorator A function object which will be called to modify - the HTTP request object generated by the implementation. This - could be used to set the User-Agent field, subprotocols, or other - application or HTTP specific fields. The function object will be - called with this equivalent signature: - @code - void decorator( - request_type& req - ); - @endcode - - @param ec Set to indicate what error occurred, if any. - - @par Example - @code - error_code ec; - response_type res; - ws.handshake(res, "localhost", "/", - [](request_type& req) - { - req.set(field::user_agent, "Beast"); - }, - ec); - if(! ec) - std::cout << res; - @endcode - - @see - @li Websocket Opening Handshake Client Requirements (RFC6455) - @li Host field (RFC7230) - @li request-target (RFC7230) - @li origin-form (RFC7230) - */ - template - void - handshake_ex( - response_type& res, - string_view host, - string_view target, - RequestDecorator const& decorator, - error_code& ec); - /** Perform the WebSocket handshake asynchronously in the client role. This initiating function is used to asynchronously begin performing the @@ -1244,191 +969,6 @@ public: string_view target, HandshakeHandler&& handler); - /** Perform the WebSocket handshake asynchronously in the client role. - - This initiating function is used to asynchronously begin performing the - WebSocket handshake, - required before messages can be sent and received. During the handshake, - the client sends the Websocket Upgrade HTTP request, and the server - replies with an HTTP response indicating the result of the handshake. - - This call always returns immediately. The asynchronous operation - will continue until one of the following conditions is true: - - @li The request is sent and the response is received. - - @li An error occurs. - - The algorithm, known as a composed asynchronous operation, - is implemented in terms of calls to the next layer's `async_read_some` - and `async_write_some` functions. No other operation may be performed - on the stream until this operation completes. - - The handshake is successful if the received HTTP response - indicates the upgrade was accepted by the server, represented by a - status-code - of @ref beast::http::status::switching_protocols. - - @param host The name of the remote host. This is required by - the HTTP protocol to set the "Host" header field. - The implementation will not access the string data after the - initiating function returns. - - @param target The request-target, in origin-form. The server may use the - target to distinguish different services on the same listening port. - The implementation will not access the string data after the - initiating function returns. - - @param decorator A function object which will be called to modify - the HTTP request object generated by the implementation. This - could be used to set the User-Agent field, subprotocols, or other - application or HTTP specific fields. The function object will be - called with this equivalent signature: - @code - void decorator( - request_type& req - ); - @endcode - - @param handler Invoked when the operation completes. Ownership - of the handler will be transferred by move-construction as needed. - The equivalent function signature of the handler must be: - @code - void handler( - error_code const& ec // Result of operation - ); - @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - - @par Example - @code - ws.async_handshake("localhost", "/", - [](request_type& req) - { - req.set(field::user_agent, "Beast"); - }, - [](error_code ec) - { - if(ec) - std::cerr << "Error: " << ec.message() << "\n"; - }); - @endcode - - @see - @li Websocket Opening Handshake Client Requirements (RFC6455) - @li Host field (RFC7230) - @li request-target (RFC7230) - @li origin-form (RFC7230) - */ - template - BOOST_ASIO_INITFN_RESULT_TYPE( - HandshakeHandler, void(error_code)) - async_handshake_ex( - string_view host, - string_view target, - RequestDecorator const& decorator, - HandshakeHandler&& handler); - - /** Perform the WebSocket handshake asynchronously in the client role. - - This initiating function is used to asynchronously begin performing the - WebSocket handshake, - required before messages can be sent and received. During the handshake, - the client sends the Websocket Upgrade HTTP request, and the server - replies with an HTTP response indicating the result of the handshake. - - This call always returns immediately. The asynchronous operation - will continue until one of the following conditions is true: - - @li The request is sent and the response is received. - - @li An error occurs. - - The algorithm, known as a composed asynchronous operation, - is implemented in terms of calls to the next layer's `async_read_some` - and `async_write_some` functions. No other operation may be performed - on the stream until this operation completes. - - The handshake is successful if the received HTTP response - indicates the upgrade was accepted by the server, represented by a - status-code - of @ref beast::http::status::switching_protocols. - - @param res The HTTP Upgrade response returned by the remote - endpoint. The caller may use the response to access any - additional information sent by the server. This object will - be assigned before the completion handler is invoked. - - @param host The name of the remote host. This is required by - the HTTP protocol to set the "Host" header field. - The implementation will not access the string data after the - initiating function returns. - - @param target The request-target, in origin-form. The server may use the - target to distinguish different services on the same listening port. - The implementation will not access the string data after the - initiating function returns. - - @param decorator A function object which will be called to modify - the HTTP request object generated by the implementation. This - could be used to set the User-Agent field, subprotocols, or other - application or HTTP specific fields. The function object will be - called with this equivalent signature: - @code - void decorator( - request_type& req - ); - @endcode - - @param handler Invoked when the operation completes. Ownership - of the handler will be transferred by move-construction as needed. - The equivalent function signature of the handler must be: - @code - void handler( - error_code const& ec // Result of operation - ); - @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - - @par Example - @code - response_type res; - ws.async_handshake("localhost", "/", - [](request_type& req) - { - req.set(field::user_agent, "Beast"); - }, - [&res](error_code ec) - { - if(ec) - std::cerr << "Error: " << ec.message() << "\n"; - else - std::cout << res; - }); - @endcode - - @see - @li Websocket Opening Handshake Client Requirements (RFC6455) - @li Host field (RFC7230) - @li request-target (RFC7230) - @li origin-form (RFC7230) - */ - template - BOOST_ASIO_INITFN_RESULT_TYPE( - HandshakeHandler, void(error_code)) - async_handshake_ex( - response_type& res, - string_view host, - string_view target, - RequestDecorator const& decorator, - HandshakeHandler&& handler); - //-------------------------------------------------------------------------- // // Handshaking (Server) @@ -1474,57 +1014,6 @@ public: void accept(); - /** Read and respond to a WebSocket HTTP Upgrade request. - - This function is used to perform the - WebSocket handshake, - required before messages can be sent and received. During the handshake, - the client sends the Websocket Upgrade HTTP request, and the server - replies with an HTTP response indicating the result of the handshake. - - The call blocks until one of the following conditions is true: - - @li The request is received and the response is sent. - - @li An error occurs. - - The algorithm, known as a composed operation, is implemented - in terms of calls to the next layer's `read_some` and `write_some` - functions. - - If a valid upgrade request is received, an HTTP response with a - status-code - of @ref beast::http::status::switching_protocols is sent to - the peer, otherwise a non-successful error is associated with - the operation. - - If the request size exceeds the capacity of the stream's - internal buffer, the error @ref error::buffer_overflow will be - indicated. To handle larger requests, an application should - read the HTTP request directly using @ref http::read and then - pass the request to the appropriate overload of @ref accept or - @ref async_accept - - @param decorator A function object which will be called to modify - the HTTP response object delivered by the implementation. This - could be used to set the Server field, subprotocols, or other - application or HTTP specific fields. The object will be called - with this equivalent signature: - @code - void decorator( - response_type& res - ); - @endcode - - @throws system_error Thrown on failure. - - @see - @li Websocket Opening Handshake Server Requirements (RFC6455) - */ - template - void - accept_ex(ResponseDecorator const& decorator); - /** Read and respond to a WebSocket HTTP Upgrade request. This function is used to perform the @@ -1564,59 +1053,6 @@ public: void accept(error_code& ec); - /** Read and respond to a WebSocket HTTP Upgrade request. - - This function is used to perform the - WebSocket handshake, - required before messages can be sent and received. During the handshake, - the client sends the Websocket Upgrade HTTP request, and the server - replies with an HTTP response indicating the result of the handshake. - - The call blocks until one of the following conditions is true: - - @li The request is received and the response is sent. - - @li An error occurs. - - The algorithm, known as a composed operation, is implemented - in terms of calls to the next layer's `read_some` and `write_some` - functions. - - If a valid upgrade request is received, an HTTP response with a - status-code - of @ref beast::http::status::switching_protocols is sent to - the peer, otherwise a non-successful error is associated with - the operation. - - If the request size exceeds the capacity of the stream's - internal buffer, the error @ref error::buffer_overflow will be - indicated. To handle larger requests, an application should - read the HTTP request directly using @ref http::read and then - pass the request to the appropriate overload of @ref accept or - @ref async_accept - - @param decorator A function object which will be called to modify - the HTTP response object delivered by the implementation. This - could be used to set the Server field, subprotocols, or other - application or HTTP specific fields. The object will be called - with this equivalent signature: - @code - void decorator( - response_type& res - ); - @endcode - - @param ec Set to indicate what error occurred, if any. - - @see - @li Websocket Opening Handshake Server Requirements (RFC6455) - */ - template - void - accept_ex( - ResponseDecorator const& decorator, - error_code& ec); - /** Read and respond to a WebSocket HTTP Upgrade request. This function is used to perform the @@ -1666,69 +1102,6 @@ public: #endif accept(ConstBufferSequence const& buffers); - /** Read and respond to a WebSocket HTTP Upgrade request. - - This function is used to perform the - WebSocket handshake, - required before messages can be sent and received. During the handshake, - the client sends the Websocket Upgrade HTTP request, and the server - replies with an HTTP response indicating the result of the handshake. - - The call blocks until one of the following conditions is true: - - @li The request is received and the response is sent. - - @li An error occurs. - - The algorithm, known as a composed operation, is implemented - in terms of calls to the next layer's `read_some` and `write_some` - functions. - - If a valid upgrade request is received, an HTTP response with a - status-code - of @ref beast::http::status::switching_protocols is sent to - the peer, otherwise a non-successful error is associated with - the operation. - - If the request size exceeds the capacity of the stream's - internal buffer, the error @ref error::buffer_overflow will be - indicated. To handle larger requests, an application should - read the HTTP request directly using @ref http::read and then - pass the request to the appropriate overload of @ref accept or - @ref async_accept - - @param buffers Caller provided data that has already been - received on the stream. The implementation will copy the - caller provided data before the function returns. - - @param decorator A function object which will be called to modify - the HTTP response object delivered by the implementation. This - could be used to set the Server field, subprotocols, or other - application or HTTP specific fields. The object will be called - with this equivalent signature: - @code - void decorator( - response_type& res - ); - @endcode - - @throws system_error Thrown on failure. - - @see - @li Websocket Opening Handshake Server Requirements (RFC6455) - */ - template -#if BOOST_BEAST_DOXYGEN - void -#else - typename std::enable_if::value>::type -#endif - accept_ex( - ConstBufferSequence const& buffers, - ResponseDecorator const& decorator); - /** Read and respond to a WebSocket HTTP Upgrade request. This function is used to perform the @@ -1780,69 +1153,6 @@ public: ConstBufferSequence const& buffers, error_code& ec); - /** Read and respond to a WebSocket HTTP Upgrade request. - - This function is used to perform the - WebSocket handshake, - required before messages can be sent and received. During the handshake, - the client sends the Websocket Upgrade HTTP request, and the server - replies with an HTTP response indicating the result of the handshake. - - The call blocks until one of the following conditions is true: - - @li The request is received and the response is sent. - - @li An error occurs. - - The algorithm, known as a composed operation, is implemented - in terms of calls to the next layer's `read_some` and `write_some` - functions. - - If a valid upgrade request is received, an HTTP response with a - status-code - of @ref beast::http::status::switching_protocols is sent to - the peer, otherwise a non-successful error is associated with - the operation. - - If the request size exceeds the capacity of the stream's - internal buffer, the error @ref error::buffer_overflow will be - indicated. To handle larger requests, an application should - read the HTTP request directly using @ref http::read and then - pass the request to the appropriate overload of @ref accept or - @ref async_accept - - @param buffers Caller provided data that has already been - received on the stream. The implementation will copy the - caller provided data before the function returns. - - @param decorator A function object which will be called to modify - the HTTP response object delivered by the implementation. This - could be used to set the Server field, subprotocols, or other - application or HTTP specific fields. The object will be called - with this equivalent signature: - @code - void decorator( - response_type& res - ); - @endcode - - @param ec Set to indicate what error occurred, if any. - - @see - @li Websocket Opening Handshake Server Requirements (RFC6455) - */ - template -#if BOOST_BEAST_DOXYGEN - void -#else - typename std::enable_if::value>::type -#endif - accept_ex( - ConstBufferSequence const& buffers, - ResponseDecorator const& decorator, - error_code& ec); - /** Respond to a WebSocket HTTP Upgrade request This function is used to perform the @@ -1881,57 +1191,6 @@ public: accept(http::request> const& req); - /** Respond to a WebSocket HTTP Upgrade request - - This function is used to perform the - WebSocket handshake, - required before messages can be sent and received. During the handshake, - the client sends the Websocket Upgrade HTTP request, and the server - replies with an HTTP response indicating the result of the handshake. - - The call blocks until one of the following conditions is true: - - @li The response is sent. - - @li An error occurs. - - The algorithm, known as a composed operation, is implemented - in terms of calls to the next layer's `read_some` and `write_some` - functions. - - If a valid upgrade request is received, an HTTP response with a - status-code - of @ref beast::http::status::switching_protocols is sent to - the peer, otherwise a non-successful error is associated with - the operation. - - @param req An object containing the HTTP Upgrade request. - Ownership is not transferred, the implementation will not - access this object from other threads. - - @param decorator A function object which will be called to modify - the HTTP response object delivered by the implementation. This - could be used to set the Server field, subprotocols, or other - application or HTTP specific fields. The object will be called - with this equivalent signature: - @code - void decorator( - response_type& res - ); - @endcode - - @throws system_error Thrown on failure. - - @see - @li Websocket Opening Handshake Server Requirements (RFC6455) - */ - template - void - accept_ex(http::request> const& req, - ResponseDecorator const& decorator); - /** Respond to a WebSocket HTTP Upgrade request This function is used to perform the @@ -1971,58 +1230,6 @@ public: http::basic_fields> const& req, error_code& ec); - /** Respond to a WebSocket HTTP Upgrade request - - This function is used to perform the - WebSocket handshake, - required before messages can be sent and received. During the handshake, - the client sends the Websocket Upgrade HTTP request, and the server - replies with an HTTP response indicating the result of the handshake. - - The call blocks until one of the following conditions is true: - - @li The response is sent. - - @li An error occurs. - - The algorithm, known as a composed operation, is implemented - in terms of calls to the next layer's `read_some` and `write_some` - functions. - - If a valid upgrade request is received, an HTTP response with a - status-code - of @ref beast::http::status::switching_protocols is sent to - the peer, otherwise a non-successful error is associated with - the operation. - - @param req An object containing the HTTP Upgrade request. - Ownership is not transferred, the implementation will not - access this object from other threads. - - @param decorator A function object which will be called to modify - the HTTP response object delivered by the implementation. This - could be used to set the Server field, subprotocols, or other - application or HTTP specific fields. The object will be called - with this equivalent signature: - @code - void decorator( - response_type& res - ); - @endcode - - @param ec Set to indicate what error occurred, if any. - - @see - @li Websocket Opening Handshake Server Requirements (RFC6455) - */ - template - void - accept_ex(http::request> const& req, - ResponseDecorator const& decorator, - error_code& ec); - /** Perform the WebSocket handshake asynchronously in the server role. This initiating function is used to asynchronously begin performing the @@ -2077,75 +1284,6 @@ public: AcceptHandler, void(error_code)) async_accept(AcceptHandler&& handler); - /** Perform the WebSocket handshake asynchronously in the server role. - - This initiating function is used to asynchronously begin performing the - WebSocket handshake, - required before messages can be sent and received. During the handshake, - the client sends the Websocket Upgrade HTTP request, and the server - replies with an HTTP response indicating the result of the handshake. - - This call always returns immediately. The asynchronous operation - will continue until one of the following conditions is true: - - @li The request is received and the response is sent. - - @li An error occurs. - - The algorithm, known as a composed asynchronous operation, - is implemented in terms of calls to the next layer's `async_read_some` - and `async_write_some` functions. No other operation may be performed - on the stream until this operation completes. - - If a valid upgrade request is received, an HTTP response with a - status-code - of @ref beast::http::status::switching_protocols is sent to - the peer, otherwise a non-successful error is associated with - the operation. - - If the request size exceeds the capacity of the stream's - internal buffer, the error @ref error::buffer_overflow will be - indicated. To handle larger requests, an application should - read the HTTP request directly using @ref http::async_read and then - pass the request to the appropriate overload of @ref accept or - @ref async_accept - - @param decorator A function object which will be called to modify - the HTTP response object delivered by the implementation. This - could be used to set the Server field, subprotocols, or other - application or HTTP specific fields. The object will be called - with this equivalent signature: - @code - void decorator( - response_type& res - ); - @endcode - - @param handler Invoked when the operation completes. Ownership - of the handler will be transferred by move-construction as needed. - The equivalent function signature of the handler must be: - @code - void handler( - error_code const& ec // Result of operation - ); - @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - - @see - @li Websocket Opening Handshake Server Requirements (RFC6455) - */ - template< - class ResponseDecorator, - class AcceptHandler> - BOOST_ASIO_INITFN_RESULT_TYPE( - AcceptHandler, void(error_code)) - async_accept_ex( - ResponseDecorator const& decorator, - AcceptHandler&& handler); - /** Perform the WebSocket handshake asynchronously in the server role. This initiating function is used to asynchronously begin performing the @@ -2217,90 +1355,6 @@ public: ConstBufferSequence const& buffers, AcceptHandler&& handler); - /** Perform the WebSocket handshake asynchronously in the server role. - - This initiating function is used to asynchronously begin performing the - WebSocket handshake, - required before messages can be sent and received. During the handshake, - the client sends the Websocket Upgrade HTTP request, and the server - replies with an HTTP response indicating the result of the handshake. - - This call always returns immediately. The asynchronous operation - will continue until one of the following conditions is true: - - @li The request is received and the response is sent. - - @li An error occurs. - - The algorithm, known as a composed asynchronous operation, - is implemented in terms of calls to the next layer's `async_read_some` - and `async_write_some` functions. No other operation may be performed - on the stream until this operation completes. - - If a valid upgrade request is received, an HTTP response with a - status-code - of @ref beast::http::status::switching_protocols is sent to - the peer, otherwise a non-successful error is associated with - the operation. - - If the request size exceeds the capacity of the stream's - internal buffer, the error @ref error::buffer_overflow will be - indicated. To handle larger requests, an application should - read the HTTP request directly using @ref http::async_read and then - pass the request to the appropriate overload of @ref accept or - @ref async_accept - - @param buffers Caller provided data that has already been - received on the stream. This may be used for implementations - allowing multiple protocols on the same stream. The - buffered data will first be applied to the handshake, and - then to received WebSocket frames. The implementation will - copy the caller provided data before the function returns. - - @param decorator A function object which will be called to modify - the HTTP response object delivered by the implementation. This - could be used to set the Server field, subprotocols, or other - application or HTTP specific fields. The object will be called - with this equivalent signature: - @code - void decorator( - response_type& res - ); - @endcode - - @param handler Invoked when the operation completes. Ownership - of the handler will be transferred by move-construction as needed. - The equivalent function signature of the handler must be: - @code - void handler( - error_code const& ec // Result of operation - ); - @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - - @see - @li Websocket Opening Handshake Server Requirements (RFC6455) - */ - template< - class ConstBufferSequence, - class ResponseDecorator, - class AcceptHandler> -#if BOOST_BEAST_DOXYGEN - void_or_deduced -#else - typename std::enable_if< - ! http::detail::is_header::value, - BOOST_ASIO_INITFN_RESULT_TYPE( - AcceptHandler, void(error_code))>::type -#endif - async_accept_ex( - ConstBufferSequence const& buffers, - ResponseDecorator const& decorator, - AcceptHandler&& handler); - /** Perform the WebSocket handshake asynchronously in the server role. This initiating function is used to asynchronously begin performing the @@ -2357,75 +1411,6 @@ public: http::basic_fields> const& req, AcceptHandler&& handler); - /** Perform the WebSocket handshake asynchronously in the server role. - - This initiating function is used to asynchronously begin performing the - WebSocket handshake, - required before messages can be sent and received. During the handshake, - the client sends the Websocket Upgrade HTTP request, and the server - replies with an HTTP response indicating the result of the handshake. - - This call always returns immediately. The asynchronous operation - will continue until one of the following conditions is true: - - @li The request is received and the response is sent. - - @li An error occurs. - - The algorithm, known as a composed asynchronous operation, - is implemented in terms of calls to the next layer's `async_read_some` - and `async_write_some` functions. No other operation may be performed - on the stream until this operation completes. - - If a valid upgrade request is received, an HTTP response with a - status-code - of @ref beast::http::status::switching_protocols is sent to - the peer, otherwise a non-successful error is associated with - the operation. - - @param req An object containing the HTTP Upgrade request. - Ownership is not transferred, the implementation will not access - this object from other threads. - - @param decorator A function object which will be called to modify - the HTTP response object delivered by the implementation. This - could be used to set the Server field, subprotocols, or other - application or HTTP specific fields. The object will be called - with this equivalent signature: - @code - void decorator( - response_type& res - ); - @endcode - - @param handler Invoked when the operation completes. Ownership - of the handler will be transferred by move-construction as needed. - The equivalent function signature of the handler must be: - @code - void handler( - error_code const& ec // Result of operation - ); - @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using `net::post`. - - @see - @li Websocket Opening Handshake Server Requirements (RFC6455) - */ - template< - class Body, class Allocator, - class ResponseDecorator, - class AcceptHandler> - BOOST_ASIO_INITFN_RESULT_TYPE( - AcceptHandler, void(error_code)) - async_accept_ex( - http::request> const& req, - ResponseDecorator const& decorator, - AcceptHandler&& handler); - //-------------------------------------------------------------------------- // // Close Frames @@ -3482,6 +2467,138 @@ public: async_write_some(bool fin, ConstBufferSequence const& buffers, WriteHandler&& handler); + // + // Deprecated + // + +#if ! BOOST_BEAST_DOXYGEN + template + void + handshake_ex( + string_view host, + string_view target, + RequestDecorator const& decorator); + + template + void + handshake_ex( + response_type& res, + string_view host, + string_view target, + RequestDecorator const& decorator); + + template + void + handshake_ex( + string_view host, + string_view target, + RequestDecorator const& decorator, + error_code& ec); + + template + void + handshake_ex( + response_type& res, + string_view host, + string_view target, + RequestDecorator const& decorator, + error_code& ec); + + template + BOOST_ASIO_INITFN_RESULT_TYPE( + HandshakeHandler, void(error_code)) + async_handshake_ex( + string_view host, + string_view target, + RequestDecorator const& decorator, + HandshakeHandler&& handler); + + template + BOOST_ASIO_INITFN_RESULT_TYPE( + HandshakeHandler, void(error_code)) + async_handshake_ex( + response_type& res, + string_view host, + string_view target, + RequestDecorator const& decorator, + HandshakeHandler&& handler); + + template + void + accept_ex(ResponseDecorator const& decorator); + + template + void + accept_ex( + ResponseDecorator const& decorator, + error_code& ec); + + template + typename std::enable_if::value>::type + accept_ex( + ConstBufferSequence const& buffers, + ResponseDecorator const& decorator); + + template + typename std::enable_if::value>::type + accept_ex( + ConstBufferSequence const& buffers, + ResponseDecorator const& decorator, + error_code& ec); + + template + void + accept_ex(http::request> const& req, + ResponseDecorator const& decorator); + + template + void + accept_ex(http::request> const& req, + ResponseDecorator const& decorator, + error_code& ec); + + template< + class ResponseDecorator, + class AcceptHandler> + BOOST_ASIO_INITFN_RESULT_TYPE( + AcceptHandler, void(error_code)) + async_accept_ex( + ResponseDecorator const& decorator, + AcceptHandler&& handler); + + template< + class ConstBufferSequence, + class ResponseDecorator, + class AcceptHandler> + typename std::enable_if< + ! http::detail::is_header::value, + BOOST_ASIO_INITFN_RESULT_TYPE( + AcceptHandler, void(error_code))>::type + async_accept_ex( + ConstBufferSequence const& buffers, + ResponseDecorator const& decorator, + AcceptHandler&& handler); + + template< + class Body, class Allocator, + class ResponseDecorator, + class AcceptHandler> + BOOST_ASIO_INITFN_RESULT_TYPE( + AcceptHandler, void(error_code)) + async_accept_ex( + http::request> const& req, + ResponseDecorator const& decorator, + AcceptHandler&& handler); +#endif + private: template class accept_op; template class close_op; diff --git a/include/boost/beast/websocket/stream_base.hpp b/include/boost/beast/websocket/stream_base.hpp index 7781a0f5..23e662b3 100644 --- a/include/boost/beast/websocket/stream_base.hpp +++ b/include/boost/beast/websocket/stream_base.hpp @@ -12,6 +12,7 @@ #include #include +#include #include namespace boost { @@ -48,6 +49,25 @@ struct stream_base return (duration::max)(); } + /** Stream option used to adjust HTTP fields of WebSocket upgrade request and responses. + */ + class decorator + { + detail::decorator d_; + + template + friend class stream; + + public: + decorator(decorator&&) = default; + + template + decorator(Decorator&& f) + : d_(f) + { + } + }; + /** Stream option to control the behavior of websocket timeouts. Timeout features are available for asynchronous operations only. diff --git a/test/beast/websocket/_detail_decorator.cpp b/test/beast/websocket/_detail_decorator.cpp index c666cb01..dd3d5bbf 100644 --- a/test/beast/websocket/_detail_decorator.cpp +++ b/test/beast/websocket/_detail_decorator.cpp @@ -70,10 +70,13 @@ public: struct both_t : res_t , req_t { + using req_t::operator(); + using res_t::operator(); }; struct big_t : both_t { + using both_t::operator(); char buf[2048]; }; @@ -109,11 +112,15 @@ public: decorator d1{req_t{}}; decorator d2{std::move(d1)}; d2(req); + decorator d3; + d3 = std::move(d2); } { + // this would be leaner with bind_front decorator d(std::bind( - &decorator_test::dec_req, this)); + &decorator_test::dec_req, this, + std::placeholders::_1)); BEAST_EXPECT(d.is_inline()); } } @@ -121,6 +128,7 @@ public: void run() override { + log << "sizeof(decorator)==" << sizeof(decorator) << "\n"; testDecorator(); } }; diff --git a/test/beast/websocket/stream.cpp b/test/beast/websocket/stream.cpp index 8fcf2360..7695cb52 100644 --- a/test/beast/websocket/stream.cpp +++ b/test/beast/websocket/stream.cpp @@ -28,6 +28,20 @@ public: net::io_context ioc; stream ws(ioc); + { + ws.set_option( + stream_base::decorator( + [](request_type&) + { + })); + + ws.set_option( + stream_base::decorator( + [](response_type&) + { + })); + } + { ws.set_option( stream_base::suggested_settings( diff --git a/test/doc/CMakeLists.txt b/test/doc/CMakeLists.txt index e7b932ad..106a480b 100644 --- a/test/doc/CMakeLists.txt +++ b/test/doc/CMakeLists.txt @@ -25,6 +25,7 @@ add_executable (tests-doc core_3_layers.cpp http_examples.cpp http_snippets.cpp + websocket_3_handshake.cpp websocket_snippets.cpp exemplars.cpp ) diff --git a/test/doc/Jamfile b/test/doc/Jamfile index 5332ea26..4a3abefc 100644 --- a/test/doc/Jamfile +++ b/test/doc/Jamfile @@ -23,6 +23,7 @@ alias run-tests : [ run core_1_refresher.cpp $(TEST_MAIN) ] [ run core_3_layers.cpp $(TEST_MAIN) ] [ run http_examples.cpp $(TEST_MAIN) ] + [ run websocket_3_handshake.cpp $(TEST_MAIN) ] ; exe fat-tests : @@ -31,6 +32,7 @@ exe fat-tests : core_1_refresher.cpp core_3_layers.cpp http_examples.cpp + websocket_3_handshake.cpp ; explicit fat-tests ; diff --git a/test/doc/websocket_3_handshake.cpp b/test/doc/websocket_3_handshake.cpp new file mode 100644 index 00000000..ed85b298 --- /dev/null +++ b/test/doc/websocket_3_handshake.cpp @@ -0,0 +1,236 @@ +// +// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/beast +// + +#include "snippets.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace websocket { + +namespace { + +void +websocket_3_handshake_snippets() +{ + #include "snippets.ipp" + stream ws(ioc); + { + //[code_websocket_3_client_1 + + // Note that the stream must already be connected, this + // function does not perform a DNS lookup on the host + // name, nor does it establish an outgoing connection. + + // Perform the websocket handshake in the client role. + ws.handshake( + "www.example.com", // The Host field + "/", // The request-target + ec // Set to the error + ); + + //] + } + { + //[code_websocket_3_client_2 + + // Note that the stream must already be connected, this + // function does not perform a DNS lookup on the host + // name, nor does it establish an outgoing connection. + + // This variable will receive the HTTP response from the server + response_type res; + + // Perform the websocket handshake in the client role. + ws.handshake( + res, // Receives the HTTP response + "www.example.com", // The Host field + "/" // The request-target + ,ec // Set to the error, if any + ); + + // Upon success, `res` will be set to the complete + // response received from the server. + //] + } + + //-------------------------------------------------------------------------- + + { + //[code_websocket_3_server_1 + + // Note that the stream must already be connected + // to an incoming remote peer. + + // Perform the websocket handshake in the server role. + ws.accept( + ec // Set to the error, if any + ); + + //] + } + { + //[code_websocket_3_server_2 + // This buffer is required for reading HTTP messages + flat_buffer buffer; + + // Read into our buffer until we reach the end of the HTTP request. + // No parsing takes place here, we are just accumulating data. + // We use beast::dynamic_buffer_ref to pass a lightweight, movable + // reference to our buffer, because Networking expects to take + // ownership unlike Beast algorithms which use a reference. + + net::read_until(sock, dynamic_buffer_ref(buffer), "\r\n\r\n"); + + // Now accept the connection, using the buffered data. + ws.accept(buffer.data()); + //] + } +} + +void +websocket_3_handshake_snippets_2() +{ + using namespace boost::beast; + namespace net = boost::asio; + namespace ssl = boost::asio::ssl; + using tcp = net::ip::tcp; + error_code ec; + net::io_context ioc; + tcp::socket sock(ioc); + + { + //[code_websocket_3_server_1b + // This buffer is required for reading HTTP messages + flat_buffer buffer; + + // Read the HTTP request ourselves + http::request req; + http::read(sock, buffer, req); + + // See if its a WebSocket upgrade request + if(websocket::is_upgrade(req)) + { + // Construct the stream, transferring ownership of the socket + stream ws{std::move(sock)}; + + // Clients SHOULD NOT begin sending WebSocket + // frames until the server has provided a response. + BOOST_ASSERT(buffer.size() == 0); + + // Accept the upgrade request + ws.accept(req); + } + else + { + // Its not a WebSocket upgrade, so + // handle it like a normal HTTP request. + } + //] + } +} + +//[code_websocket_3_decorator_1b +void set_user_agent(request_type& req) +{ + // Set the User-Agent on the request + req.set(http::field::user_agent, "My User Agent"); +} +//] + +void +websocket_3_handshake_snippets_3() +{ + #include "snippets.ipp" + stream ws(ioc); + { + //[code_websocket_3_decorator_1 + + ws.set_option(stream_base::decorator(&set_user_agent)); + + //] + } + { + //[code_websocket_3_decorator_2 + + struct set_server + { + void operator()(response_type& res) + { + // Set the Server field on the response + res.set(http::field::user_agent, "My Server"); + } + }; + + ws.set_option(stream_base::decorator(set_server{})); + + //] + } + { + //[code_websocket_3_decorator_3 + + ws.set_option(stream_base::decorator( + [](response_type& res) + { + // Set the Server field on the response + res.set(http::field::user_agent, "My Server"); + })); + + //] + } + //[code_websocket_3_decorator_4 + + struct multi_decorator + { + void operator()(request_type& req) + { + // Set the User-Agent on the request + req.set(http::field::user_agent, "My User Agent"); + } + + void operator()(response_type& res) + { + // Set the Server field on the response + res.set(http::field::user_agent, "My Server"); + } + }; + + ws.set_option(stream_base::decorator(multi_decorator{})); + + //] +} + +} // (anon) + +struct websocket_3_handshake_test + : public beast::unit_test::suite +{ + void + run() override + { + BEAST_EXPECT(&websocket_3_handshake_snippets); + BEAST_EXPECT(&websocket_3_handshake_snippets_2); + BEAST_EXPECT(&websocket_3_handshake_snippets_3); + } +}; + +BEAST_DEFINE_TESTSUITE(beast,doc,websocket_3_handshake); + +} // websocket +} // beast +} // boost