1use crate::{content_encoding::SupportedEncodings, BoxError};
4use bytes::{Buf, Bytes, BytesMut};
5use futures_core::Stream;
6use http::HeaderValue;
7use http_body::{Body, Frame};
8use pin_project_lite::pin_project;
9use std::{
10 io,
11 pin::Pin,
12 task::{ready, Context, Poll},
13};
14use tokio::io::AsyncRead;
15use tokio_util::io::StreamReader;
16
17#[derive(Debug, Clone, Copy)]
18pub(crate) struct AcceptEncoding {
19 pub(crate) gzip: bool,
20 pub(crate) deflate: bool,
21 pub(crate) br: bool,
22 pub(crate) zstd: bool,
23}
24
25impl AcceptEncoding {
26 #[allow(dead_code)]
27 pub(crate) fn to_header_value(self) -> Option<HeaderValue> {
28 let accept = match (self.gzip(), self.deflate(), self.br(), self.zstd()) {
29 (true, true, true, false) => "gzip,deflate,br",
30 (true, true, false, false) => "gzip,deflate",
31 (true, false, true, false) => "gzip,br",
32 (true, false, false, false) => "gzip",
33 (false, true, true, false) => "deflate,br",
34 (false, true, false, false) => "deflate",
35 (false, false, true, false) => "br",
36 (true, true, true, true) => "zstd,gzip,deflate,br",
37 (true, true, false, true) => "zstd,gzip,deflate",
38 (true, false, true, true) => "zstd,gzip,br",
39 (true, false, false, true) => "zstd,gzip",
40 (false, true, true, true) => "zstd,deflate,br",
41 (false, true, false, true) => "zstd,deflate",
42 (false, false, true, true) => "zstd,br",
43 (false, false, false, true) => "zstd",
44 (false, false, false, false) => return None,
45 };
46 Some(HeaderValue::from_static(accept))
47 }
48
49 #[allow(dead_code)]
50 pub(crate) fn set_gzip(&mut self, enable: bool) {
51 self.gzip = enable;
52 }
53
54 #[allow(dead_code)]
55 pub(crate) fn set_deflate(&mut self, enable: bool) {
56 self.deflate = enable;
57 }
58
59 #[allow(dead_code)]
60 pub(crate) fn set_br(&mut self, enable: bool) {
61 self.br = enable;
62 }
63
64 #[allow(dead_code)]
65 pub(crate) fn set_zstd(&mut self, enable: bool) {
66 self.zstd = enable;
67 }
68}
69
70impl SupportedEncodings for AcceptEncoding {
71 #[allow(dead_code)]
72 fn gzip(&self) -> bool {
73 #[cfg(any(feature = "decompression-gzip", feature = "compression-gzip"))]
74 return self.gzip;
75
76 #[cfg(not(any(feature = "decompression-gzip", feature = "compression-gzip")))]
77 return false;
78 }
79
80 #[allow(dead_code)]
81 fn deflate(&self) -> bool {
82 #[cfg(any(feature = "decompression-deflate", feature = "compression-deflate"))]
83 return self.deflate;
84
85 #[cfg(not(any(feature = "decompression-deflate", feature = "compression-deflate")))]
86 return false;
87 }
88
89 #[allow(dead_code)]
90 fn br(&self) -> bool {
91 #[cfg(any(feature = "decompression-br", feature = "compression-br"))]
92 return self.br;
93
94 #[cfg(not(any(feature = "decompression-br", feature = "compression-br")))]
95 return false;
96 }
97
98 #[allow(dead_code)]
99 fn zstd(&self) -> bool {
100 #[cfg(any(feature = "decompression-zstd", feature = "compression-zstd"))]
101 return self.zstd;
102
103 #[cfg(not(any(feature = "decompression-zstd", feature = "compression-zstd")))]
104 return false;
105 }
106}
107
108impl Default for AcceptEncoding {
109 fn default() -> Self {
110 AcceptEncoding {
111 gzip: true,
112 deflate: true,
113 br: true,
114 zstd: true,
115 }
116 }
117}
118
119pub(crate) type AsyncReadBody<B> =
121 StreamReader<StreamErrorIntoIoError<BodyIntoStream<B>, <B as Body>::Error>, <B as Body>::Data>;
122
123pub(crate) trait DecorateAsyncRead {
125 type Input: AsyncRead;
126 type Output: AsyncRead;
127
128 fn apply(input: Self::Input, quality: CompressionLevel) -> Self::Output;
130
131 fn get_pin_mut(pinned: Pin<&mut Self::Output>) -> Pin<&mut Self::Input>;
135}
136
137pin_project! {
138 pub(crate) struct WrapBody<M: DecorateAsyncRead> {
140 #[pin]
141 pub read: M::Output,
144 buf: BytesMut,
147 read_all_data: bool,
148 }
149}
150
151impl<M: DecorateAsyncRead> WrapBody<M> {
152 const INTERNAL_BUF_CAPACITY: usize = 4096;
153}
154
155impl<M: DecorateAsyncRead> WrapBody<M> {
156 #[allow(dead_code)]
157 pub(crate) fn new<B>(body: B, quality: CompressionLevel) -> Self
158 where
159 B: Body,
160 M: DecorateAsyncRead<Input = AsyncReadBody<B>>,
161 {
162 let stream = BodyIntoStream::new(body);
164
165 let stream = StreamErrorIntoIoError::<_, B::Error>::new(stream);
168
169 let read = StreamReader::new(stream);
171
172 let read = M::apply(read, quality);
174
175 Self {
176 read,
177 buf: BytesMut::with_capacity(Self::INTERNAL_BUF_CAPACITY),
178 read_all_data: false,
179 }
180 }
181}
182
183impl<B, M> Body for WrapBody<M>
184where
185 B: Body,
186 B::Error: Into<BoxError>,
187 M: DecorateAsyncRead<Input = AsyncReadBody<B>>,
188{
189 type Data = Bytes;
190 type Error = BoxError;
191
192 fn poll_frame(
193 self: Pin<&mut Self>,
194 cx: &mut Context<'_>,
195 ) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
196 let mut this = self.project();
197
198 if !*this.read_all_data {
199 if this.buf.capacity() == 0 {
200 this.buf.reserve(Self::INTERNAL_BUF_CAPACITY);
201 }
202
203 let result = tokio_util::io::poll_read_buf(this.read.as_mut(), cx, &mut this.buf);
204
205 match ready!(result) {
206 Ok(0) => {
207 *this.read_all_data = true;
208 }
209 Ok(_) => {
210 let chunk = this.buf.split().freeze();
211 return Poll::Ready(Some(Ok(Frame::data(chunk))));
212 }
213 Err(err) => {
214 let body_error: Option<B::Error> = M::get_pin_mut(this.read.as_mut())
215 .get_pin_mut()
216 .project()
217 .error
218 .take();
219
220 let read_some_data = M::get_pin_mut(this.read.as_mut())
221 .get_pin_mut()
222 .project()
223 .read_some_data;
224
225 if let Some(body_error) = body_error {
226 return Poll::Ready(Some(Err(body_error.into())));
227 } else if err.raw_os_error() == Some(SENTINEL_ERROR_CODE) {
228 unreachable!()
231 } else if *read_some_data {
232 return Poll::Ready(Some(Err(err.into())));
233 }
234 }
235 }
236 }
237
238 let body = M::get_pin_mut(this.read).get_pin_mut().get_pin_mut();
240 match ready!(body.poll_frame(cx)) {
241 Some(Ok(frame)) if frame.is_trailers() => Poll::Ready(Some(Ok(
242 frame.map_data(|mut data| data.copy_to_bytes(data.remaining()))
243 ))),
244 Some(Ok(frame)) => match frame.into_data() {
245 Ok(data) if data.has_remaining() => Poll::Ready(Some(Err(
249 "there are extra bytes after body has been decompressed".into(),
250 ))),
251 Ok(mut data) => {
256 Poll::Ready(Some(Ok(Frame::data(data.copy_to_bytes(data.remaining())))))
257 }
258 Err(frame) => Poll::Ready(Some(Ok(
259 frame.map_data(|mut data| data.copy_to_bytes(data.remaining()))
260 ))),
261 },
262 Some(Err(err)) => Poll::Ready(Some(Err(err.into()))),
263 None => Poll::Ready(None),
264 }
265 }
266}
267
268pin_project! {
269 pub(crate) struct BodyIntoStream<B>
270 where
271 B: Body,
272 {
273 #[pin]
274 body: B,
275 yielded_all_data: bool,
276 non_data_frame: Option<Frame<B::Data>>,
277 }
278}
279
280#[allow(dead_code)]
281impl<B> BodyIntoStream<B>
282where
283 B: Body,
284{
285 pub(crate) fn new(body: B) -> Self {
286 Self {
287 body,
288 yielded_all_data: false,
289 non_data_frame: None,
290 }
291 }
292
293 pub(crate) fn get_ref(&self) -> &B {
295 &self.body
296 }
297
298 pub(crate) fn get_mut(&mut self) -> &mut B {
300 &mut self.body
301 }
302
303 pub(crate) fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut B> {
305 self.project().body
306 }
307
308 pub(crate) fn into_inner(self) -> B {
310 self.body
311 }
312}
313
314impl<B> Stream for BodyIntoStream<B>
315where
316 B: Body,
317{
318 type Item = Result<B::Data, B::Error>;
319
320 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
321 loop {
322 let this = self.as_mut().project();
323
324 if *this.yielded_all_data {
325 return Poll::Ready(None);
326 }
327
328 match std::task::ready!(this.body.poll_frame(cx)) {
329 Some(Ok(frame)) => match frame.into_data() {
330 Ok(data) => return Poll::Ready(Some(Ok(data))),
331 Err(frame) => {
332 *this.yielded_all_data = true;
333 *this.non_data_frame = Some(frame);
334 }
335 },
336 Some(Err(err)) => return Poll::Ready(Some(Err(err))),
337 None => {
338 *this.yielded_all_data = true;
339 }
340 }
341 }
342 }
343}
344
345impl<B> Body for BodyIntoStream<B>
346where
347 B: Body,
348{
349 type Data = B::Data;
350 type Error = B::Error;
351
352 fn poll_frame(
353 mut self: Pin<&mut Self>,
354 cx: &mut Context<'_>,
355 ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
356 if let Some(frame) = std::task::ready!(self.as_mut().poll_next(cx)) {
359 return Poll::Ready(Some(frame.map(Frame::data)));
360 }
361
362 let this = self.project();
363
364 if let Some(frame) = this.non_data_frame.take() {
366 return Poll::Ready(Some(Ok(frame)));
367 }
368
369 this.body.poll_frame(cx)
372 }
373
374 #[inline]
375 fn size_hint(&self) -> http_body::SizeHint {
376 self.body.size_hint()
377 }
378}
379
380pin_project! {
381 pub(crate) struct StreamErrorIntoIoError<S, E> {
382 #[pin]
383 inner: S,
384 error: Option<E>,
385 read_some_data: bool
386 }
387}
388
389impl<S, E> StreamErrorIntoIoError<S, E> {
390 pub(crate) fn new(inner: S) -> Self {
391 Self {
392 inner,
393 error: None,
394 read_some_data: false,
395 }
396 }
397
398 pub(crate) fn get_ref(&self) -> &S {
400 &self.inner
401 }
402
403 pub(crate) fn get_mut(&mut self) -> &mut S {
405 &mut self.inner
406 }
407
408 pub(crate) fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut S> {
410 self.project().inner
411 }
412
413 pub(crate) fn into_inner(self) -> S {
415 self.inner
416 }
417}
418
419impl<S, T, E> Stream for StreamErrorIntoIoError<S, E>
420where
421 S: Stream<Item = Result<T, E>>,
422{
423 type Item = Result<T, io::Error>;
424
425 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
426 let this = self.project();
427 match ready!(this.inner.poll_next(cx)) {
428 None => Poll::Ready(None),
429 Some(Ok(value)) => {
430 *this.read_some_data = true;
431 Poll::Ready(Some(Ok(value)))
432 }
433 Some(Err(err)) => {
434 *this.error = Some(err);
435 Poll::Ready(Some(Err(io::Error::from_raw_os_error(SENTINEL_ERROR_CODE))))
436 }
437 }
438 }
439}
440
441pub(crate) const SENTINEL_ERROR_CODE: i32 = -837459418;
442
443#[non_exhaustive]
445#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
446pub enum CompressionLevel {
447 Fastest,
449 Best,
451 #[default]
454 Default,
455 Precise(i32),
463}
464
465#[cfg(any(
466 feature = "compression-br",
467 feature = "compression-gzip",
468 feature = "compression-deflate",
469 feature = "compression-zstd"
470))]
471use async_compression::Level as AsyncCompressionLevel;
472
473#[cfg(any(
474 feature = "compression-br",
475 feature = "compression-gzip",
476 feature = "compression-deflate",
477 feature = "compression-zstd"
478))]
479impl CompressionLevel {
480 pub(crate) fn into_async_compression(self) -> AsyncCompressionLevel {
481 match self {
482 CompressionLevel::Fastest => AsyncCompressionLevel::Fastest,
483 CompressionLevel::Best => AsyncCompressionLevel::Best,
484 CompressionLevel::Default => AsyncCompressionLevel::Default,
485 CompressionLevel::Precise(quality) => AsyncCompressionLevel::Precise(quality),
486 }
487 }
488}