Is it possible to establish an HTTP/2 connection over HTTP/1.1 with chrome as client?

327 views
Skip to first unread message

guest271314

unread,
Mar 30, 2025, 8:55:52 PM3/30/25
to Chromium-dev
I'm trying to establish an HTTP/2 connection using `fetch()` in the `console` with `duplex` property set to `"half"`, and `ReadableStream` set as `body`. The URL is http: 0.0.0.0:44818. The server is `TCPServerSocket` in an Isolated Web App. What I'm noticing is that even with `duplex` set to `"half"` Chromium Developer Build (a snapshot from today), is sending HTTP/1.1 header. Looking at this https://github.com/chromium/chromium/blob/e32f74f5a5c0b12209202ad09ba684e469b746ea/net/server/http_server_unittest.cc#L486C1-L497C2 it appears it's not possible to upgrade from HTTP/1.1 to HTTP/2 when chrome is the client?

```
// Tests that |HttpServer::HandleReadResult| will ignore Upgrade header if value
// is not WebSocket.
TEST_F(HttpServerTest, UpgradeIgnored) {
  TestHttpClient client;
  CreateConnection(&client);
  client.Send(
      "GET /test HTTP/1.1\r\n"
      "Upgrade: h2c\r\n"
      "Connection: SomethingElse, Upgrade\r\n"
      "\r\n");
  WaitForRequest();
}
```

My use case is completely local. One stream, one client. Since WHATWG Fetch #1254 and chrome not fulfilling fetch() until the complete body is read (writer closed) I'm trying to implement WebTransport using TCP over HTTP - without necessarily implementing ALPN and TLS in the browser.

 I've already done HTTP and WebSocket connections using TCPServerSocket. Now I'm trying to do WebTransport over HTTP/2 in the browser. Because it's there to be done and hasn't been done, yet, that I'm aware of.

Is there any way to establish an full-duplex streaming connection over HTTP/2 without ALPN and TLS with chrome as client?

Ryan Hamilton

unread,
Mar 30, 2025, 10:15:35 PM3/30/25
to guest...@gmail.com, Chromium-dev
As far as I know, it is not possible to create an HTTP/2 connection over an HTTP/1 connection in Chrome.

--
--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
http://groups.google.com/a/chromium.org/group/chromium-dev
---
You received this message because you are subscribed to the Google Groups "Chromium-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-dev...@chromium.org.
To view this discussion visit https://groups.google.com/a/chromium.org/d/msgid/chromium-dev/3f446b4a-cfb3-406e-896b-79a42271065en%40chromium.org.

guest271314

unread,
Apr 5, 2025, 2:58:59 PM4/5/25
to Chromium-dev, Ryan Hamilton, Chromium-dev, guest...@gmail.com
Thanks. The sole exception being between a WindowClient and a ServiceWorker, which technically uses Mojo, correct?

Ryan Hamilton

unread,
Apr 7, 2025, 12:55:52 AM4/7/25
to guest271314, Chromium-dev
I'm not familiar with how that would create an HTTP/2 connection over an HTTP/1 connection, no.

guest271314

unread,
Apr 7, 2025, 1:21:07 AM4/7/25
to Chromium-dev, Ryan Hamilton, Chromium-dev, guest271314
plnkr.co using a ServiceWorker and WindowClient outside of an extension https://plnkr.co/plunk/2PQK21kZTZvZ2oVi.A ReadableStream is posted to the server using fetch() with duplex set to half. Thereafter it's possible to keep that single connection established essentially indefinitely, read and write, due to the way ServiceWorker folks want onfetch and respondWith() to work - using Mojo. Another example https://github.com/guest271314/persistent-serviceworker/tree/main/readablestream-fetch-respondwith.

In an local extension I'm using, something like this, where I open an iframe and use fetch() in the extension so Ican full-duplexstreamback to the arbitrary Web site - after a message from the Native Messaging host - using Transferable Streams

