Skip to content

http2: connection hangs forever ♾️ when blob stream is piped 🚰 through client to server #48685

@bricss

Description

@bricss

Version

v20.4.0

Platform

Microsoft Windows NT 10.0.22621.0 x64

Subsystem

No response

What steps will reproduce the bug?

Run following scripts:

  1. Generate cert & key 🔐
openssl req -days 365 -keyout localhost.key -newkey ec -nodes -pkeyopt ec_paramgen_curve:prime256v1 -subj //SKIP=1/CN=localhost -out localhost.cert -x509

  1. Save repro.mjs in the same directory as the cert and key, run node repro.mjs
import { once } from 'node:events';
import { readFileSync } from 'node:fs';
import http2 from 'node:http2';
import { Readable } from 'node:stream';

const {
  HTTP2_HEADER_AUTHORITY,
  HTTP2_HEADER_METHOD,
  HTTP2_HEADER_PATH,
  HTTP2_HEADER_SCHEME,
  HTTP2_METHOD_POST,
} = http2.constants;

const baseH2URL = new URL('https://localhost:3443');

const cwd = process.cwd();
const cert = readFileSync(`${ cwd }/localhost.cert`);
const key = readFileSync(`${ cwd }/localhost.key`);

const h2server = http2.createSecureServer({ cert, key }, (req, res) => {
  console.log(req.url);
  req.pipe(res);
});

await once(h2server.listen(baseH2URL.port), 'listening');

console.log('h2::server listening on', h2server.address());

const client = http2.connect(baseH2URL, { rejectUnauthorized: false });

client.on('error', (err) => console.error(err));

const req = client.request({
  [HTTP2_HEADER_AUTHORITY]: baseH2URL.host,
  [HTTP2_HEADER_METHOD]: HTTP2_METHOD_POST,
  [HTTP2_HEADER_PATH]: `${ baseH2URL.pathname }${ baseH2URL.search }`,
  [HTTP2_HEADER_SCHEME]: baseH2URL.protocol.replace(/\p{Punctuation}/gu, ''),
});

req.on('end', () => console.log('response::ended')); // never get called <---|
req.on('error', (err) => console.error(err));
req.on('data', (chunk) => console.info(chunk));
req.on('response', async (headers) => {
  console.log(`response::status ${ headers[':status'] }`);
  const body = [];

  for await (const chunk of req) {
    console.info('before::chunk');
    body.push(chunk);
    console.log(chunk);
    console.info('after::chunk');
  }

  console.log(Buffer.concat(body).toString()); // not reachable <---|
});

const body = new Blob(['bits']);

Readable.from(body.stream()).pipe(req);
// Readable.fromWeb(body.stream()).pipe(req); // issue persists with both methods <---|

console.log('h2::brrrr!');

  1. Check logs 🪵 and get surprised 😯

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

Always reproduces 😮‍💨

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

The connection should close 📪 after the data transfer is complete 🏁

What do you see instead?

Connection hangs for an infinite ♾️ amount of time

Additional information

The issue only affects the node:http2 module, and does not appear in the node:http module
Initially identified in -> rekwest package 📦 tests 🧪

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions