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