Skip to content

Commit c8bddfe

Browse files
committed
Add UNIX socket support to notebook server.
1 parent 20c2c66 commit c8bddfe

File tree

2 files changed

+75
-12
lines changed

2 files changed

+75
-12
lines changed

notebook/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
os.path.join(os.path.dirname(__file__), "templates"),
2121
]
2222

23+
DEFAULT_NOTEBOOK_PORT = 8888
24+
2325
del os
2426

2527
from .nbextensions import install_nbextension

notebook/notebookapp.py

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,10 @@
6363
from tornado import web
6464
from tornado.httputil import url_concat
6565
from tornado.log import LogFormatter, app_log, access_log, gen_log
66+
from tornado.netutil import bind_unix_socket
6667

6768
from notebook import (
69+
DEFAULT_NOTEBOOK_PORT,
6870
DEFAULT_STATIC_FILES_PATH,
6971
DEFAULT_TEMPLATE_PATH_LIST,
7072
__version__,
@@ -448,8 +450,8 @@ class NbserverStopApp(JupyterApp):
448450
version = __version__
449451
description="Stop currently running notebook server for a given port"
450452

451-
port = Integer(8888, config=True,
452-
help="Port of the server to be killed. Default 8888")
453+
port = Integer(DEFAULT_NOTEBOOK_PORT, config=True,
454+
help="Port of the server to be killed. Default %s" % DEFAULT_NOTEBOOK_PORT)
453455

454456
def parse_command_line(self, argv=None):
455457
super(NbserverStopApp, self).parse_command_line(argv)
@@ -692,10 +694,18 @@ def _valdate_ip(self, proposal):
692694
or containerized setups for example).""")
693695
)
694696

695-
port = Integer(8888, config=True,
697+
port = Integer(DEFAULT_NOTEBOOK_PORT, config=True,
696698
help=_("The port the notebook server will listen on.")
697699
)
698700

701+
sock = Unicode(u'', config=True,
702+
help=_("The UNIX socket the notebook server will listen on.")
703+
)
704+
705+
sock_umask = Unicode(u'', config=True,
706+
help=_("The UNIX socket umask to set on creation (default: 0600).")
707+
)
708+
699709
port_retries = Integer(50, config=True,
700710
help=_("The number of additional ports to try if the specified port is not available.")
701711
)
@@ -1400,6 +1410,20 @@ def init_webapp(self):
14001410
self.log.critical(_("\t$ python -m notebook.auth password"))
14011411
sys.exit(1)
14021412

1413+
# Socket options validation.
1414+
if self.sock:
1415+
if self.port != DEFAULT_NOTEBOOK_PORT:
1416+
self.log.critical(
1417+
_('Options --port and --sock are mutually exclusive. Aborting.'),
1418+
)
1419+
sys.exit(1)
1420+
1421+
if sys.platform.startswith('win'):
1422+
self.log.critical(
1423+
_('Option --sock is not supported on Windows. Aborting.'),
1424+
)
1425+
sys.exit(1)
1426+
14031427
self.web_app = NotebookWebApplication(
14041428
self, self.kernel_manager, self.contents_manager,
14051429
self.session_manager, self.kernel_spec_manager,
@@ -1436,6 +1460,32 @@ def init_webapp(self):
14361460
max_body_size=self.max_body_size,
14371461
max_buffer_size=self.max_buffer_size)
14381462

1463+
success = self._bind_http_server()
1464+
if not success:
1465+
self.log.critical(_('ERROR: the notebook server could not be started because '
1466+
'no available port could be found.'))
1467+
self.exit(1)
1468+
1469+
def _bind_http_server(self):
1470+
return self._bind_http_server_unix() if self.sock else self._bind_http_server_tcp()
1471+
1472+
def _bind_http_server_unix(self):
1473+
try:
1474+
sock = bind_unix_socket(self.sock, mode=int(self.sock_umask, 8))
1475+
self.http_server.add_socket(sock)
1476+
except socket.error as e:
1477+
if e.errno == errno.EADDRINUSE:
1478+
self.log.info(_('The socket %s is already in use.') % self.sock)
1479+
return False
1480+
elif e.errno in (errno.EACCES, getattr(errno, 'WSAEACCES', errno.EACCES)):
1481+
self.log.warning(_("Permission to listen on sock %s denied") % self.sock)
1482+
return False
1483+
else:
1484+
raise
1485+
else:
1486+
return True
1487+
1488+
def _bind_http_server_tcp(self):
14391489
success = None
14401490
for port in random_ports(self.port, self.port_retries+1):
14411491
try:
@@ -1453,37 +1503,42 @@ def init_webapp(self):
14531503
self.port = port
14541504
success = True
14551505
break
1456-
if not success:
1457-
self.log.critical(_('ERROR: the notebook server could not be started because '
1458-
'no available port could be found.'))
1459-
self.exit(1)
1506+
return success
14601507

14611508
@property
14621509
def display_url(self):
14631510
if self.custom_display_url:
14641511
url = self.custom_display_url
14651512
if not url.endswith('/'):
14661513
url += '/'
1514+
elif self.sock:
1515+
url = self._unix_sock_url()
14671516
else:
14681517
if self.ip in ('', '0.0.0.0'):
14691518
ip = "%s" % socket.gethostname()
14701519
else:
14711520
ip = self.ip
1472-
url = self._url(ip)
1521+
url = self._tcp_url(ip)
14731522
if self.token:
14741523
# Don't log full token if it came from config
14751524
token = self.token if self._token_generated else '...'
14761525
url = (url_concat(url, {'token': token})
14771526
+ '\n or '
1478-
+ url_concat(self._url('127.0.0.1'), {'token': token}))
1527+
+ url_concat(self._tcp_url('127.0.0.1'), {'token': token}))
14791528
return url
14801529

14811530
@property
14821531
def connection_url(self):
1483-
ip = self.ip if self.ip else 'localhost'
1484-
return self._url(ip)
1532+
if self.sock:
1533+
return self._unix_sock_url()
1534+
else:
1535+
ip = self.ip if self.ip else 'localhost'
1536+
return self._tcp_url(ip)
1537+
1538+
def _unix_sock_url(self):
1539+
return 'http+unix://%s/%s' % (self.sock, self.base_url)
14851540

1486-
def _url(self, ip):
1541+
def _tcp_url(self, ip):
14871542
proto = 'https' if self.certfile else 'http'
14881543
return "%s://%s:%i%s" % (proto, ip, self.port, self.base_url)
14891544

@@ -1834,6 +1889,12 @@ def start(self):
18341889
self.write_browser_open_file()
18351890

18361891
if self.open_browser or self.file_to_run:
1892+
if self.sock:
1893+
# If we're bound to a UNIX socket, we can't reliably connect from a browser.
1894+
self.log.critical(
1895+
_('Options --open_browser|--file_to_run and --sock are mutually exclusive. Aborting.'),
1896+
)
1897+
sys.exit(1)
18371898
self.launch_browser()
18381899

18391900
if self.token and self._token_generated:

0 commit comments

Comments
 (0)