http-fs 0.5.0

HTTP File Service library
Documentation
//!Hyper adaptor
//!
//!## Usage
//!
//!```rust, no_run
//!use http_fs::config::{self, StaticFileConfig};
//!use http_fs::{StaticFiles};
//!use futures::Future;
//!
//!use std::path::Path;
//!
//!//Note that Clone is required to share StaticFiles among hyper threads
//!#[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
//!    }
//!}
//!
//!fn main() {
//!    let addr = ([127, 0, 0, 1], 3000).into();
//!    let static_files = StaticFiles::new(DirectoryConfig);
//!
//!    let server = hyper::Server::bind(&addr).serve(static_files).map_err(|e| eprintln!("server error: {}", e));
//!
//!    println!("Listening on http://{}", addr);
//!    hyper::rt::run(server);
//!}
//!```

use crate::config::{StaticFileConfig};
use crate::ServeEntry;

use hyper::service::{MakeService, Service};
use futures::future::FutureResult;
use percent_encoding::{percent_decode};

use std::io;
use std::path::Path;
use std::borrow::Cow;

impl<C: StaticFileConfig + Clone, Ctx> MakeService<Ctx> for crate::StaticFiles<C> {
    type ReqBody = hyper::Body;
    type ResBody = hyper::Body;
    type Error = io::Error;

    type Service = Self;
    type MakeError = io::Error;
    type Future = FutureResult<Self::Service, Self::MakeError>;

    fn make_service(&mut self, _: Ctx) -> Self::Future {
        Ok(self.clone()).into()
    }
}

impl<C: StaticFileConfig> Service for crate::StaticFiles<C> {
    type ReqBody = hyper::Body;
    type ResBody = hyper::Body;
    type Error = io::Error;
    type Future = FutureResult<hyper::Response<Self::ResBody>, Self::Error>;

    fn call(&mut self, req: hyper::Request<Self::ReqBody>) -> Self::Future {
        if !C::is_method_allowed(req.method()) {
            let res = hyper::Response::builder().status(http::StatusCode::METHOD_NOT_ALLOWED)
                                                .header(http::header::ALLOW, "GET, HEAD")
                                                .body(hyper::Body::empty())
                                                .expect("Create request with empty body");

            return futures::future::ok(res)
        }

        let path = req.uri().path().trim_start_matches("/");
        let path = percent_decode(path.as_bytes()).decode_utf8().expect("To percent decode");
        let path = &path;
        let path = match path {
            Cow::Borrowed(path) => Path::new(path),
            Cow::Owned(ref path) => Path::new(path),
        };
        let result = self.serve(&path);

        match result {
            ServeEntry::NotFound | ServeEntry::IoError(_) => {
                let mut response = hyper::Response::new(hyper::Body::empty());
                let (code, body) = self.handle_not_found(&path, response.headers_mut());
                *response.status_mut() = code;

                if body.len() > 0 {
                    futures::future::ok(response.map(|_| hyper::Body::from(body)))
                } else {
                    futures::future::ok(response)
                }
            },
            ServeEntry::Directory(path, dir) => {
                let res = hyper::Response::builder().status(http::StatusCode::OK)
                                                    .header(http::header::CONTENT_TYPE, http::header::HeaderValue::from_static("text/html; charset=utf-8"))
                                                    .body(self.list_dir(&path, dir).into())
                                                    .expect("Create request wtih empty body");
                futures::future::ok(res)
            },
            ServeEntry::File(file, meta, path) => {
                let mut response = hyper::Response::new(hyper::Body::empty());
                let (code, body) = self.serve_file(&path, file, meta, req.method().clone(), req.headers(), response.headers_mut());
                *response.status_mut() = code;

                if let Some(body) = body {
                    futures::future::ok(response.map(|_| hyper::Body::wrap_stream(body)))
                } else {
                    futures::future::ok(response)
                }
            }
        }
    }
}