Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Jupyter on the same server as FastAPI route #52

liquidcarbon started this conversation in Ideas
Discussion options

Hi! For shits & giggles, I'd like to have Jupyter running on the same server mounted to a FastAPI route. I got some of the way there following this thread. On launching both uvicorn and jupyter, localhost:8000/jupyter/ behaves like a regular jupyter notebook would: it asks for a token, accepts correct token, opens file browser, opens a new notebook, but running code cells doesn't work. 1+1 hangs forever, with endless loop in the logs.

Would you be so kind to provide a minimal working example?

You must be logged in to vote

Replies: 2 comments 3 replies

Comment options

Hi @liquidcarbon, can you check if this works for you?

Two key points:

  • The Jupyter server requires both HTTP and WebSocket connections. So you must proxy both.
  • By default, the Jupyter server does not allow cross-origin requests. You either need to configure the Jupyter server to allow cross-origin requests or modify the Origin header in the proxy (see JupyterCrossHost).
from collections.abc import AsyncIterator, Generator
from contextlib import asynccontextmanager
from typing import Any
import httpx
from fastapi import FastAPI, Request, WebSocket
from fastapi_proxy_lib.core.http import ReverseHttpProxy
from fastapi_proxy_lib.core.websocket import ReverseWebSocketProxy
from httpx import AsyncClient
class JupyterCrossHost(httpx.Auth):
 # ref: <https://www.python-httpx.org/advanced/authentication/>
 def auth_flow(self, request: httpx.Request) -> Generator[httpx.Request, Any, None]:
 request.headers["Origin"] = str(request.url)
 yield request
client = AsyncClient(auth=JupyterCrossHost())
# `localhost:8888` is my jupyter server
ws_proxy = ReverseWebSocketProxy(client, base_url="ws://localhost:8888/")
http_proxy = ReverseHttpProxy(client, base_url="http://localhost:8888/")
@asynccontextmanager
async def close_proxy_event(_: FastAPI) -> AsyncIterator[None]:
 """Close proxy."""
 yield
 await ws_proxy.aclose()
 await http_proxy.aclose()
app = FastAPI(lifespan=close_proxy_event)
@app.websocket("/{path:path}")
async def _(websocket: WebSocket, path: str = ""):
 return await ws_proxy.proxy(websocket=websocket, path=path)
@app.get("/{path:path}")
@app.post("/{path:path}")
@app.put("/{path:path}")
@app.delete("/{path:path}")
@app.patch("/{path:path}")
@app.head("/{path:path}")
@app.options("/{path:path}")
async def _(request: Request, path: str = ""):
 return await http_proxy.proxy(request=request, path=path)
You must be logged in to vote
0 replies
Comment options

Thanks for your response! Need more help...

Here's my setup:

Dockerfile

FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
RUN useradd -m -u 1000 user
ENV PATH="/home/user/.local/bin:$PATH"
ENV UV_SYSTEM_PYTHON=1
RUN uv pip install fastapi jupyter uvicorn[standard] fastapi-proxy-lib
COPY --chown=user . /app
USER user
WORKDIR /app
EXPOSE 7860
CMD jupyter notebook --no-browser --ip=0.0.0.0 --port=8888 \
 --ServerApp.allow_origin=* --ServerApp.allow_remote_access=True & \
 uvicorn app:app --host 0.0.0.0 --port 7860

app.py

from collections.abc import AsyncIterator, Generator
from contextlib import asynccontextmanager
from typing import Any
import httpx
from fastapi import FastAPI, Request, WebSocket
from fastapi_proxy_lib.core.http import ReverseHttpProxy
from fastapi_proxy_lib.core.websocket import ReverseWebSocketProxy
from httpx import AsyncClient
class JupyterCrossHost(httpx.Auth):
 def auth_flow(self, request: httpx.Request) -> Generator[httpx.Request, Any, None]:
 request.headers["Origin"] = str(request.url)
 yield request
client = AsyncClient(auth=JupyterCrossHost())
jupyter_http_proxy = ReverseHttpProxy(client, base_url="http://0.0.0.0:8888/")
jupyter_ws_proxy = ReverseWebSocketProxy(client, base_url="ws://0.0.0.0:8888/")
jupyter_path = "/jupyter/{path:path}"
@asynccontextmanager
async def close_proxy_event(_: FastAPI) -> AsyncIterator[None]:
 """Close proxy."""
 yield
 await jupyter_ws_proxy.aclose()
 await jupyter_http_proxy.aclose()