In the iframe. Notice that "r" Promise is fulfilled. It will not be fulfilled in any other case that I am aware of in any browser. Node.js, Deno, and Bun implementations of fetch all full-duplexstream by default. Not Chromium WHATWG Fetch implementation - outside of this case that I am aware of - due to WHATWG Fetch #1254. My ultimate goal in hacking ServiceWorker, among other Web API is to ultimately just full-duplex stream without networking at all, to and from a Native Messaging host, to avoid the current IPC and JSON-like implementation of Native Messaging.

    const r = await fetch("./?stream", {
          method: "POST",
          headers: { "Content-Type": "text/plain" },
          body: stream,
          duplex: "half",
        });
        console.log(r);
        //  .then((r) =>
        await r.body.pipeThrough(new TextDecoderStream()).pipeTo(
          new WritableStream({
            write(value) {
              output.textContent = value;
              console.log(input.value.length, output.value.length);
            },
            close() {
              console.log("Stream closed");
            },
            abort(reason) {
              console.log({ reason });
            },
          }),
        )

In the ServiceWorker

addEventListener("fetch", async (e) => {
  console.log(e.request.url, e.clientId, [...e.request.headers]);
  if (e.request.url.includes("stream")) {
    e.respondWith(
      new Response(
        e.request.body
          .pipeThrough(new TextDecoderStream())
          .pipeThrough(
            new TransformStream({
              async start() {
                getClientState(e.clientId);

                if (globalThis.port === null) {
                  readable = new ReadableStream({
                    start: (_) => {
                      return controller = _;
                    },
                    cancel(reason) {
                      console.log(reason);
                    },
                  });
                  reader = readable.getReader();
                  port = chrome.runtime.connectNative(
                    chrome.runtime.getManifest().short_name,
                  );
                  port.onMessage.addListener((message) => {
                    controller.enqueue(encoder.encode(message));
                  });
                  port.onDisconnect.addListener((e) => {
                    console.log(e);
                    if (chrome.runtime.lastError) {
                      console.log(chrome.runtime.lastError);
                    }
                    controller.close();
                    port = readable = controller = null;
                  });
                }
              },
              async transform(value, c) {
                console.log(value);
                port.postMessage(value);
                const { value: message, done } = await reader.read();
                console.log(message, done);
                c.enqueue(message);
              },
              flush() {
                console.log("flush");
              },
            }),
          ),
      ),
    );
  }
});

guest271314

unread,
Apr 7, 2025, 1:24:49 AM4/7/25
to Chromium-dev, guest271314, Ryan Hamilton, Chromium-dev

guest271314

unread,
Apr 7, 2025, 1:25:58 AM4/7/25
to Chromium-dev, Ryan Hamilton, Chromium-dev

guest271314

unread,
Apr 7, 2025, 1:34:01 AM4/7/25
to Chromium-dev, Chromium-dev
I don't think at that point in the MOJO IPC HTTP/1.1 to HTTP/2 Upgrade is happening. But the effect is that of a full-duplex stream using WHATWG Fetch on CHromium-based browsers. A good thing. For me. To use in lieu of creating an HTTP/3 WebTransport server in the browser over TCPServerSocket and UDPSocket in an Isolated Web App some kind of way. I'm still trying to get TLS working. Kind of there but not yet. Far simpler just to piggy-back on WindowClient and ServiceWorker, onfetch, respondWith() and Transferable Streams. Would be even simpler if it was possible to use WHATWG Streams with Native Messaging locally, without all the certificate hoops and HTTP/3 frames to jump through.

guest271314

unread,
Apr 7, 2025, 1:59:40 AM4/7/25
to Chromium-dev, guest271314
Compare echoing 1 MB of data over externally_connectable <=> runtime.connectNative(), port.postMessage(data), port.onMessage.addListener() to echoing 7 MB over HTTP/3 to a local WebTrasnport server. It's not even close. The HTTP/3 7 MB round trip using WHATWG Streams will complete before the JSON-like over IPC of extension messaging. The JSON still works, I've used it for streaming audio. However, one has to manage the conversion from plain Array to Uint8Array for media processing. That's my interest in these new fangled HTTP/3 methods - local usage without the certificate thingamajigs attached to the process. Thanks. 
Reply all
Reply to author
Forward
0 new messages