...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
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 1.32. WebSocket Decorator Interface
Name |
Description |
---|---|
This is the type of the object passed to the decorator to represent HTTP Upgrade requests. |
|
This is the type of the object passed to the decorator to represent HTTP Upgrade response. |
|
Objects of this type are used to hold a decorator to be set on
the stream using |
|
This function is used to set a |
This declares a normal function which decorates outgoing HTTP requests:
void set_user_agent(request_type& req) { // Set the User-Agent on the request req.set(http::field::user_agent, "My User Agent"); }
When using a decorator, 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:
stream<tcp_stream> ws(ioc); // The function `set_user_agent` will be invoked with // every upgrade request before it is sent by the stream. ws.set_option(stream_base::decorator(&set_user_agent));
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:
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{}));
A lambda may be used in place of a named function object:
ws.set_option(stream_base::decorator( [](response_type& res) { // Set the Server field on the response res.set(http::field::user_agent, "My Server"); }));
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:
struct set_message_fields { 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(set_message_fields{}));
The implementation takes ownership by decay-copy of the invocable object used as the decorator. Move-only types are possible:
struct set_auth { std::unique_ptr<std::string> key; void operator()(request_type& req) { // Set the authorization field req.set(http::field::authorization, *key); } }; // The stream takes ownership of the decorator object ws.set_option(stream_base::decorator( set_auth{boost::make_unique<std::string>("Basic QWxhZGRpbjpPcGVuU2VzYW1l")}));
Important | |
---|---|
Undefined behavior results if the decorator modifies the fields specific to perform the WebSocket Upgrade, such as the Upgrade or Connection fields. |