@@ -26,6 +26,38 @@ pub type OhttpJsonrpseeLayer = OhttpLayer<fn(Full<Bytes>) -> HttpBody>;
2626/// Pass this to `OhttpLayer::new` when constructing the layer.
2727pub const OHTTP_JSONRPSEE_BODY_BUILDER : fn ( Full < Bytes > ) -> HttpBody = HttpBody :: new;
2828
29+ /// The production HTTP middleware stack, applied verbatim by both transports:
30+ /// the HTTP path (`start_server`) and the HTTPS path (`tls::start_tls_server`).
31+ ///
32+ /// This is a macro rather than a helper function because the value is a deeply
33+ /// nested `ServiceBuilder<Stack<Stack<...>>>` whose type cannot be spelled in a
34+ /// signature. Defining the chain once keeps the two transports from drifting.
35+ ///
36+ /// Defined above `pub mod tls;` on purpose: `macro_rules!` scoping is textual, so moving the
37+ /// definition below the module would make it invisible to `tls.rs`. The macro also resolves
38+ /// `ServiceBuilder`, the layer types, and `HttpBody` at each call site (not at the definition),
39+ /// so every caller must have them in scope — adding a layer here adds an import obligation at
40+ /// each call site.
41+ ///
42+ /// Layer order (tower makes the last-added layer innermost):
43+ /// - `HealthLayer` sits outermost so `GET /health` is answered before any other middleware runs.
44+ /// - `OhttpLayer` must sit OUTSIDE `CompressionLayer` so compression applies to the inner JSON-RPC
45+ /// response (the client's inner `Accept-Encoding` travels through BHTTP into jsonrpsee) rather
46+ /// than to the OHTTP ciphertext envelope. `MapRequestBodyLayer`/`MapResponseBodyLayer` keep
47+ /// `HttpBody` on both sides of OHTTP to satisfy its symmetric-body bound; `HttpBody::new` is a
48+ /// zero-cost wrapper, so non-OHTTP requests still stream through unbuffered.
49+ macro_rules! prover_http_middleware {
50+ ( $cors_layer: expr, $ohttp_layer: expr $( , ) ?) => {
51+ ServiceBuilder :: new( )
52+ . layer( HealthLayer )
53+ . option_layer( $cors_layer)
54+ . layer( MapRequestBodyLayer :: new( HttpBody :: new) )
55+ . option_layer( $ohttp_layer)
56+ . layer( MapResponseBodyLayer :: new( HttpBody :: new) )
57+ . layer( CompressionLayer :: new( ) )
58+ } ;
59+ }
60+
2961pub mod config;
3062pub mod cors;
3163pub mod errors;
@@ -64,31 +96,8 @@ pub async fn start_server(
6496 . build ( ) ;
6597 let server = ServerBuilder :: default ( )
6698 . set_config ( server_config)
67- // `OhttpLayer` must sit OUTSIDE `CompressionLayer` so compression
68- // applies to the inner JSON-RPC response (the client's inner
69- // `Accept-Encoding` travels through BHTTP into jsonrpsee) rather than
70- // to the OHTTP ciphertext envelope. Because tower's `ServiceBuilder`
71- // makes the last-added layer innermost, `CompressionLayer` is added
72- // last here.
73- //
74- // `MapRequestBodyLayer` wraps hyper's `Request<Incoming>` into
75- // `Request<HttpBody>` before `OhttpLayer` sees it — `OhttpLayer`'s
76- // symmetric-body bound requires `B = HttpBody` on both sides.
77- // `MapResponseBodyLayer` converts `CompressionBody<HttpBody>` back to
78- // `HttpBody` on the response path so `OhttpLayer` receives the body
79- // type it expects. `HttpBody::new` is a zero-cost wrapper, so
80- // non-OHTTP requests still stream through unbuffered.
81- . set_http_middleware (
82- // `HealthLayer` sits outermost so `GET /health` is answered
83- // before any other middleware runs.
84- ServiceBuilder :: new ( )
85- . layer ( HealthLayer )
86- . option_layer ( cors_layer)
87- . layer ( MapRequestBodyLayer :: new ( HttpBody :: new) )
88- . option_layer ( ohttp_layer)
89- . layer ( MapResponseBodyLayer :: new ( HttpBody :: new) )
90- . layer ( CompressionLayer :: new ( ) ) ,
91- )
99+ // See `prover_http_middleware!` for the full layer-order rationale.
100+ . set_http_middleware ( prover_http_middleware ! ( cors_layer, ohttp_layer) )
92101 . build ( & addr)
93102 . await
94103 . context ( format ! ( "Failed to bind JSON-RPC server to {addr}" ) ) ?;
0 commit comments