Skip to content

Commit a2328dc

Browse files
Mark Cavagery
authored andcommitted
Add support for Unix Domain Sockets to HTTP
fixes #979.
1 parent 0b3ecc0 commit a2328dc

File tree

4 files changed

+135
-12
lines changed

4 files changed

+135
-12
lines changed

doc/api/http.markdown

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ Options:
368368

369369
- `host`: A domain name or IP address of the server to issue the request to.
370370
- `port`: Port of remote server.
371+
- `socketPath`: Unix Domain Socket (use one of host:port or socketPath)
371372
- `method`: A string specifying the HTTP request method. Possible values:
372373
`'GET'` (default), `'POST'`, `'PUT'`, and `'DELETE'`.
373374
- `path`: Request path. Should include query string and fragments if any.
@@ -443,13 +444,19 @@ Example:
443444

444445

445446
## http.Agent
446-
## http.getAgent(host, port)
447+
## http.getAgent(options)
447448

448449
`http.request()` uses a special `Agent` for managing multiple connections to
449450
an HTTP server. Normally `Agent` instances should not be exposed to user
450451
code, however in certain situations it's useful to check the status of the
451452
agent. The `http.getAgent()` function allows you to access the agents.
452453

454+
Options:
455+
456+
- `host`: A domain name or IP address of the server to issue the request to.
457+
- `port`: Port of remote server.
458+
- `socketPath`: Unix Domain Socket (use one of host:port or socketPath)
459+
453460
### Event: 'upgrade'
454461

455462
`function (request, socket, head)`

lib/http.js

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,10 +1118,12 @@ function Agent(options) {
11181118
this.options = options;
11191119
this.host = options.host;
11201120
this.port = options.port || this.defaultPort;
1121+
this.socketPath = options.socketPath;
11211122

11221123
this.queue = [];
11231124
this.sockets = [];
11241125
this.maxSockets = Agent.defaultMaxSockets;
1126+
11251127
}
11261128
util.inherits(Agent, EventEmitter);
11271129
exports.Agent = Agent;
@@ -1161,7 +1163,7 @@ Agent.prototype._establishNewConnection = function() {
11611163

11621164
// Grab a new "socket". Depending on the implementation of _getConnection
11631165
// this could either be a raw TCP socket or a TLS stream.
1164-
var socket = this._getConnection(this.host, this.port, function() {
1166+
var socket = this._getConnection(self, function() {
11651167
socket._httpConnecting = false;
11661168
self.emit('connect'); // mostly for the shim.
11671169
debug('Agent _getConnection callback');
@@ -1342,9 +1344,18 @@ Agent.prototype._establishNewConnection = function() {
13421344

13431345
// Sub-classes can overwrite this method with e.g. something that supplies
13441346
// TLS streams.
1345-
Agent.prototype._getConnection = function(host, port, cb) {
1347+
Agent.prototype._getConnection = function(options, cb) {
13461348
debug('Agent connected!');
1347-
var c = net.createConnection(port, host);
1349+
1350+
var c;
1351+
1352+
if (options.host) {
1353+
c = net.createConnection(options.port, options.host);
1354+
} else if (options.socketPath) {
1355+
c = net.createConnection(options.socketPath);
1356+
} else {
1357+
c = net.createConnection(options.port);
1358+
}
13481359
c.on('connect', cb);
13491360
return c;
13501361
};
@@ -1404,14 +1415,41 @@ Agent.prototype._cycle = function() {
14041415
// to remove it?
14051416
var agents = {};
14061417

1418+
// Backwards compatible with legacy getAgent(host, port);
1419+
function getAgent(options) {
1420+
var agent;
1421+
var host;
1422+
var id;
1423+
var port;
1424+
1425+
var _opts = {};
1426+
1427+
if (options instanceof String) {
1428+
port = arguments[1] || 80;
1429+
id = options + ':' + port;
1430+
_opts.host = options;
1431+
_opts.port = port;
1432+
} else if (options instanceof Object) {
1433+
if (options.port || options.host) {
1434+
host = options.host || 'localhost';
1435+
port = options.port || 80;
1436+
id = host + port;
1437+
_opts.host = host;
1438+
_opts.port = port;
1439+
} else if (options.socketPath) {
1440+
id = options.socketPath;
1441+
_opts.socketPath = options.socketPath;
1442+
} else {
1443+
throw new TypeError('Invalid options specification to getAgent');
1444+
}
1445+
} else {
1446+
throw new TypeError('Invalid argument to getAgent');
1447+
}
14071448

1408-
function getAgent(host, port) {
1409-
port = port || 80;
1410-
var id = host + ':' + port;
1411-
var agent = agents[id];
1449+
agent = agents[id];
14121450

14131451
if (!agent) {
1414-
agent = agents[id] = new Agent({ host: host, port: port });
1452+
agent = agents[id] = new Agent(_opts);
14151453
}
14161454

14171455
return agent;
@@ -1429,7 +1467,7 @@ exports._requestFromAgent = function(options, cb) {
14291467

14301468
exports.request = function(options, cb) {
14311469
if (options.agent === undefined) {
1432-
options.agent = getAgent(options.host, options.port);
1470+
options.agent = getAgent(options);
14331471
} else if (options.agent === false) {
14341472
options.agent = new Agent(options);
14351473
}

lib/https.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,12 @@ inherits(Agent, http.Agent);
6363
Agent.prototype.defaultPort = 443;
6464

6565

66-
Agent.prototype._getConnection = function(host, port, cb) {
66+
Agent.prototype._getConnection = function(options, cb) {
6767
if (NPN_ENABLED && !this.options.NPNProtocols) {
6868
this.options.NPNProtocols = ['http/1.1', 'http/1.0'];
6969
}
7070

71-
var s = tls.connect(port, host, this.options, function() {
71+
var s = tls.connect(options.port, options.host, this.options, function() {
7272
// do other checks here?
7373
if (cb) cb();
7474
});
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright Joyent, Inc. and other Node contributors.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the
5+
// "Software"), to deal in the Software without restriction, including
6+
// without limitation the rights to use, copy, modify, merge, publish,
7+
// distribute, sublicense, and/or sell copies of the Software, and to permit
8+
// persons to whom the Software is furnished to do so, subject to the
9+
// following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included
12+
// in all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
var common = require('../common');
23+
var assert = require('assert');
24+
var fs = require('fs');
25+
var http = require('http');
26+
27+
var SOCKET = common.tmpDir + '/http.sock';
28+
29+
var server = http.createServer(function(req, res) {
30+
res.writeHead(200, {'Content-Type': 'text/plain',
31+
'Connection': 'close'
32+
});
33+
res.write('hello ');
34+
res.write('world\n');
35+
res.end();
36+
});
37+
38+
server.listen(SOCKET, function() {
39+
40+
var options = {
41+
socketPath: SOCKET,
42+
path: '/'
43+
};
44+
45+
var req = http.get(options, function(res) {
46+
assert.equal(res.statusCode, 200);
47+
assert.equal(res.headers['content-type'], 'text/plain');
48+
res.body = '';
49+
res.setEncoding('utf8');
50+
res.on('data', function (chunk) {
51+
res.body += chunk;
52+
});
53+
res.on('end', function() {
54+
assert.equal(res.body, 'hello world\n');
55+
server.close();
56+
});
57+
});
58+
59+
req.on('error', function(e) {
60+
console.log(e.stack);
61+
process.exit(1);
62+
});
63+
64+
req.end();
65+
66+
});
67+
68+
server.on('close', function() {
69+
try {
70+
fs.unlinkSync(SOCKET);
71+
} catch (e) {}
72+
});
73+
74+
process.on('exit', function() {
75+
try {
76+
server.close();
77+
} catch (e) {}
78+
});

0 commit comments

Comments
 (0)