Commit 1f26576a3f1 for nodejs
commit 1f26576a3f13b8cc0d09b443efcfc071c667b35f
Author: Lazizbek Ergashev <lazerg2@gmail.com>
Date: Tue Sep 29 15:27:53 2026 +0500
http2: submit RST_STREAM before emitting 'aborted'
Signed-off-by: lazerg <lazerg2@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66314
Fixes: https://github.com/nodejs/node/issues/66306
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Tim Perry <pimterry@gmail.com>
diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js
index 1774808da13..9f14c5f2aef 100644
--- a/lib/internal/http2/core.js
+++ b/lib/internal/http2/core.js
@@ -2153,14 +2153,7 @@ function closeStream(stream, code, rstStreamStatus = kSubmitRstStream) {
stream.setTimeout(0);
stream.removeAllListeners('timeout');
- // Emit 'aborted' if the user hadn't ended the writable yet. This is
- // unusual and doesn't match 'error' but is deprecated & preserved for
- // backward compat (DEP0207).
const { ending } = stream._writableState;
- if (!ending && !stream.aborted) {
- state.flags |= STREAM_FLAGS_ABORTED;
- stream.emit('aborted');
- }
if (rstStreamStatus !== kNoRstStream) {
// If the user already called .end() and writes are still draining,
@@ -2174,6 +2167,14 @@ function closeStream(stream, code, rstStreamStatus = kSubmitRstStream) {
}
}
+ // Emit 'aborted' if the user hadn't ended the writable yet. This is
+ // unusual and doesn't match 'error' but is deprecated & preserved for
+ // backward compat (DEP0207).
+ if (!ending && !stream.aborted) {
+ state.flags |= STREAM_FLAGS_ABORTED;
+ stream.emit('aborted');
+ }
+
if (type === NGHTTP2_SESSION_CLIENT) {
if (onClientStreamCloseChannel.hasSubscribers) {
onClientStreamCloseChannel.publish({ stream });
diff --git a/test/parallel/test-http2-close-aborted-destroy.js b/test/parallel/test-http2-close-aborted-destroy.js
new file mode 100644
index 00000000000..61abafed194
--- /dev/null
+++ b/test/parallel/test-http2-close-aborted-destroy.js
@@ -0,0 +1,32 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasCrypto)
+ common.skip('missing crypto');
+const assert = require('assert');
+const http2 = require('http2');
+const { NGHTTP2_CANCEL } = http2.constants;
+
+const server = http2.createServer();
+
+server.on('stream', common.mustCall((stream) => {
+ stream.respond();
+ stream.on('error', common.expectsError({
+ code: 'ERR_HTTP2_STREAM_ABORTED',
+ }));
+ stream.on('close', common.mustCall(() => {
+ assert.strictEqual(stream.rstCode, NGHTTP2_CANCEL);
+ server.close();
+ }));
+}));
+
+server.listen(0, common.mustCall(() => {
+ const client = http2.connect(`http://localhost:${server.address().port}`);
+ const req = client.request({ ':method': 'POST' });
+
+ req.on('aborted', common.mustCall(() => req.destroy()));
+ req.on('close', common.mustCall(() => client.close()));
+ req.on('response', common.mustCall(() => {
+ req.close(NGHTTP2_CANCEL);
+ }));
+}));