| '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() { } |