Module http_fs::adaptors::hyper

source ·
Expand description

Hyper adaptor

Implements hyper::service::Service for StaticFiles allowing to use it to serve http connections

Usage

use hyper::server::conn::http1;
use tokio::net::TcpListener;
use pin_project_lite::pin_project;

use http_fs::config::{self, StaticFileConfig, TokioWorker};
use http_fs::StaticFiles;

use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::Path;
use core::task;
use core::pin::Pin;

#[derive(Clone)]
pub struct DirectoryConfig;
impl StaticFileConfig for DirectoryConfig {
    type FileService = config::DefaultConfig;
    type DirService = config::DefaultConfig;

    fn handle_directory(&self, _path: &Path) -> bool {
        true
    }
}

//Fucking retarded hyper traits need wrapper
pin_project! {
    struct IoWrapper<T> {
        io: T,
    }
}

impl<T: tokio::io::AsyncRead + Unpin> hyper::rt::Read for IoWrapper<T> {
    fn poll_read(self: Pin<&mut Self>, ctx: &mut task::Context<'_>, mut buf: hyper::rt::ReadBufCursor<'_>) -> task::Poll<Result<(), std::io::Error>> {
        let n = unsafe {
            let mut tbuf = tokio::io::ReadBuf::uninit(buf.as_mut());
            match tokio::io::AsyncRead::poll_read(Pin::new(self.project().io), ctx, &mut tbuf) {
                task::Poll::Ready(Ok(())) => tbuf.filled().len(),
                other => return other,
            }
        };

        unsafe {
            buf.advance(n);
        }
        task::Poll::Ready(Ok(()))
    }
}

impl<T: tokio::io::AsyncWrite + Unpin> hyper::rt::Write for IoWrapper<T> {
    fn poll_write(self: Pin<&mut Self>, ctx: &mut task::Context<'_>, buf: &[u8]) -> task::Poll<Result<usize, std::io::Error>> {
        tokio::io::AsyncWrite::poll_write(Pin::new(self.project().io), ctx, buf)
    }

    fn poll_flush(self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> task::Poll<Result<(), std::io::Error>> {
        tokio::io::AsyncWrite::poll_flush(Pin::new(self.project().io), ctx)
    }

    fn poll_shutdown(self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> task::Poll<Result<(), std::io::Error>> {
        tokio::io::AsyncWrite::poll_shutdown(Pin::new(self.project().io), ctx)
    }

    fn is_write_vectored(&self) -> bool {
        tokio::io::AsyncWrite::is_write_vectored(&self.io)
    }

    fn poll_write_vectored(self: Pin<&mut Self>, ctx: &mut task::Context<'_>, bufs: &[std::io::IoSlice<'_>]) -> task::Poll<Result<usize, std::io::Error>> {
        tokio::io::AsyncWrite::poll_write_vectored(Pin::new(self.project().io), ctx, bufs)
    }
}

async fn example() {
    let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);;
    let listener = TcpListener::bind(addr).await.expect("bind");
    println!("Listening on http://{}", addr);

    let static_files = StaticFiles::new(TokioWorker, DirectoryConfig);
    loop {
        let (io, _) = listener.accept().await.expect("accept");
        let io = IoWrapper {
            io,
        };

        let static_files = static_files.clone();
        tokio::task::spawn(async move {
            http1::Builder::new().serve_connection(io, static_files).await
        });
    }
}