Skip to content

Commit d500467

Browse files
committed
fix(sdk): preserve reverse-proxy base path in SSH proxy URL
ssh_proxy_url rebuilt the websocket URL from scheme and netloc only, hardcoding an absolute /api/v1 path and discarding any base path. Behind a reverse proxy mounted at e.g. /flowmesh, the prefix was dropped from both the printed ProxyCommand instructions and the live `ssh proxy` backend. Join the base path with the API prefix, consistent with _make_url. Signed-off-by: Noppanat Wadlom <noppanat.wad@gmail.com>
1 parent 4e50132 commit d500467

2 files changed

Lines changed: 33 additions & 3 deletions

File tree

sdk/src/flowmesh/ssh.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import yaml
1010

11+
from ._constants import API_VERSION_PREFIX
1112
from .exceptions import FlowMeshError
1213
from .models.common import TaskStatus
1314
from .models.tasks import TaskInfo
@@ -159,9 +160,8 @@ def ssh_proxy_url(base_url: str, task_id: str) -> str:
159160
"""Build the websocket proxy URL for an SSH task."""
160161
base = urlsplit(base_url)
161162
ws_scheme = "wss" if base.scheme == "https" else "ws"
162-
return urlunsplit(
163-
(ws_scheme, base.netloc, f"/api/v1/ssh/tasks/{task_id}/proxy", "", "")
164-
)
163+
path = f"{base.path.rstrip('/')}{API_VERSION_PREFIX}/ssh/tasks/{task_id}/proxy"
164+
return urlunsplit((ws_scheme, base.netloc, path, "", ""))
165165

166166

167167
def ssh_connection_commands(

tests/sdk/test_resource_helpers.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,36 @@ def test_proxy_url_and_connection_commands(self) -> None:
317317
cmds = ssh_connection_commands("task-1", ssh_info, base_url=base_url)
318318
assert isinstance(cmds, list)
319319

320+
def test_proxy_url_root_mounted(self) -> None:
321+
assert (
322+
ssh_proxy_url("https://example.com", "task-1")
323+
== "wss://example.com/api/v1/ssh/tasks/task-1/proxy"
324+
)
325+
326+
def test_proxy_url_preserves_base_path(self) -> None:
327+
assert (
328+
ssh_proxy_url("https://kv.run:8000/flowmesh", "task-1")
329+
== "wss://kv.run:8000/flowmesh/api/v1/ssh/tasks/task-1/proxy"
330+
)
331+
332+
def test_proxy_url_scheme_conversion(self) -> None:
333+
assert ssh_proxy_url("http://localhost:8000", "t").startswith("ws://")
334+
assert ssh_proxy_url("https://localhost:8000", "t").startswith("wss://")
335+
336+
def test_proxy_url_trailing_slash(self) -> None:
337+
assert (
338+
ssh_proxy_url("https://kv.run:8000/flowmesh/", "task-1")
339+
== "wss://kv.run:8000/flowmesh/api/v1/ssh/tasks/task-1/proxy"
340+
)
341+
342+
def test_connection_commands_proxy_mode_embeds_base_path(self) -> None:
343+
ssh_info = {"mode": "proxy", "username": "flowmesh", "host": "h", "port": 22}
344+
cmds = ssh_connection_commands(
345+
"task-1", ssh_info, base_url="https://kv.run:8000/flowmesh"
346+
)
347+
proxy_cmd = next(cmd for label, cmd in cmds if label == "ssh (proxy)")
348+
assert "wss://kv.run:8000/flowmesh/api/v1/ssh/tasks/task-1/proxy" in proxy_cmd
349+
320350
def test_detect_public_key_prefers_standard_keys(self, tmp_path: Path) -> None:
321351
ssh_dir = tmp_path / ".ssh"
322352
ssh_dir.mkdir()

0 commit comments

Comments
 (0)