11cb0ef41Sopenharmony_ci// Copyright Joyent, Inc. and other Node contributors.
21cb0ef41Sopenharmony_ci//
31cb0ef41Sopenharmony_ci// Permission is hereby granted, free of charge, to any person obtaining a
41cb0ef41Sopenharmony_ci// copy of this software and associated documentation files (the
51cb0ef41Sopenharmony_ci// "Software"), to deal in the Software without restriction, including
61cb0ef41Sopenharmony_ci// without limitation the rights to use, copy, modify, merge, publish,
71cb0ef41Sopenharmony_ci// distribute, sublicense, and/or sell copies of the Software, and to permit
81cb0ef41Sopenharmony_ci// persons to whom the Software is furnished to do so, subject to the
91cb0ef41Sopenharmony_ci// following conditions:
101cb0ef41Sopenharmony_ci//
111cb0ef41Sopenharmony_ci// The above copyright notice and this permission notice shall be included
121cb0ef41Sopenharmony_ci// in all copies or substantial portions of the Software.
131cb0ef41Sopenharmony_ci//
141cb0ef41Sopenharmony_ci// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
151cb0ef41Sopenharmony_ci// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
161cb0ef41Sopenharmony_ci// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
171cb0ef41Sopenharmony_ci// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
181cb0ef41Sopenharmony_ci// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
191cb0ef41Sopenharmony_ci// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
201cb0ef41Sopenharmony_ci// USE OR OTHER DEALINGS IN THE SOFTWARE.
211cb0ef41Sopenharmony_ci
221cb0ef41Sopenharmony_ci'use strict';
231cb0ef41Sopenharmony_ciconst common = require('../common');
241cb0ef41Sopenharmony_ciif (!common.hasCrypto)
251cb0ef41Sopenharmony_ci  common.skip('missing crypto');
261cb0ef41Sopenharmony_ciconst fixtures = require('../common/fixtures');
271cb0ef41Sopenharmony_ciconst assert = require('assert');
281cb0ef41Sopenharmony_ciconst tls = require('tls');
291cb0ef41Sopenharmony_ciconst { spawn } = require('child_process');
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_ciif (!common.opensslCli)
321cb0ef41Sopenharmony_ci  common.skip('node compiled without OpenSSL CLI.');
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_cidoTest({ tickets: false }, function() {
361cb0ef41Sopenharmony_ci  doTest({ tickets: true }, function() {
371cb0ef41Sopenharmony_ci    doTest({ tickets: false, invalidSession: true }, function() {
381cb0ef41Sopenharmony_ci      console.error('all done');
391cb0ef41Sopenharmony_ci    });
401cb0ef41Sopenharmony_ci  });
411cb0ef41Sopenharmony_ci});
421cb0ef41Sopenharmony_ci
431cb0ef41Sopenharmony_cifunction doTest(testOptions, callback) {
441cb0ef41Sopenharmony_ci  const key = fixtures.readKey('rsa_private.pem');
451cb0ef41Sopenharmony_ci  const cert = fixtures.readKey('rsa_cert.crt');
461cb0ef41Sopenharmony_ci  const options = {
471cb0ef41Sopenharmony_ci    key,
481cb0ef41Sopenharmony_ci    cert,
491cb0ef41Sopenharmony_ci    ca: [cert],
501cb0ef41Sopenharmony_ci    requestCert: true,
511cb0ef41Sopenharmony_ci    rejectUnauthorized: false,
521cb0ef41Sopenharmony_ci    secureProtocol: 'TLS_method',
531cb0ef41Sopenharmony_ci    ciphers: 'RSA@SECLEVEL=0'
541cb0ef41Sopenharmony_ci  };
551cb0ef41Sopenharmony_ci  let requestCount = 0;
561cb0ef41Sopenharmony_ci  let resumeCount = 0;
571cb0ef41Sopenharmony_ci  let newSessionCount = 0;
581cb0ef41Sopenharmony_ci  let session;
591cb0ef41Sopenharmony_ci
601cb0ef41Sopenharmony_ci  const server = tls.createServer(options, function(cleartext) {
611cb0ef41Sopenharmony_ci    cleartext.on('error', function(er) {
621cb0ef41Sopenharmony_ci      // We're ok with getting ECONNRESET in this test, but it's
631cb0ef41Sopenharmony_ci      // timing-dependent, and thus unreliable. Any other errors
641cb0ef41Sopenharmony_ci      // are just failures, though.
651cb0ef41Sopenharmony_ci      if (er.code !== 'ECONNRESET')
661cb0ef41Sopenharmony_ci        throw er;
671cb0ef41Sopenharmony_ci    });
681cb0ef41Sopenharmony_ci    ++requestCount;
691cb0ef41Sopenharmony_ci    cleartext.end('');
701cb0ef41Sopenharmony_ci  });
711cb0ef41Sopenharmony_ci  server.on('newSession', function(id, data, cb) {
721cb0ef41Sopenharmony_ci    ++newSessionCount;
731cb0ef41Sopenharmony_ci    // Emulate asynchronous store
741cb0ef41Sopenharmony_ci    setImmediate(() => {
751cb0ef41Sopenharmony_ci      assert.ok(!session);
761cb0ef41Sopenharmony_ci      session = { id, data };
771cb0ef41Sopenharmony_ci      cb();
781cb0ef41Sopenharmony_ci    });
791cb0ef41Sopenharmony_ci  });
801cb0ef41Sopenharmony_ci  server.on('resumeSession', function(id, callback) {
811cb0ef41Sopenharmony_ci    ++resumeCount;
821cb0ef41Sopenharmony_ci    assert.ok(session);
831cb0ef41Sopenharmony_ci    assert.strictEqual(session.id.toString('hex'), id.toString('hex'));
841cb0ef41Sopenharmony_ci
851cb0ef41Sopenharmony_ci    let data = session.data;
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci    // Return an invalid session to test Node does not crash.
881cb0ef41Sopenharmony_ci    if (testOptions.invalidSession) {
891cb0ef41Sopenharmony_ci      data = Buffer.from('INVALID SESSION');
901cb0ef41Sopenharmony_ci      session = null;
911cb0ef41Sopenharmony_ci    }
921cb0ef41Sopenharmony_ci
931cb0ef41Sopenharmony_ci    // Just to check that async really works there
941cb0ef41Sopenharmony_ci    setImmediate(() => {
951cb0ef41Sopenharmony_ci      callback(null, data);
961cb0ef41Sopenharmony_ci    });
971cb0ef41Sopenharmony_ci  });
981cb0ef41Sopenharmony_ci
991cb0ef41Sopenharmony_ci  server.listen(0, function() {
1001cb0ef41Sopenharmony_ci    const args = [
1011cb0ef41Sopenharmony_ci      's_client',
1021cb0ef41Sopenharmony_ci      '-tls1',
1031cb0ef41Sopenharmony_ci      '-cipher', (common.hasOpenSSL31 ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'),
1041cb0ef41Sopenharmony_ci      '-connect', `localhost:${this.address().port}`,
1051cb0ef41Sopenharmony_ci      '-servername', 'ohgod',
1061cb0ef41Sopenharmony_ci      '-key', fixtures.path('keys/rsa_private.pem'),
1071cb0ef41Sopenharmony_ci      '-cert', fixtures.path('keys/rsa_cert.crt'),
1081cb0ef41Sopenharmony_ci      '-reconnect',
1091cb0ef41Sopenharmony_ci    ].concat(testOptions.tickets ? [] : '-no_ticket');
1101cb0ef41Sopenharmony_ci
1111cb0ef41Sopenharmony_ci    function spawnClient() {
1121cb0ef41Sopenharmony_ci      const client = spawn(common.opensslCli, args, {
1131cb0ef41Sopenharmony_ci        stdio: [ 0, 1, 'pipe' ]
1141cb0ef41Sopenharmony_ci      });
1151cb0ef41Sopenharmony_ci      let err = '';
1161cb0ef41Sopenharmony_ci      client.stderr.setEncoding('utf8');
1171cb0ef41Sopenharmony_ci      client.stderr.on('data', function(chunk) {
1181cb0ef41Sopenharmony_ci        err += chunk;
1191cb0ef41Sopenharmony_ci      });
1201cb0ef41Sopenharmony_ci
1211cb0ef41Sopenharmony_ci      client.on('exit', common.mustCall(function(code, signal) {
1221cb0ef41Sopenharmony_ci        if (code !== 0) {
1231cb0ef41Sopenharmony_ci          // If SmartOS and connection refused, then retry. See
1241cb0ef41Sopenharmony_ci          // https://github.com/nodejs/node/issues/2663.
1251cb0ef41Sopenharmony_ci          if (common.isSunOS && err.includes('Connection refused')) {
1261cb0ef41Sopenharmony_ci            requestCount = 0;
1271cb0ef41Sopenharmony_ci            spawnClient();
1281cb0ef41Sopenharmony_ci            return;
1291cb0ef41Sopenharmony_ci          }
1301cb0ef41Sopenharmony_ci          assert.fail(`code: ${code}, signal: ${signal}, output: ${err}`);
1311cb0ef41Sopenharmony_ci        }
1321cb0ef41Sopenharmony_ci        assert.strictEqual(code, 0);
1331cb0ef41Sopenharmony_ci        server.close(common.mustCall(function() {
1341cb0ef41Sopenharmony_ci          setImmediate(callback);
1351cb0ef41Sopenharmony_ci        }));
1361cb0ef41Sopenharmony_ci      }));
1371cb0ef41Sopenharmony_ci    }
1381cb0ef41Sopenharmony_ci
1391cb0ef41Sopenharmony_ci    spawnClient();
1401cb0ef41Sopenharmony_ci  });
1411cb0ef41Sopenharmony_ci
1421cb0ef41Sopenharmony_ci  process.on('exit', function() {
1431cb0ef41Sopenharmony_ci    // Each test run connects 6 times: an initial request and 5 reconnect
1441cb0ef41Sopenharmony_ci    // requests.
1451cb0ef41Sopenharmony_ci    assert.strictEqual(requestCount, 6);
1461cb0ef41Sopenharmony_ci
1471cb0ef41Sopenharmony_ci    if (testOptions.tickets) {
1481cb0ef41Sopenharmony_ci      // No session cache callbacks are called.
1491cb0ef41Sopenharmony_ci      assert.strictEqual(resumeCount, 0);
1501cb0ef41Sopenharmony_ci      assert.strictEqual(newSessionCount, 0);
1511cb0ef41Sopenharmony_ci    } else if (testOptions.invalidSession) {
1521cb0ef41Sopenharmony_ci      // The resume callback was called, but each connection established a
1531cb0ef41Sopenharmony_ci      // fresh session.
1541cb0ef41Sopenharmony_ci      assert.strictEqual(resumeCount, 5);
1551cb0ef41Sopenharmony_ci      assert.strictEqual(newSessionCount, 6);
1561cb0ef41Sopenharmony_ci    } else {
1571cb0ef41Sopenharmony_ci      // The resume callback was called, and only the initial connection
1581cb0ef41Sopenharmony_ci      // establishes a fresh session.
1591cb0ef41Sopenharmony_ci      assert.ok(session);
1601cb0ef41Sopenharmony_ci      assert.strictEqual(resumeCount, 5);
1611cb0ef41Sopenharmony_ci      assert.strictEqual(newSessionCount, 1);
1621cb0ef41Sopenharmony_ci    }
1631cb0ef41Sopenharmony_ci  });
1641cb0ef41Sopenharmony_ci}
165