• 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');
24if (!common.hasCrypto)
25  common.skip('missing crypto');
26
27const assert = require('assert');
28const tls = require('tls');
29const cluster = require('cluster');
30const fixtures = require('../common/fixtures');
31
32const workerCount = 4;
33const expectedReqCount = 16;
34
35if (cluster.isMaster) {
36  let reusedCount = 0;
37  let reqCount = 0;
38  let lastSession = null;
39  let shootOnce = false;
40  let workerPort = null;
41
42  function shoot() {
43    console.error('[master] connecting', workerPort, 'session?', !!lastSession);
44    const c = tls.connect(workerPort, {
45      session: lastSession,
46      rejectUnauthorized: false
47    }, () => {
48      c.end();
49    }).on('close', () => {
50      // Wait for close to shoot off another connection. We don't want to shoot
51      // until a new session is allocated, if one will be. The new session is
52      // not guaranteed on secureConnect (it depends on TLS1.2 vs TLS1.3), but
53      // it is guaranteed to happen before the connection is closed.
54      if (++reqCount === expectedReqCount) {
55        Object.keys(cluster.workers).forEach(function(id) {
56          cluster.workers[id].send('die');
57        });
58      } else {
59        shoot();
60      }
61    }).once('session', (session) => {
62      assert(!lastSession);
63      lastSession = session;
64    });
65
66    c.resume(); // See close_notify comment in server
67  }
68
69  function fork() {
70    const worker = cluster.fork();
71    worker.on('message', ({ msg, port }) => {
72      console.error('[master] got %j', msg);
73      if (msg === 'reused') {
74        ++reusedCount;
75      } else if (msg === 'listening' && !shootOnce) {
76        workerPort = port || workerPort;
77        shootOnce = true;
78        shoot();
79      }
80    });
81
82    worker.on('exit', () => {
83      console.error('[master] worker died');
84    });
85  }
86  for (let i = 0; i < workerCount; i++) {
87    fork();
88  }
89
90  process.on('exit', () => {
91    assert.strictEqual(reqCount, expectedReqCount);
92    assert.strictEqual(reusedCount + 1, reqCount);
93  });
94  return;
95}
96
97const key = fixtures.readKey('rsa_private.pem');
98const cert = fixtures.readKey('rsa_cert.crt');
99
100const options = { key, cert };
101
102const server = tls.createServer(options, (c) => {
103  console.error('[worker] connection reused?', c.isSessionReused());
104  if (c.isSessionReused()) {
105    process.send({ msg: 'reused' });
106  } else {
107    process.send({ msg: 'not-reused' });
108  }
109  // Used to just .end(), but that means client gets close_notify before
110  // NewSessionTicket. Send data until that problem is solved.
111  c.end('x');
112});
113
114server.listen(0, () => {
115  const { port } = server.address();
116  process.send({
117    msg: 'listening',
118    port,
119  });
120});
121
122process.on('message', function listener(msg) {
123  console.error('[worker] got %j', msg);
124  if (msg === 'die') {
125    server.close(() => {
126      console.error('[worker] server close');
127
128      process.exit();
129    });
130  }
131});
132
133process.on('exit', () => {
134  console.error('[worker] exit');
135});
136