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