app = FastAPI(lifespan=close_proxy_event)
@app.websocket(jupyter_path)
async def _(websocket: WebSocket, path: str = ""):
 return await jupyter_ws_proxy.proxy(websocket=websocket, path=path)
@app.get(jupyter_path)
@app.post(jupyter_path)
@app.put(jupyter_path)
@app.delete(jupyter_path)
@app.patch(jupyter_path)
@app.head(jupyter_path)
@app.options(jupyter_path)
async def _(request: Request, path: str = ""):
 return await jupyter_http_proxy.proxy(request=request, path=path)

On launch:

Error in ReverseHttpProxy().proxy():
url: http://0.0.0.0:8888/api/sessions
request headers: Headers({'host': 'localhost:7860', 'user-agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0', 'accept': '*/*', 'accept-language': 'en-US,en;q=0.5', 'accept-encoding': 'gzip, deflate, br, zstd', 'referer': 'http://localhost:7860/jupyter/jupyter/tree', 'authorization': 'token 83863f145fe61a1bf4450535a4d1204efbd97336eaaa681e', 'x-xsrftoken': '2|16169370|03b4de4a78a23e2a2fa2bca59120762d|1737652476', 'content-type': 'application/json', 'connection': 'keep-alive', 'cookie': '_xsrf=2|16169370|03b4de4a78a23e2a2fa2bca59120762d|1737652476; username-localhost-7860="2|1:0|10:1737606633|23:username-localhost-7860|200:eyJ1c2VybmFtZSI6ICJiZmNjMGU0MDRjNjc0YjllYTQ3ZTJmMzg5YTIxNmIzZSIsICJuYW1lIjogIkFub255bW91cyBWYWxldHVkbyIsICJkaXNwbGF5X25hbWUiOiAiQW5vbnltb3VzIFZhbGV0dWRvIiwgImluaXRpYWxzIjogIkFWIiwgImNvbG9yIjogbnVsbH0=|270e1afc18ad4b2b82008fd3bedd14b10118d1d2ce21ac054241add14506c368"; username-0-0-0-0-8888="2|1:0|10:1737653685|21:username-0-0-0-0-8888|196:eyJ1c2VybmFtZSI6ICIxZGRmZWE4ZjQxNDc0MWE4ODRjM2Q4MjJkNzNlODUzNCIsICJuYW1lIjogIkFub255bW91cyBUaHlvbmUiLCAiZGlzcGxheV9uYW1lIjogIkFub255bW91cyBUaHlvbmUiLCAiaW5pdGlhbHMiOiAiQVQiLCAiY29sb3IiOiBudWxsfQ==|b0b73fb3e7f87a715ca3f0a680388758faca940948063c8ae7487923fd1ab161"; _ga_R1FN4KJKJH=GS1.1.1714184238.4.1.1714185852.0.0.0; _ga=GA1.1.368724536.1713990148', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'same-origin', 'priority': 'u=4', 'pragma': 'no-cache', 'cache-control': 'no-cache'})

on going to localhost:7860

httpx.ConnectError: All connection attempts failed
INFO: 172.17.0.1:43770 - "GET /jupyter/ HTTP/1.1" 502 Bad Gateway

What partially worked before:

from fastapi import FastAPI, Request, WebSocket
from starlette.responses import StreamingResponse
from starlette.websockets import WebSocketDisconnect
import httpx
import asyncio
app = FastAPI()
JUPYTER_URL = "http://0.0.0.0:8888"
@app.middleware("http")
async def proxy_middleware(request: Request, call_next):
 async with httpx.AsyncClient(follow_redirects=True) as client:
 # Forward the request to the Jupyter server
 response = await client.request(
 method=request.method,
 url=f"{JUPYTER_URL}{request.url.path}?{request.url.query}",
 headers=request.headers,
 data=await request.body()
 )
 # Create a StreamingResponse to stream the content back to the client
 async def stream_response():
 async for chunk in response.aiter_bytes():
 yield chunk
 return StreamingResponse(
 stream_response(),
 status_code=response.status_code,
 headers=response.headers
 )
