Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

Commit

Permalink
Add support for Unix Domain Sockets to HTTP
Browse files Browse the repository at this point in the history
fixes #979.
  • Loading branch information
Mark Cavage authored and ry committed Apr 25, 2011
1 parent 0b3ecc0 commit a2328dc
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 12 deletions.
9 changes: 8 additions & 1 deletion doc/api/http.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)`
Expand Down
56 changes: 47 additions & 9 deletions lib/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -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;
};
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/https.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
Expand Down
78 changes: 78 additions & 0 deletions test/simple/test-http-unix-socket.js
Original file line number Diff line number Diff line change
@@ -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) {}
});

0 comments on commit a2328dc

Please sign in to comment.