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: https://github.com/nodejs/node/pull/28435
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
This commit is contained in:
cjihrig 2019-06-26 10:15:52 -04:00
parent bf7edaa3c9
commit f9632041dc
No known key found for this signature in database
GPG Key ID: 7434390BDBE9B9C5
2 changed files with 62 additions and 3 deletions

View File

@ -942,6 +942,7 @@ class Http2Session extends EventEmitter {
socket[kSession] = this; socket[kSession] = this;
this[kState] = { this[kState] = {
destroyCode: NGHTTP2_NO_ERROR,
flags: SESSION_FLAGS_PENDING, flags: SESSION_FLAGS_PENDING,
goawayCode: null, goawayCode: null,
goawayLastStreamID: null, goawayLastStreamID: null,
@ -1206,6 +1207,7 @@ class Http2Session extends EventEmitter {
const state = this[kState]; const state = this[kState];
state.flags |= SESSION_FLAGS_DESTROYED; state.flags |= SESSION_FLAGS_DESTROYED;
state.destroyCode = code;
// Clear timeout and remove timeout listeners // Clear timeout and remove timeout listeners
this.setTimeout(0); this.setTimeout(0);
@ -1937,10 +1939,13 @@ class Http2Stream extends Duplex {
debug(`Http2Stream ${this[kID] || '<pending>'} [Http2Session ` + debug(`Http2Stream ${this[kID] || '<pending>'} [Http2Session ` +
`${sessionName(session[kType])}]: destroying stream`); `${sessionName(session[kType])}]: destroying stream`);
const state = this[kState];
const code = err != null ?
NGHTTP2_INTERNAL_ERROR : (state.rstCode || NGHTTP2_NO_ERROR);
const state = this[kState];
const sessionCode = session[kState].goawayCode ||
session[kState].destroyCode;
const code = err != null ?
sessionCode || NGHTTP2_INTERNAL_ERROR :
state.rstCode || sessionCode;
const hasHandle = handle !== undefined; const hasHandle = handle !== undefined;
if (!this.closed) if (!this.closed)

View File

@ -0,0 +1,54 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const http2 = require('http2');
const server = http2.createServer();
const errRegEx = /Session closed with error code 7/;
const destroyCode = http2.constants.NGHTTP2_REFUSED_STREAM;
server.on('error', common.mustNotCall());
server.on('session', (session) => {
session.on('close', common.mustCall());
session.on('error', common.mustCall((err) => {
assert(errRegEx.test(err));
assert.strictEqual(session.closed, false);
assert.strictEqual(session.destroyed, true);
}));
session.on('stream', common.mustCall((stream) => {
stream.on('error', common.mustCall((err) => {
assert.strictEqual(session.closed, false);
assert.strictEqual(session.destroyed, true);
assert(errRegEx.test(err));
assert.strictEqual(stream.rstCode, destroyCode);
}));
session.destroy(destroyCode);
}));
});
server.listen(0, common.mustCall(() => {
const session = http2.connect(`http://localhost:${server.address().port}`);
session.on('error', common.mustCall((err) => {
assert(errRegEx.test(err));
assert.strictEqual(session.closed, false);
assert.strictEqual(session.destroyed, true);
}));
const stream = session.request({ [http2.constants.HTTP2_HEADER_PATH]: '/' });
stream.on('error', common.mustCall((err) => {
assert(errRegEx.test(err));
assert.strictEqual(stream.rstCode, destroyCode);
}));
stream.on('close', common.mustCall(() => {
server.close();
}));
}));