@app.websocket("/{path:path}")
async def websocket_endpoint(websocket: WebSocket, path: str):
 await websocket.accept()
 async with httpx.AsyncClient() as client:
 async with client.stream("GET", f"{JUPYTER_URL}/{path}") as response:
 async for chunk in response.aiter_bytes():
 await websocket.send_bytes(chunk)
 try:
 while True:
 data = await websocket.receive_text()
 async with httpx.AsyncClient() as client:
 await client.post(f"{JUPYTER_URL}/{path}", content=data)
 except WebSocketDisconnect:
 print("Client disconnected")
You must be logged in to vote
3 replies
Comment options

from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request, WebSocket
from fastapi_proxy_lib.core.http import ReverseHttpProxy
from fastapi_proxy_lib.core.websocket import ReverseWebSocketProxy
from httpx import AsyncClient
client = AsyncClient()
prefix = "/jupyter/"
jupyter_http_proxy = ReverseHttpProxy(client, base_url=f"http://127.0.0.1:8888{prefix}")
jupyter_ws_proxy = ReverseWebSocketProxy(client, base_url=f"ws://127.0.0.1:8888{prefix}")
jupyter_path = f"{prefix}{{path:path}}"
@asynccontextmanager
async def close_proxy_event(_: FastAPI) -> AsyncIterator[None]:
 """Close proxy."""
 yield
 await jupyter_ws_proxy.aclose()
 await jupyter_http_proxy.aclose()
app = FastAPI(lifespan=close_proxy_event)
@app.websocket(jupyter_path)
async def _(websocket: WebSocket, path: str = ""):
 return await jupyter_ws_proxy.proxy(websocket=websocket, path=path)
