refactor: simplify cf workers#47
Merged
Merged
Conversation
Introduces RequestParts struct that extracts owned HTTP metadata from a web_sys::Request and provides as_request_info() to borrow it for gateway dispatch. Eliminates 8 lines of manual parsing boilerplate per handler. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reduce public exports from 8 to 2 (response_from_gateway, headermap_from_js). Remove dead code (xml_response, s3_error_response) and make internal helpers pub(crate). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ersion Introduces WsHeaders wrapper around web_sys::Headers with From<&HeaderMap>, working around orphan rule constraints. Replaces 4 manual header conversion blocks in backend.rs and response.rs, and removes the now-unused headers_to_js. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…sor methods Makes JsBody's inner Option<ReadableStream> private to prevent accidental mutation. Adds JsBody::new() constructor and stream() accessor. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nseExt trait Replaces the free function with an extension trait on GatewayResponse, enabling method syntax: `result.into_web_sys()`. Improves discoverability via IDE autocomplete on the GatewayResponse type. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Documents RequestParts, GatewayResponseExt, JsBody, and WsHeaders in the cf-workers crate description. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces the generic Body parameter on forward() with an associated type, eliminating runtime downcasts via Box<dyn Any>. Body type mismatches are now caught at compile time. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- add_cors_headers() sets permissive CORS headers for browser-accessible S3-compatible read-only proxies - RequestParts::from_web_sys() now percent-decodes the URL path automatically These eliminate two common boilerplate patterns in consumer apps. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…injection (#36) Middleware that adds response headers (CORS, Server-Timing, etc.) no longer needs to match on all three HandlerAction variants manually. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…istry (#35) This allows WASM implementations to use !Send types (like worker::Fetch and the CF Cache API) directly, eliminating the need for spawn_local + oneshot channel bridges. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
🚀 Latest commit deployed to https://multistore-proxy-pr-47.development-seed.workers.dev
|
alukach
added a commit
that referenced
this pull request
May 21, 2026
Companion changelog entry for the refactor squashed in 7197672. The underlying merge used a `refactor:` prefix so release-please didn't pick it up — this empty commit surfaces the work in the changelog without rewriting published history. The Cloudflare Workers consumer now needs three lines to wire a request through the gateway: let (parts, body) = RequestParts::from_web_sys(&req)?; let mut response = gateway.handle_request(&parts.as_request_info(), body, collect_js_body).await; add_cors_headers(response.headers_mut()); Ok(response.into_web_sys()) Highlights: - RequestParts owns method/path/query/headers and exposes as_request_info() — no more per-handler web_sys::Request parsing. - GatewayResponseExt::into_web_sys() converts the gateway's two-variant response to a web_sys::Response via method syntax. - GatewayResponse::headers_mut() and HandlerAction::response_headers_mut() expose response headers without matching the variant — used internally by Server-Timing injection and by add_cors_headers in the example. - WsHeaders newtype enables From<&HeaderMap> conversion (orphan-rule workaround); JsBody now encapsulates its ReadableStream with stream() accessor. - ProxyBackend::forward gains an associated `type Body`, eliminating Box<dyn Any> downcasts in WorkerBackend / ServerBackend / LambdaBackend (compile-time body-type guarantees instead of runtime). - BucketRegistry / CredentialRegistry rebased onto MaybeSend + MaybeSync so WASM impls can hold !Send types (worker::Fetch, CF Cache API) without spawn_local bridges. - SigV4 verification preserves the raw percent-encoded path via a new RequestParts.signing_path field, so signed object keys with encoded characters (spaces, +, unicode) verify correctly. - wasm-bindgen-test infrastructure added so the cf-workers crate's tests actually run in CI (the crate is excluded from default-members because worker types are !Send on native). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What I'm changing
Writing a Cloudflare Workers consumer of multistore required too much boilerplate: every handler manually parsed
web_sys::Requestinto method/path/query/headers, everyforward()impl downcastBox<dyn Any>to recover its concrete body type, and every backend rebuiltweb_sys::Headersfrom aHeaderMapby hand. Two trait bounds also forced WASM workarounds:ProxyBackend::forwardwas generic overBody, andBucketRegistry/CredentialRegistryrequiredSend + Sync— which meant any registry holding a!Sendtype (e.g.worker::Fetch, CF Cache API) neededspawn_local+ oneshot channel bridges to fakeSend.This PR removes that boilerplate, tightens the cf-workers public surface, and rebases the trait bounds so WASM runtimes can hold
!Sendtypes directly. The end result: the cf-workers example handler shrinks to a three-line call chain.How I did it
Core trait changes (consumed by every runtime):
crates/core/src/backend/mod.rs—ProxyBackend::forwarddrops the<Body: MaybeSend>generic in favour of an associatedtype Body. Body-type mismatches now fail at compile time instead of viaBox<dyn Any>downcast at runtime. Touches every implementer:WorkerBackend,ServerBackend,LambdaBackend.crates/core/src/registry/{bucket,credential}.rs—BucketRegistryandCredentialRegistrychange fromSend + SynctoMaybeSend + MaybeSync, matching the existingProxyBackend/Middleware/RouteHandlerbounds. WASM registries can now holdworker::Fetchand the CF Cache API directly.crates/core/src/route_handler.rs— newHandlerAction::response_headers_mut()returnsOption<&mut HeaderMap>for theResponse/Forwardvariants. Middleware that injects CORS or Server-Timing headers no longer needs a three-armmatch.crates/core/src/proxy.rs—ProxyGateway::handle_requestupdated to useB::Bodyinstead of a generic, propagating the trait change.cf-workers crate ergonomics:
crates/cf-workers/src/request.rs— newRequestPartsstruct:from_web_sys()extracts ownedmethod,path,query,headers,source_ipplus the bodyJsBody;as_request_info()borrows them as aRequestInfo<'_>forhandle_request. Replaces ~8 lines of per-handler parsing.crates/cf-workers/src/response.rs— newGatewayResponseExttrait adds.into_web_sys()directly onGatewayResponse, replacing the freeresponse_from_gatewayfunction so IDE autocomplete surfaces it on the result type.crates/cf-workers/src/headers.rs— newWsHeadersnewtype withFrom<&HeaderMap>. The newtype exists to dodge the orphan rule (Fromon foreignweb_sys::Headers). Used byWorkerBackend::forwardandWorkerBackend::send_raw, deleting four hand-rolled header-conversion blocks and the now-deadheaders_to_js.crates/cf-workers/src/body.rs—JsBody's innerOption<ReadableStream>is now private behindnew()/stream()accessors, preventing accidental mutation of the borrowed stream.crates/cf-workers/src/cors.rs— newadd_cors_headers(&mut HeaderMap)helper for the common read-only-public-proxy case. Pairs naturally withHandlerAction::response_headers_mut()above.crates/cf-workers/src/request.rs—from_web_sysnow percent-decodes the URL path viapercent-encoding, since object keys with spaces / Unicode arrive URL-encoded from browsers.crates/cf-workers/src/lib.rs— trim public exports from 8 to 2 (headermap_from_js,GatewayResponseExt). Removed deadxml_response/s3_error_response; demoted internal helpers topub(crate).Other:
docs/architecture/crate-layout.md— describe the new cf-workers exports (RequestParts,GatewayResponseExt,JsBody,WsHeaders) and theGatewayResponseExt::into_web_sys()flow.examples/cf-workers/src/lib.rs— collapse the handler body togateway.handle_request(...).await.into_web_sys()to demonstrate the new API surface end-to-end.chore: merge fix) repairs the merge frommain: theForwardMockBackendtest that came in with PR feat(core): add User-Agent header to outbound backend requests #20 still used the oldforward<Body: MaybeSend>signature, so it gainedtype Body = ();and dropped the generic.Test plan
make ci(fmt + clippy-D warnings+cargo check+cargo check -p multistore-cf-workers --target wasm32-unknown-unknown+ full test suite)