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.
This commit is contained in:
Vinnie Falco
2019-02-17 16:03:09 -08:00
parent 085fb66b26
commit 3f50efa138
41 changed files with 1097 additions and 1391 deletions

View File

@ -9,7 +9,7 @@
[section Handshaking (Clients)] [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] [@https://tools.ietf.org/html/rfc7230#section-6.7 Upgrade]
request for request for
[@https://tools.ietf.org/html/rfc6455#section-1.3 websocket], [@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] [@https://tools.ietf.org/html/rfc7230#section-5.4 Host]
field, and the field, and the
[@https://tools.ietf.org/html/rfc7230#section-5.3 target] [@https://tools.ietf.org/html/rfc7230#section-5.3 target]
of the resource to request. The stream member functions of the resource to request.
[link beast.ref.boost__beast__websocket__stream.handshake.overload1 `handshake`] and A typical HTTP Upgrade request created and sent by the implementation
[link beast.ref.boost__beast__websocket__stream.async_handshake.overload1 `async_handshake`] will look like this:
are used to send the request with the required host and target strings.
[ws_snippet_8] [table WebSocket HTTP Upgrade Request
The implementation will create and send a request that typically
looks like this:
[table WebSocket Upgrade HTTP Request
[[Serialized Octets][Description]] [[Serialized Octets][Description]]
[[ [[
``` ```
GET / HTTP/1.1 GET / HTTP/1.1
Host: localhost Host: www.example.com
Upgrade: websocket Upgrade: websocket
Connection: upgrade Connection: upgrade
Sec-WebSocket-Key: 2pGeTR0DsE4dfZs2pH+8MA== Sec-WebSocket-Key: 2pGeTR0DsE4dfZs2pH+8MA==
Sec-WebSocket-Version: 13 Sec-WebSocket-Version: 13
User-Agent: Beast User-Agent: Boost.Beast/216
``` ```
][ ][
The host and target parameters become part of the Host field The host and target parameters become part of the Host field
and request-target in the resulting HTTP request. The key is and request-target in the resulting HTTP request. The key is
generated by the implementation. Callers may add fields or 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 [code_websocket_3_client_1]
[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]
When a client receives an HTTP Upgrade response from the server indicating When a client receives an HTTP Upgrade response from the server indicating
a successful upgrade, the caller may wish to perform additional validation 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`] [link beast.ref.boost__beast__websocket__response_type `response_type`]
as follows: as follows:
[ws_snippet_10] [code_websocket_3_client_2]
[endsect] [endsect]

View File

@ -18,16 +18,10 @@ is received with the
status code. On failure, an error is returned or an exception is thrown. 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 Depending on the keep alive setting, the connection may remain open for
a subsequent handshake attempt. 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 [table WebSocket Upgrade HTTP Response
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
[[Serialized Octets][Description]] [[Serialized Octets][Description]]
[[ [[
``` ```
@ -35,7 +29,7 @@ typically look like this:
Upgrade: websocket Upgrade: websocket
Connection: upgrade Connection: upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Server: Beast/40 Server: Boost.Beast/216
``` ```
][ ][
The The
@ -44,39 +38,18 @@ typically look like this:
the WebSocket protocol. 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 [code_websocket_3_server_1]
[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:
[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] [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, Once the caller determines that the HTTP request is a WebSocket Upgrade,
additional overloads of additional overloads of
[link beast.ref.boost__beast__websocket__stream.accept `accept`], [link beast.ref.boost__beast__websocket__stream.accept `accept`] and
[link beast.ref.boost__beast__websocket__stream.accept_ex `accept_ex`], [link beast.ref.boost__beast__websocket__stream.async_accept `async_accept`]
[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`]
are provided which receive the entire HTTP request header as an object are provided which receive the entire HTTP request header as an object
to perform the handshake. In this example, the request is first read to perform the handshake. By reading the request manually, the program
in using the HTTP algorithms, and then passed to a newly constructed 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: stream:
[ws_snippet_13] [code_websocket_3_server_1b]
[heading Buffered Handshakes] [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 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 at run-time. In the case where the server wishes to accept the incoming
request as an HTTP WebSocket Upgrade request, additional overloads of 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 `accept`] and
[link beast.ref.boost__beast__websocket__stream.accept_ex `accept_ex`], [link beast.ref.boost__beast__websocket__stream.async_accept `async_accept`]
[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`]
are provided which receive the additional buffered octets and consume are provided which receive the additional buffered octets and consume
them as part of the handshake. 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 specified dynamic buffer as an octet sequence in the buffer's output
area, and later uses those octets to attempt an HTTP WebSocket Upgrade: 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 The implementation uses a fixed-size storage area to hold buffers passed
using these functions. If an application is reaching the limit of the 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 placed into the buffered read stream, which uses a dynamically sized
buffer. buffer.
[endsect] [endsect]

View File

@ -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]

View File

@ -33,9 +33,10 @@ Boost.Asio with a consistent asynchronous model using a modern C++ approach.
[include 02_connect.qbk] [include 02_connect.qbk]
[include 03_client.qbk] [include 03_client.qbk]
[include 04_server.qbk] [include 04_server.qbk]
[include 05_messages.qbk] [include 05_decorator.qbk]
[include 06_control.qbk] [include 06_messages.qbk]
[include 07_teardown.qbk] [include 07_control.qbk]
[include 08_notes.qbk] [include 08_teardown.qbk]
[include 09_notes.qbk]
[endsect] [endsect]

View File

@ -132,6 +132,7 @@
[import ../../test/doc/core_1_refresher.cpp] [import ../../test/doc/core_1_refresher.cpp]
[import ../../test/doc/core_3_layers.cpp] [import ../../test/doc/core_3_layers.cpp]
[import ../../test/doc/websocket_3_handshake.cpp]
[section:quickref Reference] [section:quickref Reference]
''' '''

View File

@ -251,6 +251,16 @@ public:
websocket::stream_base::suggested_settings( websocket::stream_base::suggested_settings(
websocket::role_type::server)); 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 // Accept the websocket handshake
derived().ws().async_accept( derived().ws().async_accept(
req, req,

View File

@ -229,10 +229,6 @@ public:
websocket_session(tcp::socket socket) websocket_session(tcp::socket socket)
: ws_(std::move(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 // Start the asynchronous accept operation
@ -240,6 +236,20 @@ public:
void void
do_accept(http::request<Body, http::basic_fields<Allocator>> req) do_accept(http::request<Body, http::basic_fields<Allocator>> 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 // Accept the websocket handshake
ws_.async_accept( ws_.async_accept(
req, req,

View File

@ -132,6 +132,15 @@ public:
websocket::stream_base::suggested_settings( websocket::stream_base::suggested_settings(
websocket::role_type::client)); 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 // Perform the websocket handshake
ws_.async_handshake(host_, "/", ws_.async_handshake(host_, "/",
beast::bind_front_handler( beast::bind_front_handler(

View File

@ -110,6 +110,15 @@ public:
websocket::stream_base::suggested_settings( websocket::stream_base::suggested_settings(
websocket::role_type::client)); 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 // Perform the websocket handshake
ws_.async_handshake(host_, "/", ws_.async_handshake(host_, "/",
beast::bind_front_handler( beast::bind_front_handler(

View File

@ -74,6 +74,15 @@ do_session(
// Set a timeout on the operation // Set a timeout on the operation
beast::get_lowest_layer(ws).expires_after(std::chrono::seconds(30)); 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 // Perform the SSL handshake
ws.next_layer().async_handshake(ssl::stream_base::client, yield[ec]); ws.next_layer().async_handshake(ssl::stream_base::client, yield[ec]);
if(ec) if(ec)

View File

@ -74,6 +74,15 @@ do_session(
websocket::stream_base::suggested_settings( websocket::stream_base::suggested_settings(
websocket::role_type::client)); 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 // Perform the websocket handshake
ws.async_handshake(host, "/", yield[ec]); ws.async_handshake(host, "/", yield[ec]);
if(ec) if(ec)

View File

@ -73,6 +73,15 @@ int main(int argc, char** argv)
// Perform the SSL handshake // Perform the SSL handshake
ws.next_layer().handshake(ssl::stream_base::client); 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 // Perform the websocket handshake
ws.handshake(host, "/"); ws.handshake(host, "/");

View File

@ -60,6 +60,15 @@ int main(int argc, char** argv)
// Make the connection on the IP address we get from a lookup // Make the connection on the IP address we get from a lookup
net::connect(ws.next_layer(), results.begin(), results.end()); 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 // Perform the websocket handshake
ws.handshake(host, "/"); ws.handshake(host, "/");

View File

@ -89,6 +89,15 @@ public:
websocket::stream_base::suggested_settings( websocket::stream_base::suggested_settings(
websocket::role_type::server)); 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 // Accept the websocket handshake
ws_.async_accept( ws_.async_accept(
beast::bind_front_handler( beast::bind_front_handler(

View File

@ -52,16 +52,26 @@ public:
session(tcp::socket socket) session(tcp::socket socket)
: ws_(std::move(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 // Start the asynchronous operation
void void
run() 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 // Accept the websocket handshake
ws_.async_accept( ws_.async_accept(
beast::bind_front_handler( beast::bind_front_handler(

View File

@ -17,10 +17,6 @@ websocket_session(
: ws_(std::move(socket)) : ws_(std::move(socket))
, state_(state) , state_(state)
{ {
// Set suggested timeout settings for the websocket
ws_.set_option(
websocket::stream_base::suggested_settings(
websocket::role_type::server));
} }
websocket_session:: websocket_session::

View File

@ -57,6 +57,20 @@ void
websocket_session:: websocket_session::
run(http::request<Body, http::basic_fields<Allocator>> req) run(http::request<Body, http::basic_fields<Allocator>> 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 // Accept the websocket handshake
ws_.async_accept( ws_.async_accept(
req, req,

View File

@ -75,6 +75,15 @@ do_session(
websocket::stream_base::suggested_settings( websocket::stream_base::suggested_settings(
websocket::role_type::server)); 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 // Accept the websocket handshake
ws.async_accept(yield[ec]); ws.async_accept(yield[ec]);
if(ec) if(ec)

View File

@ -56,6 +56,15 @@ do_session(ws_type& ws, net::yield_context yield)
websocket::stream_base::suggested_settings( websocket::stream_base::suggested_settings(
websocket::role_type::server)); 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 // Accept the websocket handshake
ws.async_accept(yield[ec]); ws.async_accept(yield[ec]);
if(ec) if(ec)

View File

@ -90,13 +90,15 @@ do_sync_session(ws_type& ws)
setup_stream(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) [](websocket::response_type& res)
{ {
res.set(http::field::server, res.set(http::field::server, std::string(
"Boost.Beast/" + std::to_string(BOOST_BEAST_VERSION) + "-Sync"); BOOST_BEAST_VERSION_STRING) + "-Sync");
}, }));
ec);
ws.accept(ec);
if(ec) if(ec)
return fail(ec, "accept"); return fail(ec, "accept");
@ -158,13 +160,16 @@ public:
void void
run() run()
{ {
// Accept the websocket handshake // Set a decorator to change the Server of the handshake
ws_.async_accept_ex( ws_.set_option(websocket::stream_base::decorator(
[](websocket::response_type& res) [](websocket::response_type& res)
{ {
res.set(http::field::server, res.set(http::field::server, std::string(
"Boost.Beast/" + std::to_string(BOOST_BEAST_VERSION) + "-Async"); BOOST_BEAST_VERSION_STRING) + "-Async");
}, }));
// Accept the websocket handshake
ws_.async_accept(
beast::bind_front_handler( beast::bind_front_handler(
&async_session::on_accept, &async_session::on_accept,
shared_from_this())); shared_from_this()));
@ -324,13 +329,15 @@ do_coro_session(ws_type& ws, net::yield_context yield)
setup_stream(ws); setup_stream(ws);
ws.async_accept_ex( // Set a decorator to change the Server of the handshake
[&](websocket::response_type& res) ws.set_option(websocket::stream_base::decorator(
[](websocket::response_type& res)
{ {
res.set(http::field::server, res.set(http::field::server, std::string(
"Boost.Beast/" + std::to_string(BOOST_BEAST_VERSION) + "-Coro"); BOOST_BEAST_VERSION_STRING) + "-Fiber");
}, }));
yield[ec]);
ws.async_accept(yield[ec]);
if(ec) if(ec)
return fail(ec, "accept"); return fail(ec, "accept");

View File

@ -102,6 +102,15 @@ public:
websocket::stream_base::suggested_settings( websocket::stream_base::suggested_settings(
websocket::role_type::server)); 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 // Accept the websocket handshake
yield ws_.async_accept( yield ws_.async_accept(
std::bind( std::bind(

View File

@ -79,6 +79,15 @@ public:
websocket::stream_base::suggested_settings( websocket::stream_base::suggested_settings(
websocket::role_type::server)); 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 // Accept the websocket handshake
yield ws_.async_accept( yield ws_.async_accept(
std::bind( std::bind(

View File

@ -48,6 +48,15 @@ do_session(tcp::socket& socket, ssl::context& ctx)
// Perform the SSL handshake // Perform the SSL handshake
ws.next_layer().handshake(ssl::stream_base::server); 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 // Accept the websocket handshake
ws.accept(); ws.accept();

View File

@ -39,6 +39,15 @@ do_session(tcp::socket& socket)
// Construct the stream by moving in the socket // Construct the stream by moving in the socket
websocket::stream<tcp::socket> ws{std::move(socket)}; websocket::stream<tcp::socket> 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 // Accept the websocket handshake
ws.accept(); ws.accept();

View File

@ -104,7 +104,7 @@ public:
void void
reset() noexcept reset() noexcept
{ {
static_assert(sizeof(I) != 0, static_assert(I != 0,
BOOST_BEAST_DEPRECATION_STRING); BOOST_BEAST_DEPRECATION_STRING);
} }
#endif #endif

View File

@ -19,6 +19,7 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <new> #include <new>
#include <type_traits>
#include <utility> #include <utility>
namespace boost { namespace boost {
@ -37,14 +38,12 @@ class decorator
void(*)(), void(*)(),
void(incomplete::*)(), void(incomplete::*)(),
decltype(std::bind( decltype(std::bind(
std::declval<void(incomplete::*)()>(), std::declval<
std::declval<std::shared_ptr<incomplete>>())) void(incomplete::*)(request_type&)>(),
std::shared_ptr<incomplete>{},
std::placeholders::_1))
>(); >();
struct none
{
};
struct base struct base
{ {
virtual ~base() = default; virtual ~base() = default;
@ -62,6 +61,16 @@ class decorator
invoke(response_type&) = 0; invoke(response_type&) = 0;
}; };
using type = typename
std::aligned_storage<Bytes>::type;
type buf_;
base* base_;
struct none
{
};
template<class T, class = void> template<class T, class = void>
struct is_req_op : std::false_type struct is_req_op : std::false_type
{ {
@ -143,13 +152,42 @@ class decorator
} }
}; };
using type = typename void
std::aligned_storage<Bytes>::type; destroy()
{
if(is_inline())
base_->~base();
else if(base_)
delete base_;
}
type buf_; template<class F>
base* base_; base*
construct(F&& f, std::true_type)
{
return ::new(&buf_) impl<
typename std::decay<F>::type>(
std::forward<F>(f));
}
template<class F>
base*
construct(F&& f, std::false_type)
{
return new impl<
typename std::decay<F>::type>(
std::forward<F>(f));
}
public: public:
decorator(decorator const&) = delete;
decorator& operator=(decorator const&) = delete;
~decorator()
{
destroy();
}
decorator() decorator()
: decorator(none{}) : decorator(none{})
{ {
@ -171,25 +209,34 @@ public:
impl<none>(none{}); impl<none>(none{});
} }
template<class F> template<class F,
class = typename std::enable_if<
! std::is_convertible<F, decorator>::value>::type>
explicit explicit
decorator(F&& f) decorator(F&& f)
: base_(sizeof(F) <= sizeof(buf_) ? : base_(construct(std::forward<F>(f),
::new(&buf_) impl< std::integral_constant<bool,
typename std::decay<F>::type>( sizeof(F) <= sizeof(buf_)>{}))
std::forward<F>(f)) :
new impl<
typename std::decay<F>::type>(
std::forward<F>(f)))
{ {
} }
~decorator() decorator&
operator=(decorator&& other)
{ {
if(is_inline()) this->destroy();
base_->~base(); base_ = nullptr;
if(other.is_inline())
{
base_ = other.base_->move(&buf_);
other.base_->~base();
}
else else
delete base_; {
base_ = other.base_;
}
other.base_ = ::new(&other.buf_)
impl<none>(none{});
return *this;
} }
bool bool

View File

@ -75,12 +75,15 @@ build_response(
error_code& result) error_code& result)
{ {
auto const decorate = auto const decorate =
[&decorator](response_type& res) [this, &decorator](response_type& res)
{ {
decorator_opt(res);
decorator(res); decorator(res);
if(! res.count(http::field::server)) 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); static_string<20> s(BOOST_BEAST_VERSION_STRING);
res.set(http::field::server, s); res.set(http::field::server, s);
} }
@ -395,23 +398,6 @@ accept()
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
} }
template<class NextLayer, bool deflateSupported>
template<class ResponseDecorator>
void
stream<NextLayer, deflateSupported>::
accept_ex(ResponseDecorator const& decorator)
{
static_assert(is_sync_stream<next_layer_type>::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<class NextLayer, bool deflateSupported> template<class NextLayer, bool deflateSupported>
void void
stream<NextLayer, deflateSupported>:: stream<NextLayer, deflateSupported>::
@ -424,22 +410,6 @@ accept(error_code& ec)
&default_decorate_res, ec); &default_decorate_res, ec);
} }
template<class NextLayer, bool deflateSupported>
template<class ResponseDecorator>
void
stream<NextLayer, deflateSupported>::
accept_ex(ResponseDecorator const& decorator, error_code& ec)
{
static_assert(is_sync_stream<next_layer_type>::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<class NextLayer, bool deflateSupported> template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence> template<class ConstBufferSequence>
typename std::enable_if<! http::detail::is_header< typename std::enable_if<! http::detail::is_header<
@ -457,32 +427,6 @@ accept(ConstBufferSequence const& buffers)
if(ec) if(ec)
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
} }
template<class NextLayer, bool deflateSupported>
template<
class ConstBufferSequence,
class ResponseDecorator>
typename std::enable_if<! http::detail::is_header<
ConstBufferSequence>::value>::type
stream<NextLayer, deflateSupported>::
accept_ex(
ConstBufferSequence const& buffers,
ResponseDecorator const &decorator)
{
static_assert(is_sync_stream<next_layer_type>::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<class NextLayer, bool deflateSupported> template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence> template<class ConstBufferSequence>
typename std::enable_if<! http::detail::is_header< typename std::enable_if<! http::detail::is_header<
@ -499,28 +443,6 @@ accept(
do_accept(buffers, &default_decorate_res, ec); do_accept(buffers, &default_decorate_res, ec);
} }
template<class NextLayer, bool deflateSupported>
template<
class ConstBufferSequence,
class ResponseDecorator>
typename std::enable_if<! http::detail::is_header<
ConstBufferSequence>::value>::type
stream<NextLayer, deflateSupported>::
accept_ex(
ConstBufferSequence const& buffers,
ResponseDecorator const& decorator,
error_code& ec)
{
static_assert(is_sync_stream<next_layer_type>::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<class NextLayer, bool deflateSupported> template<class NextLayer, bool deflateSupported>
template<class Body, class Allocator> template<class Body, class Allocator>
@ -538,28 +460,6 @@ accept(
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
} }
template<class NextLayer, bool deflateSupported>
template<
class Body, class Allocator,
class ResponseDecorator>
void
stream<NextLayer, deflateSupported>::
accept_ex(
http::request<Body,
http::basic_fields<Allocator>> const& req,
ResponseDecorator const& decorator)
{
static_assert(is_sync_stream<next_layer_type>::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<class NextLayer, bool deflateSupported> template<class NextLayer, bool deflateSupported>
template<class Body, class Allocator> template<class Body, class Allocator>
void void
@ -575,27 +475,6 @@ accept(
do_accept(req, &default_decorate_res, ec); do_accept(req, &default_decorate_res, ec);
} }
template<class NextLayer, bool deflateSupported>
template<
class Body, class Allocator,
class ResponseDecorator>
void
stream<NextLayer, deflateSupported>::
accept_ex(
http::request<Body,
http::basic_fields<Allocator>> const& req,
ResponseDecorator const& decorator,
error_code& ec)
{
static_assert(is_sync_stream<next_layer_type>::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<class NextLayer, bool deflateSupported> template<class NextLayer, bool deflateSupported>
@ -769,6 +648,156 @@ async_accept_ex(
return init.result.get(); return init.result.get();
} }
//------------------------------------------------------------------------------
template<class NextLayer, bool deflateSupported>
template<class ResponseDecorator>
void
stream<NextLayer, deflateSupported>::
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<next_layer_type>::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<class NextLayer, bool deflateSupported>
template<class ResponseDecorator>
void
stream<NextLayer, deflateSupported>::
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<next_layer_type>::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<class NextLayer, bool deflateSupported>
template<
class ConstBufferSequence,
class ResponseDecorator>
typename std::enable_if<! http::detail::is_header<
ConstBufferSequence>::value>::type
stream<NextLayer, deflateSupported>::
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<next_layer_type>::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<class NextLayer, bool deflateSupported>
template<
class ConstBufferSequence,
class ResponseDecorator>
typename std::enable_if<! http::detail::is_header<
ConstBufferSequence>::value>::type
stream<NextLayer, deflateSupported>::
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<next_layer_type>::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<class NextLayer, bool deflateSupported>
template<
class Body, class Allocator,
class ResponseDecorator>
void
stream<NextLayer, deflateSupported>::
accept_ex(
http::request<Body,
http::basic_fields<Allocator>> 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<next_layer_type>::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<class NextLayer, bool deflateSupported>
template<
class Body, class Allocator,
class ResponseDecorator>
void
stream<NextLayer, deflateSupported>::
accept_ex(
http::request<Body,
http::basic_fields<Allocator>> 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<next_layer_type>::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 } // websocket
} // beast } // beast
} // boost } // boost

View File

@ -195,57 +195,6 @@ async_handshake(response_type& res,
return init.result.get(); return init.result.get();
} }
template<class NextLayer, bool deflateSupported>
template<class RequestDecorator, class HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
HandshakeHandler, void(error_code))
stream<NextLayer, deflateSupported>::
async_handshake_ex(string_view host,
string_view target,
RequestDecorator const& decorator,
HandshakeHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::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<BOOST_ASIO_HANDLER_TYPE(
HandshakeHandler, void(error_code))>(
std::move(init.completion_handler),
impl_, nullptr, host, target,
decorator)();
return init.result.get();
}
template<class NextLayer, bool deflateSupported>
template<class RequestDecorator, class HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
HandshakeHandler, void(error_code))
stream<NextLayer, deflateSupported>::
async_handshake_ex(response_type& res,
string_view host,
string_view target,
RequestDecorator const& decorator,
HandshakeHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::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<BOOST_ASIO_HANDLER_TYPE(
HandshakeHandler, void(error_code))>(
std::move(init.completion_handler),
impl_, &res, host, target,
decorator)();
return init.result.get();
}
template<class NextLayer, bool deflateSupported> template<class NextLayer, bool deflateSupported>
void void
stream<NextLayer, deflateSupported>:: stream<NextLayer, deflateSupported>::
@ -276,45 +225,6 @@ handshake(response_type& res,
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
} }
template<class NextLayer, bool deflateSupported>
template<class RequestDecorator>
void
stream<NextLayer, deflateSupported>::
handshake_ex(string_view host,
string_view target,
RequestDecorator const& decorator)
{
static_assert(is_sync_stream<next_layer_type>::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<class NextLayer, bool deflateSupported>
template<class RequestDecorator>
void
stream<NextLayer, deflateSupported>::
handshake_ex(response_type& res,
string_view host,
string_view target,
RequestDecorator const& decorator)
{
static_assert(is_sync_stream<next_layer_type>::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<class NextLayer, bool deflateSupported> template<class NextLayer, bool deflateSupported>
void void
stream<NextLayer, deflateSupported>:: stream<NextLayer, deflateSupported>::
@ -341,6 +251,55 @@ handshake(response_type& res,
host, target, &default_decorate_req, ec); host, target, &default_decorate_req, ec);
} }
//------------------------------------------------------------------------------
template<class NextLayer, bool deflateSupported>
template<class RequestDecorator>
void
stream<NextLayer, deflateSupported>::
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<next_layer_type>::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<class NextLayer, bool deflateSupported>
template<class RequestDecorator>
void
stream<NextLayer, deflateSupported>::
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<next_layer_type>::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<class NextLayer, bool deflateSupported> template<class NextLayer, bool deflateSupported>
template<class RequestDecorator> template<class RequestDecorator>
void void
@ -350,6 +309,10 @@ handshake_ex(string_view host,
RequestDecorator const& decorator, RequestDecorator const& decorator,
error_code& ec) error_code& ec)
{ {
#if ! BOOST_BEAST_ALLOW_DEPRECATED
static_assert(sizeof(RequestDecorator) == 0,
BOOST_BEAST_DEPRECATION_STRING);
#endif
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(detail::is_request_decorator< static_assert(detail::is_request_decorator<
@ -369,6 +332,10 @@ handshake_ex(response_type& res,
RequestDecorator const& decorator, RequestDecorator const& decorator,
error_code& ec) error_code& ec)
{ {
#if ! BOOST_BEAST_ALLOW_DEPRECATED
static_assert(sizeof(RequestDecorator) == 0,
BOOST_BEAST_DEPRECATION_STRING);
#endif
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(detail::is_request_decorator< static_assert(detail::is_request_decorator<
@ -378,6 +345,68 @@ handshake_ex(response_type& res,
host, target, decorator, ec); host, target, decorator, ec);
} }
template<class NextLayer, bool deflateSupported>
template<class RequestDecorator, class HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
HandshakeHandler, void(error_code))
stream<NextLayer, deflateSupported>::
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<next_layer_type>::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<BOOST_ASIO_HANDLER_TYPE(
HandshakeHandler, void(error_code))>(
std::move(init.completion_handler),
impl_, nullptr, host, target,
decorator)();
return init.result.get();
}
template<class NextLayer, bool deflateSupported>
template<class RequestDecorator, class HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
HandshakeHandler, void(error_code))
stream<NextLayer, deflateSupported>::
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<next_layer_type>::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<BOOST_ASIO_HANDLER_TYPE(
HandshakeHandler, void(error_code))>(
std::move(init.completion_handler),
impl_, &res, host, target,
decorator)();
return init.result.get();
}
} // websocket } // websocket
} // beast } // beast
} // boost } // boost

View File

@ -147,6 +147,16 @@ read_size_hint(DynamicBuffer& buffer) const
// //
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// decorator
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer, deflateSupported>::
set_option(decorator opt)
{
impl_->decorator_opt = std::move(opt.d_);
}
// timeout // timeout
template<class NextLayer, bool deflateSupported> template<class NextLayer, bool deflateSupported>

View File

@ -96,7 +96,8 @@ struct stream<NextLayer, deflateSupported>::impl_type
bool timed_out = false; bool timed_out = false;
int idle_counter = 0; int idle_counter = 0;
timeout timeout_opt; detail::decorator decorator_opt; // Decorator for HTTP messages
timeout timeout_opt; // Timeout/idle settings
template<class... Args> template<class... Args>
@ -532,6 +533,7 @@ build_request(
req.set(http::field::sec_websocket_key, key); req.set(http::field::sec_websocket_key, key);
req.set(http::field::sec_websocket_version, "13"); req.set(http::field::sec_websocket_version, "13");
this->build_request_pmd(req); this->build_request_pmd(req);
decorator_opt(req);
decorator(req); decorator(req);
if(! req.count(http::field::user_agent)) if(! req.count(http::field::user_agent))
req.set(http::field::user_agent, req.set(http::field::user_agent,

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,7 @@
#include <boost/beast/core/detail/config.hpp> #include <boost/beast/core/detail/config.hpp>
#include <boost/beast/websocket/role.hpp> #include <boost/beast/websocket/role.hpp>
#include <boost/beast/websocket/detail/decorator.hpp>
#include <chrono> #include <chrono>
namespace boost { namespace boost {
@ -48,6 +49,25 @@ struct stream_base
return (duration::max)(); return (duration::max)();
} }
/** Stream option used to adjust HTTP fields of WebSocket upgrade request and responses.
*/
class decorator
{
detail::decorator d_;
template<class, bool>
friend class stream;
public:
decorator(decorator&&) = default;
template<class Decorator>
decorator(Decorator&& f)
: d_(f)
{
}
};
/** Stream option to control the behavior of websocket timeouts. /** Stream option to control the behavior of websocket timeouts.
Timeout features are available for asynchronous operations only. Timeout features are available for asynchronous operations only.

View File

@ -70,10 +70,13 @@ public:
struct both_t : res_t , req_t struct both_t : res_t , req_t
{ {
using req_t::operator();
using res_t::operator();
}; };
struct big_t : both_t struct big_t : both_t
{ {
using both_t::operator();
char buf[2048]; char buf[2048];
}; };
@ -109,11 +112,15 @@ public:
decorator d1{req_t{}}; decorator d1{req_t{}};
decorator d2{std::move(d1)}; decorator d2{std::move(d1)};
d2(req); d2(req);
decorator d3;
d3 = std::move(d2);
} }
{ {
// this would be leaner with bind_front
decorator d(std::bind( decorator d(std::bind(
&decorator_test::dec_req, this)); &decorator_test::dec_req, this,
std::placeholders::_1));
BEAST_EXPECT(d.is_inline()); BEAST_EXPECT(d.is_inline());
} }
} }
@ -121,6 +128,7 @@ public:
void void
run() override run() override
{ {
log << "sizeof(decorator)==" << sizeof(decorator) << "\n";
testDecorator(); testDecorator();
} }
}; };

View File

@ -28,6 +28,20 @@ public:
net::io_context ioc; net::io_context ioc;
stream<test::stream> ws(ioc); stream<test::stream> ws(ioc);
{
ws.set_option(
stream_base::decorator(
[](request_type&)
{
}));
ws.set_option(
stream_base::decorator(
[](response_type&)
{
}));
}
{ {
ws.set_option( ws.set_option(
stream_base::suggested_settings( stream_base::suggested_settings(

View File

@ -25,6 +25,7 @@ add_executable (tests-doc
core_3_layers.cpp core_3_layers.cpp
http_examples.cpp http_examples.cpp
http_snippets.cpp http_snippets.cpp
websocket_3_handshake.cpp
websocket_snippets.cpp websocket_snippets.cpp
exemplars.cpp exemplars.cpp
) )

View File

@ -23,6 +23,7 @@ alias run-tests :
[ run core_1_refresher.cpp $(TEST_MAIN) ] [ run core_1_refresher.cpp $(TEST_MAIN) ]
[ run core_3_layers.cpp $(TEST_MAIN) ] [ run core_3_layers.cpp $(TEST_MAIN) ]
[ run http_examples.cpp $(TEST_MAIN) ] [ run http_examples.cpp $(TEST_MAIN) ]
[ run websocket_3_handshake.cpp $(TEST_MAIN) ]
; ;
exe fat-tests : exe fat-tests :
@ -31,6 +32,7 @@ exe fat-tests :
core_1_refresher.cpp core_1_refresher.cpp
core_3_layers.cpp core_3_layers.cpp
http_examples.cpp http_examples.cpp
websocket_3_handshake.cpp
; ;
explicit fat-tests ; explicit fat-tests ;

View File

@ -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 <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/beast/_experimental/test/stream.hpp>
#include <boost/beast/core/async_op_base.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/stream_traits.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/async_result.hpp>
#include <cstdlib>
#include <utility>
namespace boost {
namespace beast {
namespace websocket {
namespace {
void
websocket_3_handshake_snippets()
{
#include "snippets.ipp"
stream<net::ip::tcp::socket> 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<http::string_body> 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<net::ip::tcp::socket> 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<net::ip::tcp::socket> 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