xitca_web/middleware/
compress.rs1use crate::service::Service;
4
5#[derive(Clone)]
20pub struct Compress;
21
22impl<S, E> Service<Result<S, E>> for Compress {
23 type Response = service::CompressService<S>;
24 type Error = E;
25
26 async fn call(&self, res: Result<S, E>) -> Result<Self::Response, Self::Error> {
27 res.map(service::CompressService)
28 }
29}
30mod service {
31 use http_encoding::{Coder, ContentEncoding, encoder};
32
33 use crate::{
34 body::{BodyStream, NONE_BODY_HINT},
35 http::{BorrowReq, WebResponse, header::HeaderMap},
36 service::{Service, ready::ReadyService},
37 };
38
39 pub struct CompressService<S>(pub(super) S);
40
41 impl<S, Req, ResB> Service<Req> for CompressService<S>
42 where
43 Req: BorrowReq<HeaderMap>,
44 S: Service<Req, Response = WebResponse<ResB>>,
45 ResB: BodyStream,
46 {
47 type Response = WebResponse<Coder<ResB>>;
48 type Error = S::Error;
49
50 async fn call(&self, req: Req) -> Result<Self::Response, Self::Error> {
51 let mut encoding = ContentEncoding::from_headers(req.borrow());
52 let res = self.0.call(req).await?;
53
54 match res.body().size_hint() {
56 (low, Some(up)) if low == up && low < 64 => encoding = ContentEncoding::NoOp,
57 NONE_BODY_HINT => encoding = ContentEncoding::NoOp,
59 _ => {}
60 }
61
62 Ok(encoder(res, encoding))
63 }
64 }
65
66 impl<S> ReadyService for CompressService<S>
67 where
68 S: ReadyService,
69 {
70 type Ready = S::Ready;
71
72 #[inline]
73 async fn ready(&self) -> Self::Ready {
74 self.0.ready().await
75 }
76 }
77}
78
79#[cfg(test)]
80mod test {
81 use xitca_unsafe_collection::futures::NowOrPanic;
82
83 use crate::{App, handler::handler_service, http::WebRequest};
84
85 use super::*;
86
87 #[test]
88 fn build() {
89 async fn noop() -> &'static str {
90 "noop"
91 }
92
93 App::new()
94 .at("/", handler_service(noop))
95 .enclosed(Compress)
96 .finish()
97 .call(())
98 .now_or_panic()
99 .unwrap()
100 .call(WebRequest::default())
101 .now_or_panic()
102 .ok()
103 .unwrap();
104 }
105}