http2: remove waitTrailers listener after closing a stream

When `writeHeader` of `Http2ServerResponse` instance are called with
204, 205 and 304 status codes an underlying stream closes.
If call `end` method after sending any of these status codes it will
cause an error `TypeError: Cannot read property 'Symbol(trailers)' of
undefined` because a reference to `Http2ServerResponse` instance
associated with Http2Stream already was deleted.
The closing of stream causes emitting `waitTrailers` event and, when
this event handles inside `onStreamTrailerReady` handler, there is
no reference to Http2ServerResponse instance.

Fixes: https://github.com/nodejs/node/issues/21740
PR-URL: https://github.com/nodejs/node/pull/21764
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Anatoli Papirovski <apapirovski@mac.com>
This commit is contained in:
RidgeA 2018-07-11 17:29:42 +03:00 committed by Anna Henningsen
parent fb87d8aa12
commit 8babbc5e45
No known key found for this signature in database
GPG Key ID: 9C63F3A6CD2AD8F9
2 changed files with 49 additions and 0 deletions

View File

@ -391,6 +391,8 @@ function onStreamCloseResponse() {
state.closed = true;
this[kProxySocket] = null;
this.removeListener('wantTrailers', onStreamTrailersReady);
this[kResponse] = undefined;
res.emit('finish');

View File

@ -0,0 +1,47 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const h2 = require('http2');
// This test case ensures that calling of res.end after sending
// 204, 205 and 304 HTTP statuses will not cause an error
// See issue: https://github.com/nodejs/node/issues/21740
const {
HTTP_STATUS_NO_CONTENT,
HTTP_STATUS_RESET_CONTENT,
HTTP_STATUS_NOT_MODIFIED
} = h2.constants;
const statusWithouBody = [
HTTP_STATUS_NO_CONTENT,
HTTP_STATUS_RESET_CONTENT,
HTTP_STATUS_NOT_MODIFIED,
];
const STATUS_CODES_COUNT = statusWithouBody.length;
const server = h2.createServer(common.mustCall(function(req, res) {
res.writeHead(statusWithouBody.pop());
res.end();
}, STATUS_CODES_COUNT));
server.listen(0, common.mustCall(function() {
const url = `http://localhost:${server.address().port}`;
const client = h2.connect(url, common.mustCall(() => {
let responseCount = 0;
const closeAfterResponse = () => {
if (STATUS_CODES_COUNT === ++responseCount) {
client.destroy();
server.close();
}
};
for (let i = 0; i < STATUS_CODES_COUNT; i++) {
const request = client.request();
request.on('response', common.mustCall(closeAfterResponse));
}
}));
}));