From 70db3c9ad2df6908c5bb3b099565941b83c84e5c Mon Sep 17 00:00:00 2001 From: Jose Quintana <1700322+joseluisq@users.noreply.github.com> Date: Fri, 31 Mar 2023 02:04:39 +0200 Subject: [PATCH] feat: `static_web_server` crate (#190) * feat: preliminary `static_web_server` crate setup * chore: add API docs resolves #188 --- .github/workflows/release.crate.yml | 6 +++--- Cargo.toml | 22 ++++++++++++++++++++-- Makefile | 11 +++++++++++ src/basic_auth.rs | 3 +++ src/compression.rs | 10 ++++++---- src/compression_static.rs | 6 ++++++ src/control_headers.rs | 4 +++- src/cors.rs | 47 +++++++++++++++++++---------------------------- src/custom_headers.rs | 3 +++ src/directory_listing.rs | 5 +++++ src/error.rs | 3 +++ src/error_page.rs | 3 +++ src/exts/http.rs | 4 ++++ src/exts/path.rs | 1 + src/fallback_page.rs | 3 +++ src/handler.rs | 22 +++++++++++++++++++++- src/helpers.rs | 19 ++----------------- src/lib.rs | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- src/logger.rs | 3 +++ src/redirects.rs | 3 +++ src/rewrites.rs | 3 +++ src/security_headers.rs | 3 +++ src/server.rs | 15 ++++++++++----- src/service.rs | 6 ++++++ src/settings/cli.rs | 2 ++ src/settings/file.rs | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++---------------- src/settings/mod.rs | 6 ++++++ src/signals.rs | 3 +++ src/static_files.rs | 22 +++++++++++++++++----- src/tls.rs | 17 ++++++++++++----- src/transport.rs | 9 +++++++-- src/winservice.rs | 3 ++- 32 files changed, 317 insertions(+), 94 deletions(-) diff --git a/.github/workflows/release.crate.yml b/.github/workflows/release.crate.yml index 1781b10..6010d91 100644 --- a/.github/workflows/release.crate.yml +++ b/.github/workflows/release.crate.yml @@ -9,7 +9,7 @@ on: jobs: check-secret: runs-on: ubuntu-latest - environment: crates-io-libsws + environment: crates-io-static-web-server outputs: publish: ${{ steps.check.outputs.publish }} steps: @@ -205,7 +205,7 @@ jobs: needs: test if: needs.check-secret.outputs.publish == 'true' runs-on: ubuntu-latest - environment: crates-io-libsws + environment: crates-io-static-web-server steps: - name: Checkout uses: actions/checkout@v3 @@ -217,6 +217,6 @@ jobs: - name: Publish workspace packages run: | - cargo publish -p libsws + cargo publish -p static-web-server env: CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_TOKEN }} diff --git a/Cargo.toml b/Cargo.toml index 8a9d33d..da76d60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,26 +6,44 @@ license = "MIT OR Apache-2.0" description = "A cross-platform, high-performance and asynchronous web server for static files-serving." repository = "https://github.com/static-web-server/static-web-server" readme = "README.md" +homepage = "https://static-web-server.net" keywords = [ "static-web-server", "file-server", "http-server", - "docker-image", "musl-libc", "x86", "x86-64", + "arm", "arm64", "linux", "darwin", + "freebsd", "windows", ] categories = ["network-programming", "web-programming::http-server"] edition = "2021" -include = ["src/**/*", "Cargo.toml", "Cargo.lock"] +include = [ + "src/**/*.rs", + "Cargo.toml", + "README.md", + "LICENSE-MIT", + "LICENSE-APACHE" +] +autotests = true +autoexamples = true + +[package.metadata.docs.rs] +all-features = true + +[lib] +name = "static_web_server" +path = "src/lib.rs" [[bin]] name = "static-web-server" path = "src/bin/server.rs" +doc = false [features] default = ["http2"] diff --git a/Makefile b/Makefile index 574b7da..ccfa8d8 100644 --- a/Makefile +++ b/Makefile @@ -247,6 +247,17 @@ docs-dev: @docker-compose -f docs/docker-compose.yml up --build .PHONY: docs-dev +crate-docs: + @cargo doc --no-deps +.PHONY: crate-docs + +crate-docs-dev: + @cargo doc --no-deps + @echo "Crate documentation: http://localhost:8787/static_web_server" + @static-web-server -p 8787 -d target/doc/ \ + & watchman-make -p 'src/**/*.rs' --run 'cargo doc' +.PHONY: crate-docs-dev + docs-deploy: @git stash @rm -rf /tmp/docs diff --git a/src/basic_auth.rs b/src/basic_auth.rs index 34c13b6..9daada8 100644 --- a/src/basic_auth.rs +++ b/src/basic_auth.rs @@ -1,3 +1,6 @@ +//! Basic HTTP Authorization Schema module. +//! + use bcrypt::verify as bcrypt_verify; use headers::{authorization::Basic, Authorization, HeaderMapExt}; use hyper::StatusCode; diff --git a/src/compression.rs b/src/compression.rs index 349f567..a39b1c1 100644 --- a/src/compression.rs +++ b/src/compression.rs @@ -1,5 +1,7 @@ -// Compression handler that compress the body of a response. -// -> Part of the file is borrowed from https://github.com/seanmonstar/warp/pull/513 +//! Auto-compression module to compress responses body. +//! + +// Part of the file is borrowed from * use async_compression::tokio::bufread::{BrotliEncoder, DeflateEncoder, GzipEncoder}; use bytes::Bytes; @@ -158,8 +160,8 @@ pub fn create_encoding_header(existing: Option, coding: ContentCodi coding.into() } -/// A wrapper around any type that implements [`Stream`](futures::Stream) to be -/// compatible with async_compression's Stream based encoders. +/// A wrapper around any type that implements [`Stream`](futures_util::Stream) to be +/// compatible with async_compression's `Stream` based encoders. #[pin_project] #[derive(Debug)] pub struct CompressableBody diff --git a/src/compression_static.rs b/src/compression_static.rs index e4dfb8d..85578b2 100644 --- a/src/compression_static.rs +++ b/src/compression_static.rs @@ -1,3 +1,6 @@ +//! Compression static module to serve compressed files directly from the file system. +//! + use headers::{ContentCoding, HeaderMap, HeaderValue}; use std::{ ffi::OsStr, @@ -9,8 +12,11 @@ use crate::{compression, static_files::file_metadata}; /// It defines the pre-compressed file variant metadata of a particular file path. pub struct CompressedFileVariant<'a> { + /// Current file path. pub file_path: PathBuf, + /// The metadata of the current file. pub metadata: Metadata, + /// The file extension. pub extension: &'a str, } diff --git a/src/control_headers.rs b/src/control_headers.rs index 9b1adef..aadf506 100644 --- a/src/control_headers.rs +++ b/src/control_headers.rs @@ -1,4 +1,6 @@ -// An arbitrary `Cache-Control` headers functionality for incoming requests based on a set of file types. +//! It provides an arbitrary `Cache-Control` headers functionality +//! for incoming requests based on a set of file types. +//! use headers::{CacheControl, HeaderMapExt}; use hyper::{Body, Response}; diff --git a/src/cors.rs b/src/cors.rs index c326e3f..77d2ab0 100644 --- a/src/cors.rs +++ b/src/cors.rs @@ -1,5 +1,7 @@ -// CORS handler for incoming requests. -// -> Part of the file is borrowed from https://github.com/seanmonstar/warp/blob/master/src/filters/cors.rs +//! CORS module to handle incoming requests. +//! + +// Part of the file is borrowed from https://github.com/seanmonstar/warp/blob/master/src/filters/cors.rs use headers::{ AccessControlAllowHeaders, AccessControlAllowMethods, AccessControlExposeHeaders, HeaderMapExt, @@ -139,13 +141,6 @@ impl Cors { self } - /// Sets the `Access-Control-Max-Age` header. - /// TODO: we could enable this in the future. - pub fn max_age(mut self, seconds: impl Seconds) -> Self { - self.max_age = Some(seconds.seconds()); - self - } - /// Adds multiple headers to the list of allowed request headers. /// /// **Note**: These should match the values the browser sends via `Access-Control-Request-Headers`, e.g.`content-type`. @@ -211,6 +206,7 @@ impl Default for Cors { } #[derive(Clone, Debug)] +/// CORS is configurated. pub struct Configured { cors: Cors, allowed_headers: AccessControlAllowHeaders, @@ -219,16 +215,24 @@ pub struct Configured { } #[derive(Debug)] +/// Validated CORS request. pub enum Validated { + /// Validated as preflight. Preflight(HeaderValue), + /// Validated as simple. Simple(HeaderValue), + /// Validated as not cors. NotCors, } #[derive(Debug)] +/// Forbidden errors. pub enum Forbidden { + /// Forbidden error origin. Origin, + /// Forbidden error method. Method, + /// Forbidden error header. Header, } @@ -239,6 +243,7 @@ impl Default for Forbidden { } impl Configured { + /// Check for the incoming CORS request. pub fn check_request( &self, method: &http::Method, @@ -299,19 +304,19 @@ impl Configured { } } - pub fn is_method_allowed(&self, header: &HeaderValue) -> bool { + fn is_method_allowed(&self, header: &HeaderValue) -> bool { http::Method::from_bytes(header.as_bytes()) .map(|method| self.cors.allowed_methods.contains(&method)) .unwrap_or(false) } - pub fn is_header_allowed(&self, header: &str) -> bool { + fn is_header_allowed(&self, header: &str) -> bool { HeaderName::from_bytes(header.as_bytes()) .map(|header| self.cors.allowed_headers.contains(&header)) .unwrap_or(false) } - pub fn is_origin_allowed(&self, origin: &HeaderValue) -> bool { + fn is_origin_allowed(&self, origin: &HeaderValue) -> bool { if let Some(ref allowed) = self.cors.origins { allowed.contains(origin) } else { @@ -330,23 +335,9 @@ impl Configured { } } -pub trait Seconds { - fn seconds(self) -> u64; -} - -impl Seconds for u32 { - fn seconds(self) -> u64 { - self.into() - } -} - -impl Seconds for ::std::time::Duration { - fn seconds(self) -> u64 { - self.as_secs() - } -} - +/// Cast values into the origin header. pub trait IntoOrigin { + /// Cast actual value into an origin header. fn into_origin(self) -> Origin; } diff --git a/src/custom_headers.rs b/src/custom_headers.rs index 1cb4209..e972a57 100644 --- a/src/custom_headers.rs +++ b/src/custom_headers.rs @@ -1,3 +1,6 @@ +//! Module to append custom HTTP headers via TOML config file. +//! + use hyper::{Body, Response}; use crate::settings::Headers; diff --git a/src/directory_listing.rs b/src/directory_listing.rs index dab3399..7388697 100644 --- a/src/directory_listing.rs +++ b/src/directory_listing.rs @@ -1,3 +1,8 @@ +//! It provides directory listig and auto-index support. +//! + +#![allow(missing_docs)] + use chrono::{DateTime, Local, NaiveDateTime, Utc}; use futures_util::future::Either; use futures_util::{future, FutureExt}; diff --git a/src/error.rs b/src/error.rs index 83512fa..3902eb6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,6 @@ +//! Useful error type re-exports based on [anyhow][mod@anyhow]. +//! + /// Just a `anyhow::Result` type alias. pub type Result = anyhow::Result; diff --git a/src/error_page.rs b/src/error_page.rs index fd2f783..6beb947 100644 --- a/src/error_page.rs +++ b/src/error_page.rs @@ -1,3 +1,6 @@ +//! Error page module to compose an HTML page response. +//! + use headers::{AcceptRanges, ContentLength, ContentType, HeaderMapExt}; use hyper::{Body, Method, Response, StatusCode, Uri}; use mime_guess::mime; diff --git a/src/exts/http.rs b/src/exts/http.rs index 1322d53..9ea932b 100644 --- a/src/exts/http.rs +++ b/src/exts/http.rs @@ -7,9 +7,13 @@ pub const HTTP_SUPPORTED_METHODS: &[Method; 3] = &[Method::OPTIONS, Method::HEAD /// SWS HTTP Method extensions trait. pub trait MethodExt { + /// If method is allowed. fn is_allowed(&self) -> bool; + /// If method is `GET`. fn is_get(&self) -> bool; + /// If method is `HEAD`. fn is_head(&self) -> bool; + /// If method is `OPTIONS`. fn is_options(&self) -> bool; } diff --git a/src/exts/path.rs b/src/exts/path.rs index 30628c9..ce17e49 100644 --- a/src/exts/path.rs +++ b/src/exts/path.rs @@ -4,6 +4,7 @@ use std::path::{Component, Path}; /// SWS Path extensions trait. pub trait PathExt { + /// If file path is hidden. fn is_hidden(&self) -> bool; } diff --git a/src/fallback_page.rs b/src/fallback_page.rs index 67c5ab6..9b1370e 100644 --- a/src/fallback_page.rs +++ b/src/fallback_page.rs @@ -1,3 +1,6 @@ +//! Fallback page module useful for a custom page default. +//! + use headers::{AcceptRanges, ContentLength, ContentType, HeaderMapExt}; use hyper::{Body, Response, StatusCode}; use mime_guess::mime; diff --git a/src/handler.rs b/src/handler.rs index b7410b4..bc8831d 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,3 +1,6 @@ +//! Request handler module intended to manage incoming HTTP requests. +//! + use headers::HeaderValue; use hyper::{header::WWW_AUTHENTICATE, Body, Request, Response, StatusCode}; use std::{future::Future, net::IpAddr, net::SocketAddr, path::PathBuf, sync::Arc}; @@ -16,29 +19,46 @@ use crate::{ /// It defines options for a request handler. pub struct RequestHandlerOpts { // General options + /// Root directory of static files. pub root_dir: PathBuf, + /// Compression feature. pub compression: bool, + /// Compression static feature. pub compression_static: bool, + /// Directory listing feature. pub dir_listing: bool, + /// Directory listing order feature. pub dir_listing_order: u8, + /// Directory listing format feature. pub dir_listing_format: DirListFmt, + /// CORS feature. pub cors: Option, + /// Security headers feature. pub security_headers: bool, + /// Cache control headers feature. pub cache_control_headers: bool, + /// Page for 404 errors. pub page404: Vec, + /// Page for 50x errors. pub page50x: Vec, + /// Page fallback feature. pub page_fallback: Vec, + /// Basic auth feature. pub basic_auth: String, + /// Log remote address feature. pub log_remote_address: bool, + /// Redirect trailing slash feature. pub redirect_trailing_slash: bool, + /// Ignore hidden files feature. pub ignore_hidden_files: bool, - // Advanced options + /// Advanced options from the config file. pub advanced_opts: Option, } /// It defines the main request handler used by the Hyper service request. pub struct RequestHandler { + /// Request handler options. pub opts: Arc, } diff --git a/src/helpers.rs b/src/helpers.rs index 5862ba4..866e200 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -15,21 +15,6 @@ where } } -/// Get the directory name of a valid directory path. -pub fn get_dirname>(path: P) -> Result -where - PathBuf: From

, -{ - let path = get_valid_dirpath(path)?; - match path.iter().last() { - Some(v) => Ok(v.to_str().unwrap().to_owned()), - _ => bail!( - "directory name for path {} was not determined", - path.display() - ), - } -} - /// Read the entire contents of a file into a bytes vector. pub fn read_bytes(path: &Path) -> Result> { fs::read(path).with_context(|| format!("failed to read file `{}`", path.display())) @@ -75,13 +60,13 @@ pub fn stringify(dst: &mut String, path: &serde_ignored::Path<'_>) { #[cfg(unix)] /// In Unix-like systems it just casts the `PathBuf` into an string. -pub fn adjust_canonicalization(p: PathBuf) -> String { +pub fn adjust_canonicalization(p: &Path) -> String { p.display().to_string() } #[cfg(windows)] /// In Windows systems it adjusts the `PathBuf` stripping its `\\?\` prefix. -pub fn adjust_canonicalization(p: PathBuf) -> String { +pub fn adjust_canonicalization(p: &Path) -> String { const VERBATIM_PREFIX: &str = r#"\\?\"#; let p = p.to_str().unwrap_or_default(); let p = if p.starts_with(VERBATIM_PREFIX) { diff --git a/src/lib.rs b/src/lib.rs index f395485..ba63e99 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,81 @@ +//! # Static Web Server (SWS) +//! +//! SWS is a cross-platform, high-performance and asynchronous web server for static files-serving. +//! +//! **Considerations:** +//! +//! This crate was published to make it possible for users to embed SWS with ease instead of working around it via forks. +//! For example, allowing users to turn off default features like `tls` and so on. +//! +//! **However**, because the library is highly coupled with the SWS binary project at this point, +//! users might be limited by the exposed APIs, implementations, missing functionality, or dependencies. +//! +//! In the future, we will eventually plan to make SWS library independent from the binary project. +//! +//! That said, if fine-grained control and flexibility are needed then you could want to look at other alternatives like HTTP libraries or frameworks. +//! +//! **Pre-compile binaries:** +//! +//! This is the official SWS crate. +//! If you are looking for platform pre-compile binaries then +//! take a look at [static-web-server.net/download-and-install](https://static-web-server.net/download-and-install). +//! +//! ## Overview +//! +//! **Static Web Server** (or **`SWS`** abbreviated) is a very small and fast production-ready web server suitable to serve static web files or assets. +//! +//! It is focused on **lightness and easy-to-use** principles while keeping [high performance and safety](https://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html) powered by [The Rust Programming Language](https://rust-lang.org). +//! +//! Written on top of [Hyper](https://github.com/hyperium/hyper) and [Tokio](https://github.com/tokio-rs/tokio) runtime. It provides [concurrent and asynchronous networking abilities](https://rust-lang.github.io/async-book/01_getting_started/02_why_async.html) as well as the latest HTTP/1 - HTTP/2 implementations. +//! +//! It's cross-platform and available for `Linux`, `macOS`, `Windows` and `FreeBSD` (`x86`/`x86_64`, `ARM`/`ARM64`) as well as `Docker`. +//! +//! ![static-web-server](https://user-images.githubusercontent.com/1700322/152613820-658f025c-d0a4-46b3-aa6d-bdc7f638ce77.png) +//! +//! ## Features +//! +//! - Built with [Rust](https://rust-lang.org) which is focused on [safety, speed and concurrency](https://kornel.ski/rust-c-speed). +//! - Memory safe and very reduced CPU and RAM overhead. +//! - Blazing fast static files-serving and asynchronous powered by latest [Hyper](https://github.com/hyperium/hyper/), [Tokio](https://github.com/tokio-rs/tokio) and a set of [awesome crates](https://github.com/static-web-server/static-web-server/blob/master/Cargo.toml). +//! - Single __4MB__ (uncompressed) and fully static binary with no dependencies ([Musl libc](https://doc.rust-lang.org/edition-guide/rust-2018/platform-and-target-support/musl-support-for-fully-static-binaries.html)). Suitable for running on [any Linux distro](https://en.wikipedia.org/wiki/Linux_distribution) or [Docker container](https://hub.docker.com/r/joseluisq/static-web-server/tags). +//! - Optional GZip, Deflate or Brotli compression for text-based web files only. +//! - Compression on-demand via [Accept-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) header. +//! - [Partial Content Delivery](https://en.wikipedia.org/wiki/Byte_serving) support for byte-serving of large files. +//! - Optional [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) headers for assets. +//! - [Termination signal](https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html) handling with [graceful shutdown](https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-terminating-with-grace) ability and grace period. +//! - [HTTP/2](https://tools.ietf.org/html/rfc7540) and TLS support. +//! - [Security headers](https://web.dev/security-headers/) for HTTP/2 by default. +//! - [HEAD](https://tools.ietf.org/html/rfc7231#section-4.3.2) responses. +//! - Lightweight and configurable logging via [tracing](https://github.com/tokio-rs/tracing) crate. +//! - Customizable number of worker threads. +//! - Optional directory listing. +//! - [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) support. +//! - Basic HTTP Authentication. +//! - Customizable HTTP response headers for specific file requests via glob patterns. +//! - Fallback pages for 404 errors, useful for Single-page applications. +//! - Run the server as a [Windows Service](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc783643(v=ws.10)). +//! - Configurable using CLI arguments, environment variables or a TOML file. +//! - Default and custom error pages. +//! - Custom URL rewrites and redirects via glob patterns. +//! - Support for serving pre-compressed (Gzip/Brotli) files. +//! - First-class [Docker](https://docs.docker.com/get-started/overview/) support. [Scratch](https://hub.docker.com/_/scratch) and latest [Alpine Linux](https://hub.docker.com/_/alpine) Docker images. +//! - Ability to accept a socket listener as a file descriptor for use in sandboxing and on-demand applications (E.g [systemd](http://0pointer.de/blog/projects/socket-activation.html)). +//! - Cross-platform. Pre-compiled binaries for Linux, macOS, Windows and FreeBSD (`x86`,`x86_64`,`ARM`,`ARM64`). +//! + +#![deny(missing_docs)] #![forbid(unsafe_code)] #![deny(warnings)] #![deny(rust_2018_idioms)] #![deny(dead_code)] +// Extern crates #[macro_use] extern crate anyhow; - #[macro_use] extern crate serde; +// Public modules pub mod basic_auth; pub mod compression; pub mod compression_static; @@ -20,7 +87,6 @@ pub mod error_page; pub mod exts; pub mod fallback_page; pub mod handler; -pub mod helpers; pub mod logger; pub mod redirects; pub mod rewrites; @@ -34,13 +100,15 @@ pub mod static_files; #[cfg(feature = "tls")] pub mod tls; pub mod transport; - #[cfg(windows)] pub mod winservice; - #[macro_use] pub mod error; +// Private modules +mod helpers; + +// Re-exports pub use error::*; pub use server::Server; pub use settings::Settings; diff --git a/src/logger.rs b/src/logger.rs index ac666bf..c27786b 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -1,3 +1,6 @@ +//! Provides logging initialization for the web server. +//! + use tracing::Level; use tracing_subscriber::fmt::format::FmtSpan; diff --git a/src/redirects.rs b/src/redirects.rs index d51f591..bf88616 100644 --- a/src/redirects.rs +++ b/src/redirects.rs @@ -1,3 +1,6 @@ +//! Redirection module to handle config redirect URLs with pattern matching support. +//! + use hyper::StatusCode; use crate::settings::Redirects; diff --git a/src/rewrites.rs b/src/rewrites.rs index b007b36..1b4a320 100644 --- a/src/rewrites.rs +++ b/src/rewrites.rs @@ -1,3 +1,6 @@ +//! Module that allows to rewrite request URLs with pattern matching support. +//! + use crate::settings::Rewrites; /// It returns a rewrite's destination path if the current request uri diff --git a/src/security_headers.rs b/src/security_headers.rs index 73ff7bb..eb35376 100644 --- a/src/security_headers.rs +++ b/src/security_headers.rs @@ -1,3 +1,6 @@ +//! The module provides several HTTP security headers support. +//! + use http::header::{ CONTENT_SECURITY_POLICY, STRICT_TRANSPORT_SECURITY, X_CONTENT_TYPE_OPTIONS, X_FRAME_OPTIONS, X_XSS_PROTECTION, diff --git a/src/server.rs b/src/server.rs index ef19deb..79f71e8 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,3 +1,6 @@ +//! Server module intended to construct a multi-thread HTTP or HTTP/2 web server. +//! + #[allow(unused_imports)] use hyper::server::conn::AddrIncoming; #[allow(unused_imports)] @@ -43,7 +46,8 @@ impl Server { }) } - /// Build and run the multi-thread `Server` as standalone. + /// Run the multi-thread `Server` as standalone. + /// It is a top-level function of [run_server_on_rt](#method.run_server_on_rt). pub fn run_standalone(self) -> Result { // Logging system initialization logger::init(&self.opts.general.log_level)?; @@ -51,7 +55,8 @@ impl Server { self.run_server_on_rt(None, || {}) } - /// Build and run the multi-thread `Server` which will be used by a Windows service. + /// Run the multi-thread `Server` which will be used by a Windows service. + /// It is a top-level function of [run_server_on_rt](#method.run_server_on_rt). #[cfg(windows)] pub fn run_as_service(self, cancel: Option>, cancel_fn: F) -> Result where @@ -60,8 +65,8 @@ impl Server { self.run_server_on_rt(cancel, cancel_fn) } - /// Build and run the multi-thread `Server` on Tokio runtime. - fn run_server_on_rt(self, cancel_recv: Option>, cancel_fn: F) -> Result + /// Build and run the multi-thread `Server` on the Tokio runtime. + pub fn run_server_on_rt(self, cancel_recv: Option>, cancel_fn: F) -> Result where F: FnOnce(), { @@ -100,7 +105,7 @@ impl Server { // Config file option if let Some(config_file) = general.config_file { - let config_file = helpers::adjust_canonicalization(config_file); + let config_file = helpers::adjust_canonicalization(&config_file); tracing::info!("config file: {}", config_file); } diff --git a/src/service.rs b/src/service.rs index 315fd16..9161c98 100644 --- a/src/service.rs +++ b/src/service.rs @@ -1,3 +1,6 @@ +//! The module provides a custom [Hyper service](hyper::service::Service). +//! + use hyper::{service::Service, Body, Request, Response}; use std::convert::Infallible; use std::future::{ready, Future, Ready}; @@ -14,6 +17,7 @@ pub struct RouterService { } impl RouterService { + /// Creates a new router service. pub fn new(handler: RequestHandler) -> Self { Self { builder: RequestServiceBuilder::new(handler), @@ -63,12 +67,14 @@ pub struct RequestServiceBuilder { } impl RequestServiceBuilder { + /// Initializes a new request service buider. pub fn new(handler: RequestHandler) -> Self { Self { handler: Arc::new(handler), } } + /// Build a new request service. pub fn build(&self, remote_addr: Option) -> RequestService { RequestService { handler: self.handler.clone(), diff --git a/src/settings/cli.rs b/src/settings/cli.rs index 166c9c5..d21c52e 100644 --- a/src/settings/cli.rs +++ b/src/settings/cli.rs @@ -281,11 +281,13 @@ pub struct General { // Windows commands #[cfg(windows)] #[structopt(subcommand)] + /// Subcommands to install or uninstall the SWS Windows Service. pub commands: Option, } #[cfg(windows)] #[derive(Debug, StructOpt)] +/// Subcommands to install or uninstall the SWS Windows Service. pub enum Commands { /// Install a Windows Service for the web server. #[structopt(name = "install")] diff --git a/src/settings/file.rs b/src/settings/file.rs index 6b8c4cc..06685f2 100644 --- a/src/settings/file.rs +++ b/src/settings/file.rs @@ -11,15 +11,22 @@ use crate::{helpers, Context, Result}; #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "kebab-case")] +/// Log level variants. pub enum LogLevel { + /// Error log level. Error, + /// Warn log level. Warn, + /// Info log level. Info, + /// Debug log level. Debug, + /// Trace log level. Trace, } impl LogLevel { + /// Get log level name. pub fn name(&self) -> &'static str { match self { LogLevel::Error => "error", @@ -33,14 +40,18 @@ impl LogLevel { #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "kebab-case")] +/// Represents an HTTP headers map. pub struct Headers { + /// Header source. pub source: String, #[serde(rename(deserialize = "headers"), with = "http_serde::header_map")] + /// headers list. pub headers: HeaderMap, } #[derive(Debug, Serialize_repr, Deserialize_repr, Clone)] #[repr(u16)] +/// Represents redirects types. pub enum RedirectsKind { /// Moved Permanently Permanent = 301, @@ -50,16 +61,23 @@ pub enum RedirectsKind { #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "kebab-case")] +/// Represents redirects types. pub struct Redirects { + /// Source of the redirect. pub source: String, + /// Redirect destination. pub destination: String, + /// Redirect type. pub kind: RedirectsKind, } #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "kebab-case")] +/// Represents rewrites types. pub struct Rewrites { + /// Source of the rewrite. pub source: String, + /// Rewrite destination. pub destination: String, } @@ -67,11 +85,11 @@ pub struct Rewrites { #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "kebab-case")] pub struct Advanced { - // Headers + /// Headers pub headers: Option>, - // Rewrites + /// Rewrites pub rewrites: Option>, - // Redirects + /// Redirects pub redirects: Option>, } @@ -80,70 +98,86 @@ pub struct Advanced { #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "kebab-case")] pub struct General { - // Address & Root dir + /// Server address. pub host: Option, + /// Server port. pub port: Option, + /// Root directory path. pub root: Option, - // Logging + /// Logging. pub log_level: Option, - // Cache Control headers + /// Cache Control headers. pub cache_control_headers: Option, - // Compression + /// Compression. pub compression: Option, - // Check for a pre-compressed file on disk + /// Check for a pre-compressed file on disk. pub compression_static: Option, - // Error pages + /// Error 404 pages. pub page404: Option, + /// Error 50x pages. pub page50x: Option, - // HTTP/2 + TLS + /// HTTP/2 + TLS. #[cfg(feature = "http2")] pub http2: Option, + /// Http2 tls certificate feature. #[cfg(feature = "http2")] pub http2_tls_cert: Option, + /// Http2 tls key feature. #[cfg(feature = "http2")] pub http2_tls_key: Option, - // Security headers + /// Security headers. pub security_headers: Option, - // CORS + /// Cors allow origins feature. pub cors_allow_origins: Option, + /// Cors allow headers feature. pub cors_allow_headers: Option, + /// Cors expose headers feature. pub cors_expose_headers: Option, - // Directory listing + /// Directory listing feature. pub directory_listing: Option, + /// Directory listing order feature. pub directory_listing_order: Option, + /// Directory listing format feature. pub directory_listing_format: Option, - // Basich Authentication + /// Basich Authentication feature. pub basic_auth: Option, - // File descriptor binding + /// File descriptor binding feature. pub fd: Option, - // Worker threads + /// Worker threads. pub threads_multiplier: Option, + /// Max blocking threads feature. pub max_blocking_threads: Option, + /// Grace period feature. pub grace_period: Option, + /// Page fallback feature. pub page_fallback: Option, + /// Log remote address feature. pub log_remote_address: Option, + /// Redirect trailing slash feature. pub redirect_trailing_slash: Option, + /// Ignore hidden files feature. pub ignore_hidden_files: Option, #[cfg(windows)] + /// windows service feature. pub windows_service: Option, } @@ -151,7 +185,9 @@ pub struct General { #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "kebab-case")] pub struct Settings { + /// General settings. pub general: Option, + /// Advanced settings. pub advanced: Option, } diff --git a/src/settings/mod.rs b/src/settings/mod.rs index 8bd577f..7aa3c05 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -1,3 +1,6 @@ +//! Module that provides all settings of SWS. +//! + use globset::{Glob, GlobMatcher}; use headers::HeaderMap; use hyper::StatusCode; @@ -41,8 +44,11 @@ pub struct Redirects { /// The `advanced` file options. pub struct Advanced { + /// Headers list. pub headers: Option>, + /// Rewrites list. pub rewrites: Option>, + /// Redirects list. pub redirects: Option>, } diff --git a/src/signals.rs b/src/signals.rs index d3d6d81..f28c8ba 100644 --- a/src/signals.rs +++ b/src/signals.rs @@ -1,3 +1,6 @@ +//! The module provides signals support like `SIGTERM`, `SIGINT` and `SIGQUIT`. +//! + use tokio::time::{sleep, Duration}; #[cfg(unix)] diff --git a/src/static_files.rs b/src/static_files.rs index bdeaf3f..0431938 100644 --- a/src/static_files.rs +++ b/src/static_files.rs @@ -1,7 +1,8 @@ -//! Static File handler +//! The static file module which powers the web server. //! -//! Part of the file is borrowed and adapted at a convenience from -//! https://github.com/seanmonstar/warp/blob/master/src/filters/fs.rs + +// Part of the file is borrowed and adapted at a convenience from +// https://github.com/seanmonstar/warp/blob/master/src/filters/fs.rs use bytes::{Bytes, BytesMut}; use futures_util::future::{Either, Future}; @@ -27,20 +28,31 @@ use crate::{compression_static, directory_listing, Result}; /// Defines all options needed by the static-files handler. pub struct HandleOpts<'a> { + /// Request method. pub method: &'a Method, + /// Request headers. pub headers: &'a HeaderMap, + /// Request base path. pub base_path: &'a PathBuf, + /// Request base path. pub uri_path: &'a str, + /// Request URI query. pub uri_query: Option<&'a str>, + /// Directory listing feature. pub dir_listing: bool, + /// Directory listing order feature. pub dir_listing_order: u8, + /// Directory listing format feature. pub dir_listing_format: &'a DirListFmt, + /// Redirect trailing slash feature. pub redirect_trailing_slash: bool, + /// Compression static feature. pub compression_static: bool, + /// Ignore hidden files feature. pub ignore_hidden_files: bool, } -/// Entry point to handle incoming requests which map to specific files +/// The server entry point to handle incoming requests which map to specific files /// on file system and return a file response. pub async fn handle<'a>(opts: &HandleOpts<'a>) -> Result<(Response, bool), StatusCode> { let method = opts.method; @@ -472,7 +484,7 @@ const READ_BUF_SIZE: usize = 4_096; const READ_BUF_SIZE: usize = 8_192; #[derive(Debug)] -pub struct FileStream { +struct FileStream { reader: T, } diff --git a/src/tls.rs b/src/tls.rs index aaebec9..6a20032 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -1,5 +1,7 @@ -// Handles requests over TLS -// -> Most of the file is borrowed from https://github.com/seanmonstar/warp/blob/master/src/tls.rs +//! The module handles requests over TLS via [Rustls](tokio_rustls::rustls). +//! + +// Most of the file is borrowed from https://github.com/seanmonstar/warp/blob/master/src/tls.rs use futures_util::ready; use hyper::server::accept::Accept; @@ -23,6 +25,7 @@ use crate::transport::Transport; /// Represents errors that can occur building the TlsConfig #[derive(Debug)] pub enum TlsConfigError { + /// Error type for I/O operations Io(io::Error), /// An Error parsing the Certificate CertParseError, @@ -168,6 +171,7 @@ impl TlsConfigBuilder { self } + /// Builds TLS configuration. pub fn build(mut self) -> Result { let mut cert_rdr = BufReader::new(self.cert); let cert = rustls_pemfile::certs(&mut cert_rdr) @@ -284,9 +288,10 @@ enum State { Streaming(tokio_rustls::server::TlsStream), } -// tokio_rustls::server::TlsStream doesn't expose constructor methods, -// so we have to TlsAcceptor::accept and handshake to have access to it -// TlsStream implements AsyncRead/AsyncWrite handshaking tokio_rustls::Accept first +/// TlsStream implements AsyncRead/AsyncWrite handshaking tokio_rustls::Accept first. +/// +/// tokio_rustls::server::TlsStream doesn't expose constructor methods, +/// so we have to TlsAcceptor::accept and handshake to have access to it. pub struct TlsStream { state: State, remote_addr: SocketAddr, @@ -359,12 +364,14 @@ impl AsyncWrite for TlsStream { } } +/// Type to intercept Tls incoming connections. pub struct TlsAcceptor { config: Arc, incoming: AddrIncoming, } impl TlsAcceptor { + /// Creates a new Tls interceptor. pub fn new(config: ServerConfig, incoming: AddrIncoming) -> TlsAcceptor { TlsAcceptor { config: Arc::new(config), diff --git a/src/transport.rs b/src/transport.rs index 7f52ed7..e372e89 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -1,5 +1,7 @@ -// Handles requests over TLS -// -> Most of the file is borrowed from https://github.com/seanmonstar/warp/blob/master/src/transport.rs +//! Async transport module. +//! + +// Most of the file is borrowed from https://github.com/seanmonstar/warp/blob/master/src/transport.rs use std::io; use std::net::SocketAddr; @@ -9,7 +11,9 @@ use std::task::{Context, Poll}; use hyper::server::conn::AddrStream; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; +/// Transport trait that supports the remote (peer) address. pub trait Transport: AsyncRead + AsyncWrite { + /// Returns the remote (peer) address of this connection. fn remote_addr(&self) -> Option; } @@ -19,6 +23,7 @@ impl Transport for AddrStream { } } +/// Type to support `Transport`, `AsyncRead` and `AsyncWrite`. pub struct LiftIo(pub T); impl AsyncRead for LiftIo { diff --git a/src/winservice.rs b/src/winservice.rs index 652fcdc..e197986 100644 --- a/src/winservice.rs +++ b/src/winservice.rs @@ -1,4 +1,5 @@ //! Module that lets SWS to run in a "Windows Service" context +//! use std::ffi::OsString; use std::thread; @@ -181,7 +182,7 @@ pub fn install_service(config_file: Option) -> Result { // Append a `--config-file` path to the binary arguments if present if let Some(f) = config_file { - let f = helpers::adjust_canonicalization(f); + let f = helpers::adjust_canonicalization(&f); if !f.is_empty() { service_binary_arguments.push(OsString::from(["--config-file=", &f].concat())); } -- libgit2 1.7.2