Skip to content

fix(client-python): normalize server URI paths before appending task service#738

Open
wdwd720 wants to merge 5 commits intorocketride-org:developfrom
wdwd720:fix/python-uri-normalization-paths
Open

fix(client-python): normalize server URI paths before appending task service#738
wdwd720 wants to merge 5 commits intorocketride-org:developfrom
wdwd720:fix/python-uri-normalization-paths

Conversation

@wdwd720
Copy link
Copy Markdown

@wdwd720 wdwd720 commented May 1, 2026

Summary

  • Normalize Python SDK server URI paths before building the WebSocket task-service endpoint.
  • Prevent //task/service for trailing-slash server URLs.
  • Prevent duplicate /task/service/task/service when callers pass an already-normalized WebSocket endpoint.

Type

fix

Testing

  • Tests added or updated
  • Tested locally
  • ./builder test passes

Checklist

  • Commit messages follow conventional commits
  • No secrets or credentials included
  • Wiki updated (if applicable)
  • Breaking changes documented (if applicable)

Linked Issue

Fixes #737

Problem

ConnectionMixin._get_websocket_uri() converted the URI scheme and then appended /task/service to the full URL string unconditionally. Inputs with a trailing slash produced a double slash before task/service, and inputs already ending in /task/service duplicated the service path.

Fix

Normalize the parsed URL path first, then append /task/service only when the cleaned path does not already end with it.

This preserves existing host-only URI behavior and keeps successful scheme conversion unchanged.

Local Testing

  • PYTHONDONTWRITEBYTECODE=1 python3 -m pytest packages/client-python/tests/test_connection_uri.py -q
  • PYTHONDONTWRITEBYTECODE=1 python3 -m pytest packages/client-python/tests/test_connection_uri.py packages/client-python/tests/test_client_env_loading.py -q

Notes

This PR is intentionally scoped to Python client URI path normalization and does not change authentication, environment loading, connection retry behavior, or network transport behavior.

Summary by CodeRabbit

  • Bug Fixes

    • Corrected WebSocket URI normalization in Python and TypeScript clients to handle trailing slashes and prevent duplicate path segments, ensuring reliable endpoint construction and scheme conversion.
  • Tests

    • Added tests covering multiple URL formats and schemes to validate consistent WebSocket URI normalization and behavior.

@github-actions github-actions Bot added the module:client-python Python SDK and MCP client label May 1, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 1, 2026

📝 Walkthrough

Walkthrough

Parses and normalizes configured URIs to produce websocket endpoints: trims trailing slashes, ensures /task/service appears exactly once, and maps http/https to ws/wss. Adds parameterized tests covering various input URI forms and expected websocket outputs.

Changes

Cohort / File(s) Summary
Python URI Normalization
packages/client-python/src/rocketride/mixins/connection.py
Reimplemented _get_websocket_uri to parse the URL, normalize the path (trim trailing slashes, ensure single /task/service), convert scheme (httpws, httpswss), and return the composed URL to avoid duplicate path segments.
Python Tests
packages/client-python/tests/test_connection_uri.py
Added parameterized tests verifying scheme conversion, host/port preservation, trailing-slash handling, and prevention of duplicated /task/service across multiple input variants.
TypeScript URI Normalization
packages/client-typescript/src/client/client.ts
Updated _getWebsocketUri to normalize path similarly: strip trailing slashes and append /task/service only if not already present; applied same behavior in the unparseable-URI fallback.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

module:client-typescript

Suggested reviewers

  • jmaionchi
  • stepmikhaylov
  • Rod-Christensen

Poem

🐇 I hop through URLs, nibble each slash,
I tuck /task/service in—no duplicate stash.
Schemes turn to webs with a soft little twirl,
Clean endpoints for every client and curl. 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive While most changes align with the scope (Python URI normalization), a TypeScript client modification was included in a commit, which extends beyond the stated PR scope of Python SDK changes. Clarify whether the TypeScript changes are intentional; if unintended, separate them into a distinct PR or document the rationale for including TypeScript modifications.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: normalizing server URI paths in the Python client before appending the task service endpoint.
Linked Issues check ✅ Passed The changes address all coding requirements from issue #737: normalize paths to prevent double slashes, avoid duplicating /task/service, and preserve host-only URL behavior.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 1, 2026

