http: emit 'drain' on OutgoingMessage only after buffers drain · nodejs/node@717476a
1+'use strict';
2+// Regression test: when a pipelined ServerResponse (whose writes were
3+// buffered in outputData while the socket belonged to a previous response)
4+// is finally assigned its socket and flushed, 'drain' must not be emitted
5+// until the socket's own buffer has actually drained. Previously,
6+// socketOnDrain was called synchronously from _flushOutput via _onPendingData
7+// and emitted 'drain' even though the bytes we just wrote were still sitting
8+// in the socket's writable buffer, so res.writableLength was non-zero.
9+10+const common = require('../common');
11+const http = require('http');
12+const net = require('net');
13+const assert = require('assert');
14+15+let step = 0;
16+17+const server = http.createServer(common.mustCall((req, res) => {
18+step++;
19+20+if (step === 1) {
21+// Keep the first response open briefly so the second is queued with
22+// res.socket === null.
23+res.writeHead(200, { 'Content-Type': 'text/plain' });
24+setTimeout(() => res.end('ok'), 50);
25+return;
26+}
27+28+// Second (pipelined) response - queued in state.outgoing, no socket yet.
29+assert.strictEqual(res.socket, null);
30+31+res.writeHead(200, { 'Content-Type': 'text/plain' });
32+33+// Write past the response's highWaterMark so res.write() returns false
34+// and kNeedDrain is set. Data is buffered in outputData.
35+const chunk = Buffer.alloc(16 * 1024, 'x');
36+while (res.write(chunk));
37+assert.strictEqual(res.writableNeedDrain, true);
38+39+res.on('drain', common.mustCall(() => {
40+assert.strictEqual(
41+res.writableLength, 0,
42+`'drain' fired with writableLength=${res.writableLength}`,
43+);
44+res.end();
45+server.close();
46+}));
47+}, 2));
48+49+server.listen(0, common.mustCall(function() {
50+const port = this.address().port;
51+const client = net.connect(port);
52+client.write(
53+`GET /1 HTTP/1.1\r\nHost: localhost:${port}\r\n\r\n` +
54+`GET /2 HTTP/1.1\r\nHost: localhost:${port}\r\n\r\n`,
55+);
56+client.resume();
57+client.on('error', () => {});
58+}));