Skip to content

fix: preserve encoded development proxy URLs#400

Merged
mohamedmansour merged 11 commits into
mainfrom
mohamedmansour-fix-dev-proxy-url-encoding
Jul 23, 2026
Merged

fix: preserve encoded development proxy URLs#400
mohamedmansour merged 11 commits into
mainfrom
mohamedmansour-fix-dev-proxy-url-encoding

Conversation

@mohamedmansour

@mohamedmansour mohamedmansour commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Development requests with encoded route parameters were decoded by Actix path extraction before forwarding. Encoded slashes changed route boundaries, while spaces and UTF-8 values could produce invalid upstream URIs, leading to partial path mismatches or fallback state.

Build backend requests from the original URI path and query for both route-state resolution and /api forwarding, while retaining decoded paths for local asset lookup. Add wire-level regression coverage and document the transport contract.

Bundled build-system fixes

While validating this change through cargo xtask check, two unrelated but blocking build-system issues were fixed here to keep the gate and CI green. They are called out separately so the scope stays clear:

  • Parallel tsc example-build race (ensure_example_deps + TypeScript incremental): each example app's build:deps recompiled the shared JS packages into the same dist/ concurrently, so one app could read a half-written loader.js/esbuild.js while another app's tsc rewrote it, surfacing in CI as does not provide an export named 'compileProjection'. The shared packages are now built once up front and the shared tsconfigs are incremental, so each per-app build:deps is a no-op emit.
  • pnpm package-manager self-management stall (npm_config_manage_package_manager_versions=false in xtask's build_command): every pnpm run re-ran pnpm's version self-management, which could block for tens of seconds before the script started on a machine whose shared pnpm store was busy. xtask now runs the already-resolved pnpm directly (engines/packageManager and CI already pin the version).

Copilot AI review requested due to automatic review settings July 22, 2026 20:56
Qusic
Qusic previously approved these changes Jul 22, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a dev-server fidelity issue where Actix path extraction decoded percent-encoded route parameters before proxying, causing encoded slashes to alter route boundaries and producing invalid upstream URIs for some inputs. The dev server now builds backend state/proxy requests from the original request target (encoded path + query) while keeping decoded paths for local asset lookup, and it documents this transport contract.

Changes:

  • Build route-state resolution inputs from HttpRequest::uri() (encoded path/query) instead of decoded web::Path values.
  • Forward /api/* requests using the original encoded path+query from the incoming request target.
  • Add regression tests covering encoded spaces, %2F, %25, and UTF-8 sequences; document the encoded-forwarding contract in user docs and DESIGN.md.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
docs/guide/cli/index.md Documents that --api-port forwards encoded paths/queries unchanged and warns against double-encoding.
docs/ai.md Updates AI/CLI reference to reflect the encoded-forwarding behavior under --api-port.
DESIGN.md Specifies the encoded-path/query preservation contract for dev-server proxying and route state requests.
crates/webui-cli/src/commands/serve.rs Uses the original request target for route-state fetch and /api/* proxying; adds wire-level regression tests.

Comment thread crates/webui-cli/src/commands/serve.rs Outdated
Copilot AI review requested due to automatic review settings July 22, 2026 21:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

Comment thread crates/webui-cli/src/commands/serve.rs Outdated
Comment thread docs/guide/cli/index.md
Comment thread docs/ai.md
Comment thread DESIGN.md Outdated
mohamedmansour and others added 3 commits July 22, 2026 14:42
Development requests with encoded route parameters were decoded by Actix path
extraction before forwarding. Encoded slashes changed route boundaries, while
spaces and UTF-8 values could produce invalid upstream URIs, leading to partial
path mismatches or fallback state.

Build backend requests from the original URI path and query for both route-state
resolution and /api forwarding, while retaining decoded paths for local asset
lookup. Add wire-level regression coverage and document the transport contract.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Preserve the raw request URI behavior while restoring explicit capacity
planning in handle_api_proxy. Append the origin, port, and encoded request
target into a pre-sized buffer instead of formatting a new URL on every
proxied request.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The dev-server SPA fallback decided whether a request was a static asset
by testing the Actix-decoded path for a literal '.'. An encoded dot (%2E)
in a route parameter decoded to '.', so encoded-dot routes were wrongly
treated as file requests and returned 404 instead of resolving backend
state, contradicting the encoded-URI preservation this PR added.

Test the original encoded request path for the extension heuristic so
%2E stays a route-parameter byte, and forward it to the backend unchanged.
Add a regression test covering an encoded-dot route parameter and a
genuinely missing asset.

Also document the one intentional exception to the "encoded path forwarded
exactly" contract: the entry route alias. Both / and /index.html resolve
backend state at / (the entry path is normalized) while preserving the
query string. Updated DESIGN.md, the CLI guide, and the AI reference so the
transport contract is accurate.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 22, 2026 22:23
@mohamedmansour
mohamedmansour force-pushed the mohamedmansour-fix-dev-proxy-url-encoding branch from bc92d4e to 45f8f98 Compare July 22, 2026 22:23

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

crates/webui-cli/src/commands/serve.rs:982

  • spa_fallback treats any dot anywhere in the path as a file-extension indicator. That misclassifies valid route paths like /v2.1/users or /folder.with.dot/profile as assets and returns 404 even though only the last segment needs to look like a filename. Check the final segment of the encoded path instead.
    if paths.route_path.contains('.') {

Comment thread crates/webui-cli/src/commands/serve.rs Outdated
The `build (examples)` step builds every example app in parallel, and each
app's `build:deps` recompiles the shared workspace packages
(@microsoft/webui, @microsoft/webui-framework, @microsoft/webui-router)
with `tsc` into their shared `dist/` directories. Running those tsc
invocations concurrently races on the shared output: one app reads
packages/webui/dist/projection/adapters/esbuild.js while another app's tsc
is mid-rewrite of the sibling loader.js, surfacing in CI as
"SyntaxError: does not provide an export named 'compileProjection'".

Seed the shared packages once before the parallel app builds
(ensure_example_deps, mirroring ensure_example_wasm) and enable tsc
`incremental` on the three shared tsconfigs so each per-app build:deps
becomes a no-op emit that never rewrites the shared files concurrently.
The incremental buildinfo is gitignored. Verified: six concurrent tsc
runs against a seeded tree produce zero re-emit. This is strictly less
work than before, where the shared build ran once per app in parallel.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 22, 2026 23:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated 2 comments.

Comment thread crates/webui-cli/src/commands/serve.rs
Comment thread xtask/src/build_examples.rs
pnpm honours the root `packageManager` pin by re-running its
package-manager self-management on every `pnpm run`, resolving a pinned
pnpm under ~/AppData/Local/pnpm/.tools. On a machine whose shared global
pnpm store is being churned by other long-running pnpm processes, that
pre-run step blocks for tens of seconds before the script even starts,
even though the underlying work (e.g. the electron esbuild integration
build) takes milliseconds. This made `cargo xtask check` appear to hang
after `integration: electron`.

Set `npm_config_manage_package_manager_versions=false` for pnpm commands
in `build_command`, mirroring the existing `CARGO_INCREMENTAL=0` handling
for cargo. CI and the engines/packageManager fields already guarantee the
correct pnpm, so xtask runs the already-resolved binary directly. The
electron integration drops from a 90s+ stall to 0.1s and the full
build-examples phase completes in ~25s.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 22, 2026 23:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated no new comments.

The `--api-port` help text claimed encoded path and query bytes are
forwarded unchanged, but the entry-route alias resolves backend state for
both `/` and `/index.html` at `/` (query preserved). Note that exception
in the CLI help so `webui serve --help` matches the behavior already
documented in docs and DESIGN.md.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 22, 2026 23:47
@mohamedmansour
mohamedmansour requested a review from Qusic July 22, 2026 23:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated 1 comment.

Comment thread crates/webui-cli/src/commands/serve.rs Outdated
Qusic
Qusic previously approved these changes Jul 23, 2026
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 23, 2026 00:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 11 changed files in this pull request and generated 1 comment.

Comment thread crates/webui-cli/src/commands/serve.rs
Parse Accept q weights before selecting SPA fallback responses so q=0
prevents HTML or JSON fallback and higher weights win deterministically.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 23, 2026 00:23

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 11 changed files in this pull request and generated 1 comment.

Comment thread crates/webui-cli/src/commands/serve.rs
@mohamedmansour
mohamedmansour requested review from a team and mcritzjam July 23, 2026 00:30
Out-of-range but parseable q values (e.g. q=2 or q=-1) are malformed per
HTTP qvalue semantics. Treat them as absent with the default q of 1.0 so
an invalid Accept header cannot force a 404 or skew HTML/JSON tie-breaking.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 23, 2026 00:31

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 11 changed files in this pull request and generated 4 comments.

Comment thread docs/guide/cli/index.md
Comment thread docs/ai.md
Comment thread docs/guide/concepts/routing.md
Comment thread DESIGN.md Outdated
The docs claimed invalid Accept headers return 404, but a malformed or
out-of-range q value is treated as the default q=1.0 (still acceptable).
Drop the inaccurate 'invalid' wording and document the q=1.0 fallback so
DESIGN.md and the guide match webui serve behavior.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 23, 2026 00:39

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 11 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

crates/webui-cli/src/commands/serve.rs:1181

  • handle_api_proxy still allocates a temporary String via api_port.to_string() on every proxied request. This defeats some of the capacity planning here and adds avoidable work on the proxy hot path.

You can write the port directly into the already-allocated url buffer (infallible for String) and then append the path/query in the same formatting call.

    let mut url =
        String::with_capacity(API_URL_PREFIX.len() + MAX_PORT_DIGITS + path_and_query.len());
    url.push_str(API_URL_PREFIX);
    url.push_str(&api_port.to_string());
    url.push_str(path_and_query);

handle_api_proxy reserved MAX_PORT_DIGITS of capacity for the port but
then allocated a throwaway String via api_port.to_string() on every
proxied request. Write the digits directly into the buffer with write!,
matching the capacity planning and the crate's existing buffer-write
convention. Addresses a low-confidence proxy hot-path review suggestion.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 23, 2026 00:45

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 11 changed files in this pull request and generated no new comments.

@mohamedmansour
mohamedmansour requested a review from Qusic July 23, 2026 01:08
@mohamedmansour
mohamedmansour merged commit e79b7e2 into main Jul 23, 2026
22 checks passed
@mohamedmansour
mohamedmansour deleted the mohamedmansour-fix-dev-proxy-url-encoding branch July 23, 2026 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants