Skip to content

Commit

Permalink
Support panel fastapi behind reverse proxy (#7278)
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcSkovMadsen authored Sep 16, 2024
1 parent e41f21d commit ad3a75b
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 28 deletions.
68 changes: 41 additions & 27 deletions panel/io/location.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,46 @@
from bokeh.server.contexts import BokehSessionContext
from pyviz_comms import Comm

def _get_location_params(protocol: str|None, host: str| None, uri: str| None)->dict:
params = {}
href = ''
if protocol:
params['protocol'] = href = f'{protocol}:'
if host:
if host.startswith("::ffff:"):
host = host.replace("::ffff:", "")

href += f'//{host}'
if ':' in host:
params['hostname'], params['port'] = host.split(':')
else:
params['hostname'] = host
if uri:
search = hash = None

if uri.startswith("https,"):
uri = uri.replace("https,", "")

if uri.startswith("http"):
uri = urlparse.urlparse(uri).path

href += uri
if '?' in uri and '#' in uri:
params['pathname'], query = uri.split('?')
search, hash = query.split('#')
elif '?' in uri:
params['pathname'], search = uri.split('?')
elif '#' in uri:
params['pathname'], hash = uri.split('#')
else:
params['pathname'] = uri
if search:
params['search'] = f'?{search}'
if hash:
params['hash'] = f'#{hash}'
params['href'] = href
return params

class Location(Syncable):
"""
The Location component can be made available in a server context
Expand Down Expand Up @@ -70,33 +110,7 @@ def from_request(cls, request):
except ImportError:
return cls()

params = {}
href = ''
if request.protocol:
params['protocol'] = href = f'{request.protocol}:'
if request.host:
href += f'//{request.host}'
if ':' in request.host:
params['hostname'], params['port'] = request.host.split(':')
else:
params['hostname'] = request.host
if request.uri:
search = hash = None
href += request.uri
if '?' in request.uri and '#' in request.uri:
params['pathname'], query = request.uri.split('?')
search, hash = query.split('#')
elif '?' in request.uri:
params['pathname'], search = request.uri.split('?')
elif '#' in request.uri:
params['pathname'], hash = request.uri.split('#')
else:
params['pathname'] = request.uri
if search:
params['search'] = f'?{search}'
if hash:
params['hash'] = f'#{hash}'
params['href'] = href
params = _get_location_params(request.protocol, request.host, request.uri)
loc = cls()
with edit_readonly(loc):
loc.param.update(params)
Expand Down
19 changes: 18 additions & 1 deletion panel/tests/io/test_location.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import param
import pytest

from panel.io.location import Location
from panel.io.location import Location, _get_location_params
from panel.io.state import state
from panel.tests.util import serve_and_request, wait_until
from panel.util import edit_readonly
Expand Down Expand Up @@ -188,3 +188,20 @@ def test_location_sync_to_dataframe_with_initial_value(location, dataframe):
location.search = "?dataframe=%5B%7B%22x%22%3A+1%7D%5D"
location.sync(p)
pd.testing.assert_frame_equal(p.dataframe, dataframe)

@pytest.mark.parametrize(("protocol", "host", "uri", "expected"), [
# Started with the command fastapi dev script.py on local laptop
(
"http", "127.0.0.1", "/panel",
{'protocol': 'http:', 'hostname': '127.0.0.1', 'pathname': '/panel', 'href': 'http://127.0.0.1/panel'}
),
# Started with the command fastapi dev script.py --root-path /some/path in VS Code terminal on JupyterHub
(
"http", "::ffff:172.20.0.233", "https,http://sub.domain.dk/some/path/panel",
# I believe the below should be the result. But do not know for sure
{"protocol": "http:", "hostname": "172.20.0.233", "pathname": "/some/path/panel", 'href': 'http://172.20.0.233/some/path/panel'}
)
])
def test_get_location_params(protocol, host, uri, expected):
params = _get_location_params(protocol, host, uri)
assert params==expected

0 comments on commit ad3a75b

Please sign in to comment.