11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ci// Flags: --expose-internals
31cb0ef41Sopenharmony_ciconst common = require('../common');
41cb0ef41Sopenharmony_ciconst initHooks = require('./init-hooks');
51cb0ef41Sopenharmony_ciconst { checkInvocations } = require('./hook-checks');
61cb0ef41Sopenharmony_ciconst assert = require('assert');
71cb0ef41Sopenharmony_ciconst { async_id_symbol } = require('internal/async_hooks').symbols;
81cb0ef41Sopenharmony_ciconst http = require('http');
91cb0ef41Sopenharmony_ci
101cb0ef41Sopenharmony_ci// Checks that the async resource used in init in case of a reused handle
111cb0ef41Sopenharmony_ci// is not reused. Test is based on parallel\test-async-hooks-http-agent.js.
121cb0ef41Sopenharmony_ci
131cb0ef41Sopenharmony_ciconst hooks = initHooks();
141cb0ef41Sopenharmony_cihooks.enable();
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_cilet asyncIdAtFirstReq;
171cb0ef41Sopenharmony_cilet asyncIdAtSecondReq;
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ci// Make sure a single socket is transparently reused for 2 requests.
201cb0ef41Sopenharmony_ciconst agent = new http.Agent({
211cb0ef41Sopenharmony_ci  keepAlive: true,
221cb0ef41Sopenharmony_ci  keepAliveMsecs: Infinity,
231cb0ef41Sopenharmony_ci  maxSockets: 1,
241cb0ef41Sopenharmony_ci});
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ciconst server = http.createServer(common.mustCall((req, res) => {
271cb0ef41Sopenharmony_ci  req.once('data', common.mustCallAtLeast(() => {
281cb0ef41Sopenharmony_ci    res.writeHead(200, { 'Content-Type': 'text/plain' });
291cb0ef41Sopenharmony_ci    res.write('foo');
301cb0ef41Sopenharmony_ci  }));
311cb0ef41Sopenharmony_ci  req.on('end', common.mustCall(() => {
321cb0ef41Sopenharmony_ci    res.end('bar');
331cb0ef41Sopenharmony_ci  }));
341cb0ef41Sopenharmony_ci}, 2)).listen(0, common.mustCall(() => {
351cb0ef41Sopenharmony_ci  const port = server.address().port;
361cb0ef41Sopenharmony_ci  const payload = 'hello world';
371cb0ef41Sopenharmony_ci
381cb0ef41Sopenharmony_ci  // First request. This is useless except for adding a socket to the
391cb0ef41Sopenharmony_ci  // agent’s pool for reuse.
401cb0ef41Sopenharmony_ci  const r1 = http.request({
411cb0ef41Sopenharmony_ci    agent, port, method: 'POST',
421cb0ef41Sopenharmony_ci  }, common.mustCall((res) => {
431cb0ef41Sopenharmony_ci    // Remember which socket we used.
441cb0ef41Sopenharmony_ci    const socket = res.socket;
451cb0ef41Sopenharmony_ci    asyncIdAtFirstReq = socket[async_id_symbol];
461cb0ef41Sopenharmony_ci    assert.ok(asyncIdAtFirstReq > 0, `${asyncIdAtFirstReq} > 0`);
471cb0ef41Sopenharmony_ci    // Check that request and response share their socket.
481cb0ef41Sopenharmony_ci    assert.strictEqual(r1.socket, socket);
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ci    res.on('data', common.mustCallAtLeast());
511cb0ef41Sopenharmony_ci    res.on('end', common.mustCall(() => {
521cb0ef41Sopenharmony_ci      // setImmediate() to give the agent time to register the freed socket.
531cb0ef41Sopenharmony_ci      setImmediate(common.mustCall(() => {
541cb0ef41Sopenharmony_ci        // The socket is free for reuse now.
551cb0ef41Sopenharmony_ci        assert.strictEqual(socket[async_id_symbol], -1);
561cb0ef41Sopenharmony_ci
571cb0ef41Sopenharmony_ci        // Second request. To re-create the exact conditions from the
581cb0ef41Sopenharmony_ci        // referenced issue, we use a POST request without chunked encoding
591cb0ef41Sopenharmony_ci        // (hence the Content-Length header) and call .end() after the
601cb0ef41Sopenharmony_ci        // response header has already been received.
611cb0ef41Sopenharmony_ci        const r2 = http.request({
621cb0ef41Sopenharmony_ci          agent, port, method: 'POST', headers: {
631cb0ef41Sopenharmony_ci            'Content-Length': payload.length,
641cb0ef41Sopenharmony_ci          },
651cb0ef41Sopenharmony_ci        }, common.mustCall((res) => {
661cb0ef41Sopenharmony_ci          asyncIdAtSecondReq = res.socket[async_id_symbol];
671cb0ef41Sopenharmony_ci          assert.ok(asyncIdAtSecondReq > 0, `${asyncIdAtSecondReq} > 0`);
681cb0ef41Sopenharmony_ci          assert.strictEqual(r2.socket, socket);
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_ci          // Empty payload, to hit the “right” code path.
711cb0ef41Sopenharmony_ci          r2.end('');
721cb0ef41Sopenharmony_ci
731cb0ef41Sopenharmony_ci          res.on('data', common.mustCallAtLeast());
741cb0ef41Sopenharmony_ci          res.on('end', common.mustCall(() => {
751cb0ef41Sopenharmony_ci            // Clean up to let the event loop stop.
761cb0ef41Sopenharmony_ci            server.close();
771cb0ef41Sopenharmony_ci            agent.destroy();
781cb0ef41Sopenharmony_ci          }));
791cb0ef41Sopenharmony_ci        }));
801cb0ef41Sopenharmony_ci
811cb0ef41Sopenharmony_ci        // Schedule a payload to be written immediately, but do not end the
821cb0ef41Sopenharmony_ci        // request just yet.
831cb0ef41Sopenharmony_ci        r2.write(payload);
841cb0ef41Sopenharmony_ci      }));
851cb0ef41Sopenharmony_ci    }));
861cb0ef41Sopenharmony_ci  }));
871cb0ef41Sopenharmony_ci  r1.end(payload);
881cb0ef41Sopenharmony_ci}));
891cb0ef41Sopenharmony_ci
901cb0ef41Sopenharmony_ci
911cb0ef41Sopenharmony_ciprocess.on('exit', onExit);
921cb0ef41Sopenharmony_ci
931cb0ef41Sopenharmony_cifunction onExit() {
941cb0ef41Sopenharmony_ci  hooks.disable();
951cb0ef41Sopenharmony_ci  hooks.sanityCheck();
961cb0ef41Sopenharmony_ci  const activities = hooks.activities;
971cb0ef41Sopenharmony_ci
981cb0ef41Sopenharmony_ci  // Verify both invocations
991cb0ef41Sopenharmony_ci  const first = activities.filter((x) => x.uid === asyncIdAtFirstReq)[0];
1001cb0ef41Sopenharmony_ci  checkInvocations(first, { init: 1, destroy: 1 }, 'when process exits');
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ci  const second = activities.filter((x) => x.uid === asyncIdAtSecondReq)[0];
1031cb0ef41Sopenharmony_ci  checkInvocations(second, { init: 1, destroy: 1 }, 'when process exits');
1041cb0ef41Sopenharmony_ci
1051cb0ef41Sopenharmony_ci  // Verify reuse handle has been wrapped
1061cb0ef41Sopenharmony_ci  assert.strictEqual(first.type, second.type);
1071cb0ef41Sopenharmony_ci  assert.ok(first.handle !== second.handle, 'Resource reused');
1081cb0ef41Sopenharmony_ci  assert.ok(first.handle === second.handle.handle,
1091cb0ef41Sopenharmony_ci            'Resource not wrapped correctly');
1101cb0ef41Sopenharmony_ci}
111