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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions