index : static-web-server.git

ascending towards madness

author Jose Quintana <joseluisquintana20@gmail.com> 2021-02-12 0:36:46.0 +00:00:00
committer Jose Quintana <joseluisquintana20@gmail.com> 2021-02-12 0:36:46.0 +00:00:00
commit
71cc8a9cd6419c2e5eff3584b623d3ca7eb6d52e [patch]
tree
2f7051ac5618348b85e4d42be80a8a7e770a768a
parent
fbd5f99338988fc30008494bda01dadc235ab163
download
71cc8a9cd6419c2e5eff3584b623d3ca7eb6d52e.tar.gz

feat: preliminary windows support

refs #29

Diff

 src/helpers.rs                          | 16 +++++++-
 src/lib.rs                              |  3 +-
 src/staticfile_middleware/staticfile.rs | 74 +++++++++++-----------------------
 src/staticfiles.rs                      | 28 ++++---------
 4 files changed, 54 insertions(+), 67 deletions(-)

diff --git a/src/helpers.rs b/src/helpers.rs
index d251a0c..f8b1713 100644
--- a/src/helpers.rs
+++ b/src/helpers.rs
@@ -33,3 +33,19 @@ where
        ))),
    }
}

#[cfg(not(windows))]
pub fn adjust_canonicalization<P: AsRef<std::path::Path>>(p: P) -> String {
    p.as_ref().display().to_string()
}

#[cfg(windows)]
pub fn adjust_canonicalization<P: AsRef<std::path::Path>>(p: P) -> String {
    const VERBATIM_PREFIX: &str = r#"\\?\"#;
    let p = p.as_ref().display().to_string();
    if p.starts_with(VERBATIM_PREFIX) {
        p[VERBATIM_PREFIX.len()..].to_string()
    } else {
        p
    }
}
diff --git a/src/lib.rs b/src/lib.rs
index 4841d9b..b0eea07 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -13,7 +13,10 @@ pub mod gzip;
pub mod helpers;
pub mod logger;
pub mod server;

#[cfg(not(windows))]
pub mod signal_manager;

pub mod staticfile_middleware;
pub mod staticfiles;

diff --git a/src/staticfile_middleware/staticfile.rs b/src/staticfile_middleware/staticfile.rs
index 06836c9..392e614 100644
--- a/src/staticfile_middleware/staticfile.rs
+++ b/src/staticfile_middleware/staticfile.rs
@@ -1,7 +1,7 @@
use humansize::{file_size_opts, FileSize};
use iron::headers::{
    AcceptEncoding, AcceptRanges, ContentEncoding, ContentLength, Encoding, HttpDate,
    IfModifiedSince, LastModified, Range, RangeUnit,
    AcceptRanges, ContentEncoding, ContentLength, Encoding, HttpDate, IfModifiedSince,
    LastModified, Range, RangeUnit,
};
use iron::method::Method;
use iron::middleware::Handler;
@@ -10,11 +10,11 @@ use iron::prelude::*;
use iron::status;
use std::fs::{File, Metadata};
use std::path::{Path, PathBuf};
use std::time::SystemTime;
use std::time::UNIX_EPOCH;
use std::{error, io};
use std::{ffi::OsString, time::SystemTime};

use crate::staticfile_middleware::helpers;
use crate::helpers as utils;
use crate::staticfile_middleware::partial_file::PartialFile;

/// Recursively serves files from the specified root and assets directories.
@@ -25,12 +25,12 @@ pub struct Staticfile {
}

