Skip to content

Commit ece428e

Browse files
indutnyMyles Borins
authored and
Myles Borins
committed
http: fix no dumping after maybeReadMore
When `maybeReadMore` kicks in on a first bytes of incoming data, the `req.read(0)` will be invoked and the `req._consuming` will be set to `true`. This seemingly harmless property leads to a dire consequences: the server won't call `req._dump()` and the whole HTTP/1.1 pipeline will hang (single connection). PR-URL: #7211 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent 629a76f commit ece428e

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

lib/_http_incoming.js

+9
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ exports.readStop = readStop;
2020
function IncomingMessage(socket) {
2121
Stream.Readable.call(this);
2222

23+
// Set this to `true` so that stream.Readable won't attempt to read more
24+
// data on `IncomingMessage#push` (see `maybeReadMore` in
25+
// `_stream_readable.js`). This is important for proper tracking of
26+
// `IncomingMessage#_consuming` which is used to dump requests that users
27+
// haven't attempted to read.
28+
this._readableState.readingMore = true;
29+
2330
this.socket = socket;
2431
this.connection = socket;
2532

@@ -67,6 +74,8 @@ IncomingMessage.prototype.setTimeout = function(msecs, callback) {
6774

6875

6976
IncomingMessage.prototype.read = function(n) {
77+
if (!this._consuming)
78+
this._readableState.readingMore = false;
7079
this._consuming = true;
7180
this.read = Stream.Readable.prototype.read;
7281
return this.read(n);
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict';
2+
const common = require('../common');
3+
const http = require('http');
4+
5+
let onPause = null;
6+
7+
const server = http.createServer((req, res) => {
8+
if (req.method === 'GET')
9+
return res.end();
10+
11+
res.writeHead(200);
12+
res.flushHeaders();
13+
14+
req.connection.on('pause', () => {
15+
res.end();
16+
onPause();
17+
});
18+
}).listen(common.PORT, common.mustCall(() => {
19+
const agent = new http.Agent({
20+
maxSockets: 1,
21+
keepAlive: true
22+
});
23+
24+
const post = http.request({
25+
agent: agent,
26+
method: 'POST',
27+
port: common.PORT,
28+
}, common.mustCall((res) => {
29+
res.resume();
30+
31+
post.write(Buffer.alloc(16 * 1024).fill('X'));
32+
onPause = () => {
33+
post.end('something');
34+
};
35+
}));
36+
37+
/* What happens here is that the server `end`s the response before we send
38+
* `something`, and the client thought that this is a green light for sending
39+
* next GET request
40+
*/
41+
post.write('initial');
42+
43+
http.request({
44+
agent: agent,
45+
method: 'GET',
46+
port: common.PORT,
47+
}, common.mustCall((res) => {
48+
server.close();
49+
res.connection.end();
50+
})).end();
51+
}));

0 commit comments

Comments
 (0)