rama_http/layer/compression/
body.rs1#![allow(unused_imports)]
2
3use crate::HeaderMap;
4use crate::layer::util::compression::{
5 AsyncReadBody, BodyIntoStream, CompressionLevel, DecorateAsyncRead, WrapBody,
6 compressed_body_poll_frame, impl_decorate_async_read,
7};
8use rama_core::{
9 bytes::{Buf, Bytes},
10 error::BoxError,
11};
12
13use async_compression::tokio::bufread::{BrotliEncoder, GzipEncoder, ZlibEncoder, ZstdEncoder};
14use pin_project_lite::pin_project;
15use rama_core::futures::ready;
16use rama_core::stream::io::StreamReader;
17use rama_http_types::StreamingBody;
18use rama_http_types::body::Frame;
19use std::{
20 io,
21 marker::PhantomData,
22 pin::Pin,
23 task::{Context, Poll},
24};
25
26use super::pin_project_cfg::pin_project_cfg;
27
28pin_project! {
29 pub struct CompressionBody<B>
33 where
34 B: StreamingBody,
35 {
36 #[pin]
37 pub(crate) inner: BodyInner<B>,
38 }
39}
40
41impl<B> Default for CompressionBody<B>
42where
43 B: StreamingBody + Default,
44{
45 fn default() -> Self {
46 Self {
47 inner: BodyInner::Identity {
48 inner: B::default(),
49 },
50 }
51 }
52}
53
54impl<B> CompressionBody<B>
55where
56 B: StreamingBody,
57{
58 pub(crate) fn new(inner: BodyInner<B>) -> Self {
59 Self { inner }
60 }
61}
62
63type GzipBody<B> = WrapBody<GzipEncoder<B>>;
64
65type DeflateBody<B> = WrapBody<ZlibEncoder<B>>;
66
67type BrotliBody<B> = WrapBody<BrotliEncoder<B>>;
68
69type ZstdBody<B> = WrapBody<ZstdEncoder<B>>;
70
71pin_project_cfg! {
72 #[project = BodyInnerProj]
73 pub(crate) enum BodyInner<B>
74 where
75 B: StreamingBody,
76 {
77 Gzip {
78 #[pin]
79 inner: GzipBody<B>,
80 },
81 Deflate {
82 #[pin]
83 inner: DeflateBody<B>,
84 },
85 Brotli {
86 #[pin]
87 inner: BrotliBody<B>,
88 },
89 Zstd {
90 #[pin]
91 inner: ZstdBody<B>,
92 },
93 Identity {
94 #[pin]
95 inner: B,
96 },
97 }
98}
99
100impl<B: StreamingBody> BodyInner<B> {
101 pub(crate) fn gzip(inner: WrapBody<GzipEncoder<B>>) -> Self {
102 Self::Gzip { inner }
103 }
104
105 pub(crate) fn deflate(inner: WrapBody<ZlibEncoder<B>>) -> Self {
106 Self::Deflate { inner }
107 }
108
109 pub(crate) fn brotli(inner: WrapBody<BrotliEncoder<B>>) -> Self {
110 Self::Brotli { inner }
111 }
112
113 pub(crate) fn zstd(inner: WrapBody<ZstdEncoder<B>>) -> Self {
114 Self::Zstd { inner }
115 }
116
117 pub(crate) fn identity(inner: B) -> Self {
118 Self::Identity { inner }
119 }
120}
121
122impl<B> StreamingBody for CompressionBody<B>
123where
124 B: StreamingBody<Error: Into<BoxError>>,
125{
126 type Data = Bytes;
127 type Error = BoxError;
128
129 fn poll_frame(
130 self: Pin<&mut Self>,
131 cx: &mut Context<'_>,
132 ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
133 compressed_body_poll_frame!(self, cx)
134 }
135
136 fn size_hint(&self) -> rama_http_types::body::SizeHint {
137 if let BodyInner::Identity { inner } = &self.inner {
138 inner.size_hint()
139 } else {
140 rama_http_types::body::SizeHint::new()
141 }
142 }
143
144 fn is_end_stream(&self) -> bool {
145 if let BodyInner::Identity { inner } = &self.inner {
146 inner.is_end_stream()
147 } else {
148 false
149 }
150 }
151}
152
153impl_decorate_async_read!(GzipEncoder: |input, quality| {
154 GzipEncoder::with_quality(input, quality.into_async_compression())
155});
156
157impl_decorate_async_read!(ZlibEncoder: |input, quality| {
158 ZlibEncoder::with_quality(input, quality.into_async_compression())
159});
160
161impl_decorate_async_read!(BrotliEncoder: |input, quality| {
162 let level = match quality {
168 CompressionLevel::Default => async_compression::Level::Precise(4),
169 other => other.into_async_compression(),
170 };
171 BrotliEncoder::with_quality(input, level)
172});
173
174impl_decorate_async_read!(ZstdEncoder: |input, quality| {
175 let needs_window_limit = match quality {
186 CompressionLevel::Best => true, CompressionLevel::Precise(level) => level >= 17,
188 CompressionLevel::Default | CompressionLevel::Fastest => false,
189 };
190 if needs_window_limit {
193 let params = [async_compression::zstd::CParameter::window_log(23)];
194 ZstdEncoder::with_quality_and_params(input, quality.into_async_compression(), ¶ms)
195 } else {
196 ZstdEncoder::with_quality(input, quality.into_async_compression())
197 }
198});