1'use strict'; 2const common = require('../common'); 3const assert = require('assert'); 4const http = require('http'); 5 6const REQ_TIMEOUT = 500; // Set max ms of request time before abort 7 8// Set total allowed test timeout to avoid infinite loop 9// that will hang test suite 10const TOTAL_TEST_TIMEOUT = 1000; 11 12// Placeholder for sockets handled, to make sure that we 13// will reach a socket re-use case. 14const handledSockets = new Set(); 15 16let metReusedSocket = false; // Flag for request loop termination. 17 18const doubleEndResponse = (res) => { 19 // First end the request while sending some normal data 20 res.end('regular end of request', 'utf8', common.mustCall()); 21 // Make sure the response socket is uncorked after first call of end 22 assert.strictEqual(res.writableCorked, 0); 23 res.end(); // Double end the response to prep for next socket re-use. 24}; 25 26const sendDrainNeedingData = (res) => { 27 // Send data to socket more than the high watermark so that 28 // it definitely needs drain 29 const highWaterMark = res.socket.writableHighWaterMark; 30 const bufferToSend = Buffer.alloc(highWaterMark + 100); 31 const ret = res.write(bufferToSend); // Write the request data. 32 // Make sure that we had back pressure on response stream. 33 assert.strictEqual(ret, false); 34 res.once('drain', () => res.end()); // End on drain. 35}; 36 37const server = http.createServer((req, res) => { 38 const { socket: responseSocket } = res; 39 if (handledSockets.has(responseSocket)) { // re-used socket, send big data! 40 metReusedSocket = true; // stop request loop 41 console.debug('FOUND REUSED SOCKET!'); 42 sendDrainNeedingData(res); 43 } else { // not used again 44 // add to make sure we recognise it when we meet socket again 45 handledSockets.add(responseSocket); 46 doubleEndResponse(res); 47 } 48}); 49 50server.listen(0); // Start the server on a random port. 51 52const sendRequest = (agent) => new Promise((resolve, reject) => { 53 const timeout = setTimeout(common.mustNotCall(() => { 54 reject(new Error('Request timed out')); 55 }), REQ_TIMEOUT); 56 http.get({ 57 port: server.address().port, 58 path: '/', 59 agent 60 }, common.mustCall((res) => { 61 const resData = []; 62 res.on('data', (data) => resData.push(data)); 63 res.on('end', common.mustCall(() => { 64 const totalData = resData.reduce((total, elem) => total + elem.length, 0); 65 clearTimeout(timeout); // Cancel rejection timeout. 66 resolve(totalData); // fulfill promise 67 })); 68 })); 69}); 70 71server.once('listening', async () => { 72 const testTimeout = setTimeout(common.mustNotCall(() => { 73 console.error('Test running for a while but could not met re-used socket'); 74 process.exit(1); 75 }), TOTAL_TEST_TIMEOUT); 76 // Explicitly start agent to force socket reuse. 77 const agent = new http.Agent({ keepAlive: true }); 78 // Start the request loop 79 let reqNo = 0; 80 while (!metReusedSocket) { 81 try { 82 console.log(`Sending req no ${++reqNo}`); 83 const totalData = await sendRequest(agent); 84 console.log(`${totalData} bytes were received for request ${reqNo}`); 85 } catch (err) { 86 console.error(err); 87 process.exit(1); 88 } 89 } 90 // Successfully tested conditions and ended loop 91 clearTimeout(testTimeout); 92 console.log('Closing server'); 93 agent.destroy(); 94 server.close(); 95}); 96