Skip to content

Commit 492bdde

Browse files
committed
http: wait for both prefinish/end to keepalive
When `free`ing the socket to be reused in keep-alive Agent wait for both `prefinish` and `end` events. Otherwise the next request may be written before the previous one has finished sending the body, leading to a parser errors.
1 parent edbb092 commit 492bdde

File tree

2 files changed

+62
-3
lines changed

2 files changed

+62
-3
lines changed

lib/_http_client.js

+24-3
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ function ClientRequest(options, cb) {
195195
self._flush();
196196
self = null;
197197
});
198+
199+
this._ended = false;
198200
}
199201

200202
util.inherits(ClientRequest, OutgoingMessage);
@@ -466,6 +468,7 @@ function parserOnIncomingClient(res, shouldKeepAlive) {
466468

467469
// add our listener first, so that we guarantee socket cleanup
468470
res.on('end', responseOnEnd);
471+
req.on('prefinish', requestOnPrefinish);
469472
var handled = req.emit('response', res);
470473

471474
// If the user did not listen for the 'response' event, then they
@@ -478,9 +481,7 @@ function parserOnIncomingClient(res, shouldKeepAlive) {
478481
}
479482

480483
// client
481-
function responseOnEnd() {
482-
var res = this;
483-
var req = res.req;
484+
function responseKeepAlive(res, req) {
484485
var socket = req.socket;
485486

486487
if (!req.shouldKeepAlive) {
@@ -504,6 +505,26 @@ function responseOnEnd() {
504505
}
505506
}
506507

508+
function responseOnEnd() {
509+
const res = this;
510+
const req = this.req;
511+
512+
req._ended = true;
513+
if (!req.shouldKeepAlive || req.finished)
514+
responseKeepAlive(res, req);
515+
}
516+
517+
function requestOnPrefinish() {
518+
const req = this;
519+
const res = this.res;
520+
521+
if (!req.shouldKeepAlive)
522+
return;
523+
524+
if (req._ended)
525+
responseKeepAlive(res, req);
526+
}
527+
507528
function emitFreeNT(socket) {
508529
socket.emit('free');
509530
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use strict';
2+
const common = require('../common');
3+
const http = require('http');
4+
5+
const server = http.createServer((req, res) => {
6+
res.end();
7+
}).listen(common.PORT, common.mustCall(() => {
8+
const agent = new http.Agent({
9+
maxSockets: 1,
10+
keepAlive: true
11+
});
12+
13+
const post = http.request({
14+
agent: agent,
15+
method: 'POST',
16+
port: common.PORT,
17+
}, common.mustCall((res) => {
18+
res.resume();
19+
}));
20+
21+
/* What happens here is that the server `end`s the response before we send
22+
* `something`, and the client thought that this is a green light for sending
23+
* next GET request
24+
*/
25+
post.write(Buffer.alloc(16 * 1024).fill('X'));
26+
setTimeout(() => {
27+
post.end('something');
28+
}, 100);
29+
30+
http.request({
31+
agent: agent,
32+
method: 'GET',
33+
port: common.PORT,
34+
}, common.mustCall((res) => {
35+
server.close();
36+
res.connection.end();
37+
})).end();
38+
}));

0 commit comments

Comments
 (0)