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'; 23// This test starts two clustered HTTP servers on the same port. It expects the 24// first cluster to succeed and the second cluster to fail with EADDRINUSE. 25// 26// The test may seem complex but most of it is plumbing that routes messages 27// from the child processes back to the supervisor. As a tree it looks something 28// like this: 29// 30// <supervisor> 31// / \ 32// <master 1> <master 2> 33// / \ 34// <worker 1> <worker 2> 35// 36// The first worker starts a server on a fixed port and fires a ready message 37// that is routed to the second worker. When it tries to bind, it expects to 38// see an EADDRINUSE error. 39// 40// See https://github.com/joyent/node/issues/2721 for more details. 41 42const common = require('../common'); 43const assert = require('assert'); 44const cluster = require('cluster'); 45const fork = require('child_process').fork; 46const http = require('http'); 47 48const id = process.argv[2]; 49 50if (!id) { 51 const a = fork(__filename, ['one']); 52 const b = fork(__filename, ['two']); 53 54 a.on('exit', common.mustCall((c) => { 55 if (c) { 56 b.send('QUIT'); 57 throw new Error(`A exited with ${c}`); 58 } 59 })); 60 61 b.on('exit', common.mustCall((c) => { 62 if (c) { 63 a.send('QUIT'); 64 throw new Error(`B exited with ${c}`); 65 } 66 })); 67 68 69 a.on('message', common.mustCall((m) => { 70 assert.strictEqual(m.msg, 'READY'); 71 b.send({ msg: 'START', port: m.port }); 72 })); 73 74 b.on('message', common.mustCall((m) => { 75 assert.strictEqual(m, 'EADDRINUSE'); 76 a.send('QUIT'); 77 b.send('QUIT'); 78 })); 79 80} else if (id === 'one') { 81 if (cluster.isMaster) return startWorker(); 82 83 const server = http.createServer(common.mustNotCall()); 84 server.listen(0, common.mustCall(() => { 85 process.send({ msg: 'READY', port: server.address().port }); 86 })); 87 88 process.on('message', common.mustCall((m) => { 89 if (m === 'QUIT') process.exit(); 90 })); 91} else if (id === 'two') { 92 if (cluster.isMaster) return startWorker(); 93 94 const server = http.createServer(common.mustNotCall()); 95 process.on('message', common.mustCall((m) => { 96 if (m === 'QUIT') process.exit(); 97 assert.strictEqual(m.msg, 'START'); 98 server.listen(m.port, common.mustNotCall()); 99 server.on('error', common.mustCall((e) => { 100 assert.strictEqual(e.code, 'EADDRINUSE'); 101 process.send(e.code); 102 })); 103 }, 2)); 104} else { 105 assert(0); // Bad command line argument 106} 107 108function startWorker() { 109 const worker = cluster.fork(); 110 worker.on('exit', process.exit); 111 worker.on('message', process.send.bind(process)); 112 process.on('message', worker.send.bind(worker)); 113} 114