// Flags: --expose-internals 'use strict'; const common = require('../common'); if (common.isWindows) common.skip('Does not support wrapping sockets with fd on Windows'); const assert = require('assert'); const net = require('net'); const path = require('path'); const { internalBinding } = require('internal/test/binding'); const { Pipe, constants: PipeConstants } = internalBinding('pipe_wrap'); const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); function testClients(getSocketOpt, getConnectOpt, getConnectCb) { const cloneOptions = (index) => ({ ...getSocketOpt(index), ...getConnectOpt(index) }); return [ net.connect(cloneOptions(0), getConnectCb(0)), net.connect(cloneOptions(1)) .on('connect', getConnectCb(1)), net.createConnection(cloneOptions(2), getConnectCb(2)), net.createConnection(cloneOptions(3)) .on('connect', getConnectCb(3)), new net.Socket(getSocketOpt(4)).connect(getConnectOpt(4), getConnectCb(4)), new net.Socket(getSocketOpt(5)).connect(getConnectOpt(5)) .on('connect', getConnectCb(5)) ]; } const CLIENT_VARIANTS = 6; // Same length as array above const forAllClients = (cb) => common.mustCall(cb, CLIENT_VARIANTS); // Test Pipe fd is wrapped correctly { // Use relative path to avoid hitting 108-char length limit // for socket paths in libuv. const prefix = path.relative('.', `${common.PIPE}-net-connect-options-fd`); const serverPath = `${prefix}-server`; let counter = 0; let socketCounter = 0; const handleMap = new Map(); const server = net.createServer() .on('connection', forAllClients(function serverOnConnection(socket) { let clientFd; socket.on('data', common.mustCall(function(data) { clientFd = data.toString(); console.error(`[Pipe]Received data from fd ${clientFd}`); socket.end(); })); socket.on('end', common.mustCall(function() { counter++; console.error(`[Pipe]Received end from fd ${clientFd}, total ${counter}`); if (counter === CLIENT_VARIANTS) { setTimeout(() => { console.error(`[Pipe]Server closed by fd ${clientFd}`); server.close(); }, 10); } }, 1)); })) .on('close', function() { setTimeout(() => { for (const pair of handleMap) { console.error(`[Pipe]Clean up handle with fd ${pair[1].fd}`); pair[1].close(); // clean up handles } }, 10); }) .on('error', function(err) { console.error(err); assert.fail(`[Pipe server]${err}`); }) .listen({ path: serverPath }, common.mustCall(function serverOnListen() { const getSocketOpt = (index) => { const handle = new Pipe(PipeConstants.SOCKET); const err = handle.bind(`${prefix}-client-${socketCounter++}`); assert(err >= 0, String(err)); assert.notStrictEqual(handle.fd, -1); handleMap.set(index, handle); console.error(`[Pipe]Bound handle with Pipe ${handle.fd}`); return { fd: handle.fd, readable: true, writable: true }; }; const getConnectOpt = () => ({ path: serverPath }); const getConnectCb = (index) => common.mustCall(function clientOnConnect() { // Test if it's wrapping an existing fd assert(handleMap.has(index)); const oldHandle = handleMap.get(index); assert.strictEqual(oldHandle.fd, this._handle.fd); this.write(String(oldHandle.fd)); console.error(`[Pipe]Sending data through fd ${oldHandle.fd}`); this.on('error', function(err) { console.error(err); assert.fail(`[Pipe Client]${err}`); }); }); testClients(getSocketOpt, getConnectOpt, getConnectCb); })); }