Skip to content

Commit d8d4f9b

Browse files
cjihrigtargos
authored andcommitted
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 dce4947 commit d8d4f9b

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
@@ -946,6 +946,7 @@ class Http2Session extends EventEmitter {
946946
socket[kSession] = this;
947947

948948
this[kState] = {
949+
destroyCode: NGHTTP2_NO_ERROR,
949950
flags: SESSION_FLAGS_PENDING,
950951
goawayCode: null,
951952
goawayLastStreamID: null,
@@ -1210,6 +1211,7 @@ class Http2Session extends EventEmitter {
12101211

12111212
const state = this[kState];
12121213
state.flags |= SESSION_FLAGS_DESTROYED;
1214+
state.destroyCode = code;
12131215

12141216
// Clear timeout and remove timeout listeners
12151217
this.setTimeout(0);
@@ -1941,10 +1943,13 @@ class Http2Stream extends Duplex {
19411943

19421944
debug(`Http2Stream ${this[kID] || '<pending>'} [Http2Session ` +
19431945
`${sessionName(session[kType])}]: destroying stream`);
1946+
19441947
const state = this[kState];
1948+
const sessionCode = session[kState].goawayCode ||
1949+
session[kState].destroyCode;
19451950
const code = err != null ?
1946-
NGHTTP2_INTERNAL_ERROR : (state.rstCode || NGHTTP2_NO_ERROR);
1947-
1951+
sessionCode || NGHTTP2_INTERNAL_ERROR :
1952+
state.rstCode || sessionCode;
19481953
const hasHandle = handle !== undefined;
19491954

19501955
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)