From 7de12713fae97196bee4b66db50bb1e7df6fb803 Mon Sep 17 00:00:00 2001 From: Anthony Lukach Date: Thu, 16 Apr 2026 09:20:40 -0700 Subject: [PATCH] feat(cf-workers): add CORS utility and auto-decode request paths - 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) --- Cargo.lock | 1 + crates/cf-workers/Cargo.toml | 1 + crates/cf-workers/src/cors.rs | 56 ++++++++++++++++++++++++++++++++ crates/cf-workers/src/lib.rs | 3 ++ crates/cf-workers/src/request.rs | 4 ++- 5 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 crates/cf-workers/src/cors.rs diff --git a/Cargo.lock b/Cargo.lock index 057f955..b28b43a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1157,6 +1157,7 @@ dependencies = [ "js-sys", "multistore", "object_store", + "percent-encoding", "tracing", "wasm-bindgen", "wasm-bindgen-futures", diff --git a/crates/cf-workers/Cargo.toml b/crates/cf-workers/Cargo.toml index 2619a71..e6e58c5 100644 --- a/crates/cf-workers/Cargo.toml +++ b/crates/cf-workers/Cargo.toml @@ -14,6 +14,7 @@ gcp = ["multistore/gcp"] multistore.workspace = true bytes.workspace = true http.workspace = true +percent-encoding = "2" tracing.workspace = true object_store.workspace = true futures.workspace = true diff --git a/crates/cf-workers/src/cors.rs b/crates/cf-workers/src/cors.rs new file mode 100644 index 0000000..bf8aa79 --- /dev/null +++ b/crates/cf-workers/src/cors.rs @@ -0,0 +1,56 @@ +//! CORS header utilities for browser-accessible S3 proxies. + +use http::HeaderMap; + +/// Set permissive CORS headers suitable for public, browser-accessible +/// S3-compatible read-only proxies. +/// +/// Sets: +/// - `access-control-allow-origin: *` +/// - `access-control-allow-methods: GET, HEAD, OPTIONS` +/// - `access-control-allow-headers: *` +/// - `access-control-expose-headers: *` +/// +/// Existing CORS headers in the map are overwritten. +pub fn add_cors_headers(headers: &mut HeaderMap) { + let pairs = [ + ("access-control-allow-origin", "*"), + ("access-control-allow-methods", "GET, HEAD, OPTIONS"), + ("access-control-allow-headers", "*"), + ("access-control-expose-headers", "*"), + ]; + for (name, value) in pairs { + if let Ok(v) = value.parse() { + headers.insert(name, v); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sets_all_four_cors_headers() { + let mut h = HeaderMap::new(); + add_cors_headers(&mut h); + assert_eq!(h.get("access-control-allow-origin").unwrap(), "*"); + assert_eq!( + h.get("access-control-allow-methods").unwrap(), + "GET, HEAD, OPTIONS" + ); + assert_eq!(h.get("access-control-allow-headers").unwrap(), "*"); + assert_eq!(h.get("access-control-expose-headers").unwrap(), "*"); + } + + #[test] + fn overwrites_existing() { + let mut h = HeaderMap::new(); + h.insert( + "access-control-allow-origin", + "https://example.com".parse().unwrap(), + ); + add_cors_headers(&mut h); + assert_eq!(h.get("access-control-allow-origin").unwrap(), "*"); + } +} diff --git a/crates/cf-workers/src/lib.rs b/crates/cf-workers/src/lib.rs index 463377b..c98919e 100644 --- a/crates/cf-workers/src/lib.rs +++ b/crates/cf-workers/src/lib.rs @@ -9,11 +9,13 @@ //! - [`WorkerSubscriber`] — `tracing::Subscriber` routing to `console.log` //! - [`NoopCredentialRegistry`] — anonymous-only credential registry //! - [`response`] — helpers for building `web_sys::Response` from proxy results +//! - [`add_cors_headers`] — set permissive CORS headers on a `HeaderMap` pub(crate) mod fetch_connector; pub mod backend; pub mod body; +pub mod cors; pub mod headers; pub mod noop_creds; pub mod request; @@ -22,6 +24,7 @@ pub mod tracing_layer; pub use backend::WorkerBackend; pub use body::{collect_js_body, JsBody}; +pub use cors::add_cors_headers; pub use headers::WsHeaders; pub use noop_creds::NoopCredentialRegistry; pub use request::RequestParts; diff --git a/crates/cf-workers/src/request.rs b/crates/cf-workers/src/request.rs index 0dc4b76..d006d65 100644 --- a/crates/cf-workers/src/request.rs +++ b/crates/cf-workers/src/request.rs @@ -51,7 +51,9 @@ impl RequestParts { let uri: Uri = req.url().parse().map_err(|e| format!("invalid URL: {e}"))?; - let path = uri.path().to_string(); + let path = percent_encoding::percent_decode_str(uri.path()) + .decode_utf8_lossy() + .to_string(); let query = uri.query().map(|q| q.to_string()); let headers = headermap_from_js(&req.headers());