Skip to content

Commit afa72df

Browse files
jasnellMylesBorins
authored andcommitted
http2: guard against destroyed session, timeouts
Guard against destroyed session in timeouts and goaway event. Improve timeout handling a bit. PR-URL: #15106 Fixes: #14964 Reviewed-By: Matteo Collina <[email protected]>
1 parent f6c5188 commit afa72df

File tree

3 files changed

+38
-7
lines changed

3 files changed

+38
-7
lines changed

lib/internal/http2/core.js

+10-5
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,8 @@ function onFrameError(id, type, code) {
377377
function emitGoaway(state, code, lastStreamID, buf) {
378378
this.emit('goaway', code, lastStreamID, buf);
379379
// Tear down the session or destroy
380+
if (state.destroying || state.destroyed)
381+
return;
380382
if (!state.shuttingDown && !state.shutdown) {
381383
this.shutdown({}, this.destroy.bind(this));
382384
} else {
@@ -970,7 +972,7 @@ class Http2Session extends EventEmitter {
970972
state.destroying = true;
971973

972974
// Unenroll the timer
973-
unenroll(this);
975+
this.setTimeout(0);
974976

975977
// Shut down any still open streams
976978
const streams = state.streams;
@@ -1530,7 +1532,7 @@ class Http2Stream extends Duplex {
15301532
session.removeListener('close', this[kState].closeHandler);
15311533

15321534
// Unenroll the timer
1533-
unenroll(this);
1535+
this.setTimeout(0);
15341536

15351537
setImmediate(finishStreamDestroy.bind(this, handle));
15361538

@@ -2180,7 +2182,7 @@ function socketDestroy(error) {
21802182
const type = this[kSession][kType];
21812183
debug(`[${sessionName(type)}] socket destroy called`);
21822184
delete this[kServer];
2183-
this.removeListener('timeout', socketOnTimeout);
2185+
this.setTimeout(0);
21842186
// destroy the session first so that it will stop trying to
21852187
// send data while we close the socket.
21862188
this[kSession].destroy();
@@ -2249,10 +2251,13 @@ function socketOnTimeout() {
22492251
process.nextTick(() => {
22502252
const server = this[kServer];
22512253
const session = this[kSession];
2252-
// If server or session are undefined, then we're already in the process of
2253-
// shutting down, do nothing.
2254+
// If server or session are undefined, or session.destroyed is true
2255+
// then we're already in the process of shutting down, do nothing.
22542256
if (server === undefined || session === undefined)
22552257
return;
2258+
const state = session[kState];
2259+
if (state.destroyed || state.destroying)
2260+
return;
22562261
if (!server.emit('timeout', session, this)) {
22572262
session.shutdown(
22582263
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Flags: --expose-http2
2+
'use strict';
3+
4+
const common = require('../common');
5+
if (!common.hasCrypto)
6+
common.skip('missing crypto');
7+
const http2 = require('http2');
8+
9+
const server = http2.createServer();
10+
server.on('stream', common.mustCall((stream) => {
11+
stream.on('error', common.mustCall());
12+
stream.session.shutdown();
13+
}));
14+
15+
server.listen(0, common.mustCall(() => {
16+
const client = http2.connect(`http://localhost:${server.address().port}`);
17+
18+
client.on('goaway', common.mustCall(() => {
19+
// We ought to be able to destroy the client in here without an error
20+
server.close();
21+
client.destroy();
22+
}));
23+
24+
client.request();
25+
}));

test/parallel/test-http2-server-shutdown-before-respond.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ server.on('listening', common.mustCall(() => {
2626

2727
const client = h2.connect(`http://localhost:${server.address().port}`);
2828

29-
const req = client.request({ ':path': '/' });
29+
client.on('goaway', common.mustCall());
30+
31+
const req = client.request();
3032

3133
req.resume();
3234
req.on('end', common.mustCall(() => server.close()));
33-
req.end();
3435
}));

0 commit comments

Comments
 (0)