Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Decorator ★
PrevUpHomeNext

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.31. 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.

stream_base::decorator

Objects of this type are used to hold a decorator to be set on the stream using 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:

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

Undefined behavior results if the decorator modifies the fields specific to perform the WebSocket Upgrade, such as the Upgrade or Connection fields.


PrevUpHomeNext