From 238ef58841fef61cda438589705c2af9d2fb4b91 Mon Sep 17 00:00:00 2001 From: RidgeA Date: Wed, 11 Jul 2018 17:29:42 +0300 Subject: [PATCH] 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 Reviewed-By: Matteo Collina Reviewed-By: Ruben Bridgewater Reviewed-By: Anatoli Papirovski --- lib/internal/http2/compat.js | 2 + ...esponse-end-after-statuses-without-body.js | 47 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 test/parallel/test-http2-compat-serverresponse-end-after-statuses-without-body.js diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js index 88879484ebaab7..4a006107b2fcea 100644 --- a/lib/internal/http2/compat.js +++ b/lib/internal/http2/compat.js @@ -391,6 +391,8 @@ function onStreamCloseResponse() { state.closed = true; this[kProxySocket] = null; + + this.removeListener('wantTrailers', onStreamTrailersReady); this[kResponse] = undefined; res.emit('finish'); diff --git a/test/parallel/test-http2-compat-serverresponse-end-after-statuses-without-body.js b/test/parallel/test-http2-compat-serverresponse-end-after-statuses-without-body.js new file mode 100644 index 00000000000000..83d5521bf2473f --- /dev/null +++ b/test/parallel/test-http2-compat-serverresponse-end-after-statuses-without-body.js @@ -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)); + } + + })); +}));