...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
A stream
automatically handles receiving
and processing the HTTP response to the handshake request. The call to handshake
is successful if a HTTP response is received with the 101
Switching Protocols status code. On failure, an error
is returned or an exception is thrown. Depending on the keep alive setting,
the connection may remain open for a subsequent handshake attempt.
Performing a handshake for an incoming websocket upgrade request operates similarly. If the handshake fails, an error is returned or exception thrown:
ws.accept();
Successful WebSocket Upgrade responses generated by the implementation will typically look like this:
Table 1.26. Decorated WebSocket Upgrade HTTP Request
Serialized Octets |
Description |
---|---|
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Server: Beast/40 |
The Sec-WebSocket-Accept field value is generated from the request in a fashion specified by the WebSocket protocol. |
If the caller wishes to add or modify fields, the member functions accept_ex
and 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.accept_ex( [](response_type& m) { m.insert(http::field::server, "MyServer"); });
The HTTP Upgrade response produced by the previous call looks like this:
Table 1.27. 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. |
When implementing an HTTP server that also supports WebSocket, the server
usually reads the HTTP request from the client. To detect when the incoming
HTTP request is a WebSocket Upgrade request, the function is_upgrade
may be used.
Once the caller determines that the HTTP request is a WebSocket Upgrade,
additional overloads of accept
, accept_ex
, async_accept
, and async_accept_ex
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 stream:
// Buffer 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<boost::asio::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. }
Sometimes a server implementation wishes to read octets on the stream in
order to route the incoming request. For example, a server may read 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 accept
, accept_ex
, async_accept
, and async_accept_ex
are provided which
receive the additional buffered octets and consume them as part of the handshake.
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:
// Read into our buffer until we reach the end of the HTTP request. // No parsing takes place here, we are just accumulating data. boost::asio::streambuf buffer; boost::asio::read_until(sock, buffer, "\r\n\r\n"); // Now accept the connection, using the buffered data. ws.accept(buffer.data());
The implementation uses a fixed-size storage area to hold buffers passed
using these functions. If an application is reaching the limit of the internal
buffer size, then the websocket stream may be instantiated with the next
layer type of buffered_read_stream
to wrap the
underlying stream. The buffered handshake data may be first placed into the
buffered read stream, which uses a dynamically sized buffer.