11cb0ef41Sopenharmony_ci// Copyright Joyent, Inc. and other Node contributors.
21cb0ef41Sopenharmony_ci//
31cb0ef41Sopenharmony_ci// Permission is hereby granted, free of charge, to any person obtaining a
41cb0ef41Sopenharmony_ci// copy of this software and associated documentation files (the
51cb0ef41Sopenharmony_ci// "Software"), to deal in the Software without restriction, including
61cb0ef41Sopenharmony_ci// without limitation the rights to use, copy, modify, merge, publish,
71cb0ef41Sopenharmony_ci// distribute, sublicense, and/or sell copies of the Software, and to permit
81cb0ef41Sopenharmony_ci// persons to whom the Software is furnished to do so, subject to the
91cb0ef41Sopenharmony_ci// following conditions:
101cb0ef41Sopenharmony_ci//
111cb0ef41Sopenharmony_ci// The above copyright notice and this permission notice shall be included
121cb0ef41Sopenharmony_ci// in all copies or substantial portions of the Software.
131cb0ef41Sopenharmony_ci//
141cb0ef41Sopenharmony_ci// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
151cb0ef41Sopenharmony_ci// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
161cb0ef41Sopenharmony_ci// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
171cb0ef41Sopenharmony_ci// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
181cb0ef41Sopenharmony_ci// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
191cb0ef41Sopenharmony_ci// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
201cb0ef41Sopenharmony_ci// USE OR OTHER DEALINGS IN THE SOFTWARE.
211cb0ef41Sopenharmony_ci
221cb0ef41Sopenharmony_ci'use strict';
231cb0ef41Sopenharmony_ciconst common = require('../common');
241cb0ef41Sopenharmony_ciconst assert = require('assert');
251cb0ef41Sopenharmony_ciconst net = require('net');
261cb0ef41Sopenharmony_ci
271cb0ef41Sopenharmony_ci// Test allowHalfOpen
281cb0ef41Sopenharmony_ci{
291cb0ef41Sopenharmony_ci  let clientReceivedFIN = 0;
301cb0ef41Sopenharmony_ci  let serverConnections = 0;
311cb0ef41Sopenharmony_ci  let clientSentFIN = 0;
321cb0ef41Sopenharmony_ci  let serverReceivedFIN = 0;
331cb0ef41Sopenharmony_ci  const host = common.localhostIPv4;
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ci  function serverOnConnection(socket) {
361cb0ef41Sopenharmony_ci    console.log(`'connection' ${++serverConnections} emitted on server`);
371cb0ef41Sopenharmony_ci    const srvConn = serverConnections;
381cb0ef41Sopenharmony_ci    socket.resume();
391cb0ef41Sopenharmony_ci    socket.on('data', common.mustCall(function socketOnData(data) {
401cb0ef41Sopenharmony_ci      this.clientId = data.toString();
411cb0ef41Sopenharmony_ci      console.log(
421cb0ef41Sopenharmony_ci        `server connection ${srvConn} is started by client ${this.clientId}`);
431cb0ef41Sopenharmony_ci    }));
441cb0ef41Sopenharmony_ci    // 'end' on each socket must not be emitted twice
451cb0ef41Sopenharmony_ci    socket.on('end', common.mustCall(function socketOnEnd() {
461cb0ef41Sopenharmony_ci      console.log(`Server received FIN sent by client ${this.clientId}`);
471cb0ef41Sopenharmony_ci      if (++serverReceivedFIN < CLIENT_VARIANTS) return;
481cb0ef41Sopenharmony_ci      setTimeout(() => {
491cb0ef41Sopenharmony_ci        server.close();
501cb0ef41Sopenharmony_ci        console.log(`connection ${this.clientId} is closing the server:
511cb0ef41Sopenharmony_ci          FIN ${serverReceivedFIN} received by server,
521cb0ef41Sopenharmony_ci          FIN ${clientReceivedFIN} received by client
531cb0ef41Sopenharmony_ci          FIN ${clientSentFIN} sent by client,
541cb0ef41Sopenharmony_ci          FIN ${serverConnections} sent by server`.replace(/ {3,}/g, ''));
551cb0ef41Sopenharmony_ci      }, 50);
561cb0ef41Sopenharmony_ci    }, 1));
571cb0ef41Sopenharmony_ci    socket.end();
581cb0ef41Sopenharmony_ci    console.log(`Server has sent ${serverConnections} FIN`);
591cb0ef41Sopenharmony_ci  }
601cb0ef41Sopenharmony_ci
611cb0ef41Sopenharmony_ci  // These two levels of functions (and not arrows) are necessary in order to
621cb0ef41Sopenharmony_ci  // bind the `index`, and the calling socket (`this`)
631cb0ef41Sopenharmony_ci  function clientOnConnect(index) {
641cb0ef41Sopenharmony_ci    return common.mustCall(function clientOnConnectInner() {
651cb0ef41Sopenharmony_ci      const client = this;
661cb0ef41Sopenharmony_ci      console.log(`'connect' emitted on Client ${index}`);
671cb0ef41Sopenharmony_ci      client.resume();
681cb0ef41Sopenharmony_ci      client.on('end', common.mustCall(function clientOnEnd() {
691cb0ef41Sopenharmony_ci        setTimeout(function closeServer() {
701cb0ef41Sopenharmony_ci          // When allowHalfOpen is true, client must still be writable
711cb0ef41Sopenharmony_ci          // after the server closes the connections, but not readable
721cb0ef41Sopenharmony_ci          console.log(`client ${index} received FIN`);
731cb0ef41Sopenharmony_ci          assert(!client.readable);
741cb0ef41Sopenharmony_ci          assert(client.writable);
751cb0ef41Sopenharmony_ci          assert(client.write(String(index)));
761cb0ef41Sopenharmony_ci          client.end();
771cb0ef41Sopenharmony_ci          clientSentFIN++;
781cb0ef41Sopenharmony_ci          console.log(
791cb0ef41Sopenharmony_ci            `client ${index} sent FIN, ${clientSentFIN} have been sent`);
801cb0ef41Sopenharmony_ci        }, 50);
811cb0ef41Sopenharmony_ci      }));
821cb0ef41Sopenharmony_ci      client.on('close', common.mustCall(function clientOnClose() {
831cb0ef41Sopenharmony_ci        clientReceivedFIN++;
841cb0ef41Sopenharmony_ci        console.log(`connection ${index} has been closed by both sides,` +
851cb0ef41Sopenharmony_ci          ` ${clientReceivedFIN} clients have closed`);
861cb0ef41Sopenharmony_ci      }));
871cb0ef41Sopenharmony_ci    });
881cb0ef41Sopenharmony_ci  }
891cb0ef41Sopenharmony_ci
901cb0ef41Sopenharmony_ci  function serverOnClose() {
911cb0ef41Sopenharmony_ci    console.log(`Server has been closed:
921cb0ef41Sopenharmony_ci      FIN ${serverReceivedFIN} received by server
931cb0ef41Sopenharmony_ci      FIN ${clientReceivedFIN} received by client
941cb0ef41Sopenharmony_ci      FIN ${clientSentFIN} sent by client
951cb0ef41Sopenharmony_ci      FIN ${serverConnections} sent by server`.replace(/ {3,}/g, ''));
961cb0ef41Sopenharmony_ci  }
971cb0ef41Sopenharmony_ci
981cb0ef41Sopenharmony_ci  function serverOnListen() {
991cb0ef41Sopenharmony_ci    const port = server.address().port;
1001cb0ef41Sopenharmony_ci    console.log(`Server started listening at ${host}:${port}`);
1011cb0ef41Sopenharmony_ci    const opts = { allowHalfOpen: true, host, port };
1021cb0ef41Sopenharmony_ci    // 6 variations === CLIENT_VARIANTS
1031cb0ef41Sopenharmony_ci    net.connect(opts, clientOnConnect(1));
1041cb0ef41Sopenharmony_ci    net.connect(opts).on('connect', clientOnConnect(2));
1051cb0ef41Sopenharmony_ci    net.createConnection(opts, clientOnConnect(3));
1061cb0ef41Sopenharmony_ci    net.createConnection(opts).on('connect', clientOnConnect(4));
1071cb0ef41Sopenharmony_ci    new net.Socket(opts).connect(opts, clientOnConnect(5));
1081cb0ef41Sopenharmony_ci    new net.Socket(opts).connect(opts).on('connect', clientOnConnect(6));
1091cb0ef41Sopenharmony_ci  }
1101cb0ef41Sopenharmony_ci
1111cb0ef41Sopenharmony_ci  const CLIENT_VARIANTS = 6;
1121cb0ef41Sopenharmony_ci
1131cb0ef41Sopenharmony_ci  // The trigger
1141cb0ef41Sopenharmony_ci  const server = net.createServer({ allowHalfOpen: true })
1151cb0ef41Sopenharmony_ci    .on('connection', common.mustCall(serverOnConnection, CLIENT_VARIANTS))
1161cb0ef41Sopenharmony_ci    .on('close', common.mustCall(serverOnClose))
1171cb0ef41Sopenharmony_ci    .listen(0, host, common.mustCall(serverOnListen));
1181cb0ef41Sopenharmony_ci}
119