Skip to content

Commit f963204

Browse files
committed
http2: propagate session destroy code to streams
Currently, when an HTTP2 session is destroyed with a code, that code is not propagated to the destroy() call of the session's streams. This commit forwards any code used to destroy a session to its corresponding streams. PR-URL: #28435 Reviewed-By: Rich Trott <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Trivikram Kamat <[email protected]>
1 parent bf7edaa commit f963204

File tree

2 files changed

+61
-2
lines changed

2 files changed

+61
-2
lines changed

lib/internal/http2/core.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,7 @@ class Http2Session extends EventEmitter {
942942
socket[kSession] = this;
943943

944944
this[kState] = {
945+
destroyCode: NGHTTP2_NO_ERROR,
945946
flags: SESSION_FLAGS_PENDING,
946947
goawayCode: null,
947948
goawayLastStreamID: null,
@@ -1206,6 +1207,7 @@ class Http2Session extends EventEmitter {
12061207

12071208
const state = this[kState];
12081209
state.flags |= SESSION_FLAGS_DESTROYED;
1210+
state.destroyCode = code;
12091211

12101212
// Clear timeout and remove timeout listeners
12111213
this.setTimeout(0);
@@ -1937,10 +1939,13 @@ class Http2Stream extends Duplex {
19371939

19381940
debug(`Http2Stream ${this[kID] || '<pending>'} [Http2Session ` +
19391941
`${sessionName(session[kType])}]: destroying stream`);
1942+
19401943
const state = this[kState];
1944+
const sessionCode = session[kState].goawayCode ||
1945+
session[kState].destroyCode;
19411946
const code = err != null ?
1942-
NGHTTP2_INTERNAL_ERROR : (state.rstCode || NGHTTP2_NO_ERROR);
1943-
1947+
sessionCode || NGHTTP2_INTERNAL_ERROR :
1948+
state.rstCode || sessionCode;
19441949
const hasHandle = handle !== undefined;
19451950

19461951
if (!this.closed)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
'use strict';
2+
const common = require('../common');
3+
4+
if (!common.hasCrypto)
5+
common.skip('missing crypto');
6+
7+
const assert = require('assert');
8+
const http2 = require('http2');
9+
const server = http2.createServer();
10+
const errRegEx = /Session closed with error code 7/;
11+
const destroyCode = http2.constants.NGHTTP2_REFUSED_STREAM;
12+
13+
server.on('error', common.mustNotCall());
14+
15+
server.on('session', (session) => {
16+
session.on('close', common.mustCall());
17+
session.on('error', common.mustCall((err) => {
18+
assert(errRegEx.test(err));
19+
assert.strictEqual(session.closed, false);
20+
assert.strictEqual(session.destroyed, true);
21+
}));
22+
23+
session.on('stream', common.mustCall((stream) => {
24+
stream.on('error', common.mustCall((err) => {
25+
assert.strictEqual(session.closed, false);
26+
assert.strictEqual(session.destroyed, true);
27+
assert(errRegEx.test(err));
28+
assert.strictEqual(stream.rstCode, destroyCode);
29+
}));
30+
31+
session.destroy(destroyCode);
32+
}));
33+
});
34+
35+
server.listen(0, common.mustCall(() => {
36+
const session = http2.connect(`http://localhost:${server.address().port}`);
37+
38+
session.on('error', common.mustCall((err) => {
39+
assert(errRegEx.test(err));
40+
assert.strictEqual(session.closed, false);
41+
assert.strictEqual(session.destroyed, true);
42+
}));
43+
44+
const stream = session.request({ [http2.constants.HTTP2_HEADER_PATH]: '/' });
45+
46+
stream.on('error', common.mustCall((err) => {
47+
assert(errRegEx.test(err));
48+
assert.strictEqual(stream.rstCode, destroyCode);
49+
}));
50+
51+
stream.on('close', common.mustCall(() => {
52+
server.close();
53+
}));
54+
}));

0 commit comments

Comments
 (0)