http_fs/adaptors/
hyper.rs

1//!Hyper adaptor
2//!
3//!Implements `hyper::service::Service` for `StaticFiles` allowing to use it to serve http connections
4//!
5//!## Usage
6//!
7//!```rust,ignore
8//!use hyper::server::conn::http1;
9//!use tokio::net::TcpListener;
10//!use pin_project_lite::pin_project;
11//!
12//!use http_fs::config::{self, StaticFileConfig, TokioWorker};
13//!use http_fs::StaticFiles;
14//!
15//!use std::net::{IpAddr, Ipv4Addr, SocketAddr};
16//!use std::path::Path;
17//!use core::task;
18//!use core::pin::Pin;
19//!
20//!#[derive(Clone)]
21//!pub struct DirectoryConfig;
22//!impl StaticFileConfig for DirectoryConfig {
23//!    type FileService = config::DefaultConfig;
24//!    type DirService = config::DefaultConfig;
25//!
26//!    fn handle_directory(&self, _path: &Path) -> bool {
27//!        true
28//!    }
29//!}
30//!
31//!//Fucking retarded hyper traits need wrapper
32//!pin_project! {
33//!    struct IoWrapper<T> {
34//!        io: T,
35//!    }
36//!}
37//!
38//!impl<T: tokio::io::AsyncRead + Unpin> hyper::rt::Read for IoWrapper<T> {
39//!    fn poll_read(self: Pin<&mut Self>, ctx: &mut task::Context<'_>, mut buf: hyper::rt::ReadBufCursor<'_>) -> task::Poll<Result<(), std::io::Error>> {
40//!        let n = unsafe {
41//!            let mut tbuf = tokio::io::ReadBuf::uninit(buf.as_mut());
42//!            match tokio::io::AsyncRead::poll_read(Pin::new(self.project().io), ctx, &mut tbuf) {
43//!                task::Poll::Ready(Ok(())) => tbuf.filled().len(),
44//!                other => return other,
45//!            }
46//!        };
47//!
48//!        unsafe {
49//!            buf.advance(n);
50//!        }
51//!        task::Poll::Ready(Ok(()))
52//!    }
53//!}
54//!
55//!impl<T: tokio::io::AsyncWrite + Unpin> hyper::rt::Write for IoWrapper<T> {
56//!    fn poll_write(self: Pin<&mut Self>, ctx: &mut task::Context<'_>, buf: &[u8]) -> task::Poll<Result<usize, std::io::Error>> {
57//!        tokio::io::AsyncWrite::poll_write(Pin::new(self.project().io), ctx, buf)
58//!    }
59//!
60//!    fn poll_flush(self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> task::Poll<Result<(), std::io::Error>> {
61//!        tokio::io::AsyncWrite::poll_flush(Pin::new(self.project().io), ctx)
62//!    }
63//!
64//!    fn poll_shutdown(self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> task::Poll<Result<(), std::io::Error>> {
65//!        tokio::io::AsyncWrite::poll_shutdown(Pin::new(self.project().io), ctx)
66//!    }
67//!
68//!    fn is_write_vectored(&self) -> bool {
69//!        tokio::io::AsyncWrite::is_write_vectored(&self.io)
70//!    }
71//!
72//!    fn poll_write_vectored(self: Pin<&mut Self>, ctx: &mut task::Context<'_>, bufs: &[std::io::IoSlice<'_>]) -> task::Poll<Result<usize, std::io::Error>> {
73//!        tokio::io::AsyncWrite::poll_write_vectored(Pin::new(self.project().io), ctx, bufs)
74//!    }
75//!}
76//!
77//!async fn example() {
78//!    let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);;
79//!    let listener = TcpListener::bind(addr).await.expect("bind");
80//!    println!("Listening on http://{}", addr);
81//!
82//!    let static_files = StaticFiles::new(TokioWorker, DirectoryConfig);
83//!    loop {
84//!        let (io, _) = listener.accept().await.expect("accept");
85//!        let io = IoWrapper {
86//!            io,
87//!        };
88//!
89//!        let static_files = static_files.clone();
90//!        tokio::task::spawn(async move {
91//!            http1::Builder::new().serve_connection(io, static_files).await
92//!        });
93//!    }
94//!}
95//!```
96
97use hyper::{Request, Response};
98use hyper::body::Incoming;
99use hyper::service::Service;
100
101use crate::config::{StaticFileConfig, FileServeConfig, FsTaskSpawner};
102use crate::StaticFiles;
103use crate::Body;
104
105use std::io;
106use core::pin::Pin;
107use core::convert::Infallible;
108use core::{mem, task, future};
109
110impl<W: FsTaskSpawner, C: FileServeConfig> hyper::body::Body for Body<W, C> {
111    type Data = hyper::body::Bytes;
112    type Error = io::Error;
113
114    #[inline(always)]
115    fn poll_frame(self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> task::Poll<Option<Result<hyper::body::Frame<Self::Data>, Self::Error>>> {
116        match self.get_mut() {
117            Self::Full(bytes) => {
118                if bytes.is_empty() {
119                    task::Poll::Ready(None)
120                } else {
121                    let mut result = bytes::Bytes::new();
122                    mem::swap(&mut result, bytes);
123                    task::Poll::Ready(Some(Ok(hyper::body::Frame::data(result))))
124                }
125            },
126            Self::Chunked(chunked) => match future::Future::poll(Pin::new(chunked), ctx) {
127                task::Poll::Pending => task::Poll::Pending,
128                task::Poll::Ready(Ok(Some(result))) => {
129                    task::Poll::Ready(Some(Ok(hyper::body::Frame::data(result))))
130                }
131                task::Poll::Ready(Ok(None)) => task::Poll::Ready(None),
132                task::Poll::Ready(Err(error)) => task::Poll::Ready(Some(Err(error))),
133            }
134        }
135    }
136
137    #[inline(always)]
138    fn is_end_stream(&self) -> bool {
139        self.is_finished()
140    }
141
142    #[inline(always)]
143    fn size_hint(&self) -> hyper::body::SizeHint {
144        hyper::body::SizeHint::with_exact(self.len())
145    }
146}
147
148impl<W: FsTaskSpawner, C: StaticFileConfig> Service<Request<Incoming>> for StaticFiles<W, C> {
149    type Response = Response<Body<W, C::FileService>>;
150    type Error = Infallible;
151    type Future = future::Ready<Result<Self::Response, Self::Error>>;
152
153    #[inline(always)]
154    fn call(&self, req: Request<Incoming>) -> Self::Future {
155        future::ready(Ok(self.serve_http(req.method(), req.uri(), req.headers())))
156    }
157}