|
| 1 | +'use strict'; |
| 2 | +constcommon=require('../common'); |
| 3 | +constassert=require('node:assert'); |
| 4 | +const{ AsyncLocalStorage }=require('node:async_hooks'); |
| 5 | +consthttp=require('node:http'); |
| 6 | + |
| 7 | +// Similar as test-async-hooks-http-agent added via |
| 8 | +// https://github.com/nodejs/node/issues/13325 but verifies |
| 9 | +// AsyncLocalStorage functionality instead async_hooks |
| 10 | + |
| 11 | +constcls=newAsyncLocalStorage(); |
| 12 | + |
| 13 | +// Make sure a single socket is transparently reused for 2 requests. |
| 14 | +constagent=newhttp.Agent({ |
| 15 | +keepAlive: true, |
| 16 | +keepAliveMsecs: Infinity, |
| 17 | +maxSockets: 1 |
| 18 | +}); |
| 19 | + |
| 20 | +constserver=http.createServer(common.mustCall((req,res)=>{ |
| 21 | +req.once('data',common.mustCallAtLeast(()=>{ |
| 22 | +res.writeHead(200,{'Content-Type': 'text/plain'}); |
| 23 | +res.write('foo'); |
| 24 | +})); |
| 25 | +req.on('end',common.mustCall(()=>{ |
| 26 | +res.end('bar'); |
| 27 | +})); |
| 28 | +},2)).listen(0,common.mustCall(()=>{ |
| 29 | +constport=server.address().port; |
| 30 | +constpayload='hello world'; |
| 31 | + |
| 32 | +// First request. This is useless except for adding a socket to the |
| 33 | +// agent’s pool for reuse. |
| 34 | +cls.run('first',common.mustCall(()=>{ |
| 35 | +assert.strictEqual(cls.getStore(),'first'); |
| 36 | +constr1=http.request({ |
| 37 | + agent, port,method: 'POST' |
| 38 | +},common.mustCall((res)=>{ |
| 39 | +assert.strictEqual(cls.getStore(),'first'); |
| 40 | +res.on('data',common.mustCallAtLeast(()=>{ |
| 41 | +assert.strictEqual(cls.getStore(),'first'); |
| 42 | +})); |
| 43 | +res.on('end',common.mustCall(()=>{ |
| 44 | +assert.strictEqual(cls.getStore(),'first'); |
| 45 | +// setImmediate() to give the agent time to register the freed socket. |
| 46 | +setImmediate(common.mustCall(()=>{ |
| 47 | +assert.strictEqual(cls.getStore(),'first'); |
| 48 | + |
| 49 | +cls.run('second',common.mustCall(()=>{ |
| 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 | +constr2=http.request({ |
| 55 | + agent, port,method: 'POST',headers: { |
| 56 | +'Content-Length': payload.length |
| 57 | +} |
| 58 | +},common.mustCall((res)=>{ |
| 59 | +assert.strictEqual(cls.getStore(),'second'); |
| 60 | +// Empty payload, to hit the “right” code path. |
| 61 | +r2.end(''); |
| 62 | + |
| 63 | +res.on('data',common.mustCallAtLeast(()=>{ |
| 64 | +assert.strictEqual(cls.getStore(),'second'); |
| 65 | +})); |
| 66 | +res.on('end',common.mustCall(()=>{ |
| 67 | +assert.strictEqual(cls.getStore(),'second'); |
| 68 | +// Clean up to let the event loop stop. |
| 69 | +server.close(); |
| 70 | +agent.destroy(); |
| 71 | +})); |
| 72 | +})); |
| 73 | + |
| 74 | +// Schedule a payload to be written immediately, but do not end the |
| 75 | +// request just yet. |
| 76 | +r2.write(payload); |
| 77 | +})); |
| 78 | +})); |
| 79 | +})); |
| 80 | +})); |
| 81 | +r1.end(payload); |
| 82 | +})); |
| 83 | +})); |
0 commit comments