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
PR-URL: https://github.com/nodejs/node/pull/15415
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Anatoli Papirovski 2017-09-14 14:40:42 -04:00 committed by Matteo Collina
parent de51717c3e
commit 8fa5fcc0ba
2 changed files with 51 additions and 2 deletions

View File

@ -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);

View File

@ -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();
}));
});