Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

grpcwebproxy websocket implementation fails on multiple subprotocols #1111

Closed
niloc132 opened this issue Jul 8, 2022 · 2 comments · Fixed by #1112
Closed

grpcwebproxy websocket implementation fails on multiple subprotocols #1111

niloc132 opened this issue Jul 8, 2022 · 2 comments · Fixed by #1112

Comments

@niloc132
Copy link
Contributor

niloc132 commented Jul 8, 2022

Versions of relevant software used
At least 0.12 through latest

What happened
Websockets support a mechanism known as "subprotocols" where a connecting client can offer zero to many options, and the server can pick from that list the best suited. The server then should communicate that negotiated protocol back to the client so both sides know which protocol is in use for the duration of the connection as the websocket finishes opening, and both client and server then can proceed sending messages using that protocol.

The websocket spec calls for the Sec-Websocket-Protocol header(s) to be defined by the client (multiple headers can be set, each header entry should be comma separated). The server then must select one of these values, or respond that it does not accept these options. See https://datatracker.ietf.org/doc/html/rfc6455#section-4.1, See https://datatracker.ietf.org/doc/html/rfc6455#section-4.2 for more details.

The use case here is to implement an alternative but compatible websocket client/server (see #1081, #198) that is able to share a single websocket for all active streams. The proposed client implementation would support both grpc-websockets and grpc-websockets-multiplex. Server negotiation should inform the client which implementation will be used.

Presently, grpcwebproxy's websocket implementation when offered multiple protocols will simply fail the connection, rather than pick the acceptable option grpc-websockets.

What you expected to happen
If the client websocket contains grpc-websockets as an option, it is expected that the server will respond by opening the websocket selecting that as the negotiated subprotocol. Note that the nhooyr.io/websocket library correctly handles this, but only the IsGrpcWebSocketRequest check incorrectly fails.

How to reproduce it (as minimally and precisely as possible):
The simplest way is to modify client/grpc-web/src/transports/websocket/websocket.ts to also offer any other protocol along with grpc-websockets, under the assumption that any server which can use that protocol will respond back with that same string. For example:

    start: (metadata: Metadata) => {
      ws = new WebSocket(webSocketAddress, ["grpc-websockets"]);
      ws.binaryType = "arraybuffer";

Anything else we need to know
I'll offer a pull request to fix this. The project github.com/deephaven/deephaven-core has an in-process grpc-web and grpc-websockets proxy, and is seeking to add a grpc-websockets-multiplex proxy for faster non-tls stream initiation (and to avoid exhausting the browser's allowed websocket count). If there is interest, we can provide a ts client here, and see about an update to the Go proxy provided by improbable-eng/grpc-web.

niloc132 added a commit to niloc132/improbable-eng-grpc-web that referenced this issue Jul 8, 2022
Correctly reads all Sec-Websocket-Protocol headers and reads all values
in those headers, and only succeeds if the expected subprotcol value of
"grpc-websockets" is present. This implementation roughly mirrors what
the nhooyr.io/websocket Accept() method does, with a few
simplifications.

Fixes improbable-eng#1111
@johanbrandhorst
Copy link
Contributor

Hi, thanks for your issue! This is great, thanks for all the explanation and research. Please note that this repo is in maintenance mode, and we recommend users migrate to the official grpc-web client: https://github.com/grpc/grpc-web. That said, I'm happy to review PRs that fix bugs in the existing code.

@niloc132
Copy link
Contributor Author

niloc132 commented Jul 8, 2022

Thanks for the heads up - we've found the upstream grpc/grpc-web to be lacking with no binary streaming support, and in the absence of tls browsers cannot use h2, so there is a very low limit on the number of concurrent streams (not unlike with websockets). We are trying to reduce our dependence (but not our support for) the improbably-eng/grpc-web grpcwebproxy, but if we should migrate away entirely, perhaps we will need to manage our own implementation entirely.

johanbrandhorst added a commit that referenced this issue Jul 16, 2022
…ls (#1112)

* Server websocket implementation should allow, ignore other subprotocols

Correctly reads all Sec-Websocket-Protocol headers and reads all values
in those headers, and only succeeds if the expected subprotcol value of
"grpc-websockets" is present. This implementation roughly mirrors what
the nhooyr.io/websocket Accept() method does, with a few
simplifications.

Fixes #1111

* Manually update docs, correct indentation

* Review feedback

Canonicalize the header key before read, normalizing the possible values.

Co-authored-by: Johan Brandhorst-Satzkorn <[email protected]>

Co-authored-by: Johan Brandhorst-Satzkorn <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants