1#![expect(
4 clippy::allow_attributes,
5 reason = "macro-generated `#[allow]` attributes whose underlying lints fire only for some expansions"
6)]
7#![expect(
8 clippy::unreachable,
9 reason = "SENTINEL_ERROR_CODE is only stored alongside an underlying body error, never observed at the unreachable branch"
10)]
11
12use crate::body::{Frame, SizeHint, StreamingBody};
13use pin_project_lite::pin_project;
14use rama_core::bytes::{Buf, Bytes, BytesMut};
15use rama_core::error::BoxError;
16use rama_core::futures::Stream;
17use rama_core::futures::ready;
18use rama_core::stream::io::StreamReader;
19use std::io::ErrorKind;
20use std::{
21 io,
22 pin::Pin,
23 task::{Context, Poll},
24};
25use tokio::io::AsyncRead;
26
27macro_rules! compressed_body_poll_frame {
34 ($self:expr, $cx:expr) => {
35 match $self.project().inner.project() {
36 BodyInnerProj::Gzip { inner } => inner.poll_frame($cx),
37 BodyInnerProj::Deflate { inner } => inner.poll_frame($cx),
38 BodyInnerProj::Brotli { inner } => inner.poll_frame($cx),
39 BodyInnerProj::Zstd { inner } => inner.poll_frame($cx),
40 BodyInnerProj::Identity { inner } => match ready!(inner.poll_frame($cx)) {
41 Some(Ok(frame)) => {
42 let frame = frame.map_data(|mut buf| buf.copy_to_bytes(buf.remaining()));
43 Poll::Ready(Some(Ok(frame)))
44 }
45 Some(Err(err)) => Poll::Ready(Some(Err(err.into()))),
46 None => Poll::Ready(None),
47 },
48 }
49 };
50}
51pub(crate) use compressed_body_poll_frame;
52
53macro_rules! impl_decorate_async_read {
58 ($codec:ident: |$input:pat_param, $quality:pat_param| $apply:block) => {
59 impl<B> DecorateAsyncRead for $codec<B>
60 where
61 B: StreamingBody,
62 {
63 type Input = AsyncReadBody<B>;
64 type Output = $codec<Self::Input>;
65
66 fn apply($input: Self::Input, $quality: CompressionLevel) -> Self::Output $apply
67
68 fn get_pin_mut(pinned: Pin<&mut Self::Output>) -> Pin<&mut Self::Input> {
69 pinned.get_pin_mut()
70 }
71 }
72 };
73}
74pub(crate) use impl_decorate_async_read;
75
76pub(crate) type AsyncReadBody<B> = StreamReader<
78 StreamErrorIntoIoError<BodyIntoStream<B>, <B as StreamingBody>::Error>,
79 <B as StreamingBody>::Data,
80>;
81
82pub(crate) trait DecorateAsyncRead {
84 type Input: AsyncRead;
85 type Output: AsyncRead;
86
87 fn apply(input: Self::Input, quality: CompressionLevel) -> Self::Output;
89
90 fn get_pin_mut(pinned: Pin<&mut Self::Output>) -> Pin<&mut Self::Input>;
94}
95
96pin_project! {
97 pub(crate) struct WrapBody<M: DecorateAsyncRead> {
99 #[pin]
100 pub read: M::Output,
103 buf: BytesMut,
106 read_all_data: bool,
107 tolerate_decode_errors: bool,
111 }
112}
113
114impl<M: DecorateAsyncRead> WrapBody<M> {
115 const INTERNAL_BUF_CAPACITY: usize = 8096;
116}
117
118impl<M: DecorateAsyncRead> WrapBody<M> {
119 #[allow(dead_code)]
120 pub(crate) fn new<B>(body: B, quality: CompressionLevel) -> Self
121 where
122 B: StreamingBody,
123 M: DecorateAsyncRead<Input = AsyncReadBody<B>>,
124 {
125 let stream = BodyIntoStream::new(body);
127
128 let stream = StreamErrorIntoIoError::<_, B::Error>::new(stream);
131
132 let read = StreamReader::new(stream);
134
135 let read = M::apply(read, quality);
137
138 Self {
139 read,
140 buf: BytesMut::with_capacity(Self::INTERNAL_BUF_CAPACITY),
141 read_all_data: false,
142 tolerate_decode_errors: false,
143 }
144 }
145
146 pub(crate) fn with_tolerate_decode_errors(mut self, tolerate: bool) -> Self {
155 self.tolerate_decode_errors = tolerate;
156 self
157 }
158}
159
160impl<B, M> StreamingBody for WrapBody<M>
161where
162 B: StreamingBody<Error: Into<BoxError>>,
163 M: DecorateAsyncRead<Input = AsyncReadBody<B>>,
164{
165 type Data = Bytes;
166 type Error = BoxError;
167
168 fn poll_frame(
169 self: Pin<&mut Self>,
170 cx: &mut Context<'_>,
171 ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
172 let mut this = self.project();
173
174 if !*this.read_all_data {
175 if this.buf.capacity() == 0 {
176 this.buf.reserve(Self::INTERNAL_BUF_CAPACITY);
177 }
178
179 let result =
180 rama_core::stream::io::poll_read_buf(this.read.as_mut(), cx, &mut this.buf);
181
182 match ready!(result) {
183 Ok(0) => {
184 *this.read_all_data = true;
185 }
186 Ok(_) => {
187 let chunk = this.buf.split().freeze();
188 return Poll::Ready(Some(Ok(Frame::data(chunk))));
189 }
190 Err(err) => {
191 let body_error: Option<B::Error> = M::get_pin_mut(this.read.as_mut())
192 .get_pin_mut()
193 .project()
194 .error
195 .take();
196
197 let read_some_data = M::get_pin_mut(this.read.as_mut())
198 .get_pin_mut()
199 .project()
200 .read_some_data;
201
202 if let Some(body_error) = body_error {
203 return Poll::Ready(Some(Err(body_error.into())));
204 } else if err.raw_os_error() == Some(SENTINEL_ERROR_CODE) {
205 unreachable!()
208 } else if *read_some_data {
209 if err.kind() == ErrorKind::UnexpectedEof
210 && M::get_pin_mut(this.read.as_mut())
211 .get_pin_mut()
212 .inner
213 .yielded_all_data
214 {
215 *this.read_all_data = true;
216 } else if *this.tolerate_decode_errors {
217 rama_core::telemetry::tracing::debug!(
229 "decompression: tolerating mid-stream decode error ({err}); ending body cleanly"
230 );
231 return Poll::Ready(None);
232 } else {
233 return Poll::Ready(Some(Err(err.into())));
234 }
235 }
236 }
237 }
238 }
239
240 let body = M::get_pin_mut(this.read).get_pin_mut().get_pin_mut();
242 match ready!(body.poll_frame(cx)) {
243 Some(Ok(frame)) if frame.is_trailers() => Poll::Ready(Some(Ok(
244 frame.map_data(|mut data| data.copy_to_bytes(data.remaining()))
245 ))),
246 Some(Ok(frame)) => {
247 if let Ok(bytes) = frame.into_data()
248 && bytes.has_remaining()
249 {
250 return Poll::Ready(Some(Err(
251 "there are extra bytes after body has been decompressed".into(),
252 )));
253 }
254 Poll::Ready(None)
255 }
256 Some(Err(err)) => Poll::Ready(Some(Err(err.into()))),
257 None => Poll::Ready(None),
258 }
259 }
260}
261
262pin_project! {
263 pub(crate) struct BodyIntoStream<B>
264 where
265 B: StreamingBody,
266 {
267 #[pin]
268 body: B,
269 yielded_all_data: bool,
270 non_data_frame: Option<Frame<B::Data>>,
271 }
272}
273
274#[allow(dead_code)]
275impl<B> BodyIntoStream<B>
276where
277 B: StreamingBody,
278{
279 pub(crate) fn new(body: B) -> Self {
280 Self {
281 body,
282 yielded_all_data: false,
283 non_data_frame: None,
284 }
285 }
286
287 pub(crate) fn get_ref(&self) -> &B {
289 &self.body
290 }
291
292 pub(crate) fn get_mut(&mut self) -> &mut B {
294 &mut self.body
295 }
296
297 pub(crate) fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut B> {
299 self.project().body
300 }
301
302 pub(crate) fn into_inner(self) -> B {
304 self.body
305 }
306}
307
308impl<B> Stream for BodyIntoStream<B>
309where
310 B: StreamingBody,
311{
312 type Item = Result<B::Data, B::Error>;
313
314 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
315 loop {
316 let this = self.as_mut().project();
317
318 if *this.yielded_all_data {
319 return Poll::Ready(None);
320 }
321
322 match std::task::ready!(this.body.poll_frame(cx)) {
323 Some(Ok(frame)) => match frame.into_data() {
324 Ok(data) => return Poll::Ready(Some(Ok(data))),
325 Err(frame) => {
326 *this.yielded_all_data = true;
327 *this.non_data_frame = Some(frame);
328 }
329 },
330 Some(Err(err)) => return Poll::Ready(Some(Err(err))),
331 None => {
332 *this.yielded_all_data = true;
333 }
334 }
335 }
336 }
337}
338
339impl<B> StreamingBody for BodyIntoStream<B>
340where
341 B: StreamingBody,
342{
343 type Data = B::Data;
344 type Error = B::Error;
345
346 fn poll_frame(
347 mut self: Pin<&mut Self>,
348 cx: &mut Context<'_>,
349 ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
350 if let Some(frame) = std::task::ready!(self.as_mut().poll_next(cx)) {
353 return Poll::Ready(Some(frame.map(Frame::data)));
354 }
355
356 let this = self.project();
357
358 if let Some(frame) = this.non_data_frame.take() {
360 return Poll::Ready(Some(Ok(frame)));
361 }
362
363 this.body.poll_frame(cx)
366 }
367
368 #[inline]
369 fn size_hint(&self) -> SizeHint {
370 self.body.size_hint()
371 }
372}
373
374pin_project! {
375 pub(crate) struct StreamErrorIntoIoError<S, E> {
376 #[pin]
377 inner: S,
378 error: Option<E>,
379 read_some_data: bool,
380 }
381}
382
383impl<S, E> StreamErrorIntoIoError<S, E> {
384 pub(crate) fn new(inner: S) -> Self {
385 Self {
386 inner,
387 error: None,
388 read_some_data: false,
389 }
390 }
391
392 pub(crate) fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut S> {
394 self.project().inner
395 }
396}
397
398impl<S, T, E> Stream for StreamErrorIntoIoError<S, E>
399where
400 S: Stream<Item = Result<T, E>>,
401{
402 type Item = Result<T, io::Error>;
403
404 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
405 let this = self.project();
406 match ready!(this.inner.poll_next(cx)) {
407 None => Poll::Ready(None),
408 Some(Ok(value)) => {
409 *this.read_some_data = true;
410 Poll::Ready(Some(Ok(value)))
411 }
412 Some(Err(err)) => {
413 *this.error = Some(err);
414 Poll::Ready(Some(Err(io::Error::from_raw_os_error(SENTINEL_ERROR_CODE))))
415 }
416 }
417 }
418}
419
420pub(crate) const SENTINEL_ERROR_CODE: i32 = -837459418;
421
422#[non_exhaustive]
424#[derive(Default, Clone, Copy, Debug, Eq, PartialEq, Hash)]
425pub enum CompressionLevel {
426 Fastest,
428 Best,
430 #[default]
433 Default,
434 Precise(u32),
442}
443
444use async_compression::Level as AsyncCompressionLevel;
445use compression_core::Level as CompressionCoreLevel;
446
447impl CompressionLevel {
448 #[allow(dead_code)]
449 pub(crate) fn into_async_compression(self) -> AsyncCompressionLevel {
450 match self {
451 Self::Fastest => AsyncCompressionLevel::Fastest,
452 Self::Best => AsyncCompressionLevel::Best,
453 Self::Default => AsyncCompressionLevel::Default,
454 Self::Precise(quality) => {
455 AsyncCompressionLevel::Precise(quality.try_into().unwrap_or(i32::MAX))
456 }
457 }
458 }
459}
460
461impl CompressionLevel {
462 #[allow(dead_code)]
463 pub(crate) fn into_compression_core(self) -> CompressionCoreLevel {
464 match self {
465 Self::Fastest => CompressionCoreLevel::Fastest,
466 Self::Best => CompressionCoreLevel::Best,
467 Self::Default => CompressionCoreLevel::Default,
468 Self::Precise(quality) => {
469 CompressionCoreLevel::Precise(quality.try_into().unwrap_or(i32::MAX))
470 }
471 }
472 }
473}