No description provided.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/client-python/tests/test_connection_uri.py`:
- Around line 6-15: Add explicit http->ws and wss trailing-slash regression
cases to the pytest parametrize list: include entries mapping
'http://localhost:5565' -> 'ws://localhost:5565/task/service' and
'http://localhost:5565/' -> 'ws://localhost:5565/task/service' (to cover
HTTP->WS conversion), and include 'wss://cloud.rocketride.ai' ->
'wss://cloud.rocketride.ai/task/service' and 'wss://cloud.rocketride.ai/' ->
'wss://cloud.rocketride.ai/task/service' (to cover WSS trailing-slash handling)
alongside the existing ('input_uri','expected_uri') tuples in the
test_connection_uri parametrize block.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 31f616bf-0012-47aa-af07-f1d072ec806b

📥 Commits

Reviewing files that changed from the base of the PR and between a665f46 and 508664f.

📒 Files selected for processing (2)
  • packages/client-python/src/rocketride/mixins/connection.py
  • packages/client-python/tests/test_connection_uri.py

Comment thread packages/client-python/tests/test_connection_uri.py
@wdwd720
Copy link
Copy Markdown
Author

wdwd720 commented May 1, 2026

Thanks, added explicit http→ws and wss trailing-slash regression cases and reran the focused URI normalization test.

PYTHONDONTWRITEBYTECODE=1 python3 -m pytest packages/client-python/tests/test_connection_uri.py -q

…ask service

Apply the same URI normalization fix from the Python client to the
TypeScript client. _getWebsocketUri() now strips trailing slashes and
checks whether /task/service is already present before appending it,
preventing double-slash and duplicate-path issues.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@Rod-Christensen Rod-Christensen left a comment

Choose a reason for hiding this comment

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

Thank you --- this is great!

I also added the typescript client fix in there as well!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/client-typescript/src/client/client.ts`:
- Around line 446-454: Add TypeScript tests to the existing "RocketRideClient
URI normalization" suite to cover the regression cases this PR fixes: add cases
that ensure duplicate slashes are collapsed (e.g., input
"wss://host//task/service" => normalized "wss://host/task/service") and cases
that prevent duplicated path segments (e.g., input
"wss://host/task/service/task/service" => normalized "wss://host/task/service").
Place these new assertions alongside the existing RocketRideClient tests (the
suite referenced in client.ts URI normalization logic that uses SERVICE_PATH and
the try/catch path normalization), verifying both the parsed/wsScheme branch and
the fallback unparseable-URI branch produce the expected single canonical path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f19fabee-6f99-400d-84db-d51237daed3a

📥 Commits

Reviewing files that changed from the base of the PR and between 6b7d776 and 4de158d.

📒 Files selected for processing (1)
  • packages/client-typescript/src/client/client.ts

