1// Flags: --expose-internals 2'use strict'; 3const common = require('../common'); 4if (common.isWindows) 5 common.skip('Does not support wrapping sockets with fd on Windows'); 6 7const assert = require('assert'); 8const net = require('net'); 9const path = require('path'); 10const { internalBinding } = require('internal/test/binding'); 11const { Pipe, constants: PipeConstants } = internalBinding('pipe_wrap'); 12 13const tmpdir = require('../common/tmpdir'); 14tmpdir.refresh(); 15 16function testClients(getSocketOpt, getConnectOpt, getConnectCb) { 17 const cloneOptions = (index) => 18 ({ ...getSocketOpt(index), ...getConnectOpt(index) }); 19 return [ 20 net.connect(cloneOptions(0), getConnectCb(0)), 21 net.connect(cloneOptions(1)) 22 .on('connect', getConnectCb(1)), 23 net.createConnection(cloneOptions(2), getConnectCb(2)), 24 net.createConnection(cloneOptions(3)) 25 .on('connect', getConnectCb(3)), 26 new net.Socket(getSocketOpt(4)).connect(getConnectOpt(4), getConnectCb(4)), 27 new net.Socket(getSocketOpt(5)).connect(getConnectOpt(5)) 28 .on('connect', getConnectCb(5)), 29 ]; 30} 31 32const CLIENT_VARIANTS = 6; // Same length as array above 33const forAllClients = (cb) => common.mustCall(cb, CLIENT_VARIANTS); 34 35// Test Pipe fd is wrapped correctly 36{ 37 // Use relative path to avoid hitting 108-char length limit 38 // for socket paths in libuv. 39 const prefix = path.relative('.', `${common.PIPE}-net-connect-options-fd`); 40 const serverPath = `${prefix}-server`; 41 let counter = 0; 42 let socketCounter = 0; 43 const handleMap = new Map(); 44 const server = net.createServer() 45 .on('connection', forAllClients(function serverOnConnection(socket) { 46 let clientFd; 47 socket.on('data', common.mustCall(function(data) { 48 clientFd = data.toString(); 49 console.error(`[Pipe]Received data from fd ${clientFd}`); 50 socket.end(); 51 })); 52 socket.on('end', common.mustCall(function() { 53 counter++; 54 console.error(`[Pipe]Received end from fd ${clientFd}, total ${counter}`); 55 if (counter === CLIENT_VARIANTS) { 56 setTimeout(() => { 57 console.error(`[Pipe]Server closed by fd ${clientFd}`); 58 server.close(); 59 }, 10); 60 } 61 }, 1)); 62 })) 63 .on('close', function() { 64 setTimeout(() => { 65 for (const pair of handleMap) { 66 console.error(`[Pipe]Clean up handle with fd ${pair[1].fd}`); 67 pair[1].close(); // clean up handles 68 } 69 }, 10); 70 }) 71 .on('error', function(err) { 72 console.error(err); 73 assert.fail(`[Pipe server]${err}`); 74 }) 75 .listen({ path: serverPath }, common.mustCall(function serverOnListen() { 76 const getSocketOpt = (index) => { 77 const handle = new Pipe(PipeConstants.SOCKET); 78 const err = handle.bind(`${prefix}-client-${socketCounter++}`); 79 assert(err >= 0, String(err)); 80 assert.notStrictEqual(handle.fd, -1); 81 handleMap.set(index, handle); 82 console.error(`[Pipe]Bound handle with Pipe ${handle.fd}`); 83 return { fd: handle.fd, readable: true, writable: true }; 84 }; 85 const getConnectOpt = () => ({ 86 path: serverPath 87 }); 88 const getConnectCb = (index) => common.mustCall(function clientOnConnect() { 89 // Test if it's wrapping an existing fd 90 assert(handleMap.has(index)); 91 const oldHandle = handleMap.get(index); 92 assert.strictEqual(oldHandle.fd, this._handle.fd); 93 this.write(String(oldHandle.fd)); 94 console.error(`[Pipe]Sending data through fd ${oldHandle.fd}`); 95 this.on('error', function(err) { 96 console.error(err); 97 assert.fail(`[Pipe Client]${err}`); 98 }); 99 }); 100 101 testClients(getSocketOpt, getConnectOpt, getConnectCb); 102 })); 103} 104