summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorisaacs <i@izs.me>2012-07-25 12:00:25 -0700
committerisaacs <i@izs.me>2012-07-25 13:38:43 -0700
commitb0c0111b04201bf99fde0fe0616b9fdf1f33655d (patch)
tree537172b8db7ccb6051020d2ffca74230f19d2ca5
parent9eddaebb79ff1954b7ecdb209587d7db6554580d (diff)
downloadnode-new-b0c0111b04201bf99fde0fe0616b9fdf1f33655d.tar.gz
https: Use host header as effective servername
-rw-r--r--lib/http.js16
-rw-r--r--test/simple/test-https-strict.js218
2 files changed, 231 insertions, 3 deletions
diff --git a/lib/http.js b/lib/http.js
index f7b2f786b6..87199ea3ac 100644
--- a/lib/http.js
+++ b/lib/http.js
@@ -1067,7 +1067,7 @@ Agent.prototype.addRequest = function(req, host, port, localAddress) {
}
if (this.sockets[name].length < this.maxSockets) {
// If we are under maxSockets create a new one.
- req.onSocket(this.createSocket(name, host, port, localAddress));
+ req.onSocket(this.createSocket(name, host, port, localAddress, req));
} else {
// We are over limit so we'll add it to the queue.
if (!this.requests[name]) {
@@ -1076,12 +1076,21 @@ Agent.prototype.addRequest = function(req, host, port, localAddress) {
this.requests[name].push(req);
}
};
-Agent.prototype.createSocket = function(name, host, port, localAddress) {
+Agent.prototype.createSocket = function(name, host, port, localAddress, req) {
var self = this;
var options = util._extend({}, self.options);
options.port = port;
options.host = host;
options.localAddress = localAddress;
+
+ options.servername = host;
+ if (req) {
+ var hostHeader = req.getHeader('host');
+ if (hostHeader) {
+ options.servername = hostHeader.replace(/:.*$/, '');
+ }
+ }
+
var s = self.createConnection(options);
if (!self.sockets[name]) {
self.sockets[name] = [];
@@ -1122,8 +1131,9 @@ Agent.prototype.removeSocket = function(s, name, host, port, localAddress) {
}
}
if (this.requests[name] && this.requests[name].length) {
+ var req = this.requests[name][0];
// If we have pending requests and a socket gets closed a new one
- this.createSocket(name, host, port, localAddress).emit('free');
+ this.createSocket(name, host, port, localAddress, req).emit('free');
}
};
diff --git a/test/simple/test-https-strict.js b/test/simple/test-https-strict.js
new file mode 100644
index 0000000000..e62c0d51a7
--- /dev/null
+++ b/test/simple/test-https-strict.js
@@ -0,0 +1,218 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+if (!process.versions.openssl) {
+ console.error('Skipping because node compiled without OpenSSL.');
+ process.exit(0);
+}
+
+var common = require('../common');
+var assert = require('assert');
+
+var fs = require('fs');
+var path = require('path');
+var https = require('https');
+
+function file(fname) {
+ return path.resolve(common.fixturesDir, 'keys', fname);
+}
+
+function read(fname) {
+ return fs.readFileSync(file(fname));
+}
+
+// key1 is signed by ca1.
+var key1 = read('agent1-key.pem');
+var cert1 = read('agent1-cert.pem');
+
+// key2 has a self signed cert
+var key2 = read('agent2-key.pem');
+var cert2 = read('agent2-cert.pem');
+
+// key3 is signed by ca2.
+var key3 = read('agent3-key.pem');
+var cert3 = read('agent3-cert.pem');
+
+var ca1 = read('ca1-cert.pem');
+var ca2 = read('ca2-cert.pem');
+
+// different agents to use different CA lists.
+// this api is beyond bad.
+var agent0 = new https.Agent();
+var agent1 = new https.Agent({ ca: [ca1] });
+var agent2 = new https.Agent({ ca: [ca2] });
+var agent3 = new https.Agent({ ca: [ca1, ca2] });
+
+var options1 = {
+ key: key1,
+ cert: cert1
+};
+
+var options2 = {
+ key: key2,
+ cert: cert2
+};
+
+var options3 = {
+ key: key3,
+ cert: cert3
+};
+
+var server1 = server(options1);
+var server2 = server(options2);
+var server3 = server(options3);
+
+var listenWait = 0;
+
+var port = common.PORT;
+var port1 = port++;
+var port2 = port++;
+var port3 = port++;
+server1.listen(port1, listening());
+server2.listen(port2, listening());
+server3.listen(port3, listening());
+
+var responseErrors = {};
+var expectResponseCount = 0;
+var responseCount = 0;
+var pending = 0;
+
+
+
+function server(options, port) {
+ var s = https.createServer(options, handler);
+ s.requests = [];
+ s.expectCount = 0;
+ return s;
+}
+
+function handler(req, res) {
+ this.requests.push(req.url);
+ res.statusCode = 200;
+ res.setHeader('foo', 'bar');
+ res.end('hello, world\n');
+}
+
+function listening() {
+ listenWait++;
+ return function() {
+ listenWait--;
+ if (listenWait === 0) {
+ allListening();
+ }
+ }
+}
+
+function makeReq(path, port, error, host, ca) {
+ pending++;
+ var options = {
+ port: port,
+ path: path,
+ ca: ca
+ };
+ var whichCa = 0;
+ if (!ca) {
+ options.agent = agent0;
+ } else {
+ if (!Array.isArray(ca)) ca = [ca];
+ if (-1 !== ca.indexOf(ca1) && -1 !== ca.indexOf(ca2)) {
+ options.agent = agent3;
+ } else if (-1 !== ca.indexOf(ca1)) {
+ options.agent = agent1;
+ } else if (-1 !== ca.indexOf(ca2)) {
+ options.agent = agent2;
+ } else {
+ options.agent = agent0;
+ }
+ }
+
+ if (host) {
+ options.headers = { host: host }
+ }
+ var req = https.get(options);
+ expectResponseCount++;
+ var server = port === port1 ? server1
+ : port === port2 ? server2
+ : port === port3 ? server3
+ : null;
+
+ if (!server) throw new Error('invalid port: '+port);
+ server.expectCount++;
+
+ req.on('response', function(res) {
+ responseCount++;
+ assert.equal(res.connection.authorizationError, error);
+ responseErrors[path] = res.connection.authorizationError;
+ pending--;
+ if (pending === 0) {
+ server1.close();
+ server2.close();
+ server3.close();
+ }
+ })
+}
+
+function allListening() {
+ // ok, ready to start the tests!
+
+ // server1: host 'agent1', signed by ca1
+ makeReq('/inv1', port1, 'UNABLE_TO_VERIFY_LEAF_SIGNATURE');
+ makeReq('/inv1-ca1', port1,
+ 'Hostname/IP doesn\'t match certificate\'s altnames',
+ null, ca1);
+ makeReq('/inv1-ca1ca2', port1,
+ 'Hostname/IP doesn\'t match certificate\'s altnames',
+ null, [ca1, ca2]);
+ makeReq('/val1-ca1', port1, null, 'agent1', ca1);
+ makeReq('/val1-ca1ca2', port1, null, 'agent1', [ca1, ca2]);
+ makeReq('/inv1-ca2', port1,
+ 'UNABLE_TO_VERIFY_LEAF_SIGNATURE', 'agent1', ca2);
+
+ // server2: self-signed, host = 'agent2'
+ // doesn't matter that thename matches, all of these will error.
+ makeReq('/inv2', port2, 'DEPTH_ZERO_SELF_SIGNED_CERT');
+ makeReq('/inv2-ca1', port2, 'DEPTH_ZERO_SELF_SIGNED_CERT',
+ 'agent2', ca1);
+ makeReq('/inv2-ca1ca2', port2, 'DEPTH_ZERO_SELF_SIGNED_CERT',
+ 'agent2', [ca1, ca2]);
+
+ // server3: host 'agent3', signed by ca2
+ makeReq('/inv3', port3, 'UNABLE_TO_VERIFY_LEAF_SIGNATURE');
+ makeReq('/inv3-ca2', port3,
+ 'Hostname/IP doesn\'t match certificate\'s altnames',
+ null, ca2);
+ makeReq('/inv3-ca1ca2', port3,
+ 'Hostname/IP doesn\'t match certificate\'s altnames',
+ null, [ca1, ca2]);
+ makeReq('/val3-ca2', port3, null, 'agent3', ca2);
+ makeReq('/val3-ca1ca2', port3, null, 'agent3', [ca1, ca2]);
+ makeReq('/inv3-ca1', port3,
+ 'UNABLE_TO_VERIFY_LEAF_SIGNATURE', 'agent1', ca1);
+
+}
+
+process.on('exit', function() {
+ console.error(responseErrors);
+ assert.equal(server1.requests.length, server1.expectCount);
+ assert.equal(server2.requests.length, server2.expectCount);
+ assert.equal(server3.requests.length, server3.expectCount);
+ assert.equal(responseCount, expectResponseCount);
+});