Skip to content

Commit 0789eec

Browse files
billywhizzaddaleax
authored andcommitted
http: prevent aborted event when already completed
Tests in progress to reproduce issue consistently. Fixes: #18756 PR-URL: #18999 Reviewed-By: Shingo Inoue <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent ae4d83f commit 0789eec

File tree

1 file changed

+83
-0
lines changed

1 file changed

+83
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const http = require('http');
5+
const assert = require('assert');
6+
const fs = require('fs');
7+
const Countdown = require('../common/countdown');
8+
9+
function cleanup(fname) {
10+
try {
11+
if (fs.statSync(fname)) fs.unlinkSync(fname);
12+
} catch (err) {}
13+
}
14+
15+
const N = 2;
16+
const fname = '/dev/null';
17+
let abortRequest = true;
18+
19+
const server = http.Server(common.mustCall((req, res) => {
20+
const headers = { 'Content-Type': 'text/plain' };
21+
headers['Content-Length'] = 50;
22+
const socket = res.socket;
23+
res.writeHead(200, headers);
24+
setTimeout(() => res.write('aaaaaaaaaa'), 100);
25+
setTimeout(() => res.write('bbbbbbbbbb'), 200);
26+
setTimeout(() => res.write('cccccccccc'), 300);
27+
setTimeout(() => res.write('dddddddddd'), 400);
28+
if (abortRequest) {
29+
setTimeout(() => socket.destroy(), 600);
30+
} else {
31+
setTimeout(() => res.end('eeeeeeeeee'), 1000);
32+
}
33+
}, N));
34+
35+
server.listen(0, common.mustCall(() => {
36+
cleanup(fname);
37+
download();
38+
}));
39+
40+
const finishCountdown = new Countdown(N, common.mustCall(() => {
41+
server.close();
42+
}));
43+
const reqCountdown = new Countdown(N, common.mustCall());
44+
45+
function download() {
46+
const opts = {
47+
port: server.address().port,
48+
path: '/',
49+
};
50+
const req = http.get(opts);
51+
req.on('error', common.mustNotCall());
52+
req.on('response', (res) => {
53+
assert.strictEqual(res.statusCode, 200);
54+
assert.strictEqual(res.headers.connection, 'close');
55+
let aborted = false;
56+
const fstream = fs.createWriteStream(fname);
57+
res.pipe(fstream);
58+
const _handle = res.socket._handle;
59+
_handle._close = res.socket._handle.close;
60+
_handle.close = function(callback) {
61+
_handle._close();
62+
// set readable to true event though request is complete
63+
if (res.complete) res.readable = true;
64+
callback();
65+
};
66+
res.on('end', common.mustCall(() => {
67+
reqCountdown.dec();
68+
}));
69+
res.on('aborted', () => {
70+
aborted = true;
71+
});
72+
res.on('error', common.mustNotCall());
73+
fstream.on('finish', () => {
74+
assert.strictEqual(aborted, abortRequest);
75+
cleanup(fname);
76+
finishCountdown.dec();
77+
if (finishCountdown.remaining === 0) return;
78+
abortRequest = false; // next one should be a good response
79+
download();
80+
});
81+
});
82+
req.end();
83+
}

0 commit comments

Comments
 (0)