Skip to content

workers assets shared routing cleanup - #14878

Open
jbwcloudflare wants to merge 5 commits into
cloudflare:mainfrom
jbwcloudflare:jwheeler/WC-5564
Open

workers assets shared routing cleanup#14878
jbwcloudflare wants to merge 5 commits into
cloudflare:mainfrom
jbwcloudflare:jwheeler/WC-5564

Conversation

@jbwcloudflare

@jbwcloudflare jbwcloudflare commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Fixes #[insert GH or internal issue link(s)].

Please see ticket above.
This change centralizes some of the URL handling for Workers Assets and Pages and begins recording telemetry to detect any impact that would be caused by updating all code to use the canonical URL handling.

This is telemetry-only for now.


  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because: no behavioral change

A picture of a cute animal (not mandatory, but encouraged)

@changeset-bot

changeset-bot Bot commented Jul 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 107ab47

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@cloudflare/workers-shared Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jul 27, 2026

Copy link
Copy Markdown
@cloudflare/autoconfig

npm i https://pkg.pr.new/@cloudflare/autoconfig@14878

@cloudflare/config

npm i https://pkg.pr.new/@cloudflare/config@14878

create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@14878

@cloudflare/deploy-helpers

npm i https://pkg.pr.new/@cloudflare/deploy-helpers@14878

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@14878

miniflare

npm i https://pkg.pr.new/miniflare@14878

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@14878

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@14878

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@14878

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@14878

@cloudflare/workers-auth

npm i https://pkg.pr.new/@cloudflare/workers-auth@14878

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@14878

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@14878

wrangler

npm i https://pkg.pr.new/wrangler@14878

commit: 107ab47

@jbwcloudflare
jbwcloudflare marked this pull request as ready for review July 27, 2026 17:49
@workers-devprod
workers-devprod requested review from a team and penalosa and removed request for a team July 27, 2026 17:49
@workers-devprod

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/deploy-config
  • @cloudflare/wrangler
Show detailed file reviewers
  • .changeset/thin-pianos-create.md: [@cloudflare/wrangler]
  • packages/workers-shared/asset-worker/src/analytics.ts: [@cloudflare/deploy-config @cloudflare/wrangler]
  • packages/workers-shared/asset-worker/src/handler.ts: [@cloudflare/deploy-config @cloudflare/wrangler]
  • packages/workers-shared/asset-worker/src/utils/canonical-path.ts: [@cloudflare/deploy-config @cloudflare/wrangler]
  • packages/workers-shared/asset-worker/src/utils/headers.ts: [@cloudflare/deploy-config @cloudflare/wrangler]
  • packages/workers-shared/asset-worker/src/utils/rules-engine.ts: [@cloudflare/deploy-config @cloudflare/wrangler]
  • packages/workers-shared/asset-worker/tests/canonical-path.test.ts: [@cloudflare/deploy-config @cloudflare/wrangler]
  • packages/workers-shared/asset-worker/tests/handler.test.ts: [@cloudflare/deploy-config @cloudflare/wrangler]
  • packages/workers-shared/asset-worker/tests/rules-engine.test.ts: [@cloudflare/deploy-config @cloudflare/wrangler]
  • packages/workers-shared/router-worker/src/analytics.ts: [@cloudflare/deploy-config @cloudflare/wrangler]
  • packages/workers-shared/router-worker/src/worker.ts: [@cloudflare/deploy-config @cloudflare/wrangler]
  • packages/workers-shared/router-worker/tests/index.test.ts: [@cloudflare/deploy-config @cloudflare/wrangler]

@jbwcloudflare

Copy link
Copy Markdown
Contributor Author

marked as ready to get AI feedback, will fix any feedback and finalize afterwards

@devin-ai-integration devin-ai-integration Bot left a comment

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.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 1 additional finding.

Open in Devin Review

@@ -0,0 +1,7 @@
---
"@cloudflare/workers-shared": minor

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.

I think technically should be patch

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

the robot told me a telemetry change should be minor

but I'll do whatever the wrangler team wants here

};
}

// Avoid decoding and matching work for paths that cannot change.

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.

super nit: your comment actually is actually indicating the reverse of what this function does. The use of this does help us avoid work, but maybe this comment needs to move, or verbiage be flipped 😅

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

haha good call, this function was removed due to the bug you called out in the other comment

};

// Returns both path representations so routing cannot accidentally reuse lookup input.
export function canonicalizePath(pathname: string): CanonicalPath {

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.

This reencodes each path segment, but the normalization bits and fast check only recognize % and //

For instance /docs+draft would canonicalize to /docs%2Bdraft, but the fast check returns false and the normalization mask would remain None. A literal /docs%2Bdraft rule would change decision and we wouldn't see any telemetry telemetry

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ah good catch!

to avoid this kind of pitfall, I removed the "fast path" skip thing and decided to be more comprehensive and always run the comparison.

None = 0,
Decoded = 1 << 0,
CollapsedSlashes = 1 << 1,
MalformedEncoding = 1 << 2,

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.

We should also add a test for this case

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

@devin-ai-integration devin-ai-integration Bot left a comment

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.

Devin Review found 1 new potential issue.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment on lines +60 to +65
function encodePath(pathname: string): string {
return pathname
.split("/")
.map((segment) => encodeURIComponent(segment))
.join("/");
}

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.

🟡 Unusual web addresses can make a request error out instead of being served

When re-encoding a request path, each segment is passed to encodeURIComponent (packages/workers-shared/asset-worker/src/utils/canonical-path.ts:63) with no error guard, so a path that decodes to an unpaired surrogate makes the shadow-mode path processing throw instead of just recording telemetry.

Impact: For such rare malformed paths the request fails with a server error rather than being handled normally, contradicting the "customer request handling is unchanged" intent.

Why the throw propagates to the request

encodeURIComponent throws a URIError on lone surrogates. The equivalent encodePath in packages/workers-shared/asset-worker/src/handler.ts:1018-1030 wraps encodeURIComponent in a try/catch for exactly this reason, but the new canonicalizePath helper does not. canonicalizePath is called from recordCanonicalPathRuleDifferences (packages/workers-shared/asset-worker/src/handler.ts:1144-1145) and from the router's static-routing block (packages/workers-shared/router-worker/src/worker.ts:286), neither of which is wrapped in try/catch, so a throw here surfaces as a request error even though this code is meant to be shadow-only.

Note: reachability is limited because URL parsing normally prevents lone surrogates from appearing in url.pathname, so this is a defensive-robustness regression relative to the code it mirrors.

Suggested change
function encodePath(pathname: string): string {
return pathname
.split("/")
.map((segment) => encodeURIComponent(segment))
.join("/");
}
function encodePath(pathname: string): string {
return pathname
.split("/")
.map((segment) => {
try {
return encodeURIComponent(segment);
} catch {
return segment;
}
})
.join("/");
}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

3 participants