◐ Shell
clean mode source ↗

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+

}));