Document websocket sub-protocols

This commit is contained in:
Richard Hodges
2020-03-25 13:13:26 +01:00
parent 6b8b54f761
commit fe9951c980
2 changed files with 111 additions and 0 deletions

View File

@@ -150,6 +150,27 @@ stream:
[code_websocket_2_5]
[heading Subprotocols]
The WebSocket protocol understands the concept of subprotocols. If the client
is requesting one of a set of subprotocols it will set the header
[@https://tools.ietf.org/html/rfc6455#section-11.3.4 Sec-WebSocket-Protocol]
in the initial WebSocket Upgrade HTTP request. It is up to the server to
parse the header and select one of the protocols to accept. The server
indicates the selected protocol by setting the
[@https://tools.ietf.org/html/rfc6455#section-11.3.4 Sec-WebSocket-Protocol]
header in the accept header.
This is accomplished with a
[link beast.ref.boost__beast__websocket__stream_base__decorator `decorator`].
The code that follows demonstrates how a server reads an HTTP request, identifies it
as a WebSocket Upgrade, and then checks for a preferred matching subprotocol before
performing the WebSocket handshake:
[code_websocket_2_6]
[/-----------------------------------------------------------------------------]
[endsect]

View File

@@ -132,6 +132,96 @@ snippets()
//]
}
{
//[code_websocket_2_6
// a function to select the most preferred protocol from a comma-separated list
auto select_protocol = [](string_view offered_tokens) -> std::string
{
// tokenize the Sec-Websocket-Protocol header offered by the client
http::token_list offered( offered_tokens );
// an array of protocols supported by this server
// in descending order of preference
static const std::array<string_view, 3>
supported = {
"v3.my.chat",
"v2.my.chat",
"v1.my.chat"
};
std::string result;
for (auto proto : supported)
{
auto iter = std::find(offered.begin(), offered.end(), proto);
if (iter != offered.end())
{
// we found a supported protocol in the list offered by the client
result.assign(proto.begin(), proto.end());
break;
}
}
return result;
};
// 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 it's a WebSocket upgrade request
if(websocket::is_upgrade(req))
{
// we store the selected protocol in a std::string here because
// we intend to capture it in the decorator's lambda below
std::string protocol =
select_protocol(
req[http::field::sec_websocket_protocol]);
if (protocol.empty())
{
// none of our supported protocols were offered
http::response<http::string_body> res;
res.result(http::status::bad_request);
res.body() = "No valid sub-protocol was offered."
" This server implements"
" v3.my.chat,"
" v2.my.chat"
" and v1.my.chat";
http::write(sock, res);
}
else
{
// Construct the stream, transferring ownership of the socket
stream<tcp_stream> ws(std::move(sock));
ws.set_option(
stream_base::decorator(
[protocol](http::response_header<> &hdr) {
hdr.set(
http::field::sec_websocket_protocol,
protocol);
}));
// Accept the upgrade request
ws.accept(req);
}
}
else
{
// Its not a WebSocket upgrade, so
// handle it like a normal HTTP request.
}
//]
}
}
struct websocket_2_test