Skip to content

Commit 2fdb5ce

Browse files
Han5991marco-ippolito
authored andcommitted
http2: fix FileHandle leak in respondWithFile
Ensure that the file handle is closed if header validation fails in respondWithFile. This prevents ERR_INVALID_STATE errors where a FileHandle object is closed during garbage collection. PR-URL: #61707 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Tim Perry <pimterry@gmail.com>
1 parent e4c0d99 commit 2fdb5ce

2 files changed

Lines changed: 55 additions & 0 deletions

File tree

lib/internal/http2/core.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2699,6 +2699,8 @@ function processRespondWithFD(self, fd, headers, offset = 0, length = -1,
26992699
try {
27002700
headersList = buildNgHeaderString(headers, assertValidPseudoHeaderResponse);
27012701
} catch (err) {
2702+
if (self.ownsFd)
2703+
tryClose(fd);
27022704
self.destroy(err);
27032705
return;
27042706
}
@@ -2712,6 +2714,8 @@ function processRespondWithFD(self, fd, headers, offset = 0, length = -1,
27122714
const ret = self[kHandle].respond(headersList, streamOptions);
27132715

27142716
if (ret < 0) {
2717+
if (self.ownsFd)
2718+
tryClose(fd);
27152719
self.destroy(new NghttpError(ret));
27162720
return;
27172721
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict';
2+
const common = require('../common');
3+
if (!common.hasCrypto)
4+
common.skip('missing crypto');
5+
const fixtures = require('../common/fixtures');
6+
const assert = require('assert');
7+
const http2 = require('http2');
8+
const fs = require('fs');
9+
10+
const fname = fixtures.path('elipses.txt');
11+
12+
const server = http2.createServer();
13+
14+
server.on('stream', common.mustCall((stream) => {
15+
const originalClose = fs.close;
16+
let fdClosed = false;
17+
18+
fs.close = common.mustCall(function(fd, cb) {
19+
fdClosed = true;
20+
return originalClose.apply(this, arguments);
21+
});
22+
23+
const headers = {
24+
':method': 'GET',
25+
'content-type': 'text/plain'
26+
};
27+
28+
stream.respondWithFile(fname, headers);
29+
30+
stream.on('error', common.mustCall((err) => {
31+
assert.strictEqual(err.code, 'ERR_HTTP2_INVALID_PSEUDOHEADER');
32+
}));
33+
34+
stream.on('close', common.mustCall(() => {
35+
fs.close = originalClose;
36+
assert.strictEqual(fdClosed, true);
37+
}));
38+
}));
39+
40+
server.listen(0, common.mustCall(() => {
41+
const client = http2.connect(`http://localhost:${server.address().port}`);
42+
const req = client.request();
43+
44+
req.on('close', common.mustCall(() => {
45+
client.close();
46+
server.close();
47+
}));
48+
49+
req.on('error', common.mustCall());
50+
req.end();
51+
}));

0 commit comments

Comments
 (0)