Skip to content

Network request fails after event loop is blocked for a few seconds (regression in v20) #54293

@nikwen

Description

@nikwen

Version

v20.16.0

Platform

Darwin MacBook.my.network 23.6.0 Darwin Kernel Version 23.6.0: Fri Jul  5 17:54:52 PDT 2024; root:xnu-10063.141.1~2/RELEASE_ARM64_T8103 arm64

Subsystem

http

What steps will reproduce the bug?

  1. Block the event loop for 10 seconds via a long-running operation
  2. Make a network request via http.request()

Code example:

const http = require("node:http");

const makePostRequest = () =>
  new Promise((resolve, reject) => {
    const onResponse = (res) => {
      let data = "";

      res.on("data", (chunk) => {
        data += chunk;
      });

      res.on("end", () => resolve(data));
    };

    const req = http.request(
      "http://127.0.0.1:3000/",
      { method: "POST" },
      onResponse,
    );

    req.on("error", (e) => reject(e));

    req.end();
  });

const makeRequests = async () => {
  await makePostRequest();

  console.log("First request worked");

  const timestamp = Date.now();

  // Block the event loop for 10 seconds
  while (Date.now() < timestamp + 10000);

  // Enable the following line and it will work:
  // await new Promise((resolve) => setTimeout(resolve, 10));

  await makePostRequest();

  console.log("It crashes before it gets here");
};

const server = http.createServer((req, res) => {
  console.log("Request received");
  res.statusCode = 200;
  res.setHeader("Content-Type", "text/plain");
  res.end("Hello world!\n");
});

server.listen(3000, "127.0.0.1", () => {
  console.log("Server up");

  makeRequests();
});

How often does it reproduce? Is there a required condition?

Reproduces always.

What is the expected behavior? Why is that the expected behavior?

The network request works.

What do you see instead?

  1. The network request fails with ECONNRESET.
  2. The network request does not reach the server.

Log from running the code above:

Server up
Request received
First request worked
node:internal/process/promises:391
    triggerUncaughtException(err, true /* fromPromise */);
    ^

Error: read ECONNRESET
    at TCP.onStreamRead (node:internal/stream_base_commons:218:20) {
  errno: -54,
  code: 'ECONNRESET',
  syscall: 'read'
}

Node.js v20.16.0

Process finished with exit code 1

Additional information

This is a regression. The code works on Node.js 18. It fails on Node.js 20.

If we wait for 10ms before running the network request, it works.

Metadata

Metadata

Assignees

No one assigned

    Labels

    httpIssues or PRs related to the http subsystem.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions