Skip to content

Commit

Permalink
Switch back to using entrypoints to serve desktop-websocket
Browse files Browse the repository at this point in the history
This is simpler than inheriting from the proxying handler
from jupyter_server_proxy directly, and more importantly keeps
our launcher entry simple and existing!
  • Loading branch information
yuvipanda committed Feb 4, 2024
1 parent a1d2280 commit 831fe1f
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 67 deletions.
6 changes: 4 additions & 2 deletions js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ function status(text) {
document.getElementById("status").textContent = text;
}

// The websockify URL to connect to is the same URL we are rendering this page on!
let websockifyUrl = new URL(window.location);
// This page is served under the /desktop/, and the websockify websocket is served
// under /desktop-websockify/ with the same base url as /desktop/. We resolve it relatively
// this way.
let websockifyUrl = new URL("../desktop-websockify/", window.location);
websockifyUrl.protocol = window.location.protocol === "https:" ? "wss" : "ws";

// Creating a new RFB object will start a new connection
Expand Down
66 changes: 3 additions & 63 deletions jupyter_remote_desktop_proxy/handlers.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import os
import shlex
import tempfile
from shutil import which

import jinja2
from jupyter_server_proxy.handlers import SuperviseAndProxyHandler
from jupyter_server.base.handlers import JupyterHandler
from tornado import web

jinja_env = jinja2.Environment(
Expand All @@ -17,66 +14,9 @@
HERE = os.path.dirname(os.path.abspath(__file__))


def get_websockify_command():
# make a secure temporary directory for sockets
# This is only readable, writeable & searchable by our uid
sockets_dir = tempfile.mkdtemp()
sockets_path = os.path.join(sockets_dir, 'vnc-socket')
vncserver = which('vncserver')

if vncserver is None:
# Use bundled tigervnc
vncserver = os.path.join(HERE, 'share/tigervnc/bin/vncserver')

# TigerVNC provides the option to connect a Unix socket. TurboVNC does not.
# TurboVNC and TigerVNC share the same origin and both use a Perl script
# as the executable vncserver. We can determine if vncserver is TigerVNC
# by searching TigerVNC string in the Perl script.
with open(vncserver) as vncserver_file:
is_tigervnc = "TigerVNC" in vncserver_file.read()

if is_tigervnc:
vnc_args = [vncserver, '-rfbunixpath', sockets_path]
socket_args = ['--unix-target', sockets_path]
else:
vnc_args = [vncserver]
socket_args = []

if not os.path.exists(os.path.expanduser('~/.vnc/xstartup')):
vnc_args.extend(['-xstartup', os.path.join(HERE, 'share/xstartup')])

vnc_command = shlex.join(
vnc_args
+ [
'-verbose',
'-geometry',
'1680x1050',
'-SecurityTypes',
'None',
'-fg',
]
)

return (
[
'websockify',
'-v',
'--heartbeat',
'30',
'{port}',
]
+ socket_args
+ ['--', '/bin/sh', '-c', f'cd {os.getcwd()} && {vnc_command}']
)


class DesktopHandler(SuperviseAndProxyHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.command = get_websockify_command()

class DesktopHandler(JupyterHandler):
@web.authenticated
async def http_get(self, *args, **kwargs):
async def get(self):
template_params = {
'base_url': self.base_url,
}
Expand Down
5 changes: 4 additions & 1 deletion jupyter_remote_desktop_proxy/server_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ def load_jupyter_server_extension(server_app):
server_app.web_app.add_handlers(
".*",
[
# Serve our own static files
(
url_path_join(base_url, "/desktop/static/(.*)"),
AuthenticatedFileHandler,
{"path": (str(HERE / "static"))},
),
# To simplify URL mapping, we make sure that /desktop/ always
# has a trailing slash
(url_path_join(base_url, "/desktop"), AddSlashHandler),
(url_path_join(base_url, "/desktop/()"), DesktopHandler, {'state': {}}),
(url_path_join(base_url, "/desktop/"), DesktopHandler),
],
)
65 changes: 65 additions & 0 deletions jupyter_remote_desktop_proxy/setup_websockify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import os
import shlex
import tempfile
from shutil import which

HERE = os.path.dirname(os.path.abspath(__file__))


def setup_websockify():
# make a secure temporary directory for sockets
# This is only readable, writeable & searchable by our uid
sockets_dir = tempfile.mkdtemp()
sockets_path = os.path.join(sockets_dir, 'vnc-socket')
vncserver = which('vncserver')

if vncserver is None:
# Use bundled tigervnc
vncserver = os.path.join(HERE, 'share/tigervnc/bin/vncserver')

# TigerVNC provides the option to connect a Unix socket. TurboVNC does not.
# TurboVNC and TigerVNC share the same origin and both use a Perl script
# as the executable vncserver. We can determine if vncserver is TigerVNC
# by searching TigerVNC string in the Perl script.
with open(vncserver) as vncserver_file:
is_tigervnc = "TigerVNC" in vncserver_file.read()

if is_tigervnc:
vnc_args = [vncserver, '-rfbunixpath', sockets_path]
socket_args = ['--unix-target', sockets_path]
else:
vnc_args = [vncserver]
socket_args = []

if not os.path.exists(os.path.expanduser('~/.vnc/xstartup')):
vnc_args.extend(['-xstartup', os.path.join(HERE, 'share/xstartup')])

vnc_command = shlex.join(
vnc_args
+ [
'-verbose',
'-geometry',
'1680x1050',
'-SecurityTypes',
'None',
'-fg',
]
)

return {
'command': [
'websockify',
'-v',
'--heartbeat',
'30',
'{port}',
]
+ socket_args
+ ['--', '/bin/sh', '-c', f'cd {os.getcwd()} && {vnc_command}'],
'timeout': 30,
'new_browser_window': True,
# We want the launcher entry to point to /desktop/, not to /desktop-websockify/
# /desktop/ is the user facing URL, while /desktop-websockify/ now *only* serves
# websockets.
"launcher_entry": {"title": "Desktop", "path_info": "desktop"},
}
7 changes: 6 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@ def run(self):
"Programming Language :: Python :: 3",
],
description="Run a desktop environments on Jupyter",
install_requires=[
entry_points={
'jupyter_serverproxy_servers': [
'desktop-websockify = jupyter_remote_desktop_proxy.setup_websockify:setup_websockify',
]
},
stall_requires=[
'jupyter-server-proxy>=1.4.0',
],
include_package_data=True,
Expand Down

0 comments on commit 831fe1f

Please sign in to comment.