@app.get(jupyter_path)
@app.post(jupyter_path)
@app.put(jupyter_path)
@app.delete(jupyter_path)
@app.patch(jupyter_path)
@app.head(jupyter_path)
@app.options(jupyter_path)
async def _(request: Request, path: str = ""):
 return await jupyter_http_proxy.proxy(request=request, path=path)
  1. I see that you allowed cross-origin requests with --ServerApp.allow_origin=* (It's better not to use the wildcard *, specify it as your proxy server address instead), so you no longer need JupyterCrossHost.
  2. You want a /jupyter/ prefix, but Jupyter doesn't know this and thinks it's still at the root path. So you need to use --ServerApp.base_url="/jupyter" to tell it to run at 0.0.0.0:8888/jupyter/. Ref: https://discourse.jupyter.org/t/trouble-reverse-proxying-to-jupyterhub-on-a-subpath/13146/13.
  3. Accordingly, the proxy's base_url also needs to include /jupyter/ prefix.
  4. The proxy's base_url cannot be set to 0.0.0.0, as this is not a valid address that your computer can access (it just means to listen on all addresses). You should set it to 127.0.0.1 or localhost.
Comment options

Thank you for the code and explanations. It works, so far can't tell the difference in performance or otherwise from regular jupyter!

It's throwing some tornado websocket errors - do you happen to know why?

$ docker run -it -p 7860:7860 fastjupyter
INFO: Started server process [8]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:7860 (Press CTRL+C to quit)
[I 2025年01月25日 02:38:48.980 ServerApp] jupyter_lsp | extension was successfully linked.
[I 2025年01月25日 02:38:48.982 ServerApp] jupyter_server_terminals | extension was successfully linked.
[I 2025年01月25日 02:38:48.986 ServerApp] jupyterlab | extension was successfully linked.
[I 2025年01月25日 02:38:48.989 ServerApp] notebook | extension was successfully linked.
[I 2025年01月25日 02:38:48.990 ServerApp] Writing Jupyter server cookie secret to /home/user/.local/share/jupyter/runtime/jupyter_cookie_secret
[I 2025年01月25日 02:38:49.654 ServerApp] notebook_shim | extension was successfully linked.
[I 2025年01月25日 02:38:49.681 ServerApp] notebook_shim | extension was successfully loaded.
[I 2025年01月25日 02:38:49.683 ServerApp] jupyter_lsp | extension was successfully loaded.
[I 2025年01月25日 02:38:49.684 ServerApp] jupyter_server_terminals | extension was successfully loaded.
[I 2025年01月25日 02:38:49.685 LabApp] JupyterLab extension loaded from /usr/local/lib/python3.12/site-packages/jupyterlab
[I 2025年01月25日 02:38:49.685 LabApp] JupyterLab application directory is /usr/local/share/jupyter/lab
[I 2025年01月25日 02:38:49.685 LabApp] Extension Manager is 'pypi'.
[I 2025年01月25日 02:38:49.807 ServerApp] jupyterlab | extension was successfully loaded.
[I 2025年01月25日 02:38:49.810 ServerApp] notebook | extension was successfully loaded.
[I 2025年01月25日 02:38:49.811 ServerApp] Serving notebooks from local directory: /app
[I 2025年01月25日 02:38:49.811 ServerApp] Jupyter Server 2.15.0 is running at:
[I 2025年01月25日 02:38:49.811 ServerApp] http://e4dd3151f087:8888/jupyter/tree?token=fefd052f7a602445ce2d8d003c86bac22cefd686e94a926c
[I 2025年01月25日 02:38:49.811 ServerApp] http://127.0.0.1:8888/jupyter/tree?token=fefd052f7a602445ce2d8d003c86bac22cefd686e94a926c
[I 2025年01月25日 02:38:49.811 ServerApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[C 2025年01月25日 02:38:49.813 ServerApp] 
 
 To access the server, open this file in a browser:
 file:///home/user/.local/share/jupyter/runtime/jpserver-7-open.html
 Or copy and paste one of these URLs:
 http://e4dd3151f087:8888/jupyter/tree?token=fefd052f7a602445ce2d8d003c86bac22cefd686e94a926c
 http://127.0.0.1:8888/jupyter/tree?token=fefd052f7a602445ce2d8d003c86bac22cefd686e94a926c
[I 2025年01月25日 02:38:49.830 ServerApp] Skipped non-installed server(s): bash-language-server, dockerfile-language-server-nodejs, javascript-typescript-langserver, jedi-language-server, julia-language-server, pyright, python-language-server, python-lsp-server, r-languageserver, sql-language-server, texlab, typescript-language-server, unified-language-server, vscode-css-languageserver-bin, vscode-html-languageserver-bin, vscode-json-languageserver-bin, yaml-language-server
INFO: 172.17.0.1:34972 - "GET / HTTP/1.1" 404 Not Found
 [I 2025年01月25日 02:39:07.632 ServerApp] 302 GET /jupyter/ (@127.0.0.1) 0.41ms
INFO: 172.17.0.1:59418 - "GET /jupyter/ HTTP/1.1" 302 Found
[I 2025年01月25日 02:39:07.640 JupyterNotebookApp] 302 GET /jupyter/tree (@127.0.0.1) 0.94ms
INFO: 172.17.0.1:59418 - "GET /jupyter/tree HTTP/1.1" 302 Found
INFO: 172.17.0.1:59418 - "GET /jupyter/login?next=%2Fjupyter%2Ftree HTTP/1.1" 200 OK
[I 2025年01月25日 02:39:19.209 ServerApp] User f90d84d32f30445d9f413482596ae8c7 logged in.
[I 2025年01月25日 02:39:19.210 ServerApp] 302 POST /jupyter/login?next=%2Fjupyter%2Ftree (f90d84d32f30445d9f413482596ae8c7@127.0.0.1) 2.46ms
INFO: 172.17.0.1:42042 - "POST /jupyter/login?next=%2Fjupyter%2Ftree HTTP/1.1" 302 Found
INFO: 172.17.0.1:42042 - "GET /jupyter/tree HTTP/1.1" 200 OK
INFO: 172.17.0.1:42042 - "GET /jupyter/custom/custom.css HTTP/1.1" 200 OK
INFO: 172.17.0.1:42042 - "GET /jupyter/lab/extensions/jupyterlab_pygments/static/remoteEntry.5cbb9d2323598fbda535.js HTTP/1.1" 200 OK
INFO: 172.17.0.1:42042 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/remoteEntry.e4ff09401a2f575928c0.js HTTP/1.1" 200 OK
INFO: 172.17.0.1:42074 - "GET /jupyter/lab/extensions/%40jupyter-notebook/lab-extension/static/remoteEntry.cad89c571bc2aee4aff2.js HTTP/1.1" 200 OK
INFO: 172.17.0.1:42074 - "GET /jupyter/lab/extensions/jupyterlab_pygments/static/568.1e2faa2ba0bbe59c4780.js?v=1e2faa2ba0bbe59c4780 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42042 - "GET /jupyter/lab/extensions/jupyterlab_pygments/static/747.67662283a5707eeb4d4c.js?v=67662283a5707eeb4d4c HTTP/1.1" 200 OK
INFO: 172.17.0.1:42074 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/898.4051a8b212a0f4c89d60.js?v=4051a8b212a0f4c89d60 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42094 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/647.f4f9578285e357c95be3.js?v=f4f9578285e357c95be3 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42084 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/651.fe40a967a60b543cf15c.js?v=fe40a967a60b543cf15c HTTP/1.1" 200 OK
INFO: 172.17.0.1:42106 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/439.33696bc45fbd403becbb.js?v=33696bc45fbd403becbb HTTP/1.1" 200 OK
INFO: 172.17.0.1:42074 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/722.3fefeac9cae358348cbc.js?v=3fefeac9cae358348cbc HTTP/1.1" 200 OK
INFO: 172.17.0.1:42042 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/420.063e2ee9f71033206b1f.js?v=063e2ee9f71033206b1f HTTP/1.1" 200 OK
INFO: 172.17.0.1:42110 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/327.8166aeb81cf1531ca240.js?v=8166aeb81cf1531ca240 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42094 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/446.bf169bd3821a9ba1aa62.js?v=bf169bd3821a9ba1aa62 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42084 - "GET /jupyter/lab/extensions/%40jupyter-notebook/lab-extension/static/568.f1a870f262b5e8588c75.js?v=f1a870f262b5e8588c75 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42106 - "GET /jupyter/lab/extensions/%40jupyter-notebook/lab-extension/static/93.c48a681bb3e8043bbbd7.js?v=c48a681bb3e8043bbbd7 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42094 - "GET /jupyter/api/me?1737772759730 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42106 - "GET /jupyter/api/kernelspecs?1737772759730 HTTP/1.1" 200 OK
INFO: ('172.17.0.1', 42120) - "WebSocket /jupyter/api/events/subscribe" [accepted]
INFO: connection open
INFO: 172.17.0.1:42084 - "GET /jupyter/lab/api/settings?1737772759733 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42094 - "GET /jupyter/api/kernels?1737772759739 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42074 - "GET /jupyter/api/sessions?1737772759740 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42110 - "GET /jupyter/api/terminals?1737772759740 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42106 - "GET /jupyter/api/me?1737772759742 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42042 - "GET /jupyter/api/kernelspecs?1737772759752 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42106 - "GET /jupyter/lab/api/translations/default?1737772759949 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42106 - "GET /jupyter/lsp/status?1737772760042 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42094 - "GET /jupyter/lab/api/settings?ids_only=true&1737772760169 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42074 - "GET /jupyter/lab/api/settings?1737772760169 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42106 - "GET /jupyter/lab/api/translations?1737772760144 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42094 - "GET /jupyter/lab/api/settings/%40jupyterlab/codemirror-extension%3Aplugin?1737772760267 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42110 - "GET /jupyter/lab/api/themes/%40jupyterlab/theme-light-extension/index.css HTTP/1.1" 200 OK
INFO: 172.17.0.1:42084 - "GET /jupyter/api/contents?content=1&hash=0&1737772760149 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42106 - "GET /jupyter/lab/api/settings/%40jupyterlab/notebook-extension%3Apanel?1737772760268 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42074 - "GET /jupyter/lab/api/settings/%40jupyterlab/application-extension%3Acontext-menu?1737772760270 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42110 - "GET /jupyter/lab/api/settings/%40jupyterlab/tooltip-extension%3Aconsoles?1737772760303 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42094 - "GET /jupyter/lab/api/settings/%40jupyterlab/tooltip-extension%3Anotebooks?1737772760302 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42084 - "GET /jupyter/lab/api/settings/%40jupyterlab/notebook-extension%3Atools?1737772760304 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42042 - "GET /jupyter/lab/api/settings/%40jupyterlab/toc-extension%3Aregistry?1737772760304 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42106 - "GET /jupyter/lab/api/settings/%40jupyterlab/notebook-extension%3Acompleter?1737772760305 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42084 - "GET /jupyter/lab/api/settings/%40jupyterlab/debugger-extension%3Amain?1737772760306 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42042 - "GET /jupyter/lab/api/settings/%40jupyterlab/celltags-extension%3Aplugin?1737772760306 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42074 - "GET /jupyter/lab/api/settings/%40jupyterlab/metadataform-extension%3Ametadataforms?1737772760305 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42110 - "GET /jupyter/lab/api/settings/%40jupyterlab/markdownviewer-extension%3Aplugin?1737772760305 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42094 - "GET /jupyter/lab/api/settings/%40jupyterlab/fileeditor-extension%3Acompleter?1737772760306 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42106 - "GET /jupyter/lab/api/settings/%40jupyterlab/cell-toolbar-extension%3Aplugin?1737772760306 HTTP/1.1" 200 OK
INFO: 172.17.0.1:42106 - "GET /jupyter/api/contents?content=1&hash=0&1737772760494 HTTP/1.1" 200 OK
[I 2025年01月25日 02:39:28.479 ServerApp] Creating new notebook in 
[I 2025年01月25日 02:39:28.567 ServerApp] Writing notebook-signing key to /home/user/.local/share/jupyter/notebook_secret
INFO: 172.17.0.1:34952 - "POST /jupyter/api/contents?1737772768472 HTTP/1.1" 201 Created
INFO: 172.17.0.1:34952 - "GET /jupyter/api/contents/Untitled.ipynb?content=0&hash=0&1737772768601 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34954 - "GET /jupyter/api/contents?content=1&hash=0&1737772768602 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34952 - "GET /jupyter/api/contents/Untitled.ipynb?type=notebook&content=1&hash=1&1737772768666 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34952 - "GET /jupyter/api/contents/Untitled.ipynb/checkpoints?1737772768934 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34962 - "GET /jupyter/api/contents/Untitled.ipynb/checkpoints?1737772768935 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34966 - "GET /jupyter/api/contents/Untitled.ipynb?content=0&hash=1&1737772768936 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34972 - "GET /jupyter/api/contents/Untitled.ipynb/checkpoints?1737772768935 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34966 - "GET /jupyter/api/sessions?1737772768949 HTTP/1.1" 200 OK
[I 2025年01月25日 02:39:28.954 ServerApp] Saving file at /Untitled.ipynb
INFO: 172.17.0.1:34962 - "PUT /jupyter/api/contents/Untitled.ipynb?1737772768951 HTTP/1.1" 200 OK
[I 2025年01月25日 02:39:29.064 ServerApp] Kernel started: 0d93a86d-b375-4f6d-a2e4-3ae92caecbad
INFO: 172.17.0.1:34966 - "POST /jupyter/api/sessions?1737772768956 HTTP/1.1" 201 Created
INFO: 172.17.0.1:34962 - "GET /jupyter/api/contents/Untitled.ipynb?content=0&hash=1&1737772769059 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34966 - "GET /jupyter/api/sessions?1737772769070 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34962 - "GET /jupyter/api/contents/Untitled.ipynb/checkpoints?1737772769075 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34972 - "GET /jupyter/api/contents?content=1&hash=0&1737772769059 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34972 - "GET /jupyter/notebooks/Untitled.ipynb HTTP/1.1" 200 OK
INFO: 172.17.0.1:34966 - "PATCH /jupyter/api/sessions/f3448cae-0cb7-47e7-9e3e-81f7bd809f47?1737772769165 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34962 - "GET /jupyter/api/kernels?1737772769183 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34972 - "PATCH /jupyter/api/sessions/f3448cae-0cb7-47e7-9e3e-81f7bd809f47?1737772769204 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34966 - "GET /jupyter/api/sessions?1737772769205 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34962 - "GET /jupyter/custom/custom.css HTTP/1.1" 304 Not Modified
INFO: 172.17.0.1:34954 - "GET /jupyter/api/nbconvert?1737772768656 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34972 - "GET /jupyter/lab/extensions/jupyterlab_pygments/static/remoteEntry.5cbb9d2323598fbda535.js HTTP/1.1" 304 Not Modified
INFO: 172.17.0.1:34966 - "GET /jupyter/lab/extensions/%40jupyter-notebook/lab-extension/static/remoteEntry.cad89c571bc2aee4aff2.js HTTP/1.1" 304 Not Modified
INFO: 172.17.0.1:34954 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/remoteEntry.e4ff09401a2f575928c0.js HTTP/1.1" 304 Not Modified
INFO: 172.17.0.1:34972 - "GET /jupyter/lab/extensions/jupyterlab_pygments/static/568.1e2faa2ba0bbe59c4780.js?v=1e2faa2ba0bbe59c4780 HTTP/1.1" 304 Not Modified
INFO: 172.17.0.1:34954 - "GET /jupyter/lab/extensions/jupyterlab_pygments/static/747.67662283a5707eeb4d4c.js?v=67662283a5707eeb4d4c HTTP/1.1" 304 Not Modified
INFO: 172.17.0.1:34966 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/898.4051a8b212a0f4c89d60.js?v=4051a8b212a0f4c89d60 HTTP/1.1" 304 Not Modified
INFO: 172.17.0.1:34972 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/651.fe40a967a60b543cf15c.js?v=fe40a967a60b543cf15c HTTP/1.1" 304 Not Modified
INFO: 172.17.0.1:34962 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/647.f4f9578285e357c95be3.js?v=f4f9578285e357c95be3 HTTP/1.1" 304 Not Modified
INFO: 172.17.0.1:34952 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/420.063e2ee9f71033206b1f.js?v=063e2ee9f71033206b1f HTTP/1.1" 304 Not Modified
INFO: 172.17.0.1:34954 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/439.33696bc45fbd403becbb.js?v=33696bc45fbd403becbb HTTP/1.1" 304 Not Modified
INFO: 172.17.0.1:34966 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/327.8166aeb81cf1531ca240.js?v=8166aeb81cf1531ca240 HTTP/1.1" 304 Not Modified
INFO: 172.17.0.1:34962 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/446.bf169bd3821a9ba1aa62.js?v=bf169bd3821a9ba1aa62 HTTP/1.1" 304 Not Modified
INFO: 172.17.0.1:34972 - "GET /jupyter/lab/extensions/%40jupyter-widgets/jupyterlab-manager/static/722.3fefeac9cae358348cbc.js?v=3fefeac9cae358348cbc HTTP/1.1" 304 Not Modified
INFO: 172.17.0.1:34954 - "GET /jupyter/lab/extensions/%40jupyter-notebook/lab-extension/static/93.c48a681bb3e8043bbbd7.js?v=c48a681bb3e8043bbbd7 HTTP/1.1" 304 Not Modified
INFO: 172.17.0.1:34952 - "GET /jupyter/lab/extensions/%40jupyter-notebook/lab-extension/static/568.f1a870f262b5e8588c75.js?v=f1a870f262b5e8588c75 HTTP/1.1" 304 Not Modified
INFO: 172.17.0.1:34972 - "GET /jupyter/api/kernelspecs?1737772769519 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34954 - "GET /jupyter/api/me?1737772769519 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34966 - "GET /jupyter/lab/api/settings?1737772769525 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34972 - "GET /jupyter/api/kernels?1737772769531 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34954 - "GET /jupyter/api/sessions?1737772769531 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34952 - "GET /jupyter/api/kernelspecs?1737772769532 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34962 - "GET /jupyter/api/terminals?1737772769531 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34966 - "GET /jupyter/api/me?1737772769532 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34966 - "GET /jupyter/lab/api/translations/default?1737772769696 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34966 - "GET /jupyter/lsp/status?1737772769722 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34966 - "GET /jupyter/lab/api/translations?1737772769818 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34954 - "GET /jupyter/lab/api/settings?ids_only=true&1737772769929 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34972 - "GET /jupyter/api/contents/Untitled.ipynb/checkpoints?1737772769929 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34962 - "GET /jupyter/api/contents/Untitled.ipynb?type=notebook&content=1&hash=1&1737772769951 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34954 - "GET /jupyter/lab/api/settings/%40jupyterlab/codemirror-extension%3Aplugin?1737772770000 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34972 - "GET /jupyter/lab/api/themes/%40jupyterlab/theme-light-extension/index.css HTTP/1.1" 304 Not Modified
INFO: 172.17.0.1:34954 - "GET /jupyter/lab/api/settings/%40jupyterlab/notebook-extension%3Apanel?1737772770001 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34952 - "GET /jupyter/api/contents/Untitled.ipynb/checkpoints?1737772770010 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34954 - "GET /jupyter/api/contents/Untitled.ipynb/checkpoints?1737772770050 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34972 - "GET /jupyter/api/contents/Untitled.ipynb/checkpoints?1737772770051 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34952 - "GET /jupyter/api/contents/Untitled.ipynb/checkpoints?1737772770051 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34962 - "GET /jupyter/api/contents?content=1&hash=0&1737772770002 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34954 - "GET /jupyter/api/contents/Untitled.ipynb/checkpoints?1737772770056 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34954 - "GET /jupyter/api/sessions?1737772770208 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34954 - "GET /jupyter/api/kernelspecs?1737772770227 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34966 - "GET /jupyter/api/nbconvert?1737772769908 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34966 - "GET /jupyter/api/terminals?1737772770468 HTTP/1.1" 200 OK
[I 2025年01月25日 02:39:31.258 ServerApp] Connecting to kernel 0d93a86d-b375-4f6d-a2e4-3ae92caecbad.
INFO: ('172.17.0.1', 34984) - "WebSocket /jupyter/api/kernels/0d93a86d-b375-4f6d-a2e4-3ae92caecbad/channels?session_id=75224964-c85e-4dc5-af93-1d3f6e4f07a4" [accepted]
INFO: connection open
[I 2025年01月25日 02:39:31.273 ServerApp] Connecting to kernel 0d93a86d-b375-4f6d-a2e4-3ae92caecbad.
INFO: ('172.17.0.1', 58168) - "WebSocket /jupyter/api/kernels/0d93a86d-b375-4f6d-a2e4-3ae92caecbad/channels?session_id=8ba904f4-8403-45db-8659-d414df12ceda" [accepted]
INFO: connection open
[I 2025年01月25日 02:39:31.290 ServerApp] Connecting to kernel 0d93a86d-b375-4f6d-a2e4-3ae92caecbad.
INFO: ('172.17.0.1', 58184) - "WebSocket /jupyter/api/kernels/0d93a86d-b375-4f6d-a2e4-3ae92caecbad/channels?session_id=c15b8b3f-0507-4df4-9357-1dcdb7f65cbf" [accepted]
INFO: connection open
INFO: ('172.17.0.1', 58196) - "WebSocket /jupyter/api/events/subscribe" [accepted]
INFO: connection open
[I 2025年01月25日 02:39:31.337 ServerApp] Connecting to kernel 0d93a86d-b375-4f6d-a2e4-3ae92caecbad.
INFO: ('172.17.0.1', 58210) - "WebSocket /jupyter/api/kernels/0d93a86d-b375-4f6d-a2e4-3ae92caecbad/channels?session_id=8c38fbdd-5152-4664-8c40-039f707b0a85" [accepted]
INFO: connection open
INFO: connection closed
INFO: connection closed
Task exception was never retrieved
future: <Task finished name='Task-261' coro=<WebSocketProtocol13.write_message.<locals>.wrapper() done, defined at /usr/local/lib/python3.12/site-packages/tornado/websocket.py:1086> exception=WebSocketClosedError()>
Traceback (most recent call last):
 File "/usr/local/lib/python3.12/site-packages/tornado/websocket.py", line 1088, in wrapper
 await fut
tornado.iostream.StreamClosedError: Stream is closed
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
 File "/usr/local/lib/python3.12/site-packages/tornado/websocket.py", line 1090, in wrapper
 raise WebSocketClosedError()
tornado.websocket.WebSocketClosedError
[I 2025年01月25日 02:39:31.378 ServerApp] Connecting to kernel 0d93a86d-b375-4f6d-a2e4-3ae92caecbad.
INFO: ('172.17.0.1', 58212) - "WebSocket /jupyter/api/kernels/0d93a86d-b375-4f6d-a2e4-3ae92caecbad/channels?session_id=25c6da44-08f7-41a6-9885-3bb9939703a4" [accepted]
INFO: connection open
INFO: connection closed
INFO: 172.17.0.1:34966 - "GET /jupyter/api/sessions?1737772771433 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34954 - "GET /jupyter/api/kernels?1737772771433 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34966 - "GET /jupyter/api/sessions?1737772771508 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34954 - "GET /jupyter/api/kernels?1737772771509 HTTP/1.1" 200 OK
INFO: 172.17.0.1:34954 - "GET /jupyter/static/favicons/favicon-busy-1.ico HTTP/1.1" 200 OK
INFO: 172.17.0.1:34954 - "GET /jupyter/api/contents/Untitled.ipynb/checkpoints?1737772772200 HTTP/1.1" 200 OK
Comment options

IDK. But tornado is not a dependency of fastapi, uvicorn, or fastapi-proxy-lib, but rather a dependency of jupyter, so you might need to ask jupyter or tornado.

The only thing I can think of is that when the browser requests to close the websocket connection, fastapi-proxy-lib or its dependency httpx-ws did not properly complete the closing handshake with the proxied jupyter websocket connection.

My suggestion is "just let it go". If it is indeed due to the websocket closing handshake, then it has already closed anyway, so it should not affect the normal transmission of messages.

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

AltStyle によって変換されたページ (->オリジナル) /