node/test/parallel/test-tls-alpn-server-client.js
Shigeki Ohtsu 62ad1d0113 tls, crypto: add ALPN Support
ALPN is added to tls according to RFC7301, which supersedes NPN.
When the server receives both NPN and ALPN extensions from the client,
ALPN takes precedence over NPN and the server does not send NPN
extension to the client. alpnProtocol in TLSSocket always returns
false when no selected protocol exists by ALPN.
In https server, http/1.1 token is always set when no
options.ALPNProtocols exists.

PR-URL: https://github.com/nodejs/node/pull/2564
Reviewed-By: Fedor Indutny <fedor@indutny.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
2015-10-27 09:01:01 +11:00

540 lines
16 KiB
JavaScript

'use strict';
const common = require('../common');
if (!common.hasCrypto) {
console.log('1..0 # Skipped: missing crypto');
return;
}
if (!process.features.tls_alpn) {
console.error('Skipping because node compiled without OpenSSL or ' +
'with old OpenSSL version.');
process.exit(0);
}
const assert = require('assert');
const fs = require('fs');
const tls = require('tls');
function filenamePEM(n) {
return require('path').join(common.fixturesDir, 'keys', n + '.pem');
}
function loadPEM(n) {
return fs.readFileSync(filenamePEM(n));
}
var serverPort = common.PORT;
var serverIP = common.localhostIPv4;
function checkResults(result, expected) {
assert.strictEqual(result.server.ALPN, expected.server.ALPN);
assert.strictEqual(result.server.NPN, expected.server.NPN);
assert.strictEqual(result.client.ALPN, expected.client.ALPN);
assert.strictEqual(result.client.NPN, expected.client.NPN);
}
function runTest(clientsOptions, serverOptions, cb) {
serverOptions.key = loadPEM('agent2-key');
serverOptions.cert = loadPEM('agent2-cert');
var results = [];
var index = 0;
var server = tls.createServer(serverOptions, function(c) {
results[index].server = {ALPN: c.alpnProtocol, NPN: c.npnProtocol};
});
server.listen(serverPort, serverIP, function() {
connectClient(clientsOptions);
});
function connectClient(options) {
var opt = options.shift();
opt.port = serverPort;
opt.host = serverIP;
opt.rejectUnauthorized = false;
results[index] = {};
var client = tls.connect(opt, function() {
results[index].client = {ALPN: client.alpnProtocol,
NPN: client.npnProtocol};
client.destroy();
if (options.length) {
index++;
connectClient(options);
} else {
server.close();
cb(results);
}
});
};
}
// Server: ALPN/NPN, Client: ALPN/NPN
function Test1() {
var serverOptions = {
ALPNProtocols: ['a', 'b', 'c'],
NPNProtocols: ['a', 'b', 'c']
};
var clientsOptions = [{
ALPNProtocols: ['a', 'b', 'c'],
NPNProtocols: ['a', 'b', 'c']
}, {
ALPNProtocols: ['c', 'b', 'e'],
NPNProtocols: ['c', 'b', 'e']
}, {
ALPNProtocols: ['first-priority-unsupported', 'x', 'y'],
NPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// 'a' is selected by ALPN
checkResults(results[0],
{server: {ALPN: 'a', NPN: false},
client: {ALPN: 'a', NPN: undefined}});
// 'b' is selected by ALPN
checkResults(results[1],
{server: {ALPN: 'b', NPN: false},
client: {ALPN: 'b', NPN: undefined}});
// nothing is selected by ALPN
checkResults(results[2],
{server: {ALPN: false, NPN: false},
client: {ALPN: false, NPN: undefined}});
// execute next test
Test2();
});
}
// Server: ALPN/NPN, Client: ALPN
function Test2() {
var serverOptions = {
ALPNProtocols: ['a', 'b', 'c'],
NPNProtocols: ['a', 'b', 'c']
};
var clientsOptions = [{
ALPNProtocols: ['a', 'b', 'c']
}, {
ALPNProtocols: ['c', 'b', 'e']
}, {
ALPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// 'a' is selected by ALPN
checkResults(results[0],
{server: {ALPN: 'a', NPN: false},
client: {ALPN: 'a', NPN: undefined}});
// 'b' is selected by ALPN
checkResults(results[1],
{server: {ALPN: 'b', NPN: false},
client: {ALPN: 'b', NPN: undefined}});
// nothing is selected by ALPN
checkResults(results[2],
{server: {ALPN: false, NPN: false},
client: {ALPN: false, NPN: undefined}});
// execute next test
Test3();
});
}
// Server: ALPN/NPN, Client: NPN
function Test3() {
var serverOptions = {
ALPNProtocols: ['a', 'b', 'c'],
NPNProtocols: ['a', 'b', 'c']
};
var clientsOptions = [{
NPNProtocols: ['a', 'b', 'c']
}, {
NPPNProtocols: ['c', 'b', 'e']
}, {
NPPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// 'a' is selected by NPN
checkResults(results[0],
{server: {ALPN: false, NPN: 'a'},
client: {ALPN: false, NPN: 'a'}});
// nothing is selected by ALPN
checkResults(results[1],
{server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// nothing is selected by ALPN
checkResults(results[2],
{server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// execute next test
Test4();
});
}
// Server: ALPN/NPN, Client: Nothing
function Test4() {
var serverOptions = {
ALPNProtocols: ['a', 'b', 'c'],
NPNProtocols: ['a', 'b', 'c']
};
var clientsOptions = [{}, {}, {}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected by ALPN
checkResults(results[0],
{server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// nothing is selected by ALPN
checkResults(results[1],
{server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// nothing is selected by ALPN
checkResults(results[2],
{server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// execute next test
Test5();
});
}
// Server: ALPN, Client: ALPN/NPN
function Test5() {
var serverOptions = {
ALPNProtocols: ['a', 'b', 'c']
};
var clientsOptions = [{
ALPNProtocols: ['a', 'b', 'c'],
NPNProtocols: ['a', 'b', 'c']
}, {
ALPNProtocols: ['c', 'b', 'e'],
NPNProtocols: ['c', 'b', 'e']
}, {
ALPNProtocols: ['first-priority-unsupported', 'x', 'y'],
NPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// 'a' is selected by ALPN
checkResults(results[0], {server: {ALPN: 'a', NPN: false},
client: {ALPN: 'a', NPN: undefined}});
// 'b' is selected by ALPN
checkResults(results[1], {server: {ALPN: 'b', NPN: false},
client: {ALPN: 'b', NPN: undefined}});
// nothing is selected by ALPN
checkResults(results[2], {server: {ALPN: false, NPN: false},
client: {ALPN: false, NPN: undefined}});
// execute next test
Test6();
});
}
// Server: ALPN, Client: ALPN
function Test6() {
var serverOptions = {
ALPNProtocols: ['a', 'b', 'c']
};
var clientsOptions = [{
ALPNProtocols: ['a', 'b', 'c']
}, {
ALPNProtocols: ['c', 'b', 'e']
}, {
ALPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// 'a' is selected by ALPN
checkResults(results[0], {server: {ALPN: 'a', NPN: false},
client: {ALPN: 'a', NPN: undefined}});
// 'b' is selected by ALPN
checkResults(results[1], {server: {ALPN: 'b', NPN: false},
client: {ALPN: 'b', NPN: undefined}});
// nothing is selected by ALPN
checkResults(results[2], {server: {ALPN: false, NPN: false},
client: {ALPN: false, NPN: undefined}});
// execute next test
Test7();
});
}
// Server: ALPN, Client: NPN
function Test7() {
var serverOptions = {
ALPNProtocols: ['a', 'b', 'c']
};
var clientsOptions = [{
NPNProtocols: ['a', 'b', 'c']
}, {
NPNProtocols: ['c', 'b', 'e']
}, {
NPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected by ALPN
checkResults(results[0], {server: {ALPN: false, NPN: 'a'},
client: {ALPN: false, NPN: false}});
// nothing is selected by ALPN
checkResults(results[1], {server: {ALPN: false, NPN: 'c'},
client: {ALPN: false, NPN: false}});
// nothing is selected by ALPN
checkResults(results[2],
{server: {ALPN: false, NPN: 'first-priority-unsupported'},
client: {ALPN: false, NPN: false}});
// execute next test
Test8();
});
}
// Server: ALPN, Client: Nothing
function Test8() {
var serverOptions = {
ALPNProtocols: ['a', 'b', 'c']
};
var clientsOptions = [{}, {}, {}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected by ALPN
checkResults(results[0], {server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// nothing is selected by ALPN
checkResults(results[1], {server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// nothing is selected by ALPN
checkResults(results[2],
{server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// execute next test
Test9();
});
}
// Server: NPN, Client: ALPN/NPN
function Test9() {
var serverOptions = {
NPNProtocols: ['a', 'b', 'c']
};
var clientsOptions = [{
ALPNrotocols: ['a', 'b', 'c'],
NPNProtocols: ['a', 'b', 'c']
}, {
ALPNProtocols: ['c', 'b', 'e'],
NPNProtocols: ['c', 'b', 'e']
}, {
ALPNProtocols: ['first-priority-unsupported', 'x', 'y'],
NPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// 'a' is selected by NPN
checkResults(results[0], {server: {ALPN: false, NPN: 'a'},
client: {ALPN: false, NPN: 'a'}});
// 'b' is selected by NPN
checkResults(results[1], {server: {ALPN: false, NPN: 'b'},
client: {ALPN: false, NPN: 'b'}});
// nothing is selected
checkResults(results[2],
{server: {ALPN: false, NPN: 'first-priority-unsupported'},
client: {ALPN: false, NPN: false}});
// execute next test
Test10();
});
}
// Server: NPN, Client: ALPN
function Test10() {
var serverOptions = {
NPNProtocols: ['a', 'b', 'c']
};
var clientsOptions = [{
ALPNProtocols: ['a', 'b', 'c']
}, {
ALPNProtocols: ['c', 'b', 'e']
}, {
ALPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected
checkResults(results[0], {server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// nothing is selected
checkResults(results[1], {server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// nothing is selected
checkResults(results[2], {server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// execute next test
Test11();
});
}
// Server: NPN, Client: NPN
function Test11() {
var serverOptions = {
NPNProtocols: ['a', 'b', 'c']
};
var clientsOptions = [{
NPNProtocols: ['a', 'b', 'c']
}, {
NPNProtocols: ['c', 'b', 'e']
}, {
NPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// 'a' is selected by NPN
checkResults(results[0], {server: {ALPN: false, NPN: 'a'},
client: {ALPN: false, NPN: 'a'}});
// 'b' is selected by NPN
checkResults(results[1], {server: {ALPN: false, NPN: 'b'},
client: {ALPN: false, NPN: 'b'}});
// nothing is selected
checkResults(results[2],
{server: {ALPN: false, NPN: 'first-priority-unsupported'},
client: {ALPN: false, NPN: false}});
// execute next test
Test12();
});
}
// Server: NPN, Client: Nothing
function Test12() {
var serverOptions = {
NPNProtocols: ['a', 'b', 'c']
};
var clientsOptions = [{}, {}, {}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected
checkResults(results[0], {server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// nothing is selected
checkResults(results[1], {server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// nothing is selected
checkResults(results[2],
{server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// execute next test
Test13();
});
}
// Server: Nothing, Client: ALPN/NPN
function Test13() {
var serverOptions = {};
var clientsOptions = [{
ALPNrotocols: ['a', 'b', 'c'],
NPNProtocols: ['a', 'b', 'c']
}, {
ALPNProtocols: ['c', 'b', 'e'],
NPNProtocols: ['c', 'b', 'e']
}, {
ALPNProtocols: ['first-priority-unsupported', 'x', 'y'],
NPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected
checkResults(results[0], {server: {ALPN: false, NPN: 'a'},
client: {ALPN: false, NPN: false}});
// nothing is selected
checkResults(results[1], {server: {ALPN: false, NPN: 'c'},
client: {ALPN: false, NPN: false}});
// nothing is selected
checkResults(results[2],
{server: {ALPN: false, NPN: 'first-priority-unsupported'},
client: {ALPN: false, NPN: false}});
// execute next test
Test14();
});
}
// Server: Nothing, Client: ALPN
function Test14() {
var serverOptions = {};
var clientsOptions = [{
ALPNrotocols: ['a', 'b', 'c']
}, {
ALPNProtocols: ['c', 'b', 'e']
}, {
ALPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected
checkResults(results[0], {server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// nothing is selected
checkResults(results[1], {server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// nothing is selected
checkResults(results[2],
{server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// execute next test
Test15();
});
}
// Server: Nothing, Client: NPN
function Test15() {
var serverOptions = {};
var clientsOptions = [{
NPNProtocols: ['a', 'b', 'c']
}, {
NPNProtocols: ['c', 'b', 'e']
}, {
NPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected
checkResults(results[0], {server: {ALPN: false, NPN: 'a'},
client: {ALPN: false, NPN: false}});
// nothing is selected
checkResults(results[1], {server: {ALPN: false, NPN: 'c'},
client: {ALPN: false, NPN: false}});
// nothing is selected
checkResults(results[2],
{server: {ALPN: false, NPN: 'first-priority-unsupported'},
client: {ALPN: false, NPN: false}});
// execute next test
Test16();
});
}
// Server: Nothing, Client: Nothing
function Test16() {
var serverOptions = {};
var clientsOptions = [{}, {}, {}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected
checkResults(results[0], {server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// nothing is selected
checkResults(results[1], {server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
// nothing is selected
checkResults(results[2],
{server: {ALPN: false, NPN: 'http/1.1'},
client: {ALPN: false, NPN: false}});
});
}
Test1();