diff --git a/doc/api/http.markdown b/doc/api/http.markdown index 09efedebf7..6500a5c0f2 100644 --- a/doc/api/http.markdown +++ b/doc/api/http.markdown @@ -368,6 +368,7 @@ Options: - `host`: A domain name or IP address of the server to issue the request to. - `port`: Port of remote server. +- `socketPath`: Unix Domain Socket (use one of host:port or socketPath) - `method`: A string specifying the HTTP request method. Possible values: `'GET'` (default), `'POST'`, `'PUT'`, and `'DELETE'`. - `path`: Request path. Should include query string and fragments if any. @@ -443,13 +444,19 @@ Example: ## http.Agent -## http.getAgent(host, port) +## http.getAgent(options) `http.request()` uses a special `Agent` for managing multiple connections to an HTTP server. Normally `Agent` instances should not be exposed to user code, however in certain situations it's useful to check the status of the agent. The `http.getAgent()` function allows you to access the agents. +Options: + +- `host`: A domain name or IP address of the server to issue the request to. +- `port`: Port of remote server. +- `socketPath`: Unix Domain Socket (use one of host:port or socketPath) + ### Event: 'upgrade' `function (request, socket, head)` diff --git a/lib/http.js b/lib/http.js index a4447289a3..9339dd6844 100644 --- a/lib/http.js +++ b/lib/http.js @@ -1118,10 +1118,12 @@ function Agent(options) { this.options = options; this.host = options.host; this.port = options.port || this.defaultPort; + this.socketPath = options.socketPath; this.queue = []; this.sockets = []; this.maxSockets = Agent.defaultMaxSockets; + } util.inherits(Agent, EventEmitter); exports.Agent = Agent; @@ -1161,7 +1163,7 @@ Agent.prototype._establishNewConnection = function() { // Grab a new "socket". Depending on the implementation of _getConnection // this could either be a raw TCP socket or a TLS stream. - var socket = this._getConnection(this.host, this.port, function() { + var socket = this._getConnection(self, function() { socket._httpConnecting = false; self.emit('connect'); // mostly for the shim. debug('Agent _getConnection callback'); @@ -1342,9 +1344,18 @@ Agent.prototype._establishNewConnection = function() { // Sub-classes can overwrite this method with e.g. something that supplies // TLS streams. -Agent.prototype._getConnection = function(host, port, cb) { +Agent.prototype._getConnection = function(options, cb) { debug('Agent connected!'); - var c = net.createConnection(port, host); + + var c; + + if (options.host) { + c = net.createConnection(options.port, options.host); + } else if (options.socketPath) { + c = net.createConnection(options.socketPath); + } else { + c = net.createConnection(options.port); + } c.on('connect', cb); return c; }; @@ -1404,14 +1415,41 @@ Agent.prototype._cycle = function() { // to remove it? var agents = {}; +// Backwards compatible with legacy getAgent(host, port); +function getAgent(options) { + var agent; + var host; + var id; + var port; + + var _opts = {}; + + if (options instanceof String) { + port = arguments[1] || 80; + id = options + ':' + port; + _opts.host = options; + _opts.port = port; + } else if (options instanceof Object) { + if (options.port || options.host) { + host = options.host || 'localhost'; + port = options.port || 80; + id = host + port; + _opts.host = host; + _opts.port = port; + } else if (options.socketPath) { + id = options.socketPath; + _opts.socketPath = options.socketPath; + } else { + throw new TypeError('Invalid options specification to getAgent'); + } + } else { + throw new TypeError('Invalid argument to getAgent'); + } -function getAgent(host, port) { - port = port || 80; - var id = host + ':' + port; - var agent = agents[id]; + agent = agents[id]; if (!agent) { - agent = agents[id] = new Agent({ host: host, port: port }); + agent = agents[id] = new Agent(_opts); } return agent; @@ -1429,7 +1467,7 @@ exports._requestFromAgent = function(options, cb) { exports.request = function(options, cb) { if (options.agent === undefined) { - options.agent = getAgent(options.host, options.port); + options.agent = getAgent(options); } else if (options.agent === false) { options.agent = new Agent(options); } diff --git a/lib/https.js b/lib/https.js index 7f4aaff36a..1036ea3d78 100644 --- a/lib/https.js +++ b/lib/https.js @@ -63,12 +63,12 @@ inherits(Agent, http.Agent); Agent.prototype.defaultPort = 443; -Agent.prototype._getConnection = function(host, port, cb) { +Agent.prototype._getConnection = function(options, cb) { if (NPN_ENABLED && !this.options.NPNProtocols) { this.options.NPNProtocols = ['http/1.1', 'http/1.0']; } - var s = tls.connect(port, host, this.options, function() { + var s = tls.connect(options.port, options.host, this.options, function() { // do other checks here? if (cb) cb(); }); diff --git a/test/simple/test-http-unix-socket.js b/test/simple/test-http-unix-socket.js new file mode 100644 index 0000000000..2184663ed6 --- /dev/null +++ b/test/simple/test-http-unix-socket.js @@ -0,0 +1,78 @@ +// 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. + +var common = require('../common'); +var assert = require('assert'); +var fs = require('fs'); +var http = require('http'); + +var SOCKET = common.tmpDir + '/http.sock'; + +var server = http.createServer(function(req, res) { + res.writeHead(200, {'Content-Type': 'text/plain', + 'Connection': 'close' + }); + res.write('hello '); + res.write('world\n'); + res.end(); +}); + +server.listen(SOCKET, function() { + + var options = { + socketPath: SOCKET, + path: '/' + }; + + var req = http.get(options, function(res) { + assert.equal(res.statusCode, 200); + assert.equal(res.headers['content-type'], 'text/plain'); + res.body = ''; + res.setEncoding('utf8'); + res.on('data', function (chunk) { + res.body += chunk; + }); + res.on('end', function() { + assert.equal(res.body, 'hello world\n'); + server.close(); + }); + }); + + req.on('error', function(e) { + console.log(e.stack); + process.exit(1); + }); + + req.end(); + +}); + +server.on('close', function() { + try { + fs.unlinkSync(SOCKET); + } catch (e) {} +}); + +process.on('exit', function() { + try { + server.close(); + } catch (e) {} +});