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