monolake_services/http/handlers/
content_handler.rs

1//! Content encoding and decoding handler for HTTP requests and responses.
2//!
3//! This module provides a `ContentHandler` that manages content encoding and decoding
4//! for both incoming requests and outgoing responses in an HTTP service stack. It supports
5//! various content encodings and can be easily integrated into a service pipeline.
6//!
7//! # Key Components
8//!
9//! - [`ContentHandler`]: The main service component responsible for content encoding/decoding.
10//!
11//! # Features
12//!
13//! - Transparent content decoding for incoming requests
14//! - Content encoding for outgoing responses based on client preferences
15//! - Support for various content encodings (e.g., gzip, deflate)
16//! - Integration with service-async framework for easy composition
17//! - Error handling for decoding and encoding failures
18//!
19//! # Usage
20//!
21//! This handler is typically used as part of a larger service stack. Here's a basic example:
22//!
23//! ```rust
24//! use monolake_services::{
25//!     common::ContextService,
26//!     http::{
27//!         core::HttpCoreService,
28//!         detect::H2Detect,
29//!         handlers::{
30//!             route::RouteConfig, ConnectionReuseHandler, ContentHandler, RewriteAndRouteHandler,
31//!             UpstreamHandler,
32//!         },
33//!         HttpServerTimeout,
34//!     },
35//! };
36//! use service_async::{layer::FactoryLayer, stack::FactoryStack, Param};
37//!
38//! // Dummy struct to satisfy Param trait requirements
39//! struct DummyConfig;
40//!
41//! // Implement Param for DummyConfig to return Vec<RouteConfig>
42//! impl Param<Vec<RouteConfig>> for DummyConfig {
43//!     fn param(&self) -> Vec<RouteConfig> {
44//!         vec![]
45//!     }
46//! }
47//! impl Param<HttpServerTimeout> for DummyConfig {
48//!     fn param(&self) -> HttpServerTimeout {
49//!         HttpServerTimeout::default()
50//!     }
51//! }
52//!
53//! let config = DummyConfig;
54//! let stacks = FactoryStack::new(config)
55//!     .replace(UpstreamHandler::factory(
56//!         Default::default(),
57//!         Default::default(),
58//!     ))
59//!     .push(ContentHandler::layer())
60//!     .push(RewriteAndRouteHandler::layer())
61//!     .push(ConnectionReuseHandler::layer())
62//!     .push(HttpCoreService::layer())
63//!     .push(H2Detect::layer());
64//!
65//! // Use the service to handle HTTP requests
66//! ```
67//! # Error Handling
68//!
69//! - Decoding errors for incoming requests result in 400 Bad Request responses
70//! - Encoding errors for outgoing responses result in 500 Internal Server Error responses
71//!
72//! # Performance Considerations
73//!
74//! - Content encoding/decoding is only performed when necessary (i.e., non-identity encoding)
75//! - The handler avoids unnecessary allocations and copies where possible
76use std::fmt::Debug;
77
78use http::{Request, StatusCode};
79use monoio_http::common::{
80    body::{BodyEncodeExt, FixedBody},
81    response::Response,
82};
83use monolake_core::http::{HttpHandler, ResponseWithContinue};
84use service_async::{
85    layer::{layer_fn, FactoryLayer},
86    AsyncMakeService, MakeService, Service,
87};
88
89use crate::http::generate_response;
90
91/// Handles content encoding and decoding for HTTP requests and responses.
92///
93/// `ContentHandler` is responsible for:
94/// 1. Decoding the content of incoming requests based on their Content-Encoding header.
95/// 2. Encoding the content of outgoing responses based on the client's Accept-Encoding preferences.
96///
97/// It wraps an inner handler and preprocesses requests before passing them to the inner handler,
98/// as well as postprocessing responses from the inner handler. For implementation details and
99/// example usage, see the [module level documentation](crate::http::handlers::content_handler).
100#[derive(Clone)]
101pub struct ContentHandler<H> {
102    inner: H,
103}
104
105impl<H, CX, B> Service<(Request<B>, CX)> for ContentHandler<H>
106where
107    H: HttpHandler<CX, B>,
108    B: BodyEncodeExt + FixedBody,
109    H::Body: BodyEncodeExt + FixedBody,
110    B::EncodeDecodeError: Debug,
111    <H::Body as BodyEncodeExt>::EncodeDecodeError: Debug,
112{
113    type Response = ResponseWithContinue<H::Body>;
114    type Error = H::Error;
115
116    async fn call(&self, (request, ctx): (Request<B>, CX)) -> Result<Self::Response, Self::Error> {
117        let content_encoding = request
118            .headers()
119            .get(http::header::CONTENT_ENCODING)
120            .and_then(|value: &http::HeaderValue| value.to_str().ok())
121            .unwrap_or("identity")
122            .to_string();
123
124        let accept_encoding = request
125            .headers()
126            .get(http::header::ACCEPT_ENCODING)
127            .and_then(|value| value.to_str().ok())
128            .unwrap_or("identity")
129            .to_string();
130
131        let content_length = request
132            .headers()
133            .get(http::header::CONTENT_LENGTH)
134            .and_then(|value| value.to_str().ok())
135            .map(|value| value.parse::<usize>().unwrap_or_default())
136            .unwrap_or_default();
137
138        if content_length == 0 || content_encoding == "identity" {
139            let (response, _) = self.inner.handle(request, ctx).await?;
140            return Ok((response, true));
141        }
142
143        let (parts, body) = request.into_parts();
144        match body.decode_content(content_encoding).await {
145            Ok(decodec_data) => {
146                let req = Request::from_parts(parts, B::fixed_body(Some(decodec_data)));
147                let (mut response, _) = self.inner.handle(req, ctx).await?;
148                if accept_encoding != "identity" {
149                    let (parts, body) = response.into_parts();
150                    match body.encode_content(accept_encoding).await {
151                        Ok(encoded_data) => {
152                            response =
153                                Response::from_parts(parts, H::Body::fixed_body(Some(encoded_data)))
154                        }
155                        Err(e) => {
156                            tracing::error!("Response content encoding failed {e:?}");
157                            return Ok((
158                                generate_response(StatusCode::INTERNAL_SERVER_ERROR, false),
159                                true,
160                            ));
161                        }
162                    }
163                }
164                Ok((response, true))
165            }
166            Err(e) => {
167                tracing::error!("Request content decode failed {e:?}");
168                Ok((generate_response(StatusCode::BAD_REQUEST, false), true))
169            }
170        }
171    }
172}
173
174// ContentHandler is a Service and a MakeService.
175impl<F> MakeService for ContentHandler<F>
176where
177    F: MakeService,
178{
179    type Service = ContentHandler<F::Service>;
180    type Error = F::Error;
181
182    fn make_via_ref(&self, old: Option<&Self::Service>) -> Result<Self::Service, Self::Error> {
183        Ok(ContentHandler {
184            inner: self.inner.make_via_ref(old.map(|o| &o.inner))?,
185        })
186    }
187}
188
189impl<F: AsyncMakeService> AsyncMakeService for ContentHandler<F> {
190    type Service = ContentHandler<F::Service>;
191    type Error = F::Error;
192
193    async fn make_via_ref(
194        &self,
195        old: Option<&Self::Service>,
196    ) -> Result<Self::Service, Self::Error> {
197        Ok(ContentHandler {
198            inner: self.inner.make_via_ref(old.map(|o| &o.inner)).await?,
199        })
200    }
201}
202
203impl<F> ContentHandler<F> {
204    pub fn layer<C>() -> impl FactoryLayer<C, F, Factory = Self> {
205        layer_fn(|_: &C, inner| Self { inner })
206    }
207
208    /// Returns a factory layer for the `ContentHandler`.
209    ///
210    /// This allows the 'ContentHandler to be selectively enabled or
211    /// disabled based on a configuration at runtime.
212    pub fn opt_layer<C>(enabled: bool) -> Option<impl FactoryLayer<C, F, Factory = Self>> {
213        if enabled {
214            Some(layer_fn(|_: &C, inner| Self { inner }))
215        } else {
216            None
217        }
218    }
219}