skaarj1989/mWebSockets

Feature request: subprotocol negotiation API

fkromer opened this issue · 7 comments

I've not found something related to subprotocol negotiation in the sources. Is mWebSockets supporting it already? In case not you could of course expect either contribution or sponsoring (if this is an option for you).

It's just a proposal draft of what I might do.

Client: add additional parameter in open(), an array of subprotocols:

WebSocketClient client;
const char *supportedProtocols[] = {"soap", "wamp", nullptr};
client.open("...", 3000, "/", supportedProtocols);
// The client will send:
// Sec-WebSocket-Protocol: soap, wamp

Server: add callback

WebSocketServer server(3000);
server.setProtocolHandler([](const char **protocols) {
  // In this callback you are supposed to negotiate the subprotocol and return its name
  // (or nullptr on fail)

  // Iterate the list of protocols sent by the client
  for (int i = 0;; ++i) {
    const auto &protocol = protocols[i];
    if (protocol == nullptr) break;
  
    // The server will send: Sec-WebSocket-Protocol: soap
    if (strcmp(protocol, "soap") == 0) return "soap";
  }
  return nullptr; // supported protocol not found, terminates connection
});

I've not looked into implementations of other libs yet. Nevertheless... your idea sounds reasonable.

Without investing too much time I was able to find the relevant sources in Pythons websockets. The utility function verifying the subprotocols is /src/websockets/client.py#L243-L274. It checks the subprotocols in the response header. The verification is called in the client code in src/websockets/client.py#L169 as well as in the server code in src/websockets/server.py#L229. In both cases (client as well as server) the function is triggered after a handshake request has been received.

In case of Pythons websockets the subprotocols may be defined API-wise via the client connection class instance constructor as well as the server class instance constructur.

Initial implementation for subprotocol negotiation has been uploaded to master branch.
Instead of array of values I decided to use comma separated values.

Client side:

WebSocketClient client;
client.open("192.168.46.31", 3000, "/", "foo,bar,baz");
const auto protocol = client.getProtocol();
if (protocol) {
  // do something about it ...
}

Additionally, you have an option to query protocol straight out of a WebSocket via getProtocol() (might return nullptr)

Server side:

WebSocketServer server{3000};
wss.begin(nullptr, [](const char *protocols) {
  // Iterate csv with strtok or whatever you like ...
  // ...

  return "foo";
});

wss.onConnection([](WebSocket &ws) {
  const auto protocol = ws.getProtocol();
  if (protocol) {
    // whatever ...
  }
  // connect callbacks ...
});

Wow, great. Thanks a lot.

stale commented

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.