impl Staticfile {
    pub fn new<P>(root: P, assets: P, dir_list: bool) -> io::Result<Staticfile>
    pub fn new<P: AsRef<Path>>(root: P, assets: P, dir_list: bool) -> io::Result<Staticfile>
    where
        P: AsRef<Path>,
        PathBuf: From<P>,
    {
        let root = root.as_ref().canonicalize()?;
        let assets = assets.as_ref().canonicalize()?;
        let root = root.into();
        let assets = assets.into();

        Ok(Staticfile {
            root,
@@ -64,12 +64,15 @@ impl Staticfile {
            res
        };

        let resolved = resolved.canonicalize()?;
        let path = if is_assets { &self.assets } else { &self.root };
        let resolved = PathBuf::from(utils::adjust_canonicalization(resolved));
        let base_path = if is_assets { &self.assets } else { &self.root };

        // Protect against path/directory traversal
        if !resolved.starts_with(&path) {
            return Result::Err(From::from(format!("Cannot leave {:?} path", &path)));
        if !resolved.starts_with(&base_path) {
            return Err(From::from(format!(
                "Cannot leave {:?} base path",
                &base_path
            )));
        }

        Ok(resolved)
@@ -173,10 +176,12 @@ impl Handler for Staticfile {
        // 2. Otherwise proceed with the normal file-response process

        // Get current file metadata
        let accept_gz = helpers::accept_gzip(req.headers.get::<AcceptEncoding>());
        let file = match StaticFileWithMetadata::search(&file_path, accept_gz) {
        let file = match StaticFileWithMetadata::search(file_path.clone()) {
            Ok(f) => f,
            Err(_) => return Ok(Response::with(status::NotFound)),
            Err(e) => {
                trace!("{}", e);
                return Ok(Response::with(status::NotFound));
            }
        };

        // Apply last modified date time
@@ -260,47 +265,20 @@ struct StaticFileWithMetadata {
}

impl StaticFileWithMetadata {
    pub fn search<P>(
        path: P,
        allow_gz: bool,
    ) -> Result<StaticFileWithMetadata, Box<dyn error::Error>>
    // TODO: unbox
    where
        P: Into<PathBuf>,
    {
        let mut file_path = path.into();
    pub fn search(path: PathBuf) -> Result<StaticFileWithMetadata, Box<dyn error::Error>> {
        let mut file_path = path;
        trace!("Opening {}", file_path.display());
        let mut file = StaticFileWithMetadata::open(&file_path)?;
        let metadata = std::fs::metadata(&file_path)?;

        // Look for index.html inside of a directory
        if file.metadata.is_dir() {
        if metadata.is_dir() {
            file_path.push("index.html");
            trace!("Redirecting to index {}", file_path.display());
            file = StaticFileWithMetadata::open(&file_path)?;
        }

        let file = StaticFileWithMetadata::open(&file_path)?;
        if file.metadata.is_file() {
            if allow_gz {
                // Find the foo.gz sibling of foo
                let mut side_by_side_path: OsString = file_path.into();
                side_by_side_path.push(".gz");
                file_path = side_by_side_path.into();
                trace!("Attempting to find side-by-side GZ {}", file_path.display());

                match StaticFileWithMetadata::open(&file_path) {
                    Ok(mut gz_file) => {
                        if gz_file.metadata.is_file() {
                            gz_file.is_gz = true;
                            Ok(gz_file)
                        } else {
                            Ok(file)
                        }
                    }
                    Err(_) => Ok(file),
                }
            } else {
                Ok(file)
            }
            Ok(file)
        } else {
            Err(From::from("Requested path was not a regular file"))
        }
diff --git a/src/staticfiles.rs b/src/staticfiles.rs
index 1794385..ac3ef2e 100644
--- a/src/staticfiles.rs
+++ b/src/staticfiles.rs
@@ -1,8 +1,8 @@
use iron::mime;
use iron::prelude::*;
use iron_cors::CorsMiddleware;
use std::collections::HashSet;
use std::time::Duration;
use std::{collections::HashSet, path::PathBuf};

use crate::error_page::ErrorPage;
use crate::gzip::GzipMiddleware;
@@ -32,26 +32,16 @@ impl StaticFiles {

    /// Handle static files for current `StaticFiles` middleware.
    pub fn handle(&self) -> Chain {
        // Check the root directory
        let root_dir = &match helpers::get_valid_dirpath(&self.opts.root_dir) {
            Err(e) => {
                error!("{}", e);
                std::process::exit(1)
            }
            Ok(v) => v,
        };
        // Check root directory
        let p = PathBuf::from(&self.opts.root_dir).canonicalize().unwrap();
        let root_dir = PathBuf::from(helpers::adjust_canonicalization(p));

        // Check the assets directory
        let assets_dir = &match helpers::get_valid_dirpath(&self.opts.assets_dir) {
            Err(e) => {
                error!("{}", e);
                std::process::exit(1)
            }
            Ok(v) => v,
        };
        // Check assets directory
        let p = PathBuf::from(&self.opts.assets_dir).canonicalize().unwrap();
        let assets_dir = PathBuf::from(helpers::adjust_canonicalization(p));

        // Get the assets directory name
        let assets_dirname = &match helpers::get_dirname(assets_dir) {
        // Get assets directory name
        let assets_dirname = &match helpers::get_dirname(&assets_dir) {
            Err(e) => {
                error!("{}", e);
                std::process::exit(1)