Skip to content

Commit

Permalink
Cleanup host, port, and address config. Closes #2224
Browse files Browse the repository at this point in the history
  • Loading branch information
hueniverse committed Nov 28, 2014
1 parent 5f444db commit f9a386b
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 55 deletions.
28 changes: 18 additions & 10 deletions docs/Reference.md
Expand Up @@ -264,17 +264,21 @@ the sole connection where:
- `id` - a unique connection identifier (using the format '{hostname}:{pid}:{now base36}').
- `created` - the connection creation timestamp.
- `started` - the connection start timestamp (`0` when stopped).
- `port` - the port the connection was configured to (before
[`server.start()`](#serverstartcallback)) or bound to (after
[`server.start()`](#serverstartcallback)).
- `host` - the host name the connection was configured to (defaults to `'0.0.0.0'` if no host was
provided).
- `port` - the connection port based on the following rules:
- `undefined` when no port is configured or set to `0` and the server has not been started.
- the configured port value when set before the server has been started.
- the actual port assigned when no port is configured or set to `0` after the server has been
started.
- `host` - the host name the connection was configured to. Defaults to the operating system
hostname when available, otherwise `'localhost'`.
- `address` - the active IP address the connection was bound to after starting. Set to `undefined`
until the server has been started or when using a non TCP port (e.g. UNIX domain socket).
- `protocol` - the protocol used:
- `'http'` - HTTP.
- `'https'` - HTTPS.
- `'socket'` - UNIX domain socket or Windows named pipe.
- `uri` - a string representing the connection (e.g. 'http://example.com:8080' or
'socket:/unix/domain/socket/path').
'socket:/unix/domain/socket/path'). Only available when `info.port` is available.

When the server contains more than one connection, each [`server.connections`](#serverconnections)
array member provides its own `connection.info`.
Expand Down Expand Up @@ -701,10 +705,14 @@ cache.set('norway', { capital: 'oslo' }, function (err) {
### `server.connection([options])`

Adds an incoming server connection where:
- `host` - the hostname or IP address. Defaults to `0.0.0.0` which means any available network
interface. Set to `127.0.0.1` or `localhost` to restrict connection to only those coming from
the same machine.
- `port` - the TCP port the connection is listening to. Defaults to an ephemeral port (`0`) which
- `host` - the public hostname or IP address. Used only to set `server.info.host` and
`server.info.uri`. If not configured, defaults to the operating system hostname and if not
available, to `'localhost'`.
- `address` - sets the host name or IP address the connection will listen on. If not configured,
defaults to `host` if present, otherwise to all available network interfaces (i.e. `'0.0.0.0'`).
Set to `127.0.0.1` or `localhost` to restrict connection to only those coming from the same
machine.
- `port` - the TCP port the connection will listen to. Defaults to an ephemeral port (`0`) which
uses an available port when the server is started (and assigned to `server.info.port`). If `port`
is a string containing a '/' character, it is used as a UNIX domain socket path and if it starts
with '\\.\pipe' as a Windows named pipe.
Expand Down
45 changes: 19 additions & 26 deletions lib/connection.js
Expand Up @@ -43,19 +43,17 @@ exports = module.exports = internals.Connection = function (server, options) {
this.settings.port = 0;
}

this.type = (typeof this.settings.port === 'string' ? 'socket' : 'tcp');
if (this.type === 'socket') {
this.settings.port = (this.settings.port.indexOf('/') !== -1 ? Path.resolve(this.settings.port) : this.settings.port.toLowerCase());
}

if (this.settings.autoListen === undefined) {
this.settings.autoListen = true;
}

Hoek.assert(this.settings.autoListen || !this.settings.port, 'Cannot specify port when autoListen is false');

// Connection properties

this._port = this.settings.port;
this.type = (typeof this._port === 'string' ? 'socket' : 'tcp');
if (this.type === 'socket') {
this._port = (this._port.indexOf('/') !== -1 ? Path.resolve(this._port) : this._port.toLowerCase());
}
Hoek.assert(this.settings.autoListen || !this.settings.address, 'Cannot specify address when autoListen is false');

// Connection facilities

Expand Down Expand Up @@ -94,13 +92,15 @@ exports = module.exports = internals.Connection = function (server, options) {
this.info = {
created: now,
started: 0,
host: this._hostname(),
port: this._port,
protocol: this.type === 'tcp' ? (this.settings.tls ? 'https' : 'http') : this.type
host: this.settings.host || Os.hostname() || 'localhost',
protocol: this.type === 'tcp' ? (this.settings.tls ? 'https' : 'http') : this.type,
id: Os.hostname() + ':' + process.pid + ':' + now.toString(36)
};

this.info.uri = this.info.protocol + ':' + (this.type === 'tcp' ? '//' + this.info.host + ':' + this.info.port : this.info.port);
this.info.id = Os.hostname() + ':' + process.pid + ':' + now.toString(36);
if (this.settings.port) {
this.info.port = this.settings.port;
this.info.uri = this.info.protocol + ':' + (this.type === 'tcp' ? '//' + this.info.host + ':' + this.info.port : this.info.port);
}
};

Hoek.inherits(internals.Connection, Events.EventEmitter);
Expand All @@ -114,11 +114,11 @@ internals.Connection.prototype._init = function () {

this.listener.once('listening', function () {

// Update the host, port, and uri with active values
// Update the address, port, and uri with active values

if (self.type === 'tcp') {
var address = self.listener.address();
self.info.host = self._hostname(address.address);
self.info.address = address.address;
self.info.port = address.port;
self.info.uri = self.info.protocol + '://' + self.info.host + ':' + self.info.port;
}
Expand All @@ -140,12 +140,6 @@ internals.Connection.prototype._init = function () {
};


internals.Connection.prototype._hostname = function (address) {

return this.settings.host || address || Os.hostname() || 'localhost';
};


internals.Connection.prototype._start = function (callback) {

if (this._started) {
Expand All @@ -159,13 +153,12 @@ internals.Connection.prototype._start = function (callback) {
return process.nextTick(callback);
}

if (this.type !== 'tcp' ||
!this.settings.host) {

this.listener.listen(this._port, callback);
if (this.type !== 'tcp') {
this.listener.listen(this.settings.port, callback);
}
else {
this.listener.listen(this._port, this.settings.host, callback);
var address = this.settings.address || this.settings.host || '0.0.0.0';
this.listener.listen(this.settings.port, address, callback);
}
};

Expand Down
2 changes: 1 addition & 1 deletion lib/plugin.js
Expand Up @@ -213,7 +213,7 @@ internals.Plugin.prototype.register = function (plugins /*, [options], callback

for (var i = 0, il = selection.connections.length; i < il; ++i) {
var connection = selection.connections[i];
Hoek.assert(item.multiple || !connection._registrations[item.name], 'Plugin', item.name, 'already registered in:', connection.info.uri);
Hoek.assert(item.multiple || !connection._registrations[item.name], 'Plugin', item.name, 'already registered in:', connection.info.host + ':' + connection.settings.port);
connection._registrations[item.name] = item;
}

Expand Down
3 changes: 2 additions & 1 deletion lib/schema.js
Expand Up @@ -193,7 +193,8 @@ internals.server = Joi.object({

internals.connection = internals.connectionBase.keys({
autoListen: Joi.boolean(),
host: Joi.string().hostname().allow(null),
host: Joi.string().hostname(),
address: Joi.string().hostname(),
labels: internals.labels,
listener: Joi.any(),
port: Joi.alternatives([
Expand Down
4 changes: 2 additions & 2 deletions lib/server.js
Expand Up @@ -103,7 +103,7 @@ internals.Server.prototype.connection = function (options) {
settings.routes.cors = Hoek.applyToDefaults(this._settings.connections.routes.cors || Defaults.cors, settings.routes.cors);
settings.routes.security = Hoek.applyToDefaults(this._settings.connections.routes.security || Defaults.security, settings.routes.security);

settings = Schema.assert('connection', settings); // Applies validation changes (type cast)
settings = Schema.assert('connection', settings); // Applies validation changes (type cast)

var connection = new Connection(this, settings);
this.connections.push(connection);
Expand Down Expand Up @@ -133,7 +133,7 @@ internals.Server.prototype.start = function (callback) {
var connection = dependency.connections[s];
for (var d = 0, dl = dependency.deps.length; d < dl; ++d) {
var dep = dependency.deps[d];
Hoek.assert(connection._registrations[dep], 'Plugin', dependency.plugin, 'missing dependency', dep, 'in connection:', connection.info.uri);
Hoek.assert(connection._registrations[dep], 'Plugin', dependency.plugin, 'missing dependency', dep, 'in connection:', connection.info.host + ':' + connection.settings.port);
}
}
}
Expand Down
27 changes: 19 additions & 8 deletions test/connection.js
Expand Up @@ -70,7 +70,7 @@ describe('Connection', function () {
done();
});

it('defaults to 0.0.0.0 or :: when no host is provided', function (done) {
it('defaults address to 0.0.0.0 or :: when no host is provided', function (done) {

var server = new Hapi.Server();
server.connection();
Expand All @@ -81,7 +81,19 @@ describe('Connection', function () {
expectedBoundAddress = '::';
}

expect(server.info.host).to.equal(expectedBoundAddress);
expect(server.info.address).to.equal(expectedBoundAddress);
done();
});
});

it('uses address when present instead of host', function (done) {

var server = new Hapi.Server();
server.connection({ host: 'no.such.domain.hapi', address: 'localhost' });
server.start(function () {

expect(server.info.host).to.equal('no.such.domain.hapi');
expect(server.info.address).to.equal('127.0.0.1');
done();
});
});
Expand All @@ -93,7 +105,6 @@ describe('Connection', function () {
server.connection({ port: port });

expect(server.connections[0].type).to.equal('socket');
expect(server.connections[0]._port).to.equal(port);

server.start(function () {

Expand All @@ -116,7 +127,6 @@ describe('Connection', function () {
server.connection({ port: port });

expect(server.connections[0].type).to.equal('socket');
expect(server.connections[0]._port).to.equal(port);

server.start(function () {

Expand Down Expand Up @@ -265,8 +275,9 @@ describe('Connection', function () {
expectedBoundAddress = '::';
}

expect(server.info.host).to.equal(expectedBoundAddress);
expect(server.info.port).to.not.equal(0);
expect(server.info.host).to.equal(Os.hostname());
expect(server.info.address).to.equal(expectedBoundAddress);
expect(server.info.port).to.be.a.number().and.above(1);
server.stop();
done();
});
Expand Down Expand Up @@ -300,9 +311,9 @@ describe('Connection', function () {
};

var server = new Hapi.Server();
server.connection();
server.connection({ port: '8000' });
expect(server.info.host).to.equal('localhost');
expect(server.info.uri).to.equal('http://localhost:' + server.info.port);
expect(server.info.uri).to.equal('http://localhost:8000');
done();
});

Expand Down
15 changes: 8 additions & 7 deletions test/plugin.js
@@ -1,5 +1,6 @@
// Load modules

var Os = require('os');
var Path = require('path');
var Boom = require('boom');
var CatboxMemory = require('catbox-memory');
Expand Down Expand Up @@ -317,14 +318,14 @@ describe('Plugin', function () {
};

var server = new Hapi.Server();
server.connection();
server.connection({ host: 'example.com' });
server.register(test, function (err) {

expect(err).to.not.exist();
expect(function () {

server.register(test, function (err) { });
}).to.throw('Plugin test already registered in: ' + server.info.uri);
}).to.throw('Plugin test already registered in: example.com:0');

done();
});
Expand Down Expand Up @@ -1096,21 +1097,21 @@ describe('Plugin', function () {
expect(function () {

server.start();
}).to.throw('Plugin test missing dependency none in connection: ' + server.info.uri);
}).to.throw('Plugin test missing dependency none in connection: ' + Os.hostname() + ':0');
done();
});
});

it('fails to register multiple plugins with dependencies', function (done) {

var server = new Hapi.Server();
server.connection();
server.connection({ port: 80, host: 'localhost' });
server.register([internals.plugins.deps1, internals.plugins.deps3], function (err) {

expect(function () {

server.start();
}).to.throw('Plugin deps1 missing dependency deps2 in connection: ' + server.info.uri);
}).to.throw('Plugin deps1 missing dependency deps2 in connection: localhost:80');
done();
});
});
Expand Down Expand Up @@ -1176,13 +1177,13 @@ describe('Plugin', function () {
};

var server = new Hapi.Server();
server.connection();
server.connection({ port: 80, host: 'localhost' });
server.register(a, function (err) {

expect(function () {

server.start();
}).to.throw('Plugin b missing dependency c in connection: ' + server.info.uri);
}).to.throw('Plugin b missing dependency c in connection: localhost:80');
done();
});
});
Expand Down

0 comments on commit f9a386b

Please sign in to comment.