socket.io icon indicating copy to clipboard operation
socket.io copied to clipboard

HTTP/2 Web Socket in Chrome not working

Open ackava opened this issue 1 year ago • 2 comments

Describe the bug Creating HTTP/2 server mentioned in the example works well with Firefox because Firefox still uses HTTP/1.1 to connect to web server. However, chrome has implemented CONNECT method of HTTP/2, and thus socket.io no longer connects with HTTP/2 server.

Chrome 120 still connects Web Socket over HTTP 1.1, but this stops working from 121 onwards.

To Reproduce

  1. Juse use simple socket.io sample to create HTTP/2 server with SSL certificate.
  2. Try to connect to socket.io server from Google Chorme.

Expected behavior It should connect to socket.io server, but it does not.

Platform:

  • OS: Desktop
  • Browser: Google Chorme (121.0.6167.161) +

Additional context For HTTP/1.1 , http2Server emits upgrade event, which engine.io handles correctly. For HTTP/2, http2Server does not emit upgrade event if web socket client sends HTTP-METHOD='CONNECT'.

Work Around/Recommendation

httpServer.prependListener("stream", (stream, headers) => {

    // we need to look for CONNECT
    if(headers[":method"] !== "CONNECT") {
        return;
    }

    try {

      // this keeps socket alive...
      stream.setTimeout(0);
      (stream as any).setKeepAlive?.(true, 0);
      (stream as any).setNoDelay = function() {
          // this will keep the stream open
      };


      const websocket = new WebSocket(null, void 0, {
          headers
      });
      websocket.setSocket(stream, Buffer.alloc(0), {
          maxPayload: 104857600,
          skipUTF8Validation: false,
      });
      const path = headers[":path"];
      const url = new URL(path, "http://a");
      const _query = {};
      for (const [key, value] of url.searchParams.entries()) {
          _query[key] = value;
      }
      // fake build request
      const req = {
          url: path,
          headers,
          websocket,
          _query
      };
      stream.respond({
          ":status": 200
      });
     // for some reason this is not working !!
      this.handshake("websocket",req,() => { try { stream.end(); } catch {} }).catch(console.error);
  } catch (error) {
      console.error(error);
  }
});

ackava avatar Feb 12 '24 07:02 ackava

I could indeed reproduce the issue, thanks for raising this.

Another possible workaround is to start the HTTP/2 server with allowHTTP1: true:

const server = createSecureServer(
  {
    key: readFileSync("./key.pem"),
    cert: readFileSync("./cert.pem"),
    allowHTTP1: true,
  }
);
const io = new Server(server);

Related: https://github.com/websockets/ws/issues/1458

darrachequesne avatar Feb 26 '24 22:02 darrachequesne

@darrachequesne allowHTTP1 no longer works with chrome version 121 onwards, it used to work till 120.

ackava avatar Feb 27 '24 03:02 ackava