tower_http/services/fs/
mod.rs

1//! File system related services.
2
3use bytes::Bytes;
4use futures_core::Stream;
5use http_body::{Body, Frame};
6use pin_project_lite::pin_project;
7use std::{
8    io,
9    pin::Pin,
10    task::{Context, Poll},
11};
12use tokio::io::{AsyncRead, AsyncReadExt, Take};
13use tokio_util::io::ReaderStream;
14
15mod serve_dir;
16mod serve_file;
17
18pub use self::{
19    serve_dir::{
20        future::ResponseFuture as ServeFileSystemResponseFuture,
21        DefaultServeDirFallback,
22        // The response body and future are used for both ServeDir and ServeFile
23        ResponseBody as ServeFileSystemResponseBody,
24        ServeDir,
25    },
26    serve_file::ServeFile,
27};
28
29pin_project! {
30    // NOTE: This could potentially be upstreamed to `http-body`.
31    /// Adapter that turns an [`impl AsyncRead`][tokio::io::AsyncRead] to an [`impl Body`][http_body::Body].
32    #[derive(Debug)]
33    pub struct AsyncReadBody<T> {
34        #[pin]
35        reader: ReaderStream<T>,
36    }
37}
38
39impl<T> AsyncReadBody<T>
40where
41    T: AsyncRead,
42{
43    /// Create a new [`AsyncReadBody`] wrapping the given reader,
44    /// with a specific read buffer capacity
45    fn with_capacity(read: T, capacity: usize) -> Self {
46        Self {
47            reader: ReaderStream::with_capacity(read, capacity),
48        }
49    }
50
51    fn with_capacity_limited(
52        read: T,
53        capacity: usize,
54        max_read_bytes: u64,
55    ) -> AsyncReadBody<Take<T>> {
56        AsyncReadBody {
57            reader: ReaderStream::with_capacity(read.take(max_read_bytes), capacity),
58        }
59    }
60}
61
62impl<T> Body for AsyncReadBody<T>
63where
64    T: AsyncRead,
65{
66    type Data = Bytes;
67    type Error = io::Error;
68
69    fn poll_frame(
70        self: Pin<&mut Self>,
71        cx: &mut Context<'_>,
72    ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
73        match std::task::ready!(self.project().reader.poll_next(cx)) {
74            Some(Ok(chunk)) => Poll::Ready(Some(Ok(Frame::data(chunk)))),
75            Some(Err(err)) => Poll::Ready(Some(Err(err))),
76            None => Poll::Ready(None),
77        }
78    }
79}