diff --git a/jupyterhub/apihandlers/proxy.py b/jupyterhub/apihandlers/proxy.py index 58d13793..4d17b8d9 100644 --- a/jupyterhub/apihandlers/proxy.py +++ b/jupyterhub/apihandlers/proxy.py @@ -58,7 +58,7 @@ class ProxyAPIHandler(APIHandler): if 'auth_token' in model: self.proxy.auth_token = model['auth_token'] self.db.commit() - self.log.info("Updated proxy at %s", server.url) + self.log.info("Updated proxy at %s", server.bind_url) yield self.proxy.check_routes() diff --git a/jupyterhub/app.py b/jupyterhub/app.py index 4343d801..c9a1500c 100644 --- a/jupyterhub/app.py +++ b/jupyterhub/app.py @@ -710,13 +710,13 @@ class JupyterHub(Application): if isinstance(e, HTTPError) and e.code == 403: msg = "Did CONFIGPROXY_AUTH_TOKEN change?" else: - msg = "Is something else using %s?" % self.proxy.public_server.url + msg = "Is something else using %s?" % self.proxy.public_server.bind_url self.log.error("Proxy appears to be running at %s, but I can't access it (%s)\n%s", - self.proxy.public_server.url, e, msg) + self.proxy.public_server.bind_url, e, msg) self.exit(1) return else: - self.log.info("Proxy already running at: %s", self.proxy.public_server.url) + self.log.info("Proxy already running at: %s", self.proxy.public_server.bind_url) self.proxy_process = None return @@ -735,7 +735,7 @@ class JupyterHub(Application): cmd.extend(['--ssl-key', self.ssl_key]) if self.ssl_cert: cmd.extend(['--ssl-cert', self.ssl_cert]) - self.log.info("Starting proxy @ %s", self.proxy.public_server.url) + self.log.info("Starting proxy @ %s", self.proxy.public_server.bind_url) self.log.debug("Proxy cmd: %s", cmd) try: self.proxy_process = Popen(cmd, env=env) @@ -992,7 +992,13 @@ class JupyterHub(Application): # start the webserver self.http_server = tornado.httpserver.HTTPServer(self.tornado_application, xheaders=True) - self.http_server.listen(self.hub_port) + try: + self.http_server.listen(self.hub_port) + except Exception: + self.log.error("Failed to bind hub to %s" % self.hub.server.bind_url) + raise + else: + self.log.info("Hub API listening on %s" % self.hub.server.bind_url) # register cleanup on both TERM and INT atexit.register(self.atexit) diff --git a/jupyterhub/orm.py b/jupyterhub/orm.py index 8e1f9b69..a24dd9b6 100644 --- a/jupyterhub/orm.py +++ b/jupyterhub/orm.py @@ -75,12 +75,16 @@ class Server(Base): @property def host(self): + ip = self.ip + if ip in {'', '0.0.0.0'}: + # when listening on all interfaces, connect to localhost + ip = 'localhost' return "{proto}://{ip}:{port}".format( proto=self.proto, - ip=self.ip or 'localhost', + ip=ip, port=self.port, ) - + @property def url(self): return "{host}{uri}".format( @@ -88,6 +92,17 @@ class Server(Base): uri=self.base_url, ) + @property + def bind_url(self): + """representation of URL used for binding + + Never used in APIs, only logging, + since it can be non-connectable value, such as '', meaning all interfaces. + """ + if self.ip in {'', '0.0.0.0'}: + return self.url.replace('localhost', self.ip or '*', 1) + return self.url + @gen.coroutine def wait_up(self, timeout=10, http=False): """Wait for this server to come up""" diff --git a/jupyterhub/tests/test_orm.py b/jupyterhub/tests/test_orm.py index 3f46ee2b..9aa6dc22 100644 --- a/jupyterhub/tests/test_orm.py +++ b/jupyterhub/tests/test_orm.py @@ -21,6 +21,7 @@ def test_server(db): assert isinstance(server.cookie_name, str) assert server.host == 'http://localhost:%i' % server.port assert server.url == server.host + '/' + assert server.bind_url == 'http://*:%i/' % server.port server.ip = '127.0.0.1' assert server.host == 'http://127.0.0.1:%i' % server.port assert server.url == server.host + '/'