-
Notifications
You must be signed in to change notification settings - Fork 356
Description
Description
When configuring a Gateway Server with session affinity, it is important to set the accept cookies configurable option so that session-specific cookies are maintained within the GatewayClient
and used on subsequent requests. This handling was introduced in #969 but does not account for the case when multiple entries are located in the Set-Cookie
key of the response headers.
When multiple cookies are present, response.headers.get("Set-Cookie")
returns a comma-separated string of cookies. This breaks the parsing logic on the subsequently called SimpleCookie.load()
method.
I have a proposed fix and will provide a PR soon.
Reproduce
- Configure a Gateway Server (I used JupyterKernelGateway) that configures session affinity.
- Configure the gateway client to accept cookies via
--GatewayClient.accept_cookies=true
- Start lab with the appropriate
--gateway-url
option, along with accept_cookies.
When the server starts and issues the /api/kernelspecs
to fetch kernelspecs from the gateway, the following stack trace is produced:
Stack trace when multiple cookies are returned...
[E 2025-08-25 17:13:31.233 ServerApp] Uncaught exception GET /api/kernelspecs?1756167210712 (::1)
HTTPServerRequest(protocol='http', host='localhost:8888', method='GET', uri='/api/kernelspecs?1756167210712', version='HTTP/1.1', remote_ip='::1')
Traceback (most recent call last):
File "/Users/kbates/dev/playground/jupyter/.venv/lib/python3.12/site-packages/tornado/web.py", line 1848, in _execute
result = await result
^^^^^^^^^^^^
File "/Users/kbates/dev/playground/jupyter/.venv/lib/python3.12/site-packages/jupyter_server/auth/decorator.py", line 73, in inner
return await out
^^^^^^^^^
File "/Users/kbates/dev/playground/jupyter/.venv/lib/python3.12/site-packages/jupyter_server/services/kernelspecs/handlers.py", line 73, in get
kspecs = await ensure_async(ksm.get_all_specs())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/kbates/dev/playground/jupyter/.venv/lib/python3.12/site-packages/jupyter_core/utils/__init__.py", line 197, in ensure_async
result = await obj
^^^^^^^^^
File "/Users/kbates/dev/playground/jupyter/.venv/lib/python3.12/site-packages/jupyter_server/gateway/managers.py", line 277, in get_all_specs
fetched_kspecs = await self.list_kernel_specs()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/kbates/dev/playground/jupyter/.venv/lib/python3.12/site-packages/jupyter_server/gateway/managers.py", line 301, in list_kernel_specs
response = await gateway_request(kernel_spec_url, method="GET")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/kbates/dev/playground/jupyter/.venv/lib/python3.12/site-packages/jupyter_server/gateway/gateway_client.py", line 828, in gateway_request
cookie.load(cookie_values)
File "/Users/kbates/.local/share/uv/python/cpython-3.12.11-macos-aarch64-none/lib/python3.12/http/cookies.py", line 516, in load
self.__parse_string(rawdata)
File "/Users/kbates/.local/share/uv/python/cpython-3.12.11-macos-aarch64-none/lib/python3.12/http/cookies.py", line 580, in __parse_string
self.__set(key, rval, cval)
File "/Users/kbates/.local/share/uv/python/cpython-3.12.11-macos-aarch64-none/lib/python3.12/http/cookies.py", line 472, in __set
M.set(key, real_value, coded_value)
File "/Users/kbates/.local/share/uv/python/cpython-3.12.11-macos-aarch64-none/lib/python3.12/http/cookies.py", line 335, in set
raise CookieError('Illegal key %r' % (key,))
http.cookies.CookieError: Illegal key 'HttpOnly,SERVERSESSION'
[W 2025-08-25 17:13:31.237 ServerApp] wrote error: 'Unhandled error'
Traceback (most recent call last):
File "/Users/kbates/dev/playground/jupyter/.venv/lib/python3.12/site-packages/tornado/web.py", line 1848, in _execute
result = await result
^^^^^^^^^^^^
File "/Users/kbates/dev/playground/jupyter/.venv/lib/python3.12/site-packages/jupyter_server/auth/decorator.py", line 73, in inner
return await out
^^^^^^^^^
File "/Users/kbates/dev/playground/jupyter/.venv/lib/python3.12/site-packages/jupyter_server/services/kernelspecs/handlers.py", line 73, in get
kspecs = await ensure_async(ksm.get_all_specs())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/kbates/dev/playground/jupyter/.venv/lib/python3.12/site-packages/jupyter_core/utils/__init__.py", line 197, in ensure_async
result = await obj
^^^^^^^^^
File "/Users/kbates/dev/playground/jupyter/.venv/lib/python3.12/site-packages/jupyter_server/gateway/managers.py", line 277, in get_all_specs
fetched_kspecs = await self.list_kernel_specs()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/kbates/dev/playground/jupyter/.venv/lib/python3.12/site-packages/jupyter_server/gateway/managers.py", line 301, in list_kernel_specs
response = await gateway_request(kernel_spec_url, method="GET")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/kbates/dev/playground/jupyter/.venv/lib/python3.12/site-packages/jupyter_server/gateway/gateway_client.py", line 828, in gateway_request
cookie.load(cookie_values)
File "/Users/kbates/.local/share/uv/python/cpython-3.12.11-macos-aarch64-none/lib/python3.12/http/cookies.py", line 516, in load
self.__parse_string(rawdata)
File "/Users/kbates/.local/share/uv/python/cpython-3.12.11-macos-aarch64-none/lib/python3.12/http/cookies.py", line 580, in __parse_string
self.__set(key, rval, cval)
File "/Users/kbates/.local/share/uv/python/cpython-3.12.11-macos-aarch64-none/lib/python3.12/http/cookies.py", line 472, in __set
M.set(key, real_value, coded_value)
File "/Users/kbates/.local/share/uv/python/cpython-3.12.11-macos-aarch64-none/lib/python3.12/http/cookies.py", line 335, in set
raise CookieError('Illegal key %r' % (key,))
http.cookies.CookieError: Illegal key 'HttpOnly,SERVERSESSION'
[E 2025-08-25 17:13:31.238 ServerApp] {
"Host": "localhost:8888",
"Accept": "*/*",
"Referer": "http://localhost:8888/lab",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
}
Here's an example of the string returned from response.headers.get("Set-Cookie")
:
SERVERSESSION=016723f57acc1447927bd43584d16dd6|12345678; Max-Age=172800; Path=/; Secure; HttpOnly,SERVERSESSION=a46923feb0ab14538230061651140472|12345678; Max-Age=172800; Path=/; Secure; HttpOnly,SERVERSESSION=4826c0e5c56a89c84c3906ba6ffd3abe|12345678; Max-Age=172800; Path=/; Secure; HttpOnly,username-ce-gateway-my-server=2|1:0|10:1756250589|35:username-gateway-server|144:123abc789|87654321; expires=Thu, 25 Sep 2025 23:23:09 GMT; HttpOnly; Path=/
Expected behavior
GatewayClient
should record each cookie in its state.
Context
- Operating System and version: Linux, Kuberenetes
- Browser and version: n/a
- Jupyter Server version: Latest
Troubleshoot Output
Paste the output from running `jupyter troubleshoot` from the command line here. You may want to sanitize the paths in the output.
Command Line Output
Paste the output from your command line running `jupyter lab` here, use `--debug` if possible.
Browser Output
Paste the output from your browser Javascript console here, if applicable.