Skip to main content

hyper_util/rt/
tokio.rs

1//! [`tokio`] runtime components integration for [`hyper`].
2//!
3//! [`hyper::rt`] exposes a set of traits to allow hyper to be agnostic to
4//! its underlying asynchronous runtime. This submodule provides glue for
5//! [`tokio`] users to bridge those types to [`hyper`]'s interfaces.
6//!
7//! # IO
8//!
9//! [`hyper`] abstracts over asynchronous readers and writers using [`Read`]
10//! and [`Write`], while [`tokio`] abstracts over this using [`AsyncRead`]
11//! and [`AsyncWrite`]. This submodule provides a collection of IO adaptors
12//! to bridge these two IO ecosystems together: [`TokioIo<I>`],
13//! [`WithHyperIo<I>`], and [`WithTokioIo<I>`].
14//!
15//! To compare and constrast these IO adaptors and to help explain which
16//! is the proper choice for your needs, here is a table showing which IO
17//! traits these implement, given two types `T` and `H` which implement
18//! Tokio's and Hyper's corresponding IO traits:
19//!
20//! |                    | [`AsyncRead`]    | [`AsyncWrite`]    | [`Read`]     | [`Write`]    |
21//! |--------------------|------------------|-------------------|--------------|--------------|
22//! | `T`                | ✅ **true**      | ✅ **true**       | ❌ **false** | ❌ **false** |
23//! | `H`                | ❌ **false**     | ❌ **false**      | ✅ **true**  | ✅ **true**  |
24//! | [`TokioIo<T>`]     | ❌ **false**     | ❌ **false**      | ✅ **true**  | ✅ **true**  |
25//! | [`TokioIo<H>`]     | ✅ **true**      | ✅ **true**       | ❌ **false** | ❌ **false** |
26//! | [`WithHyperIo<T>`] | ✅ **true**      | ✅ **true**       | ✅ **true**  | ✅ **true**  |
27//! | [`WithHyperIo<H>`] | ❌ **false**     | ❌ **false**      | ❌ **false** | ❌ **false** |
28//! | [`WithTokioIo<T>`] | ❌ **false**     | ❌ **false**      | ❌ **false** | ❌ **false** |
29//! | [`WithTokioIo<H>`] | ✅ **true**      | ✅ **true**       | ✅ **true**  | ✅ **true**  |
30//!
31//! For most situations, [`TokioIo<I>`] is the proper choice. This should be
32//! constructed, wrapping some underlying [`hyper`] or [`tokio`] IO, at the
33//! call-site of a function like [`hyper::client::conn::http1::handshake`].
34//!
35//! [`TokioIo<I>`] switches across these ecosystems, but notably does not
36//! preserve the existing IO trait implementations of its underlying IO. If
37//! one wishes to _extend_ IO with additional implementations,
38//! [`WithHyperIo<I>`] and [`WithTokioIo<I>`] are the correct choice.
39//!
40//! For example, a Tokio reader/writer can be wrapped in [`WithHyperIo<I>`].
41//! That will implement _both_ sets of IO traits. Conversely,
42//! [`WithTokioIo<I>`] will implement both sets of IO traits given a
43//! reader/writer that implements Hyper's [`Read`] and [`Write`].
44//!
45//! See [`tokio::io`] and ["_Asynchronous IO_"][tokio-async-docs] for more
46//! information.
47//!
48//! [`AsyncRead`]: tokio::io::AsyncRead
49//! [`AsyncWrite`]: tokio::io::AsyncWrite
50//! [`Read`]: hyper::rt::Read
51//! [`Write`]: hyper::rt::Write
52//! [tokio-async-docs]: https://docs.rs/tokio/latest/tokio/#asynchronous-io
53
54use std::{
55    pin::Pin,
56    task::{Context, Poll},
57    time::{Duration, Instant},
58};
59
60use hyper::rt::{Executor, Sleep, Timer};
61use pin_project_lite::pin_project;
62
63#[cfg(feature = "rt-tracing-exec-force")]
64use tracing::instrument::Instrument;
65
66pub use self::{with_hyper_io::WithHyperIo, with_tokio_io::WithTokioIo};
67
68mod with_hyper_io;
69mod with_tokio_io;
70
71/// Future executor that utilises `tokio` threads.
72///
73/// Spawned futures do not inherit the current tracing span, even when the
74/// `tracing` feature is enabled. To propagate spans, wrap this executor in
75/// one of the components from [`rt::tracing`](crate::rt::tracing), such as
76/// [`CurrentSpanExecutor`](crate::rt::CurrentSpanExecutor) (available with
77/// the `tracing` feature).
78///
79/// See the module-level documentation of [`rt::tracing`](crate::rt::tracing)
80/// for more information about propagating [`tracing`] spans to spawned tasks.
81///
82/// The temporary `rt-tracing-exec-force` feature restores propagation of the
83/// current span for libraries that do not allow customizing their executor.
84/// It is excluded from `full` and may be removed in a future breaking release.
85#[non_exhaustive]
86#[derive(Default, Debug, Clone)]
87pub struct TokioExecutor {}
88
89pin_project! {
90    /// A wrapper that implements Tokio's IO traits for an inner type that
91    /// implements hyper's IO traits, or vice versa (implements hyper's IO
92    /// traits for a type that implements Tokio's IO traits).
93    #[derive(Debug)]
94    pub struct TokioIo<T> {
95        #[pin]
96        inner: T,
97    }
98}
99
100/// A Timer that uses the tokio runtime.
101#[non_exhaustive]
102#[derive(Default, Clone, Debug)]
103pub struct TokioTimer;
104
105// Use TokioSleep to get tokio::time::Sleep to implement Unpin.
106// see https://docs.rs/tokio/latest/tokio/time/struct.Sleep.html
107pin_project! {
108    #[derive(Debug)]
109    struct TokioSleep {
110        #[pin]
111        inner: tokio::time::Sleep,
112    }
113}
114
115// ===== impl TokioExecutor =====
116
117impl<Fut> Executor<Fut> for TokioExecutor
118where
119    Fut: Future + Send + 'static,
120    Fut::Output: Send + 'static,
121{
122    fn execute(&self, fut: Fut) {
123        #[cfg(feature = "rt-tracing-exec-force")]
124        tokio::spawn(fut.in_current_span());
125
126        #[cfg(not(feature = "rt-tracing-exec-force"))]
127        tokio::spawn(fut);
128    }
129}
130
131impl TokioExecutor {
132    /// Create new executor that relies on [`tokio::spawn`] to execute futures.
133    pub fn new() -> Self {
134        Self {}
135    }
136}
137
138// ==== impl TokioIo =====
139
140impl<T> TokioIo<T> {
141    /// Wrap a type implementing Tokio's or hyper's IO traits.
142    pub fn new(inner: T) -> Self {
143        Self { inner }
144    }
145
146    /// Borrow the inner type.
147    pub fn inner(&self) -> &T {
148        &self.inner
149    }
150
151    /// Mut borrow the inner type.
152    pub fn inner_mut(&mut self) -> &mut T {
153        &mut self.inner
154    }
155
156    /// Consume this wrapper and get the inner type.
157    pub fn into_inner(self) -> T {
158        self.inner
159    }
160}
161
162impl<T> hyper::rt::Read for TokioIo<T>
163where
164    T: tokio::io::AsyncRead,
165{
166    fn poll_read(
167        self: Pin<&mut Self>,
168        cx: &mut Context<'_>,
169        mut buf: hyper::rt::ReadBufCursor<'_>,
170    ) -> Poll<Result<(), std::io::Error>> {
171        let n = unsafe {
172            let mut tbuf = tokio::io::ReadBuf::uninit(buf.as_mut());
173            match tokio::io::AsyncRead::poll_read(self.project().inner, cx, &mut tbuf) {
174                Poll::Ready(Ok(())) => tbuf.filled().len(),
175                other => return other,
176            }
177        };
178
179        unsafe {
180            buf.advance(n);
181        }
182        Poll::Ready(Ok(()))
183    }
184}
185
186impl<T> hyper::rt::Write for TokioIo<T>
187where
188    T: tokio::io::AsyncWrite,
189{
190    fn poll_write(
191        self: Pin<&mut Self>,
192        cx: &mut Context<'_>,
193        buf: &[u8],
194    ) -> Poll<Result<usize, std::io::Error>> {
195        tokio::io::AsyncWrite::poll_write(self.project().inner, cx, buf)
196    }
197
198    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> {
199        tokio::io::AsyncWrite::poll_flush(self.project().inner, cx)
200    }
201
202    fn poll_shutdown(
203        self: Pin<&mut Self>,
204        cx: &mut Context<'_>,
205    ) -> Poll<Result<(), std::io::Error>> {
206        tokio::io::AsyncWrite::poll_shutdown(self.project().inner, cx)
207    }
208
209    fn is_write_vectored(&self) -> bool {
210        tokio::io::AsyncWrite::is_write_vectored(&self.inner)
211    }
212
213    fn poll_write_vectored(
214        self: Pin<&mut Self>,
215        cx: &mut Context<'_>,
216        bufs: &[std::io::IoSlice<'_>],
217    ) -> Poll<Result<usize, std::io::Error>> {
218        tokio::io::AsyncWrite::poll_write_vectored(self.project().inner, cx, bufs)
219    }
220}
221
222impl<T> tokio::io::AsyncRead for TokioIo<T>
223where
224    T: hyper::rt::Read,
225{
226    fn poll_read(
227        self: Pin<&mut Self>,
228        cx: &mut Context<'_>,
229        tbuf: &mut tokio::io::ReadBuf<'_>,
230    ) -> Poll<Result<(), std::io::Error>> {
231        //let init = tbuf.initialized().len();
232        let filled = tbuf.filled().len();
233        let sub_filled = unsafe {
234            let mut buf = hyper::rt::ReadBuf::uninit(tbuf.unfilled_mut());
235
236            match hyper::rt::Read::poll_read(self.project().inner, cx, buf.unfilled()) {
237                Poll::Ready(Ok(())) => buf.filled().len(),
238                other => return other,
239            }
240        };
241
242        let n_filled = filled + sub_filled;
243        // At least sub_filled bytes had to have been initialized.
244        let n_init = sub_filled;
245        unsafe {
246            tbuf.assume_init(n_init);
247            tbuf.set_filled(n_filled);
248        }
249
250        Poll::Ready(Ok(()))
251    }
252}
253
254impl<T> tokio::io::AsyncWrite for TokioIo<T>
255where
256    T: hyper::rt::Write,
257{
258    fn poll_write(
259        self: Pin<&mut Self>,
260        cx: &mut Context<'_>,
261        buf: &[u8],
262    ) -> Poll<Result<usize, std::io::Error>> {
263        hyper::rt::Write::poll_write(self.project().inner, cx, buf)
264    }
265
266    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> {
267        hyper::rt::Write::poll_flush(self.project().inner, cx)
268    }
269
270    fn poll_shutdown(
271        self: Pin<&mut Self>,
272        cx: &mut Context<'_>,
273    ) -> Poll<Result<(), std::io::Error>> {
274        hyper::rt::Write::poll_shutdown(self.project().inner, cx)
275    }
276
277    fn is_write_vectored(&self) -> bool {
278        hyper::rt::Write::is_write_vectored(&self.inner)
279    }
280
281    fn poll_write_vectored(
282        self: Pin<&mut Self>,
283        cx: &mut Context<'_>,
284        bufs: &[std::io::IoSlice<'_>],
285    ) -> Poll<Result<usize, std::io::Error>> {
286        hyper::rt::Write::poll_write_vectored(self.project().inner, cx, bufs)
287    }
288}
289
290// ==== impl TokioTimer =====
291
292impl Timer for TokioTimer {
293    fn sleep(&self, duration: Duration) -> Pin<Box<dyn Sleep>> {
294        Box::pin(TokioSleep {
295            inner: tokio::time::sleep(duration),
296        })
297    }
298
299    fn sleep_until(&self, deadline: Instant) -> Pin<Box<dyn Sleep>> {
300        Box::pin(TokioSleep {
301            inner: tokio::time::sleep_until(deadline.into()),
302        })
303    }
304
305    fn reset(&self, sleep: &mut Pin<Box<dyn Sleep>>, new_deadline: Instant) {
306        if let Some(sleep) = sleep.as_mut().downcast_mut_pin::<TokioSleep>() {
307            sleep.reset(new_deadline)
308        }
309    }
310
311    fn now(&self) -> Instant {
312        tokio::time::Instant::now().into()
313    }
314}
315
316impl TokioTimer {
317    /// Create a new TokioTimer
318    pub fn new() -> Self {
319        Self {}
320    }
321}
322
323impl Future for TokioSleep {
324    type Output = ();
325
326    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
327        self.project().inner.poll(cx)
328    }
329}
330
331impl Sleep for TokioSleep {}
332
333impl TokioSleep {
334    fn reset(self: Pin<&mut Self>, deadline: Instant) {
335        self.project().inner.as_mut().reset(deadline.into());
336    }
337}
338
339#[cfg(test)]
340mod tests {
341    use crate::rt::TokioExecutor;
342    use hyper::rt::Executor;
343    use tokio::sync::oneshot;
344
345    #[tokio::test]
346    async fn simple_execute() -> Result<(), Box<dyn std::error::Error>> {
347        let (tx, rx) = oneshot::channel();
348        let executor = TokioExecutor::new();
349        executor.execute(async move {
350            tx.send(()).unwrap();
351        });
352        rx.await.map_err(Into::into)
353    }
354
355    #[cfg(feature = "tracing")]
356    #[tokio::test]
357    async fn execute_tracing_span() {
358        // The current-thread runtime keeps the subscriber active while the
359        // spawned future is polled, after the caller has exited its span.
360        let _subscriber = tracing::subscriber::set_default(tracing_subscriber::registry());
361        let span = tracing::info_span!("caller");
362        assert!(span.id().is_some());
363        let (tx, rx) = oneshot::channel();
364
365        {
366            let _entered = span.enter();
367            TokioExecutor::new().execute(async move {
368                tx.send(tracing::Span::current().id()).unwrap();
369            });
370        }
371
372        let spawned_span = rx.await.unwrap();
373        if cfg!(feature = "rt-tracing-exec-force") {
374            assert_eq!(spawned_span, span.id());
375        } else {
376            assert_eq!(spawned_span, None);
377        }
378    }
379}