From 6a56c687280a4608823c58f5874a2682556ddde3 Mon Sep 17 00:00:00 2001 From: Darshan Sen Date: Sun, 1 Jun 2025 18:38:20 +0530 Subject: [PATCH] http2: add diagnostics channel 'http2.server.stream.error' Signed-off-by: Darshan Sen PR-URL: https://github.com/nodejs/node/pull/58512 Reviewed-By: Luigi Pinca Reviewed-By: Matteo Collina --- doc/api/diagnostics_channel.md | 7 +++ lib/internal/http2/core.js | 22 +++++--- ...stics-channel-http2-server-stream-error.js | 53 +++++++++++++++++++ 3 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 test/parallel/test-diagnostics-channel-http2-server-stream-error.js diff --git a/doc/api/diagnostics_channel.md b/doc/api/diagnostics_channel.md index e25848ad323..08f2565b2cf 100644 --- a/doc/api/diagnostics_channel.md +++ b/doc/api/diagnostics_channel.md @@ -1252,6 +1252,13 @@ Emitted when a stream is created on the server. Emitted when a stream is started on the server. +`http2.server.stream.error` + +* `stream` {ServerHttp2Stream} +* `error` {Error} + +Emitted when an error occurs during the processing of a stream on the server. + #### Modules > Stability: 1 - Experimental diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index ca657da60d2..8c52c4c60c6 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -197,6 +197,7 @@ const onClientStreamFinishChannel = dc.channel('http2.client.stream.finish'); const onClientStreamCloseChannel = dc.channel('http2.client.stream.close'); const onServerStreamCreatedChannel = dc.channel('http2.server.stream.created'); const onServerStreamStartChannel = dc.channel('http2.server.stream.start'); +const onServerStreamErrorChannel = dc.channel('http2.server.stream.error'); let debug = require('internal/util/debuglog').debuglog('http2', (fn) => { debug = fn; @@ -2455,13 +2456,20 @@ class Http2Stream extends Duplex { setImmediate(() => { session[kMaybeDestroy](); }); - if (err && - session[kType] === NGHTTP2_SESSION_CLIENT && - onClientStreamErrorChannel.hasSubscribers) { - onClientStreamErrorChannel.publish({ - stream: this, - error: err, - }); + if (err) { + if (session[kType] === NGHTTP2_SESSION_CLIENT) { + if (onClientStreamErrorChannel.hasSubscribers) { + onClientStreamErrorChannel.publish({ + stream: this, + error: err, + }); + } + } else if (onServerStreamErrorChannel.hasSubscribers) { + onServerStreamErrorChannel.publish({ + stream: this, + error: err, + }); + } } callback(err); } diff --git a/test/parallel/test-diagnostics-channel-http2-server-stream-error.js b/test/parallel/test-diagnostics-channel-http2-server-stream-error.js new file mode 100644 index 00000000000..ea4f49e200c --- /dev/null +++ b/test/parallel/test-diagnostics-channel-http2-server-stream-error.js @@ -0,0 +1,53 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +// This test ensures that the built-in HTTP/2 diagnostics channels are reporting +// the diagnostics messages for the 'http2.server.stream.error' channel when +// an error occurs during the processing of a ServerHttp2Stream. + +const assert = require('assert'); +const dc = require('diagnostics_channel'); +const http2 = require('http2'); +const { Duplex } = require('stream'); + +let expectedError = null; + +dc.subscribe('http2.server.stream.error', common.mustCall(({ stream, error }) => { + // Since ServerHttp2Stream is not exported from any module, this just checks + // if the stream is an instance of Duplex and the constructor name is + // 'ServerHttp2Stream'. + assert.ok(stream instanceof Duplex); + assert.strictEqual(stream.constructor.name, 'ServerHttp2Stream'); + assert.strictEqual(stream.closed, true); + assert.strictEqual(stream.destroyed, true); + + assert.ok(error); + assert.strictEqual(error, expectedError); +})); + +const server = http2.createServer(); + +server.on('stream', common.mustCall((stream) => { + stream.on('error', common.mustCall((err) => { + assert.strictEqual(err, expectedError); + })); + + expectedError = new Error('HTTP/2 server stream error'); + stream.destroy(expectedError); +})); + +server.listen(0, common.mustCall(() => { + const port = server.address().port; + const client = http2.connect(`http://localhost:${port}`); + + const stream = client.request({}); + stream.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_HTTP2_STREAM_ERROR'); + + client.close(); + server.close(); + })); +}));