Comment on lines +446 to +454
let path = url.pathname.replace(/\/+$/, '');
if (!path.endsWith(SERVICE_PATH)) {
path = path ? `${path}${SERVICE_PATH}` : SERVICE_PATH;
}
return `${wsScheme}//${url.host}${path}`;
} catch {
return `${httpUrl}/task/service`;
// Fallback for unparseable URIs — same trailing-slash + dedup logic
const stripped = httpUrl.replace(/\/+$/, '');
return stripped.endsWith(SERVICE_PATH) ? stripped : `${stripped}${SERVICE_PATH}`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether the TS test file already covers trailing-slash and pre-normalized-path cases.
rg -n "trailing|task/service" --type=ts packages/client-typescript/tests/

Repository: rocketride-org/rocketride-server

Length of output: 608


🏁 Script executed:

# First, read the actual test file context around the URI normalization tests
sed -n '2186,2200p' packages/client-typescript/tests/RocketRideClient.test.ts

Repository: rocketride-org/rocketride-server

Length of output: 681


🏁 Script executed:

# Read the code being tested to verify the logic matches the suggested test cases
sed -n '440,460p' packages/client-typescript/src/client/client.ts

Repository: rocketride-org/rocketride-server

Length of output: 880


TypeScript tests are missing the exact regression cases this PR fixes.

The existing RocketRideClient URI normalization suite (lines 2186-2198) covers only bare host inputs. The PR's stated fixes — preventing double trailing slashes (e.g., wss://host//task/service) and avoiding duplicate path segments (e.g., wss://host/task/service/task/service) — have no TS-side test coverage.

Suggested additions:

 it.each([
   ['wss://cloud.rocketride.ai', 'wss://cloud.rocketride.ai/task/service'],
   ['https://cloud.rocketride.ai', 'wss://cloud.rocketride.ai/task/service'],
   ['ws://localhost:5565', 'ws://localhost:5565/task/service'],
   ['http://localhost:5565', 'ws://localhost:5565/task/service'],
+  // trailing-slash regression: must not produce double slash
+  ['wss://cloud.rocketride.ai/', 'wss://cloud.rocketride.ai/task/service'],
+  ['https://cloud.rocketride.ai/', 'wss://cloud.rocketride.ai/task/service'],
+  ['http://localhost:5565/', 'ws://localhost:5565/task/service'],
+  // pre-normalized path: must not duplicate /task/service
+  ['wss://cloud.rocketride.ai/task/service', 'wss://cloud.rocketride.ai/task/service'],
+  ['https://cloud.rocketride.ai/task/service', 'wss://cloud.rocketride.ai/task/service'],
 ])('normalizes %s to %s', (inputUri, expectedUri) => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client-typescript/src/client/client.ts` around lines 446 - 454, Add
TypeScript tests to the existing "RocketRideClient URI normalization" suite to
cover the regression cases this PR fixes: add cases that ensure duplicate
slashes are collapsed (e.g., input "wss://host//task/service" => normalized
"wss://host/task/service") and cases that prevent duplicated path segments
(e.g., input "wss://host/task/service/task/service" => normalized
"wss://host/task/service"). Place these new assertions alongside the existing
RocketRideClient tests (the suite referenced in client.ts URI normalization
logic that uses SERVICE_PATH and the try/catch path normalization), verifying
both the parsed/wsScheme branch and the fallback unparseable-URI branch produce
the expected single canonical path.

kwit75 added a commit that referenced this pull request May 1, 2026
…#742)

Replace \`ROCKETRIDE_APIKEY: \${{ secrets.ROCKETRIDE_APIKEY }}\` with a
literal \`MYAPIKEY\` in the Test step env block.

Why this is unblocking the queue
--------------------------------
PR #712 set up the env var to fix "No authentication configured" failures
in client-python integration tests. Its own inline comment correctly
noted that "the secret value itself doesn't matter — it just has to
match between server and client in this single CI run." Sourcing it
from \`secrets.ROCKETRIDE_APIKEY\` introduced an empty-string failure
mode that we hit:

  1. The secret was created on 2026-04-27, has not been updated since,
     and may be set to "" (or rotated to a value the engine no longer
     accepts).
  2. When that happens, the workflow silently expands the expression to
     \`ROCKETRIDE_APIKEY=""\` for the Test step.
  3. The test client reads it via \`os.getenv('ROCKETRIDE_APIKEY',
     'MYAPIKEY')\`. \`os.getenv\` returns the empty string when the
     variable is set-but-empty — NOT the default — so the client
     authenticates with \`""\`.
  4. The server (running in the same step) sees the same empty key and
     responds AuthenticationException.
  5. All 48 client-python integration tests fail uniformly across
     Ubuntu, Windows, and macOS (which is what's been happening on
     develop's most recent runs and on PRs #715, #728, #738).

Using a literal value eliminates the entire failure mode without
changing observable behaviour: the value still isn't a secret (the
inline comment was always explicit on this), it never leaves the runner,
and it matches the "MYAPIKEY" dev key the engine already recognises
elsewhere in the codebase (\`.env.template\`).

Together with #734 (the sequential test execution flag, already on
develop) this should clear both failure modes that have been blocking
PRs since yesterday.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python SDK URI normalization can duplicate task-service path

3 participants