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
24 changes: 24 additions & 0 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,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 @@ -372,6 +373,15 @@ var parseConfig = function (configFilePath, cliOptions) {
}

var config = new Config()

// save and reset hostname and listenAddress so we can detect if the user
// changed them
var defaultHostname = config.hostname
config.hostname = null
var defaultListenAddress = config.listenAddress
config.listenAddress = null

// add the user's configuration in
config.set(cliOptions)

try {
Expand All @@ -384,6 +394,20 @@ var parseConfig = function (configFilePath, cliOptions) {
// merge the config from config file and cliOptions (precedence)
config.set(cliOptions)

// if the user changed listenAddress, but didn't set a hostname, warn them
if (config.hostname === null && config.listenAddress !== null) {
log.warn('ListenAddress was set to %s but hostname was left as the default: ' +
'%s. If your browsers fail to connect, consider changing the hostname option.',
config.listenAddress, defaultHostname)
}
// restore values that weren't overwritten by the user
if (config.hostname === null) {
config.hostname = defaultHostname
}
if (config.listenAddress === null) {
config.listenAddress = defaultListenAddress
}

// configure the logger as soon as we can
logger.setup(config.logLevel, config.colors, config.loggers)

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
23 changes: 22 additions & 1 deletion test/unit/server.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ describe('server', () => {
{frameworks: [],
port: 9876,
autoWatch: true,
listenAddress: '127.0.0.1',
hostname: 'localhost',
urlRoot: '/',
browsers: ['fake'],
Expand Down Expand Up @@ -77,7 +78,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 Expand Up @@ -135,6 +142,20 @@ describe('server', () => {
expect(server._injector.invoke).to.have.been.calledWith(mockLauncher.launch, mockLauncher)
})

it('should listen on the listenAddress in the config', () => {
server._start(mockConfig, mockLauncher, null, mockFileList, browserCollection, mockExecutor, doneSpy)

expect(mockWebServer.listen).not.to.have.been.called
expect(webServerOnError).not.to.be.null

expect(mockConfig.listenAddress).to.be.equal('127.0.0.1')

fileListOnResolve()

expect(mockWebServer.listen).to.have.been.calledWith(9876, '127.0.0.1')
expect(mockConfig.listenAddress).to.be.equal('127.0.0.1')
})

it('should try next port if already in use', () => {
server._start(mockConfig, mockLauncher, null, mockFileList, browserCollection, mockExecutor, doneSpy)

Expand Down