mirror of https://github.com/lukechilds/node.git
Browse Source
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>v5.x
Shigeki Ohtsu
10 years ago
committed by
Rod Vagg
11 changed files with 771 additions and 31 deletions
@ -0,0 +1,540 @@ |
|||
'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(); |
Loading…
Reference in new issue