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'); 27 28// child 29if (process.argv[2] === 'child') { 30 31 // Check that the 'disconnect' event is deferred to the next event loop tick. 32 const disconnect = process.disconnect; 33 process.disconnect = function() { 34 disconnect.apply(this, arguments); 35 // If the event is emitted synchronously, we're too late by now. 36 process.once('disconnect', common.mustCall(disconnectIsNotAsync)); 37 // The funky function name makes it show up legible in mustCall errors. 38 function disconnectIsNotAsync() {} 39 }; 40 41 const server = net.createServer(); 42 43 server.on('connection', function(socket) { 44 45 socket.resume(); 46 47 process.on('disconnect', function() { 48 socket.end((process.connected).toString()); 49 }); 50 51 // When the socket is closed, we will close the server 52 // allowing the process to self terminate 53 socket.on('end', function() { 54 server.close(); 55 }); 56 57 socket.write('ready'); 58 }); 59 60 // When the server is ready tell parent 61 server.on('listening', function() { 62 process.send({ msg: 'ready', port: server.address().port }); 63 }); 64 65 server.listen(0); 66 67} else { 68 // testcase 69 const child = fork(process.argv[1], ['child']); 70 71 let childFlag = false; 72 let parentFlag = false; 73 74 // When calling .disconnect the event should emit 75 // and the disconnected flag should be true. 76 child.on('disconnect', common.mustCall(function() { 77 parentFlag = child.connected; 78 })); 79 80 // The process should also self terminate without using signals 81 child.on('exit', common.mustCall()); 82 83 // When child is listening 84 child.on('message', function(obj) { 85 if (obj && obj.msg === 'ready') { 86 87 // Connect to child using TCP to know if disconnect was emitted 88 const socket = net.connect(obj.port); 89 90 socket.on('data', function(data) { 91 data = data.toString(); 92 93 // Ready to be disconnected 94 if (data === 'ready') { 95 child.disconnect(); 96 assert.throws( 97 child.disconnect.bind(child), 98 { 99 code: 'ERR_IPC_DISCONNECTED' 100 }); 101 return; 102 } 103 104 // 'disconnect' is emitted 105 childFlag = (data === 'true'); 106 }); 107 108 } 109 }); 110 111 process.on('exit', function() { 112 assert.strictEqual(childFlag, false); 113 assert.strictEqual(parentFlag, false); 114 }); 115} 116