1// Covers TCPWRAP and related TCPCONNECTWRAP 2'use strict'; 3 4const common = require('../common'); 5if (!common.hasIPv6) 6 common.skip('IPv6 support required'); 7 8const assert = require('assert'); 9const tick = require('../common/tick'); 10const initHooks = require('./init-hooks'); 11const { checkInvocations } = require('./hook-checks'); 12const net = require('net'); 13 14let tcp1, tcp2; 15let tcpserver; 16let tcpconnect; 17 18const hooks = initHooks(); 19hooks.enable(); 20 21const server = net 22 .createServer(common.mustCall(onconnection)) 23 .on('listening', common.mustCall(onlistening)); 24 25// Calling server.listen creates a TCPWRAP synchronously 26{ 27 server.listen(0); 28 const tcpsservers = hooks.activitiesOfTypes('TCPSERVERWRAP'); 29 const tcpconnects = hooks.activitiesOfTypes('TCPCONNECTWRAP'); 30 assert.strictEqual(tcpsservers.length, 1); 31 assert.strictEqual(tcpconnects.length, 0); 32 tcpserver = tcpsservers[0]; 33 assert.strictEqual(tcpserver.type, 'TCPSERVERWRAP'); 34 assert.strictEqual(typeof tcpserver.uid, 'number'); 35 assert.strictEqual(typeof tcpserver.triggerAsyncId, 'number'); 36 checkInvocations(tcpserver, { init: 1 }, 'when calling server.listen'); 37} 38 39// Calling net.connect creates another TCPWRAP synchronously 40{ 41 net.connect( 42 { port: server.address().port, host: '::1' }, 43 common.mustCall(onconnected)); 44 const tcps = hooks.activitiesOfTypes('TCPWRAP'); 45 assert.strictEqual(tcps.length, 1); 46 process.nextTick(() => { 47 const tcpconnects = hooks.activitiesOfTypes('TCPCONNECTWRAP'); 48 assert.strictEqual(tcpconnects.length, 1); 49 }); 50 51 tcp1 = tcps[0]; 52 assert.strictEqual(tcps.length, 1); 53 assert.strictEqual(tcp1.type, 'TCPWRAP'); 54 assert.strictEqual(typeof tcp1.uid, 'number'); 55 assert.strictEqual(typeof tcp1.triggerAsyncId, 'number'); 56 57 checkInvocations(tcpserver, { init: 1 }, 58 'tcpserver when client is connecting'); 59 checkInvocations(tcp1, { init: 1 }, 'tcp1 when client is connecting'); 60} 61 62function onlistening() { 63 assert.strictEqual(hooks.activitiesOfTypes('TCPWRAP').length, 1); 64} 65 66// Depending on timing we see client: onconnected or server: onconnection first 67// Therefore we can't depend on any ordering, but when we see a connection for 68// the first time we assign the tcpconnectwrap. 69function ontcpConnection(serverConnection) { 70 if (tcpconnect != null) { 71 // When client receives connection first ('onconnected') and the server 72 // second then we see an 'after' here, otherwise not 73 const expected = serverConnection ? 74 { init: 1, before: 1, after: 1 } : 75 { init: 1, before: 1 }; 76 checkInvocations( 77 tcpconnect, expected, 78 'tcpconnect: when both client and server received connection'); 79 return; 80 } 81 82 // Only focusing on TCPCONNECTWRAP here 83 const tcpconnects = hooks.activitiesOfTypes('TCPCONNECTWRAP'); 84 assert.strictEqual(tcpconnects.length, 1); 85 tcpconnect = tcpconnects[0]; 86 assert.strictEqual(tcpconnect.type, 'TCPCONNECTWRAP'); 87 assert.strictEqual(typeof tcpconnect.uid, 'number'); 88 assert.strictEqual(typeof tcpconnect.triggerAsyncId, 'number'); 89 // When client receives connection first ('onconnected'), we 'before' has 90 // been invoked at this point already, otherwise it only was 'init'ed 91 const expected = serverConnection ? { init: 1 } : { init: 1, before: 1 }; 92 checkInvocations(tcpconnect, expected, 93 'tcpconnect: when tcp connection is established'); 94} 95 96let serverConnected = false; 97function onconnected() { 98 ontcpConnection(false); 99 // In the case that the client connects before the server TCPWRAP 'before' 100 // and 'after' weren't invoked yet. Also @see ontcpConnection. 101 const expected = serverConnected ? 102 { init: 1, before: 1, after: 1 } : 103 { init: 1 }; 104 checkInvocations(tcpserver, expected, 'tcpserver when client connects'); 105 checkInvocations(tcp1, { init: 1 }, 'tcp1 when client connects'); 106} 107 108function onconnection(c) { 109 serverConnected = true; 110 ontcpConnection(true); 111 112 const tcps = hooks.activitiesOfTypes([ 'TCPWRAP' ]); 113 const tcpconnects = hooks.activitiesOfTypes('TCPCONNECTWRAP'); 114 assert.strictEqual(tcps.length, 2); 115 assert.strictEqual(tcpconnects.length, 1); 116 tcp2 = tcps[1]; 117 assert.strictEqual(tcp2.type, 'TCPWRAP'); 118 assert.strictEqual(typeof tcp2.uid, 'number'); 119 assert.strictEqual(typeof tcp2.triggerAsyncId, 'number'); 120 121 checkInvocations(tcpserver, { init: 1, before: 1 }, 122 'tcpserver when server receives connection'); 123 checkInvocations(tcp1, { init: 1 }, 'tcp1 when server receives connection'); 124 checkInvocations(tcp2, { init: 1 }, 'tcp2 when server receives connection'); 125 126 c.end(); 127 this.close(common.mustCall(onserverClosed)); 128} 129 130function onserverClosed() { 131 setImmediate(() => { 132 checkInvocations(tcpserver, { init: 1, before: 1, after: 1, destroy: 1 }, 133 'tcpserver when server is closed'); 134 checkInvocations(tcp1, { init: 1, before: 2, after: 2, destroy: 1 }, 135 'tcp1 after server is closed'); 136 }); 137 checkInvocations(tcp2, { init: 1, before: 1, after: 1 }, 138 'tcp2 synchronously when server is closed'); 139 140 tick(2, () => { 141 checkInvocations(tcp2, { init: 1, before: 2, after: 2, destroy: 1 }, 142 'tcp2 when server is closed'); 143 checkInvocations(tcpconnect, { init: 1, before: 1, after: 1, destroy: 1 }, 144 'tcpconnect when server is closed'); 145 }); 146} 147 148process.on('exit', onexit); 149 150function onexit() { 151 hooks.disable(); 152 hooks.sanityCheck([ 'TCPWRAP', 'TCPSERVERWRAP', 'TCPCONNECTWRAP' ]); 153 154 checkInvocations(tcpserver, { init: 1, before: 1, after: 1, destroy: 1 }, 155 'tcpserver when process exits'); 156 checkInvocations( 157 tcp1, { init: 1, before: 2, after: 2, destroy: 1 }, 158 'tcp1 when process exits'); 159 checkInvocations( 160 tcp2, { init: 1, before: 2, after: 2, destroy: 1 }, 161 'tcp2 when process exits'); 162 checkInvocations( 163 tcpconnect, { init: 1, before: 1, after: 1, destroy: 1 }, 164 'tcpconnect when process exits'); 165} 166