Browse Source

Add support for Unix Domain Sockets to HTTP

fixes #979.
v0.7.4-release
Mark Cavage 14 years ago
committed by Ryan Dahl
parent
commit
a2328dc73c
  1. 9
      doc/api/http.markdown
  2. 56
      lib/http.js
  3. 4
      lib/https.js
  4. 78
      test/simple/test-http-unix-socket.js

9
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)`

56
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);
}

4
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();
});

78
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) {}
});
Loading…
Cancel
Save