Skip to content

Commit 8fa5fcc

Browse files
apapirovskimcollina
authored andcommitted
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: #15385 PR-URL: #15415 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent de51717 commit 8fa5fcc

File tree

2 files changed

+51
-2
lines changed

2 files changed

+51
-2
lines changed

lib/internal/http2/compat.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,17 @@ function onStreamClosedResponse() {
123123
res.emit('finish');
124124
}
125125

126-
function onAborted(hadError, code) {
126+
function onStreamAbortedRequest(hadError, code) {
127127
if ((this.writable) ||
128128
(this._readableState && !this._readableState.ended)) {
129129
this.emit('aborted', hadError, code);
130+
this.emit('close');
131+
}
132+
}
133+
134+
function onStreamAbortedResponse() {
135+
if (this.writable) {
136+
this.emit('close');
130137
}
131138
}
132139

@@ -151,7 +158,7 @@ class Http2ServerRequest extends Readable {
151158
stream.on('end', onStreamEnd);
152159
stream.on('error', onStreamError);
153160
stream.on('close', onStreamClosedRequest);
154-
stream.on('aborted', onAborted.bind(this));
161+
stream.on('aborted', onStreamAbortedRequest.bind(this));
155162
const onfinish = this[kFinish].bind(this);
156163
stream.on('streamClosed', onfinish);
157164
stream.on('finish', onfinish);
@@ -274,6 +281,7 @@ class Http2ServerResponse extends Stream {
274281
this.writable = true;
275282
stream.on('drain', onStreamResponseDrain);
276283
stream.on('close', onStreamClosedResponse);
284+
stream.on('aborted', onStreamAbortedResponse.bind(this));
277285
const onfinish = this[kFinish].bind(this);
278286
stream.on('streamClosed', onfinish);
279287
stream.on('finish', onfinish);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Flags: --expose-http2 --expose-internals
2+
'use strict';
3+
4+
const common = require('../common');
5+
if (!common.hasCrypto)
6+
common.skip('missing crypto');
7+
const h2 = require('http2');
8+
9+
// Server request and response should receive close event
10+
// if the connection was terminated before response.end
11+
// could be called or flushed
12+
13+
const server = h2.createServer(common.mustCall((req, res) => {
14+
res.writeHead(200);
15+
res.write('a');
16+
17+
req.on('close', common.mustCall());
18+
res.on('close', common.mustCall());
19+
}));
20+
server.listen(0);
21+
22+
server.on('listening', function() {
23+
const port = server.address().port;
24+
25+
const url = `http://localhost:${port}`;
26+
const client = h2.connect(url, common.mustCall(function() {
27+
const headers = {
28+
':path': '/foobar',
29+
':method': 'GET',
30+
':scheme': 'http',
31+
':authority': `localhost:${port}`,
32+
};
33+
const request = client.request(headers);
34+
request.on('data', common.mustCall(function(chunk) {
35+
// cause an error on the server side
36+
client.destroy();
37+
server.close();
38+
}));
39+
request.end();
40+
}));
41+
});

0 commit comments

Comments
 (0)