1'use strict'; 2// Flags: --expose-internals 3const common = require('../common'); 4const assert = require('assert'); 5const { async_id_symbol } = require('internal/async_hooks').symbols; 6const http = require('http'); 7 8// Regression test for https://github.com/nodejs/node/issues/13325 9// Checks that an http.Agent properly asyncReset()s a reused socket handle, and 10// re-assigns the fresh async id to the reused `net.Socket` instance. 11 12// Make sure a single socket is transparently reused for 2 requests. 13const agent = new http.Agent({ 14 keepAlive: true, 15 keepAliveMsecs: Infinity, 16 maxSockets: 1 17}); 18 19const server = http.createServer(common.mustCall((req, res) => { 20 req.once('data', common.mustCallAtLeast(() => { 21 res.writeHead(200, { 'Content-Type': 'text/plain' }); 22 res.write('foo'); 23 })); 24 req.on('end', common.mustCall(() => { 25 res.end('bar'); 26 })); 27}, 2)).listen(0, common.mustCall(() => { 28 const port = server.address().port; 29 const payload = 'hello world'; 30 31 // First request. This is useless except for adding a socket to the 32 // agent’s pool for reuse. 33 const r1 = http.request({ 34 agent, port, method: 'POST' 35 }, common.mustCall((res) => { 36 // Remember which socket we used. 37 const socket = res.socket; 38 const asyncIdAtFirstRequest = socket[async_id_symbol]; 39 assert.ok(asyncIdAtFirstRequest > 0, `${asyncIdAtFirstRequest} > 0`); 40 // Check that request and response share their socket. 41 assert.strictEqual(r1.socket, socket); 42 43 res.on('data', common.mustCallAtLeast(() => {})); 44 res.on('end', common.mustCall(() => { 45 // setImmediate() to give the agent time to register the freed socket. 46 setImmediate(common.mustCall(() => { 47 // The socket is free for reuse now. 48 assert.strictEqual(socket[async_id_symbol], -1); 49 50 // Second request. To re-create the exact conditions from the 51 // referenced issue, we use a POST request without chunked encoding 52 // (hence the Content-Length header) and call .end() after the 53 // response header has already been received. 54 const r2 = http.request({ 55 agent, port, method: 'POST', headers: { 56 'Content-Length': payload.length 57 } 58 }, common.mustCall((res) => { 59 const asyncId = res.socket[async_id_symbol]; 60 assert.ok(asyncId > 0, `${asyncId} > 0`); 61 assert.strictEqual(r2.socket, socket); 62 // Empty payload, to hit the “right” code path. 63 r2.end(''); 64 65 res.on('data', common.mustCallAtLeast(() => {})); 66 res.on('end', common.mustCall(() => { 67 // Clean up to let the event loop stop. 68 server.close(); 69 agent.destroy(); 70 })); 71 })); 72 73 // Schedule a payload to be written immediately, but do not end the 74 // request just yet. 75 r2.write(payload); 76 })); 77 })); 78 })); 79 r1.end(payload); 80})); 81