• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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