Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding listen address option so that IPv6 and loopback interfaces can be used. #2479

Merged
merged 7 commits into from
Dec 11, 2016
6 changes: 6 additions & 0 deletions docs/config/01-configuration-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,12 @@ Please note just about all frameworks in Karma require an additional plugin/fram

Additional information can be found in [plugins].

## listenAddress
**Type:** String

**Default:** `'0.0.0.0' or LISTEN_ADDR`

**Description:** Address that the server will listen on. Change to 'localhost' to only listen to the loopback, or '::' to listen on all IPv6 interfaces

## hostname
**Type:** String
Expand Down
20 changes: 18 additions & 2 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ var normalizeProxyPath = function (proxyPath) {
return normalizedProxyPath
}

var normalizeConfig = function (config, configFilePath) {
var normalizeConfig = function (config, configFilePath, configModule, cliOptions) {
var basePathResolve = function (relativePath) {
if (helper.isUrlAbsolute(relativePath)) {
return relativePath
Expand Down Expand Up @@ -199,6 +199,21 @@ var normalizeConfig = function (config, configFilePath) {
throw new TypeError('Invalid configuration: formatError option must be a function.')
}

// if the user changed listenAddress, but didn't set a hostname, warn them
var testConfig = new Config()
testConfig.hostname = null
testConfig.listenAddress = null
if (configModule != null) {
configModule(testConfig)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not do this, as this means we are evaluating the code from the user twice. Best case this slows things down, worst case it has unexpected side effects. You can add the check probably around the original invocition of the configModule

}
if (cliOptions != null) {
testConfig.set(cliOptions)
}
if (testConfig.hostname == null && testConfig.listenAddress != null) {
log.warn('ListenAddress is set but hostname isn\'t. If your browsers fail ' +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to print the current values of both the variables here.

'to connect, consider setting the hostname option.')
}

var defaultClient = config.defaultClient || {}
Object.keys(defaultClient).forEach(function (key) {
var option = config.client[key]
Expand Down Expand Up @@ -280,6 +295,7 @@ var Config = function () {
this.frameworks = []
this.protocol = 'http:'
this.port = constant.DEFAULT_PORT
this.listenAddress = constant.DEFAULT_LISTEN_ADDR
this.hostname = constant.DEFAULT_HOSTNAME
this.httpsServerConfig = {}
this.basePath = ''
Expand Down Expand Up @@ -387,7 +403,7 @@ var parseConfig = function (configFilePath, cliOptions) {
// configure the logger as soon as we can
logger.setup(config.logLevel, config.colors, config.loggers)

return normalizeConfig(config, configFilePath)
return normalizeConfig(config, configFilePath, configModule, cliOptions)
}

// PUBLIC API
Expand Down
1 change: 1 addition & 0 deletions lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ exports.VERSION = pkg.version

exports.DEFAULT_PORT = process.env.PORT || 9876
exports.DEFAULT_HOSTNAME = process.env.IP || 'localhost'
exports.DEFAULT_LISTEN_ADDR = process.env.LISTEN_ADDR || '0.0.0.0'

// log levels
exports.LOG_DISABLE = 'OFF'
Expand Down
6 changes: 3 additions & 3 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ Server.prototype._start = function (config, launcher, preprocess, fileList,
if (e.code === 'EADDRINUSE') {
self.log.warn('Port %d in use', config.port)
config.port++
webServer.listen(config.port)
webServer.listen(config.port, config.listenAddress)
} else {
throw e
}
Expand All @@ -171,9 +171,9 @@ Server.prototype._start = function (config, launcher, preprocess, fileList,
self._injector.invoke(watcher.watch)
}

webServer.listen(config.port, function () {
webServer.listen(config.port, config.listenAddress, function () {
self.log.info('Karma v%s server started at %s//%s:%s%s', constant.VERSION,
config.protocol, config.hostname, config.port, config.urlRoot)
config.protocol, config.listenAddress, config.port, config.urlRoot)

self.emit('listening', config.port)
if (config.browsers && config.browsers.length) {
Expand Down
8 changes: 7 additions & 1 deletion test/unit/server.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,13 @@ describe('server', () => {
webServerOnError = handler
}
},
listen: sinon.spy((port, callback) => {
listen: sinon.spy((port, arg2, arg3) => {
var callback = null
if (typeof arg2 === 'function') {
callback = arg2
} else if (typeof arg3 === 'function') {
callback = arg3
}
callback && callback()
}),
removeAllListeners: () => {},
Expand Down