http-fs 0.1.0

HTTP File Service library
Documentation
//! Configuration module

use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};

use std::fs;
use std::path::Path;
use std::fmt::Write;

use crate::headers::cd::DispositionType;

const DEFAULT_SERVE_DIR: &'static str = ".";

///Describes how to serve file
pub trait FileServeConfig: std::marker::Send {
    ///Returns maximum size for buffer to read file.
    ///
    ///By default 64kb
    fn max_buffer_size() -> u64 {
        65_536
    }

    ///Describes mapping for mime type to content disposition header
    ///
    ///By default `IMAGE`, `TEXT` and `VIDEO` are mapped to Inline.
    ///Others are mapped to Attachment
    fn content_disposition_map(typ: mime::Name) -> DispositionType {
        match typ {
            mime::IMAGE | mime::TEXT | mime::VIDEO => DispositionType::Inline,
            _ => DispositionType::Attachment,
        }
    }

    ///Specifies whether `ETag` should be used for caching
    ///
    ///Provides path to the file, relative to File Service directory
    ///
    ///By default it is true
    fn is_use_etag(_path: &Path) -> bool {
        true
    }

    ///Specifies whether `Last-Modified` should be used for caching
    ///
    ///Provides path to the file, relative to File Service directory
    ///
    ///By default it is true
    fn is_use_last_modifier(_path: &Path) -> bool {
        true
    }
}

///Describes how to serve directory
pub trait DirectoryListingConfig {
    ///Describes how to create HTML text for Directory listing
    ///
    ///`base` is root directory for file service.
    ///`path` is relative to `base` path to directory.
    ///`dir` is result of `fs::read_dir`
    ///
    ///Default implementation provides trivial listing
    fn create_body(_base: &Path, path: &Path, dir: fs::ReadDir) -> String {
        let mut body = String::with_capacity(150);

        let _ = write!(body, "<html>\n");
        let _ = write!(body, "    <head>\n");
        let _ = write!(body, "        <title>Index of {}</title>\n", path.display());
        let _ = write!(body, "    </head>\n");
        let _ = write!(body, "    <body>\n");
        let _ = write!(body, "        <h1>Index of {}</h1>\n", path.display());
        let _ = write!(body, "        <ul>\n");

        for entry in dir.filter_map(|entry| entry.ok()) {
            if entry.file_name().to_str().map(|entry| entry.starts_with(".")).unwrap_or(false) {
                continue;
            }

            let is_dir = match entry.metadata().map(|meta| meta.file_type()) {
                Ok(meta) => if !meta.is_dir() && !meta.is_file() && !meta.is_symlink() {
                    continue;
                } else {
                    meta.is_dir()
                },
                Err(_) => continue,
            };

            let _ = write!(body, "<li><a href=\"{}\">{}",
                                 utf8_percent_encode(&entry.path().to_string_lossy(), DEFAULT_ENCODE_SET),
                                 v_htmlescape::escape(&entry.file_name().to_string_lossy()));
            if is_dir {
                let _ = write!(body, "/");
            }

            let _ = write!(body, "</a></li>\n");
        }

        let _ = write!(body, "        </ul>\n");
        let _ = write!(body, "    </body>\n");
        let _ = write!(body, "</html>\n");

        body
    }
}

///Configuration description
pub trait StaticFileConfig {
    ///File serve configuration
    type FileService: FileServeConfig + 'static;
    ///Directory serve configuration
    type DirService: DirectoryListingConfig;

    ///Returns whether specified method is allowed for use.
    ///By default allows `HEAD` and `GET`
    fn is_method_allowed(method: &http::Method) -> bool {
        match method {
            &http::Method::GET | &http::Method::HEAD => true,
            _ => false,
        }
    }

    ///Returns directory from where to serve files.
    ///
    ///By default returns `.`
    fn serve_dir(&self) -> &Path {
        Path::new(DEFAULT_SERVE_DIR)
    }

    ///Returns name of index file to show.
    ///
    ///`path` is always points to directory relative to `StaticFileConfig::serve_dir`.
    ///
    ///By default returns `None`
    fn index_file(&self, _path: &Path) -> Option<&Path> {
        None
    }

    ///Returns whether directory should be listed on access
    ///
    ///By default returns `false`
    fn handle_directory(&self, _path: &Path) -> bool {
        false
    }

    ///Describes how to configure thread pool.
    ///
    ///By default sets prefix `http-fs` and leaves everything else by default
    fn thread_pool_builder(builder: &mut tokio_threadpool::Builder) -> &mut tokio_threadpool::Builder {
        builder.name_prefix("http-fs")
    }
}

///Default configuration
pub struct DefaultConfig;
impl FileServeConfig for DefaultConfig {}
impl DirectoryListingConfig for DefaultConfig {}
impl StaticFileConfig for DefaultConfig {
    type FileService = Self;
    type DirService = Self;
}