forked from nodejs/node
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest-http-agent-reuse-drained-socket-only.js
122 lines (106 loc) · 4.07 KB
/
test-http-agent-reuse-drained-socket-only.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
'use strict';
const common = require('../common');
const assert = require('assert');
const http = require('http');
const net = require('net');
const agent = new http.Agent({
keepAlive: true,
maxFreeSockets: Infinity,
maxSockets: Infinity,
maxTotalSockets: Infinity,
});
const server = net.createServer({
pauseOnConnect: true,
}, (sock) => {
// Do not read anything from `sock`
sock.pause();
sock.write('HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: Keep-Alive\r\n\r\n');
});
server.listen(0, common.mustCall(() => {
sendFstReq(server.address().port);
}));
function sendFstReq(serverPort) {
const req = http.request({
agent,
host: '127.0.0.1',
port: serverPort,
}, (res) => {
res.on('data', noop);
res.on('end', common.mustCall(() => {
// Agent's socket reusing code is registered to process.nextTick(),
// and will be run after this function, make sure it take effect.
setImmediate(sendSecReq, serverPort, req.socket.localPort);
}));
});
// Make the `req.socket` non drained, i.e. has some data queued to write to
// and accept by the kernel. In Linux and Mac, we only need to call `req.end(aLargeBuffer)`.
// However, in Windows, the mechanism of acceptance is loose, the following code is a workaround
// for Windows.
/**
* https://docs.microsoft.com/en-US/troubleshoot/windows/win32/data-segment-tcp-winsock says
*
* Winsock uses the following rules to indicate a send completion to the application
* (depending on how the send is invoked, the completion notification could be the
* function returning from a blocking call, signaling an event, or calling a notification
* function, and so forth):
* - If the socket is still within SO_SNDBUF quota, Winsock copies the data from the application
* send and indicates the send completion to the application.
* - If the socket is beyond SO_SNDBUF quota and there's only one previously buffered send still
* in the stack kernel buffer, Winsock copies the data from the application send and indicates
* the send completion to the application.
* - If the socket is beyond SO_SNDBUF quota and there's more than one previously buffered send
* in the stack kernel buffer, Winsock copies the data from the application send. Winsock doesn't
* indicate the send completion to the application until the stack completes enough sends to put
* back the socket within SO_SNDBUF quota or only one outstanding send condition.
*/
req.on('socket', () => {
req.socket.on('connect', () => {
// Print tcp send buffer information
console.log(process.report.getReport().libuv.filter((handle) => handle.type === 'tcp'));
const dataLargerThanTCPSendBuf = Buffer.alloc(1024 * 1024 * 64, 0);
req.write(dataLargerThanTCPSendBuf);
req.uncork();
if (process.platform === 'win32') {
assert.ok(req.socket.writableLength === 0);
}
req.write(dataLargerThanTCPSendBuf);
req.uncork();
if (process.platform === 'win32') {
assert.ok(req.socket.writableLength === 0);
}
req.end(dataLargerThanTCPSendBuf);
assert.ok(req.socket.writableLength > 0);
});
});
}
function sendSecReq(serverPort, fstReqCliPort) {
// Make the second request, which should be sent on a new socket
// because the first socket is not drained and hence can not be reused
const req = http.request({
agent,
host: '127.0.0.1',
port: serverPort,
}, (res) => {
res.on('data', noop);
res.on('end', common.mustCall(() => {
setImmediate(sendThrReq, serverPort, req.socket.localPort);
}));
});
req.on('socket', common.mustCall((sock) => {
assert.notStrictEqual(sock.localPort, fstReqCliPort);
}));
req.end();
}
function sendThrReq(serverPort, secReqCliPort) {
// Make the third request, the agent should reuse the second socket we just made
const req = http.request({
agent,
host: '127.0.0.1',
port: serverPort,
}, noop);
req.on('socket', common.mustCall((sock) => {
assert.strictEqual(sock.localPort, secReqCliPort);
process.exit(0);
}));
}
function noop() { }