Skip to content

bug: fetch causes http2 server to hang #2311

@vegerot

Description

@vegerot

Bug Description

Reading the body of an http2 POST call causes node:http2 to hang when called from this fetch, but not from Deno's fetch or curl.

Reproducible By

import http2 from "node:http2";
import assert from "node:assert";
import { exec } from "node:child_process";

import { Client, fetch } from "undici";
import devcert from "devcert";

const ssl = await devcert.certificateFor("localhost");
const server = http2.createSecureServer(ssl, async (req, res) => {
  assert.equal(req.method, "POST");
  console.log("beginning to read body...");
  let body = [];
  for await (const chunk of req) {
    console.log({ chunk });
    body.push(chunk);
  }
  let buffer = Buffer.concat(body);
  console.log("end of reading body");
  res.end(buffer);
}).listen(3000);

const METHOD = "fetch";
const h2Client = new Client("https://localhost:3000", {
  connect: {
    rejectUnauthorized: false,
  },
  allowH2: true,
});

server.on("listening", async () => {
  console.log("listening on 3000");
  if (METHOD === "fetch") {
    const response = await fetch("https://localhost:3000", {
      dispatcher: h2Client,
      method: "POST",
      body: "12",
      headers: {
        "content-type": "text/plain",
        "content-length": "2",
      },
    });
    console.log("DONE", { response });
  } else if (METHOD === "curl") {
    const curl =
      `curl --no-progress-meter -k -X POST -H "content-type: text/plain" -H "content-length: 2" -d "12" https://localhost:3000`;
    exec(curl, (err, stdout, stderr) => {
      if (err) {
        console.error("DONE", err);
        return;
      }
      console.log("DONE", stdout);
    });
  } else {
    throw new Error();
  }
});

Play around with the value of METHOD to see what I'm talking about

Expected Behavior

both curl and fetch should receive a response quickly.

Instead, curl gets a response immediately but fetch never returns.

Logs & Screenshots

// with curl
beginning to read body...
{ chunk: <Buffer 31 32> }
end of reading body
DONE 12
// with fetch
beginning to read body...
{ chunk: <Buffer 31 32> }
// never gets passed here

Environment

node v20.3.1
Linux maxs-pc 5.19.0-43-generic #44~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Mon May 22 13:39:36 UTC 2 x86_64 x86_64 x86_64 GNU/Linux

Additional context

I have tested this code with

  • node's builtin fetch ❌
  • undici fetch ❌
  • undici Client#request
  • curl ✅
  • Deno fetch ✅

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions