11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ci// Testcase to check reporting of uv handles.
41cb0ef41Sopenharmony_ciconst common = require('../common');
51cb0ef41Sopenharmony_ciconst tmpdir = require('../common/tmpdir');
61cb0ef41Sopenharmony_ciconst path = require('path');
71cb0ef41Sopenharmony_ciif (common.isIBMi)
81cb0ef41Sopenharmony_ci  common.skip('IBMi does not support fs.watch()');
91cb0ef41Sopenharmony_ci
101cb0ef41Sopenharmony_ci// This is quite similar to common.PIPE except that it uses an extended prefix
111cb0ef41Sopenharmony_ci// of "\\?\pipe" on windows.
121cb0ef41Sopenharmony_ciconst PIPE = (() => {
131cb0ef41Sopenharmony_ci  const localRelative = path.relative(process.cwd(), `${tmpdir.path}/`);
141cb0ef41Sopenharmony_ci  const pipePrefix = common.isWindows ? '\\\\?\\pipe\\' : localRelative;
151cb0ef41Sopenharmony_ci  const pipeName = `node-test.${process.pid}.sock`;
161cb0ef41Sopenharmony_ci  return path.join(pipePrefix, pipeName);
171cb0ef41Sopenharmony_ci})();
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_cifunction createFsHandle(childData) {
201cb0ef41Sopenharmony_ci  const fs = require('fs');
211cb0ef41Sopenharmony_ci  // Watching files should result in fs_event/fs_poll uv handles.
221cb0ef41Sopenharmony_ci  let watcher;
231cb0ef41Sopenharmony_ci  try {
241cb0ef41Sopenharmony_ci    watcher = fs.watch(__filename);
251cb0ef41Sopenharmony_ci  } catch {
261cb0ef41Sopenharmony_ci    // fs.watch() unavailable
271cb0ef41Sopenharmony_ci  }
281cb0ef41Sopenharmony_ci  fs.watchFile(__filename, () => {});
291cb0ef41Sopenharmony_ci  childData.skip_fs_watch = watcher === undefined;
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_ci  return () => {
321cb0ef41Sopenharmony_ci    if (watcher) watcher.close();
331cb0ef41Sopenharmony_ci    fs.unwatchFile(__filename);
341cb0ef41Sopenharmony_ci  };
351cb0ef41Sopenharmony_ci}
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_cifunction createChildProcessHandle(childData) {
381cb0ef41Sopenharmony_ci  const spawn = require('child_process').spawn;
391cb0ef41Sopenharmony_ci  // Child should exist when this returns as child_process.pid must be set.
401cb0ef41Sopenharmony_ci  const cp = spawn(process.execPath,
411cb0ef41Sopenharmony_ci                   ['-e', "process.stdin.on('data', (x) => " +
421cb0ef41Sopenharmony_ci          'console.log(x.toString()));']);
431cb0ef41Sopenharmony_ci  childData.pid = cp.pid;
441cb0ef41Sopenharmony_ci
451cb0ef41Sopenharmony_ci  return () => {
461cb0ef41Sopenharmony_ci    cp.kill();
471cb0ef41Sopenharmony_ci  };
481cb0ef41Sopenharmony_ci}
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_cifunction createTimerHandle() {
511cb0ef41Sopenharmony_ci  const timeout = setInterval(() => {}, 1000);
521cb0ef41Sopenharmony_ci  // Make sure the timer doesn't keep the test alive and let
531cb0ef41Sopenharmony_ci  // us check we detect unref'd handles correctly.
541cb0ef41Sopenharmony_ci  timeout.unref();
551cb0ef41Sopenharmony_ci  return () => {
561cb0ef41Sopenharmony_ci    clearInterval(timeout);
571cb0ef41Sopenharmony_ci  };
581cb0ef41Sopenharmony_ci}
591cb0ef41Sopenharmony_ci
601cb0ef41Sopenharmony_cifunction createTcpHandle(childData) {
611cb0ef41Sopenharmony_ci  const http = require('http');
621cb0ef41Sopenharmony_ci
631cb0ef41Sopenharmony_ci  return new Promise((resolve) => {
641cb0ef41Sopenharmony_ci    // Simple server/connection to create tcp uv handles.
651cb0ef41Sopenharmony_ci    const server = http.createServer((req, res) => {
661cb0ef41Sopenharmony_ci      req.on('end', () => {
671cb0ef41Sopenharmony_ci        resolve(() => {
681cb0ef41Sopenharmony_ci          res.writeHead(200, { 'Content-Type': 'text/plain' });
691cb0ef41Sopenharmony_ci          res.end();
701cb0ef41Sopenharmony_ci          server.close();
711cb0ef41Sopenharmony_ci        });
721cb0ef41Sopenharmony_ci      });
731cb0ef41Sopenharmony_ci      req.resume();
741cb0ef41Sopenharmony_ci    });
751cb0ef41Sopenharmony_ci    server.listen(() => {
761cb0ef41Sopenharmony_ci      childData.tcp_address = server.address();
771cb0ef41Sopenharmony_ci      http.get({ port: server.address().port });
781cb0ef41Sopenharmony_ci    });
791cb0ef41Sopenharmony_ci  });
801cb0ef41Sopenharmony_ci}
811cb0ef41Sopenharmony_ci
821cb0ef41Sopenharmony_cifunction createUdpHandle(childData) {
831cb0ef41Sopenharmony_ci  // Datagram socket for udp uv handles.
841cb0ef41Sopenharmony_ci  const dgram = require('dgram');
851cb0ef41Sopenharmony_ci  const udpSocket = dgram.createSocket('udp4');
861cb0ef41Sopenharmony_ci  const connectedUdpSocket = dgram.createSocket('udp4');
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_ci  return new Promise((resolve) => {
891cb0ef41Sopenharmony_ci    udpSocket.bind({}, common.mustCall(() => {
901cb0ef41Sopenharmony_ci      connectedUdpSocket.connect(udpSocket.address().port);
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_ci      childData.udp_address = udpSocket.address();
931cb0ef41Sopenharmony_ci      resolve(() => {
941cb0ef41Sopenharmony_ci        connectedUdpSocket.close();
951cb0ef41Sopenharmony_ci        udpSocket.close();
961cb0ef41Sopenharmony_ci      });
971cb0ef41Sopenharmony_ci    }));
981cb0ef41Sopenharmony_ci  });
991cb0ef41Sopenharmony_ci}
1001cb0ef41Sopenharmony_ci
1011cb0ef41Sopenharmony_cifunction createNamedPipeHandle(childData) {
1021cb0ef41Sopenharmony_ci  const net = require('net');
1031cb0ef41Sopenharmony_ci  const sockPath = PIPE;
1041cb0ef41Sopenharmony_ci  return new Promise((resolve) => {
1051cb0ef41Sopenharmony_ci    const server = net.createServer((socket) => {
1061cb0ef41Sopenharmony_ci      childData.pipe_sock_path = server.address();
1071cb0ef41Sopenharmony_ci      resolve(() => {
1081cb0ef41Sopenharmony_ci        socket.end();
1091cb0ef41Sopenharmony_ci        server.close();
1101cb0ef41Sopenharmony_ci      });
1111cb0ef41Sopenharmony_ci    });
1121cb0ef41Sopenharmony_ci    server.listen(
1131cb0ef41Sopenharmony_ci      sockPath,
1141cb0ef41Sopenharmony_ci      () => {
1151cb0ef41Sopenharmony_ci        net.connect(sockPath, (socket) => {});
1161cb0ef41Sopenharmony_ci      });
1171cb0ef41Sopenharmony_ci  });
1181cb0ef41Sopenharmony_ci}
1191cb0ef41Sopenharmony_ci
1201cb0ef41Sopenharmony_ciasync function child() {
1211cb0ef41Sopenharmony_ci  // Exit on loss of parent process
1221cb0ef41Sopenharmony_ci  const exit = () => process.exit(2);
1231cb0ef41Sopenharmony_ci  process.on('disconnect', exit);
1241cb0ef41Sopenharmony_ci
1251cb0ef41Sopenharmony_ci  const childData = {};
1261cb0ef41Sopenharmony_ci  const disposes = await Promise.all([
1271cb0ef41Sopenharmony_ci    createFsHandle(childData),
1281cb0ef41Sopenharmony_ci    createChildProcessHandle(childData),
1291cb0ef41Sopenharmony_ci    createTimerHandle(childData),
1301cb0ef41Sopenharmony_ci    createTcpHandle(childData),
1311cb0ef41Sopenharmony_ci    createUdpHandle(childData),
1321cb0ef41Sopenharmony_ci    createNamedPipeHandle(childData),
1331cb0ef41Sopenharmony_ci  ]);
1341cb0ef41Sopenharmony_ci  process.send(childData);
1351cb0ef41Sopenharmony_ci
1361cb0ef41Sopenharmony_ci  // Generate the report while the connection is active.
1371cb0ef41Sopenharmony_ci  console.log(JSON.stringify(process.report.getReport(), null, 2));
1381cb0ef41Sopenharmony_ci
1391cb0ef41Sopenharmony_ci  // Tidy up to allow process to exit cleanly.
1401cb0ef41Sopenharmony_ci  disposes.forEach((it) => {
1411cb0ef41Sopenharmony_ci    it();
1421cb0ef41Sopenharmony_ci  });
1431cb0ef41Sopenharmony_ci  process.removeListener('disconnect', exit);
1441cb0ef41Sopenharmony_ci}
1451cb0ef41Sopenharmony_ci
1461cb0ef41Sopenharmony_ciif (process.argv[2] === 'child') {
1471cb0ef41Sopenharmony_ci  child();
1481cb0ef41Sopenharmony_ci} else {
1491cb0ef41Sopenharmony_ci  const helper = require('../common/report.js');
1501cb0ef41Sopenharmony_ci  const fork = require('child_process').fork;
1511cb0ef41Sopenharmony_ci  const assert = require('assert');
1521cb0ef41Sopenharmony_ci  tmpdir.refresh();
1531cb0ef41Sopenharmony_ci  const options = { encoding: 'utf8', silent: true, cwd: tmpdir.path };
1541cb0ef41Sopenharmony_ci  const child = fork(__filename, ['child'], options);
1551cb0ef41Sopenharmony_ci  let child_data;
1561cb0ef41Sopenharmony_ci  child.on('message', (data) => { child_data = data; });
1571cb0ef41Sopenharmony_ci  let stderr = '';
1581cb0ef41Sopenharmony_ci  child.stderr.on('data', (chunk) => { stderr += chunk; });
1591cb0ef41Sopenharmony_ci  let stdout = '';
1601cb0ef41Sopenharmony_ci  const report_msg = 'Report files were written: unexpectedly';
1611cb0ef41Sopenharmony_ci  child.stdout.on('data', (chunk) => { stdout += chunk; });
1621cb0ef41Sopenharmony_ci  child.on('exit', common.mustCall((code, signal) => {
1631cb0ef41Sopenharmony_ci    assert.strictEqual(stderr.trim(), '');
1641cb0ef41Sopenharmony_ci    assert.strictEqual(code, 0, 'Process exited unexpectedly with code: ' +
1651cb0ef41Sopenharmony_ci                       `${code}`);
1661cb0ef41Sopenharmony_ci    assert.strictEqual(signal, null, 'Process should have exited cleanly,' +
1671cb0ef41Sopenharmony_ci                       ` but did not: ${signal}`);
1681cb0ef41Sopenharmony_ci
1691cb0ef41Sopenharmony_ci    const reports = helper.findReports(child.pid, tmpdir.path);
1701cb0ef41Sopenharmony_ci    assert.deepStrictEqual(reports, [], report_msg, reports);
1711cb0ef41Sopenharmony_ci
1721cb0ef41Sopenharmony_ci    // Test libuv handle key order
1731cb0ef41Sopenharmony_ci    {
1741cb0ef41Sopenharmony_ci      const get_libuv = /"libuv":\s\[([\s\S]*?)\]/g;
1751cb0ef41Sopenharmony_ci      const get_handle_inner = /{([\s\S]*?),*?}/g;
1761cb0ef41Sopenharmony_ci      const libuv_handles_str = get_libuv.exec(stdout)[1];
1771cb0ef41Sopenharmony_ci      const libuv_handles_array = libuv_handles_str.match(get_handle_inner);
1781cb0ef41Sopenharmony_ci      for (const i of libuv_handles_array) {
1791cb0ef41Sopenharmony_ci        // Exclude nested structure
1801cb0ef41Sopenharmony_ci        if (i.includes('type')) {
1811cb0ef41Sopenharmony_ci          const handle_keys = i.match(/(".*"):/g);
1821cb0ef41Sopenharmony_ci          assert(handle_keys[0], 'type');
1831cb0ef41Sopenharmony_ci          assert(handle_keys[1], 'is_active');
1841cb0ef41Sopenharmony_ci        }
1851cb0ef41Sopenharmony_ci      }
1861cb0ef41Sopenharmony_ci    }
1871cb0ef41Sopenharmony_ci
1881cb0ef41Sopenharmony_ci    const report = JSON.parse(stdout);
1891cb0ef41Sopenharmony_ci    const prefix = common.isWindows ? '\\\\?\\' : '';
1901cb0ef41Sopenharmony_ci    const expected_filename = `${prefix}${__filename}`;
1911cb0ef41Sopenharmony_ci    const found_tcp = [];
1921cb0ef41Sopenharmony_ci    const found_udp = [];
1931cb0ef41Sopenharmony_ci    const found_named_pipe = [];
1941cb0ef41Sopenharmony_ci    // Functions are named to aid debugging when they are not called.
1951cb0ef41Sopenharmony_ci    const validators = {
1961cb0ef41Sopenharmony_ci      fs_event: common.mustCall(function fs_event_validator(handle) {
1971cb0ef41Sopenharmony_ci        if (!child_data.skip_fs_watch) {
1981cb0ef41Sopenharmony_ci          assert.strictEqual(handle.filename, expected_filename);
1991cb0ef41Sopenharmony_ci          assert(handle.is_referenced);
2001cb0ef41Sopenharmony_ci        }
2011cb0ef41Sopenharmony_ci      }),
2021cb0ef41Sopenharmony_ci      fs_poll: common.mustCall(function fs_poll_validator(handle) {
2031cb0ef41Sopenharmony_ci        assert.strictEqual(handle.filename, expected_filename);
2041cb0ef41Sopenharmony_ci        assert(handle.is_referenced);
2051cb0ef41Sopenharmony_ci      }),
2061cb0ef41Sopenharmony_ci      loop: common.mustCall(function loop_validator(handle) {
2071cb0ef41Sopenharmony_ci        assert.strictEqual(typeof handle.loopIdleTimeSeconds, 'number');
2081cb0ef41Sopenharmony_ci      }),
2091cb0ef41Sopenharmony_ci      pipe: common.mustCallAtLeast(function pipe_validator(handle) {
2101cb0ef41Sopenharmony_ci        assert(handle.is_referenced);
2111cb0ef41Sopenharmony_ci        // Pipe handles. The report should contain three pipes:
2121cb0ef41Sopenharmony_ci        // 1. The server's listening pipe.
2131cb0ef41Sopenharmony_ci        // 2. The inbound pipe making the request.
2141cb0ef41Sopenharmony_ci        // 3. The outbound pipe sending the response.
2151cb0ef41Sopenharmony_ci        //
2161cb0ef41Sopenharmony_ci        // There is no way to distinguish inbound and outbound in a cross
2171cb0ef41Sopenharmony_ci        // platform manner, so we just check inbound here.
2181cb0ef41Sopenharmony_ci        const sockPath = child_data.pipe_sock_path;
2191cb0ef41Sopenharmony_ci        if (handle.localEndpoint === sockPath) {
2201cb0ef41Sopenharmony_ci          if (handle.writable === false) {
2211cb0ef41Sopenharmony_ci            found_named_pipe.push('listening');
2221cb0ef41Sopenharmony_ci          }
2231cb0ef41Sopenharmony_ci        } else if (handle.remoteEndpoint === sockPath) {
2241cb0ef41Sopenharmony_ci          found_named_pipe.push('inbound');
2251cb0ef41Sopenharmony_ci        }
2261cb0ef41Sopenharmony_ci      }),
2271cb0ef41Sopenharmony_ci      process: common.mustCall(function process_validator(handle) {
2281cb0ef41Sopenharmony_ci        assert.strictEqual(handle.pid, child_data.pid);
2291cb0ef41Sopenharmony_ci        assert(handle.is_referenced);
2301cb0ef41Sopenharmony_ci      }),
2311cb0ef41Sopenharmony_ci      tcp: common.mustCall(function tcp_validator(handle) {
2321cb0ef41Sopenharmony_ci        // TCP handles. The report should contain three sockets:
2331cb0ef41Sopenharmony_ci        // 1. The server's listening socket.
2341cb0ef41Sopenharmony_ci        // 2. The inbound socket making the request.
2351cb0ef41Sopenharmony_ci        // 3. The outbound socket sending the response.
2361cb0ef41Sopenharmony_ci        const port = child_data.tcp_address.port;
2371cb0ef41Sopenharmony_ci        if (handle.localEndpoint.port === port) {
2381cb0ef41Sopenharmony_ci          if (handle.remoteEndpoint === null) {
2391cb0ef41Sopenharmony_ci            found_tcp.push('listening');
2401cb0ef41Sopenharmony_ci          } else {
2411cb0ef41Sopenharmony_ci            found_tcp.push('inbound');
2421cb0ef41Sopenharmony_ci          }
2431cb0ef41Sopenharmony_ci        } else if (handle.remoteEndpoint.port === port) {
2441cb0ef41Sopenharmony_ci          found_tcp.push('outbound');
2451cb0ef41Sopenharmony_ci        }
2461cb0ef41Sopenharmony_ci        assert(handle.is_referenced);
2471cb0ef41Sopenharmony_ci      }, 3),
2481cb0ef41Sopenharmony_ci      timer: common.mustCallAtLeast(function timer_validator(handle) {
2491cb0ef41Sopenharmony_ci        assert(!handle.is_referenced);
2501cb0ef41Sopenharmony_ci        assert.strictEqual(handle.repeat, 0);
2511cb0ef41Sopenharmony_ci      }),
2521cb0ef41Sopenharmony_ci      udp: common.mustCall(function udp_validator(handle) {
2531cb0ef41Sopenharmony_ci        if (handle.remoteEndpoint === null) {
2541cb0ef41Sopenharmony_ci          assert.strictEqual(handle.localEndpoint.port,
2551cb0ef41Sopenharmony_ci                             child_data.udp_address.port);
2561cb0ef41Sopenharmony_ci          found_udp.push('unconnected');
2571cb0ef41Sopenharmony_ci        } else {
2581cb0ef41Sopenharmony_ci          assert.strictEqual(handle.remoteEndpoint.port,
2591cb0ef41Sopenharmony_ci                             child_data.udp_address.port);
2601cb0ef41Sopenharmony_ci          found_udp.push('connected');
2611cb0ef41Sopenharmony_ci        }
2621cb0ef41Sopenharmony_ci        assert(handle.is_referenced);
2631cb0ef41Sopenharmony_ci        assert.strictEqual(handle.writeQueueSize, 0);
2641cb0ef41Sopenharmony_ci        assert.strictEqual(handle.writeQueueCount, 0);
2651cb0ef41Sopenharmony_ci      }, 2),
2661cb0ef41Sopenharmony_ci    };
2671cb0ef41Sopenharmony_ci
2681cb0ef41Sopenharmony_ci    for (const entry of report.libuv) {
2691cb0ef41Sopenharmony_ci      if (validators[entry.type]) validators[entry.type](entry);
2701cb0ef41Sopenharmony_ci    }
2711cb0ef41Sopenharmony_ci    for (const socket of ['listening', 'inbound', 'outbound']) {
2721cb0ef41Sopenharmony_ci      assert(found_tcp.includes(socket), `${socket} TCP socket was not found`);
2731cb0ef41Sopenharmony_ci    }
2741cb0ef41Sopenharmony_ci    for (const socket of ['connected', 'unconnected']) {
2751cb0ef41Sopenharmony_ci      assert(found_udp.includes(socket), `${socket} UDP socket was not found`);
2761cb0ef41Sopenharmony_ci    }
2771cb0ef41Sopenharmony_ci    for (const socket of ['listening', 'inbound']) {
2781cb0ef41Sopenharmony_ci      assert(found_named_pipe.includes(socket), `${socket} named pipe socket was not found`);
2791cb0ef41Sopenharmony_ci    }
2801cb0ef41Sopenharmony_ci
2811cb0ef41Sopenharmony_ci    // Common report tests.
2821cb0ef41Sopenharmony_ci    helper.validateContent(stdout);
2831cb0ef41Sopenharmony_ci  }));
2841cb0ef41Sopenharmony_ci}
285