From 777590e917fc34d3a1e0d5eeb9bb305180b3a12b Mon Sep 17 00:00:00 2001 From: Anatoli Papirovski Date: Thu, 14 Sep 2017 14:40:42 -0400 Subject: [PATCH] http2: emit close event if request aborted Fix Http2ServerRequest and Http2ServerResponse to emit close event if the request is aborted before response.end can be called. Fixes: https://github.com/nodejs/node/issues/15385 --- lib/internal/http2/compat.js | 12 +++++- .../test-http2-compat-serverresponse-close.js | 41 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-http2-compat-serverresponse-close.js diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js index 04f4bb3c5a338c..c36b887f178c8d 100644 --- a/lib/internal/http2/compat.js +++ b/lib/internal/http2/compat.js @@ -123,10 +123,17 @@ function onStreamClosedResponse() { res.emit('finish'); } -function onAborted(hadError, code) { +function onStreamAbortedRequest(hadError, code) { if ((this.writable) || (this._readableState && !this._readableState.ended)) { this.emit('aborted', hadError, code); + this.emit('close'); + } +} + +function onStreamAbortedResponse() { + if (this.writable) { + this.emit('close'); } } @@ -151,7 +158,7 @@ class Http2ServerRequest extends Readable { stream.on('end', onStreamEnd); stream.on('error', onStreamError); stream.on('close', onStreamClosedRequest); - stream.on('aborted', onAborted.bind(this)); + stream.on('aborted', onStreamAbortedRequest.bind(this)); const onfinish = this[kFinish].bind(this); stream.on('streamClosed', onfinish); stream.on('finish', onfinish); @@ -274,6 +281,7 @@ class Http2ServerResponse extends Stream { this.writable = true; stream.on('drain', onStreamResponseDrain); stream.on('close', onStreamClosedResponse); + stream.on('aborted', onStreamAbortedResponse.bind(this)); const onfinish = this[kFinish].bind(this); stream.on('streamClosed', onfinish); stream.on('finish', onfinish); diff --git a/test/parallel/test-http2-compat-serverresponse-close.js b/test/parallel/test-http2-compat-serverresponse-close.js new file mode 100644 index 00000000000000..9b42ae05dc24b4 --- /dev/null +++ b/test/parallel/test-http2-compat-serverresponse-close.js @@ -0,0 +1,41 @@ +// Flags: --expose-http2 --expose-internals +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const h2 = require('http2'); + +// Server request and response should receive close event +// if the connection was terminated before response.end +// could be called or flushed + +const server = h2.createServer(common.mustCall((req, res) => { + res.writeHead(200); + res.write('a'); + + req.on('close', common.mustCall()); + res.on('close', common.mustCall()); +})); +server.listen(0); + +server.on('listening', function() { + const port = server.address().port; + + const url = `http://localhost:${port}`; + const client = h2.connect(url, common.mustCall(function() { + const headers = { + ':path': '/foobar', + ':method': 'GET', + ':scheme': 'http', + ':authority': `localhost:${port}`, + }; + const request = client.request(headers); + request.on('data', common.mustCall(function(chunk) { + // cause an error on the server side + client.destroy(); + server.close(); + })); + request.end(); + })); +});