From c76edb8f796dca7cb84bb55977c0aef58958de1c Mon Sep 17 00:00:00 2001 From: Anthony Lukach Date: Thu, 16 Apr 2026 14:30:57 -0700 Subject: [PATCH] refactor: make Body an associated type on ProxyBackend Replaces the generic Body parameter on forward() with an associated type, eliminating runtime downcasts via Box. Body type mismatches are now caught at compile time. Co-Authored-By: Claude Opus 4.6 (1M context) --- crates/cf-workers/src/backend.rs | 11 ++++------- crates/core/src/backend/mod.rs | 7 +++++-- crates/core/src/proxy.rs | 12 ++++++------ examples/lambda/src/client.rs | 12 ++++-------- examples/server/src/client.rs | 12 ++++-------- 5 files changed, 23 insertions(+), 31 deletions(-) diff --git a/crates/cf-workers/src/backend.rs b/crates/cf-workers/src/backend.rs index 95d1a98..3c6e11b 100644 --- a/crates/cf-workers/src/backend.rs +++ b/crates/cf-workers/src/backend.rs @@ -31,17 +31,14 @@ pub struct WorkerBackend; impl ProxyBackend for WorkerBackend { type ResponseBody = web_sys::Response; + type Body = JsBody; - async fn forward( + async fn forward( &self, request: ForwardRequest, - body: Body, + body: JsBody, ) -> Result, ProxyError> { - // Downcast to the concrete JsBody type used by the Workers runtime. - let any_body: Box = Box::new(body); - let js_body = any_body - .downcast::() - .map_err(|_| ProxyError::Internal("unexpected body type".into()))?; + let js_body = body; // Build web_sys::RequestInit. let init = web_sys::RequestInit::new(); diff --git a/crates/core/src/backend/mod.rs b/crates/core/src/backend/mod.rs index 06b86aa..99beeca 100644 --- a/crates/core/src/backend/mod.rs +++ b/crates/core/src/backend/mod.rs @@ -45,12 +45,15 @@ pub trait ProxyBackend: Clone + MaybeSend + MaybeSync + 'static { /// The streaming body type in forwarded backend responses. type ResponseBody: MaybeSend + 'static; + /// The request body type accepted by [`forward()`](Self::forward). + type Body: MaybeSend + 'static; + /// Execute a presigned [`ForwardRequest`] against the backend and return /// the response with a streaming body. - fn forward( + fn forward( &self, request: ForwardRequest, - body: Body, + body: Self::Body, ) -> impl Future, ProxyError>> + MaybeSend; /// Create a [`PaginatedListStore`] for the given bucket configuration. diff --git a/crates/core/src/proxy.rs b/crates/core/src/proxy.rs index 9cfabb9..d45b793 100644 --- a/crates/core/src/proxy.rs +++ b/crates/core/src/proxy.rs @@ -223,15 +223,14 @@ where /// GatewayResponse::Forward(resp) => stream_response(resp), /// } /// ``` - pub async fn handle_request( + pub async fn handle_request( &self, req: &RequestInfo<'_>, - body: Body, + body: B::Body, collect_body: CF, ) -> GatewayResponse where - Body: MaybeSend + 'static, - CF: FnOnce(Body) -> Fut, + CF: FnOnce(B::Body) -> Fut, Fut: std::future::Future>, E: std::fmt::Display, { @@ -969,11 +968,12 @@ mod tests { impl ProxyBackend for MockBackend { type ResponseBody = (); + type Body = (); - async fn forward( + async fn forward( &self, _request: ForwardRequest, - _body: Body, + _body: (), ) -> Result, ProxyError> { unimplemented!("not needed for resolve_request tests") } diff --git a/examples/lambda/src/client.rs b/examples/lambda/src/client.rs index 9491734..414b78f 100644 --- a/examples/lambda/src/client.rs +++ b/examples/lambda/src/client.rs @@ -48,11 +48,12 @@ async fn body_to_bytes(body: Body) -> Result> impl ProxyBackend for LambdaBackend { type ResponseBody = Body; + type Body = Body; - async fn forward( + async fn forward( &self, request: ForwardRequest, - body: B, + body: Body, ) -> Result, ProxyError> { let mut req_builder = self .client @@ -64,12 +65,7 @@ impl ProxyBackend for LambdaBackend { // Attach body for PUT requests if request.method == http::Method::PUT { - // Downcast to the concrete lambda_http::Body type used by the Lambda runtime. - let any_body: Box = Box::new(body); - let lambda_body = any_body - .downcast::() - .map_err(|_| ProxyError::Internal("unexpected body type".into()))?; - let bytes = body_to_bytes(*lambda_body) + let bytes = body_to_bytes(body) .await .map_err(|e| ProxyError::Internal(format!("failed to read PUT body: {e}")))?; req_builder = req_builder.body(bytes); diff --git a/examples/server/src/client.rs b/examples/server/src/client.rs index b850766..5419f40 100644 --- a/examples/server/src/client.rs +++ b/examples/server/src/client.rs @@ -47,11 +47,12 @@ impl Default for ServerBackend { impl ProxyBackend for ServerBackend { type ResponseBody = reqwest::Response; + type Body = axum::body::Body; - async fn forward( + async fn forward( &self, request: ForwardRequest, - body: Body, + body: axum::body::Body, ) -> Result, ProxyError> { let mut req_builder = self .client @@ -63,12 +64,7 @@ impl ProxyBackend for ServerBackend { // Attach streaming body for PUT if request.method == http::Method::PUT { - // Downcast to the concrete axum::body::Body type used by the server runtime. - let any_body: Box = Box::new(body); - let axum_body = any_body - .downcast::() - .map_err(|_| ProxyError::Internal("unexpected body type".into()))?; - let body_stream = BodyStream::new(*axum_body) + let body_stream = BodyStream::new(body) .try_filter_map(|frame| async move { Ok(frame.into_data().ok()) }); req_builder = req_builder.body(reqwest::Body::wrap_stream(body_stream)); }