• 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 net = require('net');
26
27// Test allowHalfOpen
28{
29  let clientReceivedFIN = 0;
30  let serverConnections = 0;
31  let clientSentFIN = 0;
32  let serverReceivedFIN = 0;
33  const host = common.localhostIPv4;
34
35  function serverOnConnection(socket) {
36    console.log(`'connection' ${++serverConnections} emitted on server`);
37    const srvConn = serverConnections;
38    socket.resume();
39    socket.on('data', common.mustCall(function socketOnData(data) {
40      this.clientId = data.toString();
41      console.log(
42        `server connection ${srvConn} is started by client ${this.clientId}`);
43    }));
44    // 'end' on each socket must not be emitted twice
45    socket.on('end', common.mustCall(function socketOnEnd() {
46      console.log(`Server received FIN sent by client ${this.clientId}`);
47      if (++serverReceivedFIN < CLIENT_VARIANTS) return;
48      setTimeout(() => {
49        server.close();
50        console.log(`connection ${this.clientId} is closing the server:
51          FIN ${serverReceivedFIN} received by server,
52          FIN ${clientReceivedFIN} received by client
53          FIN ${clientSentFIN} sent by client,
54          FIN ${serverConnections} sent by server`.replace(/ {3,}/g, ''));
55      }, 50);
56    }, 1));
57    socket.end();
58    console.log(`Server has sent ${serverConnections} FIN`);
59  }
60
61  // These two levels of functions (and not arrows) are necessary in order to
62  // bind the `index`, and the calling socket (`this`)
63  function clientOnConnect(index) {
64    return common.mustCall(function clientOnConnectInner() {
65      const client = this;
66      console.log(`'connect' emitted on Client ${index}`);
67      client.resume();
68      client.on('end', common.mustCall(function clientOnEnd() {
69        setTimeout(function closeServer() {
70          // When allowHalfOpen is true, client must still be writable
71          // after the server closes the connections, but not readable
72          console.log(`client ${index} received FIN`);
73          assert(!client.readable);
74          assert(client.writable);
75          assert(client.write(String(index)));
76          client.end();
77          clientSentFIN++;
78          console.log(
79            `client ${index} sent FIN, ${clientSentFIN} have been sent`);
80        }, 50);
81      }));
82      client.on('close', common.mustCall(function clientOnClose() {
83        clientReceivedFIN++;
84        console.log(`connection ${index} has been closed by both sides,` +
85          ` ${clientReceivedFIN} clients have closed`);
86      }));
87    });
88  }
89
90  function serverOnClose() {
91    console.log(`Server has been closed:
92      FIN ${serverReceivedFIN} received by server
93      FIN ${clientReceivedFIN} received by client
94      FIN ${clientSentFIN} sent by client
95      FIN ${serverConnections} sent by server`.replace(/ {3,}/g, ''));
96  }
97
98  function serverOnListen() {
99    const port = server.address().port;
100    console.log(`Server started listening at ${host}:${port}`);
101    const opts = { allowHalfOpen: true, host, port };
102    // 6 variations === CLIENT_VARIANTS
103    net.connect(opts, clientOnConnect(1));
104    net.connect(opts).on('connect', clientOnConnect(2));
105    net.createConnection(opts, clientOnConnect(3));
106    net.createConnection(opts).on('connect', clientOnConnect(4));
107    new net.Socket(opts).connect(opts, clientOnConnect(5));
108    new net.Socket(opts).connect(opts).on('connect', clientOnConnect(6));
109  }
110
111  const CLIENT_VARIANTS = 6;
112
113  // The trigger
114  const server = net.createServer({ allowHalfOpen: true })
115    .on('connection', common.mustCall(serverOnConnection, CLIENT_VARIANTS))
116    .on('close', common.mustCall(serverOnClose))
117    .listen(0, host, common.mustCall(serverOnListen));
118}
119