• 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';
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