rama_http/service/fs/
mod.rs

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