Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions sdks/sandbox/python/src/opensandbox/config/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,16 @@ def get_domain(self) -> str:
return self.domain or os.getenv(self._ENV_DOMAIN, self._DEFAULT_DOMAIN)

def get_base_url(self) -> str:
"""Get the full base URL for API requests."""
"""Get the full base URL for API requests.

Returns the base URL without a version prefix. The generated API client
appends versioned paths (e.g. ``/sandboxes``) relative to this URL.
Appending ``/v1`` here would route all requests to the blocking
``/v1/sandboxes`` endpoint and cause 504 Gateway Timeouts when the
server-side proxy has a short idle timeout (see issue #591).
"""
domain = self.get_domain()
# Allow domain to override protocol if it explicitly starts with a scheme
if domain.startswith("http://") or domain.startswith(
"https://"
):
return f"{domain}/{self._API_VERSION}"
return f"{self.protocol}://{domain}/{self._API_VERSION}"
if domain.startswith("http://") or domain.startswith("https://"):
return domain.rstrip("/")
return f"{self.protocol}://{domain}"
Comment on lines +169 to +171

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the versioned lifecycle base path

For configurations that provide only the host (for example the default localhost:8080 or OPEN_SANDBOX_DOMAIN=api.example.com), this now sends lifecycle and diagnostics calls to /sandboxes instead of the public /v1/sandboxes API. I checked the generated Python client and its paths are unversioned (post_sandboxes.py uses "url": "/sandboxes"), while the OpenAPI specs still define the server URL as http://localhost:8080/v1 and the other SDKs document/construct lifecycle base URLs with /v1; deployments or gateways that expose only the versioned API will 404 these Python SDK calls. If a hosted gateway needs the unversioned path to avoid the blocking route, that needs to be an explicit base URL/domain choice rather than changing the default host-only behavior.

Useful? React with 👍 / 👎.

12 changes: 10 additions & 2 deletions sdks/sandbox/python/src/opensandbox/config/connection_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,15 @@ def get_domain(self) -> str:
return self.domain or os.getenv(self._ENV_DOMAIN, self._DEFAULT_DOMAIN)

def get_base_url(self) -> str:
"""Get the full base URL for API requests.

Returns the base URL without a version prefix. The generated API client
appends versioned paths (e.g. ``/sandboxes``) relative to this URL.
Appending ``/v1`` here would route all requests to the blocking
``/v1/sandboxes`` endpoint and cause 504 Gateway Timeouts when the
server-side proxy has a short idle timeout (see issue #591).
"""
domain = self.get_domain()
if domain.startswith("http://") or domain.startswith("https://"):
return f"{domain}/{self._API_VERSION}"
return f"{self.protocol}://{domain}/{self._API_VERSION}"
return domain.rstrip("/")
return f"{self.protocol}://{domain}"
31 changes: 29 additions & 2 deletions sdks/sandbox/python/tests/test_connection_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,40 @@ def test_protocol_validation() -> None:


def test_get_base_url_with_domain_and_protocol() -> None:
# get_base_url() must NOT append /v1 — doing so would route requests to the
# blocking /v1/sandboxes endpoint and cause 504 Gateway Timeouts (issue #591).
cfg = ConnectionConfig(domain="example.com:1234", protocol="https")
assert cfg.get_base_url() == "https://example.com:1234/v1"
assert cfg.get_base_url() == "https://example.com:1234"


def test_get_base_url_domain_can_include_scheme() -> None:
cfg = ConnectionConfig(domain="https://example.com:9999", protocol="http")
assert cfg.get_base_url() == "https://example.com:9999/v1"
assert cfg.get_base_url() == "https://example.com:9999"


def test_get_base_url_no_v1_prefix_direct_mode() -> None:
"""Direct mode: base URL must not contain /v1 so SDK hits POST /sandboxes (async, 202)."""
cfg = ConnectionConfig(domain="sandbox-api.example.com", use_server_proxy=False)
url = cfg.get_base_url()
assert "/v1" not in url
assert url == "http://sandbox-api.example.com"


def test_get_base_url_no_v1_prefix_proxy_mode() -> None:
"""Proxy mode: base URL must not contain /v1 to avoid hitting the blocking endpoint."""
cfg = ConnectionConfig(
domain="http://sandbox-api.example.com",
use_server_proxy=True,
)
url = cfg.get_base_url()
assert "/v1" not in url
assert url == "http://sandbox-api.example.com"


def test_get_base_url_trailing_slash_stripped() -> None:
"""Trailing slashes on the domain are stripped to prevent double-slash in paths."""
cfg = ConnectionConfig(domain="http://sandbox-api.example.com/")
assert cfg.get_base_url() == "http://sandbox-api.example.com"
Comment on lines +61 to +64


@pytest.mark.asyncio
Expand Down
Loading