summaryrefslogtreecommitdiff
path: root/test/async-hooks/test-tcpwrap.js
blob: 3256a1598b0adc1d9ad7487affb7141a927d39f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Covers TCPWRAP and related TCPCONNECTWRAP
'use strict';

const common = require('../common');
if (!common.hasIPv6)
  common.skip('IPv6 support required');

const assert = require('assert');
const tick = require('../common/tick');
const initHooks = require('./init-hooks');
const { checkInvocations } = require('./hook-checks');
const net = require('net');

let tcp1, tcp2;
let tcpserver;
let tcpconnect;

const hooks = initHooks();
hooks.enable();

const server = net
  .createServer(common.mustCall(onconnection))
  .on('listening', common.mustCall(onlistening));

// Calling server.listen creates a TCPWRAP synchronously
{
  server.listen(0);
  const tcpsservers = hooks.activitiesOfTypes('TCPSERVERWRAP');
  const tcpconnects = hooks.activitiesOfTypes('TCPCONNECTWRAP');
  assert.strictEqual(tcpsservers.length, 1);
  assert.strictEqual(tcpconnects.length, 0);
  tcpserver = tcpsservers[0];
  assert.strictEqual(tcpserver.type, 'TCPSERVERWRAP');
  assert.strictEqual(typeof tcpserver.uid, 'number');
  assert.strictEqual(typeof tcpserver.triggerAsyncId, 'number');
  checkInvocations(tcpserver, { init: 1 }, 'when calling server.listen');
}

// Calling net.connect creates another TCPWRAP synchronously
{
  net.connect(
    { port: server.address().port, host: '::1' },
    common.mustCall(onconnected));
  const tcps = hooks.activitiesOfTypes('TCPWRAP');
  assert.strictEqual(tcps.length, 1);
  process.nextTick(() => {
    const tcpconnects = hooks.activitiesOfTypes('TCPCONNECTWRAP');
    assert.strictEqual(tcpconnects.length, 1);
  });

  tcp1 = tcps[0];
  assert.strictEqual(tcps.length, 1);
  assert.strictEqual(tcp1.type, 'TCPWRAP');
  assert.strictEqual(typeof tcp1.uid, 'number');
  assert.strictEqual(typeof tcp1.triggerAsyncId, 'number');

  checkInvocations(tcpserver, { init: 1 },
                   'tcpserver when client is connecting');
  checkInvocations(tcp1, { init: 1 }, 'tcp1 when client is connecting');
}

function onlistening() {
  assert.strictEqual(hooks.activitiesOfTypes('TCPWRAP').length, 1);
}

// Depending on timing we see client: onconnected or server: onconnection first
// Therefore we can't depend on any ordering, but when we see a connection for
// the first time we assign the tcpconnectwrap.
function ontcpConnection(serverConnection) {
  if (tcpconnect != null) {
    // When client receives connection first ('onconnected') and the server
    // second then we see an 'after' here, otherwise not
    const expected = serverConnection ?
      { init: 1, before: 1, after: 1 } :
      { init: 1, before: 1 };
    checkInvocations(
      tcpconnect, expected,
      'tcpconnect: when both client and server received connection');
    return;
  }

  // Only focusing on TCPCONNECTWRAP here
  const tcpconnects = hooks.activitiesOfTypes('TCPCONNECTWRAP');
  assert.strictEqual(tcpconnects.length, 1);
  tcpconnect = tcpconnects[0];
  assert.strictEqual(tcpconnect.type, 'TCPCONNECTWRAP');
  assert.strictEqual(typeof tcpconnect.uid, 'number');
  assert.strictEqual(typeof tcpconnect.triggerAsyncId, 'number');
  // When client receives connection first ('onconnected'), we 'before' has
  // been invoked at this point already, otherwise it only was 'init'ed
  const expected = serverConnection ? { init: 1 } : { init: 1, before: 1 };
  checkInvocations(tcpconnect, expected,
                   'tcpconnect: when tcp connection is established');
}

let serverConnected = false;
function onconnected() {
  ontcpConnection(false);
  // In the case that the client connects before the server TCPWRAP 'before'
  // and 'after' weren't invoked yet. Also @see ontcpConnection.
  const expected = serverConnected ?
    { init: 1, before: 1, after: 1 } :
    { init: 1 };
  checkInvocations(tcpserver, expected, 'tcpserver when client connects');
  checkInvocations(tcp1, { init: 1 }, 'tcp1 when client connects');
}

function onconnection(c) {
  serverConnected = true;
  ontcpConnection(true);

  const tcps = hooks.activitiesOfTypes([ 'TCPWRAP' ]);
  const tcpconnects = hooks.activitiesOfTypes('TCPCONNECTWRAP');
  assert.strictEqual(tcps.length, 2);
  assert.strictEqual(tcpconnects.length, 1);
  tcp2 = tcps[1];
  assert.strictEqual(tcp2.type, 'TCPWRAP');
  assert.strictEqual(typeof tcp2.uid, 'number');
  assert.strictEqual(typeof tcp2.triggerAsyncId, 'number');

  checkInvocations(tcpserver, { init: 1, before: 1 },
                   'tcpserver when server receives connection');
  checkInvocations(tcp1, { init: 1 }, 'tcp1 when server receives connection');
  checkInvocations(tcp2, { init: 1 }, 'tcp2 when server receives connection');

  c.end();
  this.close(common.mustCall(onserverClosed));
}

function onserverClosed() {
  checkInvocations(tcpserver, { init: 1, before: 1, after: 1, destroy: 1 },
                   'tcpserver when server is closed');
  setImmediate(() => {
    checkInvocations(tcp1, { init: 1, before: 2, after: 2, destroy: 1 },
                     'tcp1 after server is closed');
  });
  checkInvocations(tcp2, { init: 1, before: 1, after: 1 },
                   'tcp2 synchronously when server is closed');

  tick(2, () => {
    checkInvocations(tcp2, { init: 1, before: 2, after: 2, destroy: 1 },
                     'tcp2 when server is closed');
    checkInvocations(tcpconnect, { init: 1, before: 1, after: 1, destroy: 1 },
                     'tcpconnect when server is closed');
  });
}

process.on('exit', onexit);

function onexit() {
  hooks.disable();
  hooks.sanityCheck([ 'TCPWRAP', 'TCPSERVERWRAP', 'TCPCONNECTWRAP' ]);

  checkInvocations(tcpserver, { init: 1, before: 1, after: 1, destroy: 1 },
                   'tcpserver when process exits');
  checkInvocations(
    tcp1, { init: 1, before: 2, after: 2, destroy: 1 },
    'tcp1 when process exits');
  checkInvocations(
    tcp2, { init: 1, before: 2, after: 2, destroy: 1 },
    'tcp2 when process exits');
  checkInvocations(
    tcpconnect, { init: 1, before: 1, after: 1, destroy: 1 },
    'tcpconnect when process exits');
}