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

Setting Sec-WebSocket-Protocol header on response to ws request fails #179

Closed
aboodman opened this issue Feb 18, 2022 · 6 comments
Closed
Labels
bug Something isn't working
Milestone

Comments

@aboodman
Copy link

The standard (and Cloudflare) require that if Sec-WebSocket-Protocol request header is sent in a websocket connection request, the server must acknowledge it by setting a response header with the same value.

If you do this in miniflare, however, the response fails:

  // While Sec-WebSocket-Protocol is just being used as a mechanism for
  // sending `auth` since custom headers are not supported by the browser
  // WebSocket API, the Sec-WebSocket-Protocol semantics must be followed.
  // Send a Sec-WebSocket-Protocol response header with a value
  // matching the Sec-WebSocket-Protocol request header, to indicate
  // support for the protocol, otherwise the client will close the connection.
  //
  // THIS MAKES MINIFLARE SAD
  responseHeaders.set("Sec-WebSocket-Protocol", encodedAuth);

  const response = new Response(body, {
    status,
    statusText,
    webSocket,
    headers: responseHeaders,
  });
  return response;
@mrbbot
Copy link
Contributor

mrbbot commented Mar 4, 2022

Hey! 👋 Thanks for raising this and apologies for the delayed response. I might be misunderstanding this, but I would've thought this code that copys headers from the Response to the WebSocket response added in 2.1.0 would solve this:

webSocketServer.on("headers", (headers, req) => {
const extra = extraHeaders.get(req);
extraHeaders.delete(req);
if (extra) {
for (const [key, value] of extra) {
if (!restrictedWebSocketUpgradeHeaders.includes(key.toLowerCase())) {
headers.push(`${key}: ${value}`);
}
}
}
});

Which version of Miniflare are you using?

@mrbbot mrbbot added the question Further information is requested label Mar 4, 2022
@aboodman
Copy link
Author

aboodman commented Mar 4, 2022 via email

@aboodman
Copy link
Author

aboodman commented Mar 4, 2022

I realize this isn't a reduced test case and I apologize. It's possible we are doing something wrong.

What we found was that in order for a web socket connection to succeed in CF's environment, if the request included the header Sec-WebSocket-Protocol: foo then we needed to manually add the response header Sec-WebSocket-Protocol: foo as well -- otherwise the connection would not suceed.

But that if we added that response header to Miniflare then ... something bad happened. I can't remember what the bad thing was, maybe an error, or maybe the connection just failed in some way.

It actually wasn't me who found this on our team. I'll poke the person and see if he can remember what the failure mode was.

@grgbkr
Copy link

grgbkr commented Mar 4, 2022

Miniflare appears to automatically set the Sec-WebSocket-Protocol response header to match the Sec-WebSocket-Protocol request header (while CF Worker does not). This test appears to assert this behavior

test("upgradingFetch: performs web socket upgrade with Sec-WebSocket-Protocol header", async (t) => {

When we set the Sec-WebSocket-Protocol response header on miniflare, we end up with two headers like:

Sec-WebSocket-Protocol: foo
sec-websocket-protocol: foo

The browser does not like receiving these duplicate headers and fails the connection upgrade.

Chrome won't show the response headers with this type of failure, but you can see them in Firefox:

image

@mrbbot
Copy link
Contributor

mrbbot commented Mar 4, 2022

Ah ok, that makes more sense! Thanks for the extra details. Will get this fixed. 🙂

For now (you might already be doing this), you could skip adding the Sec-WebSocket-Protocol header yourself if globalThis.MINIFLARE is true.

@mrbbot mrbbot added bug Something isn't working and removed question Further information is requested labels Mar 4, 2022
@grgbkr
Copy link

grgbkr commented Mar 7, 2022

Thanks @mrbbot. Thanks for the suggested workaround, that is exactly what we are doing for now.

@mrbbot mrbbot added this to the 2.4.0 milestone Mar 8, 2022
@mrbbot mrbbot modified the milestones: 2.4.0, 2.5.0 Apr 2, 2022
@mrbbot mrbbot modified the milestones: 2.5.0, 2.6.0 May 27, 2022
@mrbbot mrbbot modified the milestones: 2.6.0, 2.7.0 Jul 9, 2022
@mrbbot mrbbot modified the milestones: 2.7.0, 2.8.0 Aug 19, 2022
@mrbbot mrbbot closed this as completed in 34cc73a Sep 3, 2022
mrbbot added a commit that referenced this issue Feb 7, 2023
See #490 for a detailed description of the problem this solves.

Previously, we had `ws`'s automatic `Sec-WebSocket-Protocol` handling
enabled, in addition to adding the header ourselves. This lead to
duplicate sub-protocols in the WebSocket handshake response which is
a protocol error.

Workers require users to include this header themselves in WebSocket
`Response`s, so ignoring this key outright when copying headers would
be incorrect. Instead, we just disable `ws`'s automatic handling.
Funnily enough, we had exactly the same issue in Miniflare 2 too
(#179), and fixed it in 34cc73a. Looks like the fix didn't make it
into Miniflare 3 though. :(

This PR also fixes an issue where `1006` closures were not propagated
correctly. This is an issue in Miniflare 2 too (#465), so we should
back-port this.

Supersedes #490
mrbbot added a commit that referenced this issue Feb 9, 2023
See #490 for a detailed description of the problem this solves.

Previously, we had `ws`'s automatic `Sec-WebSocket-Protocol` handling
enabled, in addition to adding the header ourselves. This lead to
duplicate sub-protocols in the WebSocket handshake response which is
a protocol error.

Workers require users to include this header themselves in WebSocket
`Response`s, so ignoring this key outright when copying headers would
be incorrect. Instead, we just disable `ws`'s automatic handling.
Funnily enough, we had exactly the same issue in Miniflare 2 too
(#179), and fixed it in 34cc73a. Looks like the fix didn't make it
into Miniflare 3 though. :(

This PR also fixes an issue where `1006` closures were not propagated
correctly. This is an issue in Miniflare 2 too (#465), so we should
back-port this.

Supersedes #490
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants