Skip to main content

leyline/core/
body_stream.rs

1use std::future::Future;
2use std::pin::Pin;
3use std::task::{Context, Poll, ready};
4use std::time::Duration;
5
6use bytes::Bytes;
7use futures_util::Stream;
8use tokio::sync::mpsc;
9use tokio::time::Sleep;
10use tokio_util::sync::{CancellationToken, WaitForCancellationFutureOwned};
11
12use crate::core::config::HostPass;
13
14use crate::core::session::decompress::Decoder;
15use crate::trace::BodyWatch;
16
17pub struct BodyStream {
18    rx: mpsc::Receiver<std::io::Result<Bytes>>,
19    read_timeout: Option<Duration>,
20    idle: Option<Pin<Box<Sleep>>>,
21    body_limit: Option<Duration>,
22    body_deadline: Option<Pin<Box<Sleep>>>,
23    decoded: Option<Box<Decoded>>,
24    watch: Option<BodyWatch>,
25    shutdown: Option<Pin<Box<WaitForCancellationFutureOwned>>>,
26    ended: bool,
27    pass: Option<HostPass>,
28}
29
30struct Decoded {
31    raw: BodyStream,
32    decoder: Decoder,
33    done: bool,
34}
35
36impl Decoded {
37    fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Option<std::io::Result<Bytes>>> {
38        loop {
39            if self.done {
40                return Poll::Ready(None);
41            }
42            let mut out = Vec::new();
43            let step = match Pin::new(&mut self.raw).poll_next(cx) {
44                Poll::Pending => return Poll::Pending,
45                Poll::Ready(Some(Err(e))) => {
46                    self.done = true;
47                    return Poll::Ready(Some(Err(e)));
48                }
49                Poll::Ready(Some(Ok(chunk))) => self.decoder.feed(&chunk, &mut out),
50                Poll::Ready(None) => {
51                    self.done = true;
52                    self.decoder.finish(&mut out)
53                }
54            };
55            if let Err(e) = step {
56                self.done = true;
57                return Poll::Ready(Some(Err(e.into_io())));
58            }
59            if !out.is_empty() {
60                return Poll::Ready(Some(Ok(Bytes::from(out))));
61            }
62        }
63    }
64}
65
66impl BodyStream {
67    pub(crate) fn new(rx: mpsc::Receiver<std::io::Result<Bytes>>) -> Self {
68        Self {
69            rx,
70            read_timeout: None,
71            idle: None,
72            body_limit: None,
73            body_deadline: None,
74            decoded: None,
75            watch: None,
76            shutdown: None,
77            ended: false,
78            pass: None,
79        }
80    }
81
82    pub(crate) fn decoded(mut raw: BodyStream, decoder: Decoder) -> Self {
83        let (_, rx) = mpsc::channel(1);
84        let watch = raw.watch.take();
85        Self {
86            watch,
87            decoded: Some(Box::new(Decoded {
88                raw,
89                decoder,
90                done: false,
91            })),
92            ..Self::new(rx)
93        }
94    }
95
96    pub(crate) fn from_bytes(buf: Bytes) -> Self {
97        let (tx, rx) = mpsc::channel(1);
98        if !buf.is_empty() {
99            tx.try_send(Ok(buf))
100                .expect("fresh channel with capacity 1 has room");
101        }
102        drop(tx);
103        Self::new(rx)
104    }
105
106    pub(crate) fn set_read_timeout(&mut self, timeout: Option<Duration>) {
107        self.read_timeout = timeout;
108    }
109
110    pub(crate) fn set_body_timeout(&mut self, timeout: Option<Duration>) {
111        self.body_limit = timeout;
112        self.body_deadline = None;
113    }
114
115    fn poll_body_deadline(&mut self, cx: &mut Context<'_>) -> Poll<std::io::Error> {
116        if let Some(limit) = self.body_limit.take() {
117            self.body_deadline = Some(Box::pin(tokio::time::sleep(limit)));
118        }
119        let Some(sleep) = self.body_deadline.as_mut() else {
120            return Poll::Pending;
121        };
122        ready!(sleep.as_mut().poll(cx));
123        Poll::Ready(timed_out(
124            "body timeout: response body not complete within the configured body timeout",
125        ))
126    }
127
128    pub(crate) fn stop_on(&mut self, token: &CancellationToken) {
129        self.shutdown = Some(Box::pin(token.clone().cancelled_owned()));
130    }
131
132    pub(crate) fn hold(&mut self, pass: Option<HostPass>) {
133        self.pass = pass;
134    }
135
136    fn poll_shutdown(&mut self, cx: &mut Context<'_>) -> bool {
137        self.shutdown
138            .as_mut()
139            .is_some_and(|wait| wait.as_mut().poll(cx).is_ready())
140    }
141
142    fn finish(&mut self, item: Option<&std::io::Result<Bytes>>) {
143        if self.ended {
144            return;
145        }
146        self.ended = true;
147        self.rx.close();
148        let (_, closed) = mpsc::channel(1);
149        drop(std::mem::replace(&mut self.rx, closed));
150        self.decoded = None;
151        self.idle = None;
152        self.body_limit = None;
153        self.body_deadline = None;
154        self.shutdown = None;
155        self.pass = None;
156        if let Some(mut watch) = self.watch.take() {
157            watch.observe(item);
158        }
159    }
160
161    fn end_with(&mut self, err: std::io::Error) -> Poll<Option<std::io::Result<Bytes>>> {
162        let item = Some(Err(err));
163        self.finish(item.as_ref());
164        Poll::Ready(item)
165    }
166
167    pub(crate) fn watch_end(&mut self) {
168        self.watch = BodyWatch::begin();
169    }
170
171    fn poll_raw(&mut self, cx: &mut Context<'_>) -> Poll<Option<std::io::Result<Bytes>>> {
172        match self.rx.poll_recv(cx) {
173            Poll::Ready(item) => {
174                self.idle = None;
175                Poll::Ready(item)
176            }
177            Poll::Pending => {
178                let Some(timeout) = self.read_timeout else {
179                    return Poll::Pending;
180                };
181                let sleep = self
182                    .idle
183                    .get_or_insert_with(|| Box::pin(tokio::time::sleep(timeout)));
184                match sleep.as_mut().poll(cx) {
185                    Poll::Ready(()) => {
186                        self.idle = None;
187                        Poll::Ready(Some(Err(timed_out(
188                            "read timeout: no response body chunk within the configured read_timeout",
189                        ))))
190                    }
191                    Poll::Pending => Poll::Pending,
192                }
193            }
194        }
195    }
196}
197
198fn timed_out(message: &'static str) -> std::io::Error {
199    std::io::Error::new(std::io::ErrorKind::TimedOut, message)
200}
201
202impl Stream for BodyStream {
203    type Item = std::io::Result<Bytes>;
204
205    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
206        let this = self.get_mut();
207        if this.ended {
208            return Poll::Ready(None);
209        }
210        if this.poll_shutdown(cx) {
211            return this.end_with(std::io::Error::other(crate::core::error::Error::shut_down()));
212        }
213        if let Poll::Ready(err) = this.poll_body_deadline(cx) {
214            return this.end_with(err);
215        }
216        let polled = match this.decoded.as_mut() {
217            Some(decoded) => decoded.poll(cx),
218            None => this.poll_raw(cx),
219        };
220        match &polled {
221            Poll::Ready(item @ Some(Ok(_))) => {
222                if let Some(watch) = this.watch.as_mut() {
223                    watch.observe(item.as_ref());
224                }
225            }
226            Poll::Ready(item) => this.finish(item.as_ref()),
227            Poll::Pending => {}
228        }
229        polled
230    }
231}
232
233impl std::fmt::Debug for BodyStream {
234    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235        f.debug_struct("BodyStream").finish_non_exhaustive()
236    }
237}