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 { 24 mustCall, 25 mustCallAtLeast, 26 platformTimeout, 27} = require('../common'); 28const assert = require('assert'); 29const fork = require('child_process').fork; 30const net = require('net'); 31const debug = require('util').debuglog('test'); 32const count = 12; 33 34if (process.argv[2] === 'child') { 35 const needEnd = []; 36 const id = process.argv[3]; 37 38 process.on('message', mustCall((m, socket) => { 39 if (!socket) return; 40 41 debug(`[${id}] got socket ${m}`); 42 43 // Will call .end('end') or .write('write'); 44 socket[m](m); 45 46 socket.resume(); 47 48 socket.on('data', mustCallAtLeast(() => { 49 debug(`[${id}] socket.data ${m}`); 50 })); 51 52 socket.on('end', mustCall(() => { 53 debug(`[${id}] socket.end ${m}`); 54 })); 55 56 // Store the unfinished socket 57 if (m === 'write') { 58 needEnd.push(socket); 59 } 60 61 socket.on('close', mustCall((had_error) => { 62 debug(`[${id}] socket.close ${had_error} ${m}`); 63 })); 64 65 socket.on('finish', mustCall(() => { 66 debug(`[${id}] socket finished ${m}`); 67 })); 68 })); 69 70 process.on('message', mustCall((m) => { 71 if (m !== 'close') return; 72 debug(`[${id}] got close message`); 73 needEnd.forEach((endMe, i) => { 74 debug(`[${id}] ending ${i}/${needEnd.length}`); 75 endMe.end('end'); 76 }); 77 })); 78 79 process.on('disconnect', mustCall(() => { 80 debug(`[${id}] process disconnect, ending`); 81 needEnd.forEach((endMe, i) => { 82 debug(`[${id}] ending ${i}/${needEnd.length}`); 83 endMe.end('end'); 84 }); 85 })); 86 87} else { 88 89 const child1 = fork(process.argv[1], ['child', '1']); 90 const child2 = fork(process.argv[1], ['child', '2']); 91 const child3 = fork(process.argv[1], ['child', '3']); 92 93 const server = net.createServer(); 94 95 let connected = 0; 96 let closed = 0; 97 server.on('connection', function(socket) { 98 switch (connected % 6) { 99 case 0: 100 child1.send('end', socket); break; 101 case 1: 102 child1.send('write', socket); break; 103 case 2: 104 child2.send('end', socket); break; 105 case 3: 106 child2.send('write', socket); break; 107 case 4: 108 child3.send('end', socket); break; 109 case 5: 110 child3.send('write', socket); break; 111 } 112 connected += 1; 113 114 // TODO(@jasnell): This is not actually being called. 115 // It is not clear if it is needed. 116 socket.once('close', () => { 117 debug(`[m] socket closed, total ${++closed}`); 118 }); 119 120 if (connected === count) { 121 closeServer(); 122 } 123 }); 124 125 let disconnected = 0; 126 server.on('listening', mustCall(() => { 127 128 let j = count; 129 while (j--) { 130 const client = net.connect(server.address().port, '127.0.0.1'); 131 client.on('error', () => { 132 // This can happen if we kill the subprocess too early. 133 // The client should still get a close event afterwards. 134 // It likely won't so don't wrap in a mustCall. 135 debug('[m] CLIENT: error event'); 136 }); 137 client.on('close', mustCall(() => { 138 debug('[m] CLIENT: close event'); 139 disconnected += 1; 140 })); 141 client.resume(); 142 } 143 })); 144 145 let closeEmitted = false; 146 server.on('close', mustCall(function() { 147 closeEmitted = true; 148 149 child1.kill(); 150 child2.kill(); 151 child3.kill(); 152 })); 153 154 server.listen(0, '127.0.0.1'); 155 156 function closeServer() { 157 server.close(); 158 159 setTimeout(() => { 160 assert(!closeEmitted); 161 child1.send('close'); 162 child2.send('close'); 163 child3.disconnect(); 164 }, platformTimeout(200)); 165 } 166 167 process.on('exit', function() { 168 assert.strictEqual(server._workers.length, 0); 169 assert.strictEqual(disconnected, count); 170 assert.strictEqual(connected, count); 171 }); 172} 173