From 4b74749e98a65eb9c31e2297ec52e25f9d335603 Mon Sep 17 00:00:00 2001 From: Avi Cohen Date: Mon, 1 Jun 2026 13:51:55 +0300 Subject: [PATCH] starknet_transaction_prover: unify HTTP middleware stack via macro --- .../starknet_transaction_prover/src/server.rs | 59 +++++++++++-------- .../src/server/tls.rs | 17 +----- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/crates/starknet_transaction_prover/src/server.rs b/crates/starknet_transaction_prover/src/server.rs index 4732102e68d..f19a55bb8a9 100644 --- a/crates/starknet_transaction_prover/src/server.rs +++ b/crates/starknet_transaction_prover/src/server.rs @@ -26,6 +26,38 @@ pub type OhttpJsonrpseeLayer = OhttpLayer) -> HttpBody>; /// Pass this to `OhttpLayer::new` when constructing the layer. pub const OHTTP_JSONRPSEE_BODY_BUILDER: fn(Full) -> HttpBody = HttpBody::new; +/// The production HTTP middleware stack, applied verbatim by both transports: +/// the HTTP path (`start_server`) and the HTTPS path (`tls::start_tls_server`). +/// +/// This is a macro rather than a helper function because the value is a deeply +/// nested `ServiceBuilder>>` whose type cannot be spelled in a +/// signature. Defining the chain once keeps the two transports from drifting. +/// +/// Defined above `pub mod tls;` on purpose: `macro_rules!` scoping is textual, so moving the +/// definition below the module would make it invisible to `tls.rs`. The macro also resolves +/// `ServiceBuilder`, the layer types, and `HttpBody` at each call site (not at the definition), +/// so every caller must have them in scope — adding a layer here adds an import obligation at +/// each call site. +/// +/// Layer order (tower makes the last-added layer innermost): +/// - `HealthLayer` sits outermost so `GET /health` is answered before any other middleware runs. +/// - `OhttpLayer` must sit OUTSIDE `CompressionLayer` so compression applies to the inner JSON-RPC +/// response (the client's inner `Accept-Encoding` travels through BHTTP into jsonrpsee) rather +/// than to the OHTTP ciphertext envelope. `MapRequestBodyLayer`/`MapResponseBodyLayer` keep +/// `HttpBody` on both sides of OHTTP to satisfy its symmetric-body bound; `HttpBody::new` is a +/// zero-cost wrapper, so non-OHTTP requests still stream through unbuffered. +macro_rules! prover_http_middleware { + ($cors_layer:expr, $ohttp_layer:expr $(,)?) => { + ServiceBuilder::new() + .layer(HealthLayer) + .option_layer($cors_layer) + .layer(MapRequestBodyLayer::new(HttpBody::new)) + .option_layer($ohttp_layer) + .layer(MapResponseBodyLayer::new(HttpBody::new)) + .layer(CompressionLayer::new()) + }; +} + pub mod config; pub mod cors; pub mod errors; @@ -64,31 +96,8 @@ pub async fn start_server( .build(); let server = ServerBuilder::default() .set_config(server_config) - // `OhttpLayer` must sit OUTSIDE `CompressionLayer` so compression - // applies to the inner JSON-RPC response (the client's inner - // `Accept-Encoding` travels through BHTTP into jsonrpsee) rather than - // to the OHTTP ciphertext envelope. Because tower's `ServiceBuilder` - // makes the last-added layer innermost, `CompressionLayer` is added - // last here. - // - // `MapRequestBodyLayer` wraps hyper's `Request` into - // `Request` before `OhttpLayer` sees it — `OhttpLayer`'s - // symmetric-body bound requires `B = HttpBody` on both sides. - // `MapResponseBodyLayer` converts `CompressionBody` back to - // `HttpBody` on the response path so `OhttpLayer` receives the body - // type it expects. `HttpBody::new` is a zero-cost wrapper, so - // non-OHTTP requests still stream through unbuffered. - .set_http_middleware( - // `HealthLayer` sits outermost so `GET /health` is answered - // before any other middleware runs. - ServiceBuilder::new() - .layer(HealthLayer) - .option_layer(cors_layer) - .layer(MapRequestBodyLayer::new(HttpBody::new)) - .option_layer(ohttp_layer) - .layer(MapResponseBodyLayer::new(HttpBody::new)) - .layer(CompressionLayer::new()), - ) + // See `prover_http_middleware!` for the full layer-order rationale. + .set_http_middleware(prover_http_middleware!(cors_layer, ohttp_layer)) .build(&addr) .await .context(format!("Failed to bind JSON-RPC server to {addr}"))?; diff --git a/crates/starknet_transaction_prover/src/server/tls.rs b/crates/starknet_transaction_prover/src/server/tls.rs index d03e93650b0..67950a808a5 100644 --- a/crates/starknet_transaction_prover/src/server/tls.rs +++ b/crates/starknet_transaction_prover/src/server/tls.rs @@ -52,23 +52,10 @@ pub async fn start_tls_server( .max_connections(max_connections) .max_request_body_size(max_request_body_size) .build(); - // See `server.rs` for the rationale — `OhttpLayer` sits outside `CompressionLayer` - // so compression acts on the inner JSON-RPC response, not on the OHTTP envelope. - // `MapRequestBodyLayer`/`MapResponseBodyLayer` keep `HttpBody` on both sides of - // OHTTP to satisfy its symmetric-body bound. + // See `prover_http_middleware!` for the full layer-order rationale. let svc_builder = ServerBuilder::default() .set_config(server_config) - .set_http_middleware( - // `HealthLayer` sits outermost so `GET /health` is answered before - // any other middleware runs. - ServiceBuilder::new() - .layer(HealthLayer) - .option_layer(cors_layer) - .layer(MapRequestBodyLayer::new(HttpBody::new)) - .option_layer(ohttp_layer) - .layer(MapResponseBodyLayer::new(HttpBody::new)) - .layer(CompressionLayer::new()), - ) + .set_http_middleware(prover_http_middleware!(cors_layer, ohttp_layer)) .to_service_builder(); let listener = TcpListener::bind(addr)