1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//!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)
                }
            }
        }
    }
}