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

Improving usage/detection of URL prefix in deployed environments #402

Open
banesullivan opened this issue Apr 24, 2023 · 6 comments
Open

Comments

@banesullivan
Copy link

banesullivan commented Apr 24, 2023

TL;DR

Is there a better way to use jupyter-server-proxy / know the proxy URL in deployed environments such as MyBinder, JupyterHub, and SageMaker?

Users on a handful of projects where I leverage jupyter-server-proxy struggle to get the correct URL prefix in managed deployed environments; often it's just '/{JUPYTERHUB_SERVICE_PREFIX}/proxy/<port> but other times it can be more nuanced.

Are there any options or existing efforts to alleviate this pain point for users?

Use case

I've been leveraging jupyter-server-proxy on a handful of projects in a similar fashion, which, may be different from the use cases jupyter-server-proxy is designed for. I'd appreciate clarification on if this usage scenario is well-supported and if there are ways to improve my usage of jupyter-server-proxy for these use cases to relieve configuration pain for our users.

In each of these projects, we spin up one or many web servers within the Python kernel (at runtime) to then access from the client-side Jupyter notebook. Two examples:

  1. Serving tiles for large images to visualize interactively on a map. The user launches a tile server (with their configurations defined at runtime) for a given "image" instance in their kernel. We then combine Jupyter widgets like ipyleaflet with the REST tile endpoint we've launched on an arbitrary port on the host machine. We need the client browser to access that server to fetch the tiles. See Add ipyleaflet representation girder/large_image#1065 and https://github.com/banesullivan/localtileserver

Screen Shot 2023-04-24 at 11 14 57 AM

  1. Custom Jupyter "widgets" as an iFrame. In PyVista, we serve a single web application that links to individual 3D plots the user creates in their kernel. We need full access to the port this application runs on and display the application as an iFrame in the Jupyter Notebook so to the user it's just like any other widget. This web app must be launched within the same thread as the user's Python kernel's main thread. More details here and here

Screen Shot 2023-01-31 at 12 02 34 PM

The primary constraint shared in these use cases is that the web server must be a part of the user's Python kernel process: we cannot launch the server ahead of time, and we cannot share servers across Python sessions (notebooks).

Using jupyter-server-proxy has gone excellently for these use cases with only minor issues around end-user configuration and request timeouts -- which is what I hope to optimize and solicit as improved support for in jupyter-server-proxy.

Help!

  1. Are these use cases well-supported by jupyter-server-proxy? I'm asking because the documentation focuses on launching a single standalone web application on the Jupyter Server rather than this launching of arbitrary servers from within a user's Python session.

  2. Are there any ways to improve knowing the URL prefix ahead of time in deployed environments (like MyBinder or SageMaker) to prevent users from having to debug different prefixes in /<prefix>/proxy/<port> for these use cases?

Common prefix scenarios

  • MyBinder: f"{os.environ['JUPYTERHUB_SERVICE_PREFIX']}/proxy/{{port}}"
  • SageMaker: f"studiolab/default/jupyter/proxy/{{port}}"
  • Colab: actually, jupyter-server-proxy isn't needed for these use cases as localhost is remapped by Colab (reference)

Related

Pinging a few folks who may want to follow this issue: - @giswqs, @akaszynski

@welcome
Copy link

welcome bot commented Apr 24, 2023

Thank you for opening your first issue in this project! Engagement like this is essential for open source projects! 🤗

If you haven't done so already, check out Jupyter's Code of Conduct. Also, please try to follow the issue template as it helps other other community members to contribute more effectively.
welcome
You can meet the other Jovyans by joining our Discourse forum. There is also an intro thread there where you can stop by and say Hi! 👋

Welcome to the Jupyter community! 🎉

@giswqs
Copy link

giswqs commented Apr 24, 2023

It would be nice to have a solution that works across platforms, such as MyBinder, Google Colab, JupyterHub, Amazon SageMaker, Microsoft Planetary Computer.

@manics
Copy link
Member

manics commented Apr 24, 2023

JUPYTERHUB_SERVICE_PREFIX is the standard way to get the base prefix with JupyterHub. As far as I know SageMaker and Colab aren't open-source, so I don't think there's much we can do. However if there's a "well known" standard way to get the prefix we could consider supporting it. Are you aware of any documention?

If not, it might be better to move this discussion to the Jupyter Community Forum https://discourse.jupyter.org/ where people from across the Jupyter ecosystem hang out, since they may have run into this issue with other extensions.

@ryanlovett
Copy link
Collaborator

Are these use cases well-supported by jupyter-server-proxy? I'm asking because the documentation focuses on launching a single standalone web application on the Jupyter Server rather than this launching of arbitrary servers from within a user's Python session.

Perhaps it is not well-supported right now, because that hasn't been the focus, but that doesn't mean it couldn't be. I'll add some initial thoughts below, but as @manics said it might be worthwhile to discuss on discourse.

Are there any ways to improve knowing the URL prefix ahead of time in deployed environments (like MyBinder or SageMaker) to prevent users from having to debug different prefixes in //proxy/ for these use cases?

The only way to know the path_info of the managed process (the part after the path to the user's server) in advance is with a named server proxy like those for RStudio, VSCode, noVNC, etc., although these are for single instances. These are made available via entry points where the configuration is known in advance. Right now all existing entry points are loaded when the extension is loaded.

I imagine that there could be a jupyter-server-proxy HTTP API to either create new named services, or to reload any new entry points that have been created since the extension was loaded. This would enable the user to run some code to start a proxied service with an endpoint of their choosing.

@banesullivan
Copy link
Author

banesullivan commented May 1, 2023

@manics and @ryanlovett, thank you both for this initial feedback! I'll try to re-pose some of this discussion on Discourse.

I imagine that there could be a jupyter-server-proxy HTTP API to either create new named services, or to reload any new entry points that have been created since the extension was loaded. This would enable the user to run some code to start a proxied service with an endpoint of their choosing.

This is a neat idea. I suppose I am confused about why something must be a new named service to know the path_info... is this not available for the main /proxy/<port> service?

@manics
Copy link
Member

manics commented May 1, 2023

The path info (X-Forwarded-Context and X-Proxycontextpath) is already passed, see the tests:

def test_server_proxy_port_non_absolute(
a_server_port_and_token: Tuple[int, str]
) -> None:
PORT, TOKEN = a_server_port_and_token
r = request_get(PORT, "/proxy/54321/jkl", TOKEN)
assert r.code == 200
s = r.read().decode("ascii")
assert s.startswith("GET /jkl?token=")
assert "X-Forwarded-Context: /proxy/54321\n" in s
assert "X-Proxycontextpath: /proxy/54321\n" in s
def test_server_proxy_port_absolute(a_server_port_and_token: Tuple[int, str]) -> None:
PORT, TOKEN = a_server_port_and_token
r = request_get(PORT, "/proxy/absolute/54321/nmo", TOKEN)
assert r.code == 200
s = r.read().decode("ascii")
assert s.startswith("GET /proxy/absolute/54321/nmo?token=")
assert "X-Forwarded-Context" not in s
assert "X-Proxycontextpath" not in s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants