1'use strict'; 2const common = require('../common'); 3 4if (!common.hasCrypto) 5 common.skip('missing crypto'); 6 7const assert = require('assert'); 8const tls = require('tls'); 9const fixtures = require('../common/fixtures'); 10 11function loadPEM(n) { 12 return fixtures.readKey(`${n}.pem`); 13} 14 15const serverIP = common.localhostIPv4; 16 17function checkResults(result, expected) { 18 assert.strictEqual(result.server.ALPN, expected.server.ALPN); 19 assert.strictEqual(result.client.ALPN, expected.client.ALPN); 20} 21 22function runTest(clientsOptions, serverOptions, cb) { 23 serverOptions.key = loadPEM('agent2-key'); 24 serverOptions.cert = loadPEM('agent2-cert'); 25 const results = []; 26 let clientIndex = 0; 27 let serverIndex = 0; 28 const server = tls.createServer(serverOptions, function(c) { 29 results[serverIndex++].server = { ALPN: c.alpnProtocol }; 30 }); 31 32 server.listen(0, serverIP, function() { 33 connectClient(clientsOptions); 34 }); 35 36 function connectClient(options) { 37 const opt = options.shift(); 38 opt.port = server.address().port; 39 opt.host = serverIP; 40 opt.rejectUnauthorized = false; 41 42 results[clientIndex] = {}; 43 44 function startNextClient() { 45 if (options.length) { 46 clientIndex++; 47 connectClient(options); 48 } else { 49 server.close(); 50 server.on('close', () => { 51 cb(results); 52 }); 53 } 54 } 55 56 const client = tls.connect(opt, function() { 57 results[clientIndex].client = { ALPN: client.alpnProtocol }; 58 client.end(); 59 startNextClient(); 60 }).on('error', function(err) { 61 results[clientIndex].client = { error: err }; 62 startNextClient(); 63 }); 64 } 65 66} 67 68// Server: ALPN, Client: ALPN 69function Test1() { 70 const serverOptions = { 71 ALPNProtocols: ['a', 'b', 'c'], 72 }; 73 74 const clientsOptions = [{ 75 ALPNProtocols: ['a', 'b', 'c'], 76 }, { 77 ALPNProtocols: ['c', 'b', 'e'], 78 }, { 79 ALPNProtocols: ['first-priority-unsupported', 'x', 'y'], 80 }]; 81 82 runTest(clientsOptions, serverOptions, function(results) { 83 // 'a' is selected by ALPN 84 checkResults(results[0], 85 { server: { ALPN: 'a' }, 86 client: { ALPN: 'a' } }); 87 // 'b' is selected by ALPN 88 checkResults(results[1], 89 { server: { ALPN: 'b' }, 90 client: { ALPN: 'b' } }); 91 // Nothing is selected by ALPN 92 checkResults(results[2], 93 { server: { ALPN: false }, 94 client: { ALPN: false } }); 95 // execute next test 96 Test2(); 97 }); 98} 99 100// Server: ALPN, Client: Nothing 101function Test2() { 102 const serverOptions = { 103 ALPNProtocols: ['a', 'b', 'c'], 104 }; 105 106 const clientsOptions = [{}, {}, {}]; 107 108 runTest(clientsOptions, serverOptions, function(results) { 109 // Nothing is selected by ALPN 110 checkResults(results[0], 111 { server: { ALPN: false }, 112 client: { ALPN: false } }); 113 // Nothing is selected by ALPN 114 checkResults(results[1], 115 { server: { ALPN: false }, 116 client: { ALPN: false } }); 117 // Nothing is selected by ALPN 118 checkResults(results[2], 119 { server: { ALPN: false }, 120 client: { ALPN: false } }); 121 // execute next test 122 Test3(); 123 }); 124} 125 126// Server: Nothing, Client: ALPN 127function Test3() { 128 const serverOptions = {}; 129 130 const clientsOptions = [{ 131 ALPNrotocols: ['a', 'b', 'c'], 132 }, { 133 ALPNProtocols: ['c', 'b', 'e'], 134 }, { 135 ALPNProtocols: ['first-priority-unsupported', 'x', 'y'], 136 }]; 137 138 runTest(clientsOptions, serverOptions, function(results) { 139 // nothing is selected 140 checkResults(results[0], { server: { ALPN: false }, 141 client: { ALPN: false } }); 142 // nothing is selected 143 checkResults(results[1], { server: { ALPN: false }, 144 client: { ALPN: false } }); 145 // nothing is selected 146 checkResults(results[2], 147 { server: { ALPN: false }, 148 client: { ALPN: false } }); 149 // execute next test 150 Test4(); 151 }); 152} 153 154// Server: Nothing, Client: Nothing 155function Test4() { 156 const serverOptions = {}; 157 158 const clientsOptions = [{}, {}, {}]; 159 160 runTest(clientsOptions, serverOptions, function(results) { 161 // nothing is selected 162 checkResults(results[0], { server: { ALPN: false }, 163 client: { ALPN: false } }); 164 // nothing is selected 165 checkResults(results[1], { server: { ALPN: false }, 166 client: { ALPN: false } }); 167 // nothing is selected 168 checkResults(results[2], 169 { server: { ALPN: false }, 170 client: { ALPN: false } }); 171 }); 172 173 TestALPNCallback(); 174} 175 176function TestALPNCallback() { 177 // Server always selects the client's 2nd preference: 178 const serverOptions = { 179 ALPNCallback: common.mustCall(({ protocols }) => { 180 return protocols[1]; 181 }, 2) 182 }; 183 184 const clientsOptions = [{ 185 ALPNProtocols: ['a', 'b', 'c'], 186 }, { 187 ALPNProtocols: ['a'], 188 }]; 189 190 runTest(clientsOptions, serverOptions, function(results) { 191 // Callback picks 2nd preference => picks 'b' 192 checkResults(results[0], 193 { server: { ALPN: 'b' }, 194 client: { ALPN: 'b' } }); 195 196 // Callback picks 2nd preference => undefined => ALPN rejected: 197 assert.strictEqual(results[1].server, undefined); 198 assert.strictEqual(results[1].client.error.code, 'ECONNRESET'); 199 200 TestBadALPNCallback(); 201 }); 202} 203 204function TestBadALPNCallback() { 205 // Server always returns a fixed invalid value: 206 const serverOptions = { 207 ALPNCallback: common.mustCall(() => 'http/5') 208 }; 209 210 const clientsOptions = [{ 211 ALPNProtocols: ['http/1', 'h2'], 212 }]; 213 214 process.once('uncaughtException', common.mustCall((error) => { 215 assert.strictEqual(error.code, 'ERR_TLS_ALPN_CALLBACK_INVALID_RESULT'); 216 })); 217 218 runTest(clientsOptions, serverOptions, function(results) { 219 // Callback returns 'http/5' => doesn't match client ALPN => error & reset 220 assert.strictEqual(results[0].server, undefined); 221 assert.strictEqual(results[0].client.error.code, 'ECONNRESET'); 222 223 TestALPNOptionsCallback(); 224 }); 225} 226 227function TestALPNOptionsCallback() { 228 // Server sets two incompatible ALPN options: 229 assert.throws(() => tls.createServer({ 230 ALPNCallback: () => 'a', 231 ALPNProtocols: ['b', 'c'] 232 }), (error) => error.code === 'ERR_TLS_ALPN_CALLBACK_WITH_PROTOCOLS'); 233} 234 235Test1(); 236