1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22'use strict'; 23const common = require('../common'); 24const assert = require('assert'); 25const fork = require('child_process').fork; 26const net = require('net'); 27const debug = require('util').debuglog('test'); 28 29const Countdown = require('../common/countdown'); 30 31if (process.argv[2] === 'child') { 32 33 let serverScope; 34 35 // TODO(@jasnell): The message event is not called consistently 36 // across platforms. Need to investigate if it can be made 37 // more consistent. 38 const onServer = (msg, server) => { 39 if (msg.what !== 'server') return; 40 process.removeListener('message', onServer); 41 42 serverScope = server; 43 44 // TODO(@jasnell): This is apparently not called consistently 45 // across platforms. Need to investigate if it can be made 46 // more consistent. 47 server.on('connection', (socket) => { 48 debug('CHILD: got connection'); 49 process.send({ what: 'connection' }); 50 socket.destroy(); 51 }); 52 53 // Start making connection from parent. 54 debug('CHILD: server listening'); 55 process.send({ what: 'listening' }); 56 }; 57 58 process.on('message', onServer); 59 60 // TODO(@jasnell): The close event is not called consistently 61 // across platforms. Need to investigate if it can be made 62 // more consistent. 63 const onClose = (msg) => { 64 if (msg.what !== 'close') return; 65 process.removeListener('message', onClose); 66 67 serverScope.on('close', common.mustCall(() => { 68 process.send({ what: 'close' }); 69 })); 70 serverScope.close(); 71 }; 72 73 process.on('message', onClose); 74 75 process.send({ what: 'ready' }); 76} else { 77 78 const child = fork(process.argv[1], ['child']); 79 80 child.on('exit', common.mustCall((code, signal) => { 81 const message = `CHILD: died with ${code}, ${signal}`; 82 assert.strictEqual(code, 0, message); 83 })); 84 85 // Send net.Server to child and test by connecting. 86 function testServer(callback) { 87 88 // Destroy server execute callback when done. 89 const countdown = new Countdown(2, () => { 90 server.on('close', common.mustCall(() => { 91 debug('PARENT: server closed'); 92 child.send({ what: 'close' }); 93 })); 94 server.close(); 95 }); 96 97 // We expect 4 connections and close events. 98 const connections = new Countdown(4, () => countdown.dec()); 99 const closed = new Countdown(4, () => countdown.dec()); 100 101 // Create server and send it to child. 102 const server = net.createServer(); 103 104 // TODO(@jasnell): The specific number of times the connection 105 // event is emitted appears to be variable across platforms. 106 // Need to investigate why and whether it can be made 107 // more consistent. 108 server.on('connection', (socket) => { 109 debug('PARENT: got connection'); 110 socket.destroy(); 111 connections.dec(); 112 }); 113 114 server.on('listening', common.mustCall(() => { 115 debug('PARENT: server listening'); 116 child.send({ what: 'server' }, server); 117 })); 118 server.listen(0); 119 120 // Handle client messages. 121 // TODO(@jasnell): The specific number of times the message 122 // event is emitted appears to be variable across platforms. 123 // Need to investigate why and whether it can be made 124 // more consistent. 125 const messageHandlers = (msg) => { 126 if (msg.what === 'listening') { 127 // Make connections. 128 let socket; 129 for (let i = 0; i < 4; i++) { 130 socket = net.connect(server.address().port, common.mustCall(() => { 131 debug('CLIENT: connected'); 132 })); 133 socket.on('close', common.mustCall(() => { 134 closed.dec(); 135 debug('CLIENT: closed'); 136 })); 137 } 138 139 } else if (msg.what === 'connection') { 140 // Child got connection 141 connections.dec(); 142 } else if (msg.what === 'close') { 143 child.removeListener('message', messageHandlers); 144 callback(); 145 } 146 }; 147 148 child.on('message', messageHandlers); 149 } 150 151 const onReady = common.mustCall((msg) => { 152 if (msg.what !== 'ready') return; 153 child.removeListener('message', onReady); 154 testServer(common.mustCall()); 155 }); 156 157 // Create server and send it to child. 158 child.on('message', onReady); 159} 160