mirror of
https://github.com/static-web-server/static-web-server.git
synced 2026-01-25 05:06:33 +00:00
chore: update dependencies 23-10-2025 & MSRV to Rust 1.85.0 (2024 Edition) (#572)
* fix: update dependencies 23-10-2025 * chore: update dependencies 26-10-2025 * chore: update dependencies 26.10.2025 & bump up MSRV to 1.85.0 * fix: lint errors * fix: style edition 2024 SWS now requires Rust 1.85.0 (Rust 2024 Edition) or later https://blog.rust-lang.org/2025/02/20/Rust-1.85.0/
This commit is contained in:
2
.github/workflows/devel.yml
vendored
2
.github/workflows/devel.yml
vendored
@@ -74,7 +74,7 @@ jobs:
|
||||
# We test against the latest and minimum Rust stable version.
|
||||
- build: pinned
|
||||
os: ubuntu-22.04
|
||||
rust: 1.82.0
|
||||
rust: 1.85.0
|
||||
# Some of our release builds are generated by a nightly compiler to take
|
||||
# advantage of the latest optimizations/compile time improvements.
|
||||
- build: linux-musl
|
||||
|
||||
630
Cargo.lock
generated
630
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
16
Cargo.toml
16
Cargo.toml
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "static-web-server"
|
||||
version = "2.38.1"
|
||||
edition = "2021"
|
||||
rust-version = "1.82.0"
|
||||
edition = "2024"
|
||||
rust-version = "1.85.0"
|
||||
authors = ["Jose Quintana <https://joseluisq.net>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "A cross-platform, high-performance and asynchronous web server for static files-serving."
|
||||
@@ -62,10 +62,10 @@ fallback-page = []
|
||||
experimental = ["tokio-metrics-collector", "prometheus", "compact_str", "mini-moka"]
|
||||
|
||||
[dependencies]
|
||||
aho-corasick = "1.1"
|
||||
aho-corasick = "1.1.3"
|
||||
anyhow = "1.0"
|
||||
async-compression = { version = "0.4", default-features = false, optional = true, features = ["brotli", "deflate", "gzip", "zstd", "tokio"] }
|
||||
async-tar = { version = "0.5", optional = true }
|
||||
async-tar = { version = "0.5.1", optional = true }
|
||||
bcrypt = { version = "0.17", optional = true }
|
||||
bytes = "1.10"
|
||||
chrono = { version = "0.4", default-features = false, features = ["std", "clock"], optional = true }
|
||||
@@ -74,7 +74,7 @@ clap_allgen = "0.2.1"
|
||||
compact_str = { version = "0.9.0", optional = true }
|
||||
form_urlencoded = "1.2"
|
||||
futures-util = { version = "0.3", default-features = false }
|
||||
globset = { version = "0.4", features = ["serde1"] }
|
||||
globset = { version = "0.4.18", features = ["serde1"] }
|
||||
headers = "0.3"
|
||||
http = "0.2"
|
||||
http-serde = "1.1"
|
||||
@@ -86,13 +86,13 @@ mime_guess = "2.0"
|
||||
mini-moka = { version = "0.10.3", optional = true }
|
||||
percent-encoding = "2.3"
|
||||
pin-project = "1.1"
|
||||
regex = "1.11"
|
||||
regex = "1.12.2"
|
||||
rustls-pemfile = { version = "2.2", optional = true }
|
||||
serde = { version = "1.0", default-features = false, features = ["derive"] }
|
||||
serde_ignored = "0.1"
|
||||
serde_json = "1.0"
|
||||
serde_repr = "0.1"
|
||||
shadow-rs = "1.3.0"
|
||||
shadow-rs = "1.4.0"
|
||||
tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "macros", "fs", "io-util", "signal"] }
|
||||
tokio-rustls = { version = "0.26", optional = true, default-features = false, features = ["logging", "tls12", "ring"] }
|
||||
tokio-util = { version = "0.7", default-features = false, features = ["compat", "io"] }
|
||||
@@ -117,7 +117,7 @@ bytes = "1.10"
|
||||
serde_json = "1.0"
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = "1.3.0"
|
||||
shadow-rs = "1.4.0"
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
|
||||
|
||||
use hyper::{Body, Response, StatusCode};
|
||||
use static_web_server::control_headers;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
|
||||
|
||||
use hyper::Method;
|
||||
use static_web_server::http_ext::MethodExt;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
|
||||
|
||||
use static_web_server::static_files;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Follow these instructions to either build **`SWS`** project from the source or t
|
||||
|
||||
If you want to build **SWS** from the source, all you need is a [Rust 2021 Edition](https://blog.rust-lang.org/2021/05/11/edition-2021.html) installed.
|
||||
|
||||
So make sure to install Rust [1.82.0](https://blog.rust-lang.org/2024/10/17/Rust-1.82.0/) or newer (or nightly) along with [the toolchain(s)](https://rust-lang.github.io/rustup/concepts/toolchains.html) of your preference.
|
||||
So make sure to install Rust [1.85.0](https://blog.rust-lang.org/2025/02/20/Rust-1.85.0/) or newer (or nightly) along with [the toolchain(s)](https://rust-lang.github.io/rustup/concepts/toolchains.html) of your preference.
|
||||
|
||||
Then clone the repository and use [Cargo](https://doc.rust-lang.org/cargo/) to build the project from the source.
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
//!
|
||||
|
||||
use bcrypt::verify as bcrypt_verify;
|
||||
use headers::{authorization::Basic, Authorization, HeaderMap, HeaderMapExt};
|
||||
use hyper::{header::WWW_AUTHENTICATE, Body, Request, Response, StatusCode};
|
||||
use headers::{Authorization, HeaderMap, HeaderMapExt, authorization::Basic};
|
||||
use hyper::{Body, Request, Response, StatusCode, header::WWW_AUTHENTICATE};
|
||||
|
||||
use crate::{error_page, handler::RequestHandlerOpts, http_ext::MethodExt, Error};
|
||||
use crate::{Error, error_page, handler::RequestHandlerOpts, http_ext::MethodExt};
|
||||
|
||||
/// Initializes `Basic` HTTP Authorization handling
|
||||
pub(crate) fn init(credentials: &str, handler_opts: &mut RequestHandlerOpts) {
|
||||
@@ -91,9 +91,9 @@ pub fn check_request(headers: &HeaderMap, userid: &str, password: &str) -> Resul
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{check_request, pre_process};
|
||||
use crate::{handler::RequestHandlerOpts, Error};
|
||||
use crate::{Error, handler::RequestHandlerOpts};
|
||||
use headers::HeaderMap;
|
||||
use hyper::{header::WWW_AUTHENTICATE, Body, Request, Response, StatusCode};
|
||||
use hyper::{Body, Request, Response, StatusCode, header::WWW_AUTHENTICATE};
|
||||
|
||||
fn make_request(method: &str, auth_header: &str) -> Request<Body> {
|
||||
let mut builder = Request::builder();
|
||||
@@ -122,14 +122,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_auth_disabled() {
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
basic_auth: "".into(),
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("GET", "Basic anE6anE=")
|
||||
)
|
||||
.is_none());
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
basic_auth: "".into(),
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("GET", "Basic anE6anE=")
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -145,37 +147,43 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_options_request() {
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
basic_auth: "jq:$2y$05$32zazJ1yzhlDHnt26L3MFOgY0HVqPmDUvG0KUx6cjf9RDiUGp/M9q"
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("OPTIONS", "")
|
||||
)
|
||||
.is_none());
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
basic_auth: "jq:$2y$05$32zazJ1yzhlDHnt26L3MFOgY0HVqPmDUvG0KUx6cjf9RDiUGp/M9q"
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("OPTIONS", "")
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_valid_auth() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("Authorization", "Basic anE6anE=".parse().unwrap());
|
||||
assert!(check_request(
|
||||
&headers,
|
||||
"jq",
|
||||
"$2y$05$32zazJ1yzhlDHnt26L3MFOgY0HVqPmDUvG0KUx6cjf9RDiUGp/M9q"
|
||||
)
|
||||
.is_ok());
|
||||
assert!(
|
||||
check_request(
|
||||
&headers,
|
||||
"jq",
|
||||
"$2y$05$32zazJ1yzhlDHnt26L3MFOgY0HVqPmDUvG0KUx6cjf9RDiUGp/M9q"
|
||||
)
|
||||
.is_ok()
|
||||
);
|
||||
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
basic_auth: "jq:$2y$05$32zazJ1yzhlDHnt26L3MFOgY0HVqPmDUvG0KUx6cjf9RDiUGp/M9q"
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("GET", "Basic anE6anE=")
|
||||
)
|
||||
.is_none());
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
basic_auth: "jq:$2y$05$32zazJ1yzhlDHnt26L3MFOgY0HVqPmDUvG0KUx6cjf9RDiUGp/M9q"
|
||||
.into(),
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("GET", "Basic anE6anE=")
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -211,12 +219,14 @@ mod tests {
|
||||
fn test_invalid_auth() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("Authorization", "Basic anE6anE=".parse().unwrap());
|
||||
assert!(check_request(
|
||||
&headers,
|
||||
"abc",
|
||||
"$2y$05$32zazJ1yzhlDHnt26L3MFOgY0HVqPmDUvG0KUx6cjf9RDiUGp/M9q"
|
||||
)
|
||||
.is_err());
|
||||
assert!(
|
||||
check_request(
|
||||
&headers,
|
||||
"abc",
|
||||
"$2y$05$32zazJ1yzhlDHnt26L3MFOgY0HVqPmDUvG0KUx6cjf9RDiUGp/M9q"
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
assert!(check_request(&headers, "jq", "password").is_err());
|
||||
assert!(check_request(&headers, "", "password").is_err());
|
||||
assert!(check_request(&headers, "jq", "").is_err());
|
||||
@@ -256,12 +266,14 @@ mod tests {
|
||||
fn test_invalid_auth_encoding() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("Authorization", "Basic xyz".parse().unwrap());
|
||||
assert!(check_request(
|
||||
&headers,
|
||||
"jq",
|
||||
"$2y$05$32zazJ1yzhlDHnt26L3MFOgY0HVqPmDUvG0KUx6cjf9RDiUGp/M9q"
|
||||
)
|
||||
.is_err());
|
||||
assert!(
|
||||
check_request(
|
||||
&headers,
|
||||
"jq",
|
||||
"$2y$05$32zazJ1yzhlDHnt26L3MFOgY0HVqPmDUvG0KUx6cjf9RDiUGp/M9q"
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
|
||||
assert!(is_401(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
@@ -277,12 +289,14 @@ mod tests {
|
||||
fn test_invalid_auth_encoding2() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("Authorization", "abcd".parse().unwrap());
|
||||
assert!(check_request(
|
||||
&headers,
|
||||
"jq",
|
||||
"$2y$05$32zazJ1yzhlDHnt26L3MFOgY0HVqPmDUvG0KUx6cjf9RDiUGp/M9q"
|
||||
)
|
||||
.is_err());
|
||||
assert!(
|
||||
check_request(
|
||||
&headers,
|
||||
"jq",
|
||||
"$2y$05$32zazJ1yzhlDHnt26L3MFOgY0HVqPmDUvG0KUx6cjf9RDiUGp/M9q"
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
|
||||
assert!(is_401(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||
|
||||
use static_web_server::{
|
||||
settings::{cli::General, Commands},
|
||||
Result, Settings,
|
||||
settings::{Commands, cli::General},
|
||||
};
|
||||
|
||||
fn main() -> Result {
|
||||
|
||||
@@ -21,11 +21,11 @@ use bytes::Bytes;
|
||||
use futures_util::Stream;
|
||||
use headers::{ContentType, HeaderMap, HeaderMapExt, HeaderValue};
|
||||
use hyper::{
|
||||
header::{CONTENT_ENCODING, CONTENT_LENGTH},
|
||||
Body, Method, Request, Response, StatusCode,
|
||||
header::{CONTENT_ENCODING, CONTENT_LENGTH},
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use mime_guess::{mime, Mime};
|
||||
use mime_guess::{Mime, mime};
|
||||
use pin_project::pin_project;
|
||||
use std::collections::HashSet;
|
||||
use std::pin::Pin;
|
||||
@@ -33,12 +33,11 @@ use std::task::{Context, Poll};
|
||||
use tokio_util::io::{ReaderStream, StreamReader};
|
||||
|
||||
use crate::{
|
||||
error_page,
|
||||
Error, Result, error_page,
|
||||
handler::RequestHandlerOpts,
|
||||
headers_ext::{AcceptEncoding, ContentCoding},
|
||||
http_ext::MethodExt,
|
||||
settings::CompressionLevel,
|
||||
Error, Result,
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
@@ -184,7 +183,9 @@ pub fn auto(
|
||||
return Ok(zstd(head, body.into(), level));
|
||||
}
|
||||
|
||||
tracing::trace!("no compression feature matched the preferred encoding, probably not enabled or unsupported");
|
||||
tracing::trace!(
|
||||
"no compression feature matched the preferred encoding, probably not enabled or unsupported"
|
||||
);
|
||||
}
|
||||
|
||||
Ok(resp)
|
||||
|
||||
@@ -12,11 +12,11 @@ use std::ffi::OsStr;
|
||||
use std::fs::Metadata;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::Error;
|
||||
use crate::compression;
|
||||
use crate::fs::meta::try_metadata;
|
||||
use crate::handler::RequestHandlerOpts;
|
||||
use crate::headers_ext::ContentCoding;
|
||||
use crate::Error;
|
||||
|
||||
/// It defines the pre-compressed file variant metadata of a particular file path.
|
||||
pub struct CompressedFileVariant {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
use hyper::{Body, Request, Response};
|
||||
|
||||
use crate::{handler::RequestHandlerOpts, Error};
|
||||
use crate::{Error, handler::RequestHandlerOpts};
|
||||
|
||||
// Cache-Control `max-age` variants
|
||||
const MAX_AGE_ONE_HOUR: u64 = 60 * 60;
|
||||
@@ -84,8 +84,8 @@ mod tests {
|
||||
use hyper::{Body, Response, StatusCode};
|
||||
|
||||
use super::{
|
||||
append_headers, get_file_extension, CACHE_EXT_ONE_HOUR, CACHE_EXT_ONE_YEAR,
|
||||
MAX_AGE_ONE_DAY, MAX_AGE_ONE_HOUR, MAX_AGE_ONE_YEAR,
|
||||
CACHE_EXT_ONE_HOUR, CACHE_EXT_ONE_YEAR, MAX_AGE_ONE_DAY, MAX_AGE_ONE_HOUR,
|
||||
MAX_AGE_ONE_YEAR, append_headers, get_file_extension,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
||||
19
src/cors.rs
19
src/cors.rs
@@ -16,7 +16,7 @@ use http::header;
|
||||
use hyper::{Body, Request, Response, StatusCode};
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::{error_page, handler::RequestHandlerOpts, Error};
|
||||
use crate::{Error, error_page, handler::RequestHandlerOpts};
|
||||
|
||||
/// It defines CORS instance.
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -72,11 +72,11 @@ pub fn new(
|
||||
|
||||
if cors_res.is_some() {
|
||||
tracing::info!(
|
||||
"cors enabled=true, allow_methods=[GET,HEAD,OPTIONS], allow_origins={}, allow_headers=[{}], expose_headers=[{}]",
|
||||
origins_str,
|
||||
allow_headers_str,
|
||||
expose_headers_str,
|
||||
);
|
||||
"cors enabled=true, allow_methods=[GET,HEAD,OPTIONS], allow_origins={}, allow_headers=[{}], expose_headers=[{}]",
|
||||
origins_str,
|
||||
allow_headers_str,
|
||||
expose_headers_str,
|
||||
);
|
||||
}
|
||||
cors_res
|
||||
};
|
||||
@@ -292,7 +292,8 @@ impl Configured {
|
||||
let h = header.trim();
|
||||
if !self.is_header_allowed(h) {
|
||||
tracing::error!(
|
||||
"cors: header `{}` is not allowed because is missing in `cors_allow_headers` server option", h
|
||||
"cors: header `{}` is not allowed because is missing in `cors_allow_headers` server option",
|
||||
h
|
||||
);
|
||||
return Err(Forbidden::Header);
|
||||
}
|
||||
@@ -442,8 +443,8 @@ pub(crate) fn post_process<T>(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{post_process, pre_process, Configured, Cors};
|
||||
use crate::{handler::RequestHandlerOpts, Error};
|
||||
use super::{Configured, Cors, post_process, pre_process};
|
||||
use crate::{Error, handler::RequestHandlerOpts};
|
||||
use hyper::{Body, Request, Response, StatusCode};
|
||||
|
||||
fn make_request(method: &str, origin: &str) -> Request<Body> {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
use hyper::{Body, Request, Response};
|
||||
use std::{ffi::OsStr, path::PathBuf};
|
||||
|
||||
use crate::{handler::RequestHandlerOpts, settings::Headers, Error};
|
||||
use crate::{Error, handler::RequestHandlerOpts, settings::Headers};
|
||||
|
||||
/// Appends custom HTTP headers to a response if necessary
|
||||
pub(crate) fn post_process<T>(
|
||||
|
||||
@@ -11,16 +11,16 @@ use clap::ValueEnum;
|
||||
use headers::{ContentLength, ContentType, HeaderMapExt};
|
||||
use hyper::{Body, Method, Response, StatusCode};
|
||||
use mime_guess::mime;
|
||||
use percent_encoding::{percent_decode_str, percent_encode, AsciiSet, NON_ALPHANUMERIC};
|
||||
use percent_encoding::{AsciiSet, NON_ALPHANUMERIC, percent_decode_str, percent_encode};
|
||||
use serde::{Serialize, Serializer};
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg(feature = "directory-listing-download")]
|
||||
use crate::directory_listing_download::{DirDownloadFmt, DOWNLOAD_PARAM_KEY};
|
||||
use crate::directory_listing_download::{DOWNLOAD_PARAM_KEY, DirDownloadFmt};
|
||||
|
||||
use crate::{handler::RequestHandlerOpts, http_ext::MethodExt, Context, Result};
|
||||
use crate::{Context, Result, handler::RequestHandlerOpts, http_ext::MethodExt};
|
||||
|
||||
/// Non-alphanumeric characters to be percent-encoded
|
||||
/// excluding the "unreserved characters" because allowed in a URI.
|
||||
@@ -402,7 +402,7 @@ fn html_auto_index<'a>(
|
||||
order_code: u8,
|
||||
#[cfg(feature = "directory-listing-download")] download: &'a [DirDownloadFmt],
|
||||
) -> String {
|
||||
use maud::{html, DOCTYPE};
|
||||
use maud::{DOCTYPE, html};
|
||||
|
||||
let sort_attrs = sort_file_entries(entries, order_code);
|
||||
let current_path = percent_decode_str(base_path).decode_utf8_lossy();
|
||||
|
||||
@@ -12,7 +12,7 @@ use bytes::BytesMut;
|
||||
use clap::ValueEnum;
|
||||
use headers::{ContentType, HeaderMapExt};
|
||||
use http::{HeaderValue, Method, Response};
|
||||
use hyper::{body::Sender, Body};
|
||||
use hyper::{Body, body::Sender};
|
||||
use mime_guess::Mime;
|
||||
use std::fmt::Display;
|
||||
use std::path::Path;
|
||||
@@ -24,9 +24,9 @@ use tokio::io;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio_util::compat::TokioAsyncWriteCompatExt;
|
||||
|
||||
use crate::Result;
|
||||
use crate::handler::RequestHandlerOpts;
|
||||
use crate::http_ext::MethodExt;
|
||||
use crate::Result;
|
||||
|
||||
/// query parameter key to download directory as tar.gz
|
||||
pub const DOWNLOAD_PARAM_KEY: &str = "download";
|
||||
|
||||
@@ -12,7 +12,7 @@ pub type Result<T = (), E = anyhow::Error> = anyhow::Result<T, E>;
|
||||
/// Just an `anyhow::Error` type alias.
|
||||
pub type Error = anyhow::Error;
|
||||
|
||||
pub use anyhow::Context;
|
||||
/// Just re-export some `anyhow` stuff.
|
||||
pub use anyhow::anyhow;
|
||||
pub use anyhow::bail;
|
||||
pub use anyhow::Context;
|
||||
|
||||
@@ -11,7 +11,7 @@ use hyper::{Body, Method, Response, StatusCode, Uri};
|
||||
use mime_guess::mime;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::{helpers, http_ext::MethodExt, Result};
|
||||
use crate::{Result, helpers, http_ext::MethodExt};
|
||||
|
||||
/// It returns a HTTP error response which also handles available `404` or `50x` HTML content.
|
||||
pub fn error_response(
|
||||
|
||||
@@ -11,7 +11,7 @@ use hyper::{Body, Request, Response, StatusCode};
|
||||
use mime_guess::mime;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::{handler::RequestHandlerOpts, helpers, http_ext::MethodExt, Error};
|
||||
use crate::{Error, handler::RequestHandlerOpts, helpers, http_ext::MethodExt};
|
||||
|
||||
/// Initializes fallback page processing
|
||||
pub(crate) fn init(file_path: &Path, handler_opts: &mut RequestHandlerOpts) {
|
||||
@@ -72,7 +72,7 @@ pub fn fallback_response(page_fallback: &[u8]) -> Response<Body> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::post_process;
|
||||
use crate::{error_page, handler::RequestHandlerOpts, Error};
|
||||
use crate::{Error, error_page, handler::RequestHandlerOpts};
|
||||
use hyper::{Body, Method, Request, Response, StatusCode, Uri};
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ use http::StatusCode;
|
||||
use std::fs::Metadata;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::headers_ext::ContentCoding;
|
||||
use crate::Result;
|
||||
use crate::headers_ext::ContentCoding;
|
||||
|
||||
/// It defines a composed file metadata structure containing the current file
|
||||
/// and its optional pre-compressed variant.
|
||||
|
||||
@@ -36,12 +36,12 @@ use crate::metrics;
|
||||
use crate::mem_cache::cache::MemCacheOpts;
|
||||
|
||||
use crate::{
|
||||
control_headers, cors, custom_headers, error_page, health,
|
||||
Error, Result, control_headers, cors, custom_headers, error_page, health,
|
||||
http_ext::MethodExt,
|
||||
log_addr, maintenance_mode, redirects, rewrites, security_headers,
|
||||
settings::Advanced,
|
||||
static_files::{self, HandleOpts},
|
||||
virtual_hosts, Error, Result,
|
||||
virtual_hosts,
|
||||
};
|
||||
|
||||
#[cfg(feature = "directory-listing")]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Original code sourced from https://github.com/hyperium/headers/pull/70
|
||||
|
||||
use headers::{Error, Header};
|
||||
use hyper::header::{HeaderName, HeaderValue, ACCEPT_ENCODING};
|
||||
use hyper::header::{ACCEPT_ENCODING, HeaderName, HeaderValue};
|
||||
|
||||
use super::{ContentCoding, QualityValue};
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
use headers::{ContentType, HeaderMapExt};
|
||||
use hyper::{Body, Method, Request, Response};
|
||||
|
||||
use crate::{handler::RequestHandlerOpts, Error};
|
||||
use crate::{Error, handler::RequestHandlerOpts};
|
||||
|
||||
/// Initializes the health endpoint.
|
||||
pub fn init(enabled: bool, handler_opts: &mut RequestHandlerOpts) {
|
||||
@@ -61,49 +61,57 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_health_disabled() {
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
health: false,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("GET", "/health"),
|
||||
)
|
||||
.is_none());
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
health: false,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("GET", "/health"),
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wrong_uri() {
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
health: true,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("GET", "/health2"),
|
||||
)
|
||||
.is_none());
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
health: true,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("GET", "/health2"),
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wrong_method() {
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
health: true,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("POST", "/health"),
|
||||
)
|
||||
.is_none());
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
health: true,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("POST", "/health"),
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_correct_request() {
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
health: true,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("GET", "/health"),
|
||||
)
|
||||
.is_some());
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
health: true,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("GET", "/health"),
|
||||
)
|
||||
.is_some()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
//!
|
||||
|
||||
use headers::{HeaderMapExt, Host};
|
||||
use hyper::{header::LOCATION, Body, Request, Response, StatusCode};
|
||||
use hyper::{Body, Request, Response, StatusCode, header::LOCATION};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::Result;
|
||||
|
||||
@@ -11,7 +11,7 @@ use hyper::{Body, Method, Request, Response, StatusCode};
|
||||
use mime_guess::mime;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::{handler::RequestHandlerOpts, helpers, http_ext::MethodExt, Error, Result};
|
||||
use crate::{Error, Result, handler::RequestHandlerOpts, helpers, http_ext::MethodExt};
|
||||
|
||||
const DEFAULT_BODY_CONTENT: &str = "The server is in maintenance mode.";
|
||||
|
||||
@@ -72,7 +72,9 @@ pub fn get_response(
|
||||
tracing::debug!(
|
||||
"maintenance mode file path not found or not a regular file, using a default message"
|
||||
);
|
||||
format!("<html><head><title>{status_code}</title></head><body><center><h1>{DEFAULT_BODY_CONTENT}</h1></center></body></html>")
|
||||
format!(
|
||||
"<html><head><title>{status_code}</title></head><body><center><h1>{DEFAULT_BODY_CONTENT}</h1></center></body></html>"
|
||||
)
|
||||
};
|
||||
|
||||
let mut body = Body::empty();
|
||||
@@ -95,7 +97,7 @@ pub fn get_response(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::pre_process;
|
||||
use crate::{handler::RequestHandlerOpts, Error};
|
||||
use crate::{Error, handler::RequestHandlerOpts};
|
||||
use hyper::{Body, Request, Response, StatusCode};
|
||||
|
||||
fn make_request() -> Request<Body> {
|
||||
@@ -116,14 +118,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_maintenance_disabled() {
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
maintenance_mode: false,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request()
|
||||
)
|
||||
.is_none());
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
maintenance_mode: false,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request()
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -23,11 +23,11 @@ use std::sync::{Arc, OnceLock};
|
||||
use std::time::Duration;
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
use crate::Result;
|
||||
use crate::conditional_headers::{ConditionalBody, ConditionalHeaders};
|
||||
use crate::fs::stream::FileStream;
|
||||
use crate::handler::RequestHandlerOpts;
|
||||
use crate::response::{bytes_range, BadRangeError};
|
||||
use crate::Result;
|
||||
use crate::response::{BadRangeError, bytes_range};
|
||||
|
||||
/// Global cache that stores all files in memory.
|
||||
/// It provides expiration policies like Time to live (TTL) and Time to idle (TTI) support.
|
||||
|
||||
@@ -5,8 +5,8 @@ use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use crate::mem_cache::cache::{MemFile, MemFileTempOpts, CACHE_STORE};
|
||||
use crate::Result;
|
||||
use crate::mem_cache::cache::{CACHE_STORE, MemFile, MemFileTempOpts};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct MemCacheFileStream<T> {
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||
use headers::{ContentType, HeaderMapExt};
|
||||
use hyper::{Body, Request, Response};
|
||||
use prometheus::{default_registry, Encoder, TextEncoder};
|
||||
use prometheus::{Encoder, TextEncoder, default_registry};
|
||||
|
||||
use crate::{handler::RequestHandlerOpts, http_ext::MethodExt, Error};
|
||||
use crate::{Error, handler::RequestHandlerOpts, http_ext::MethodExt};
|
||||
|
||||
/// Initializes the metrics endpoint.
|
||||
pub fn init(enabled: bool, handler_opts: &mut RequestHandlerOpts) {
|
||||
@@ -78,49 +78,57 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_metrics_disabled() {
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
experimental_metrics: false,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("GET", "/metrics")
|
||||
)
|
||||
.is_none());
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
experimental_metrics: false,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("GET", "/metrics")
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wrong_uri() {
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
experimental_metrics: true,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("GET", "/metrics2")
|
||||
)
|
||||
.is_none());
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
experimental_metrics: true,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("GET", "/metrics2")
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wrong_method() {
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
experimental_metrics: true,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("POST", "/metrics")
|
||||
)
|
||||
.is_none());
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
experimental_metrics: true,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("POST", "/metrics")
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_correct_request() {
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
experimental_metrics: true,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("GET", "/metrics")
|
||||
)
|
||||
.is_some());
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
experimental_metrics: true,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("GET", "/metrics")
|
||||
)
|
||||
.is_some()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use headers::HeaderValue;
|
||||
use hyper::{Body, Request, Response, StatusCode};
|
||||
use regex::Regex;
|
||||
|
||||
use crate::{error_page, handler::RequestHandlerOpts, settings::Redirects, Error};
|
||||
use crate::{Error, error_page, handler::RequestHandlerOpts, settings::Redirects};
|
||||
|
||||
/// Applies redirect rules to a request if necessary.
|
||||
pub(crate) fn pre_process<T>(
|
||||
@@ -142,9 +142,9 @@ pub fn get_redirection<'a>(
|
||||
mod tests {
|
||||
use super::pre_process;
|
||||
use crate::{
|
||||
Error,
|
||||
handler::RequestHandlerOpts,
|
||||
settings::{Advanced, Redirects},
|
||||
Error,
|
||||
};
|
||||
use hyper::{Body, Request, Response, StatusCode};
|
||||
use regex::Regex;
|
||||
@@ -191,53 +191,61 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_no_redirects() {
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: None,
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("", "/")
|
||||
)
|
||||
.is_none());
|
||||
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: Some(Advanced {
|
||||
redirects: None,
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: None,
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("", "/")
|
||||
)
|
||||
.is_none());
|
||||
},
|
||||
&make_request("", "/")
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: Some(Advanced {
|
||||
redirects: None,
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("", "/")
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_match() {
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: Some(Advanced {
|
||||
redirects: Some(get_redirects()),
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: Some(Advanced {
|
||||
redirects: Some(get_redirects()),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("example.com", "/source2/whatever")
|
||||
)
|
||||
.is_none());
|
||||
},
|
||||
&make_request("example.com", "/source2/whatever")
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: Some(Advanced {
|
||||
redirects: Some(get_redirects()),
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: Some(Advanced {
|
||||
redirects: Some(get_redirects()),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
&make_request("", "/source2")
|
||||
)
|
||||
.is_none());
|
||||
},
|
||||
&make_request("", "/source2")
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -16,7 +16,7 @@ use std::ops::Bound;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::conditional_headers::{ConditionalBody, ConditionalHeaders};
|
||||
use crate::fs::stream::{optimal_buf_size, FileStream};
|
||||
use crate::fs::stream::{FileStream, optimal_buf_size};
|
||||
|
||||
#[cfg(feature = "experimental")]
|
||||
use {
|
||||
@@ -160,7 +160,7 @@ pub(crate) fn bytes_range(range: Option<Range>, max_len: u64) -> Result<(u64, u6
|
||||
return Ok((0, max_len));
|
||||
};
|
||||
|
||||
let resp = range
|
||||
range
|
||||
.iter()
|
||||
.map(|(start, end)| {
|
||||
tracing::trace!("range request received, {:?}-{:?}-{}", start, end, max_len);
|
||||
@@ -211,7 +211,5 @@ pub(crate) fn bytes_range(range: Option<Range>, max_len: u64) -> Result<(u64, u6
|
||||
})
|
||||
.next()
|
||||
// NOTE: default to `BadRangeError` in case of wrong `Range` bytes format
|
||||
.unwrap_or(Err(BadRangeError));
|
||||
|
||||
resp
|
||||
.unwrap_or(Err(BadRangeError))
|
||||
}
|
||||
|
||||
122
src/rewrites.rs
122
src/rewrites.rs
@@ -7,13 +7,13 @@
|
||||
//!
|
||||
|
||||
use headers::HeaderValue;
|
||||
use hyper::{header::HOST, Body, Request, Response, StatusCode, Uri};
|
||||
use hyper::{Body, Request, Response, StatusCode, Uri, header::HOST};
|
||||
|
||||
use crate::{
|
||||
Error,
|
||||
handler::RequestHandlerOpts,
|
||||
redirects::{handle_error, replace_placeholders},
|
||||
settings::{file::RedirectsKind, Rewrites},
|
||||
Error,
|
||||
settings::{Rewrites, file::RedirectsKind},
|
||||
};
|
||||
|
||||
/// Applies rewrite rules to a request if necessary.
|
||||
@@ -39,7 +39,7 @@ pub(crate) fn pre_process<T>(
|
||||
Error::new(err).context("invalid header value from current uri"),
|
||||
opts,
|
||||
req,
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
let mut resp = Response::new(Body::empty());
|
||||
@@ -58,7 +58,7 @@ pub(crate) fn pre_process<T>(
|
||||
err.context("invalid rewrite target from current uri"),
|
||||
opts,
|
||||
req,
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -121,11 +121,11 @@ pub fn rewrite_uri_path<'a>(
|
||||
mod tests {
|
||||
use super::pre_process;
|
||||
use crate::{
|
||||
handler::RequestHandlerOpts,
|
||||
settings::{file::RedirectsKind, Advanced, Rewrites},
|
||||
Error,
|
||||
handler::RequestHandlerOpts,
|
||||
settings::{Advanced, Rewrites, file::RedirectsKind},
|
||||
};
|
||||
use hyper::{header::HOST, Body, Request, Response, StatusCode};
|
||||
use hyper::{Body, Request, Response, StatusCode, header::HOST};
|
||||
use regex::Regex;
|
||||
|
||||
fn make_request(host: &str, uri: &str) -> Request<Body> {
|
||||
@@ -173,62 +173,70 @@ mod tests {
|
||||
#[test]
|
||||
fn test_no_rewrites() {
|
||||
let mut req = make_request("", "/");
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: None,
|
||||
..Default::default()
|
||||
},
|
||||
&mut req
|
||||
)
|
||||
.is_none());
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: None,
|
||||
..Default::default()
|
||||
},
|
||||
&mut req
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
assert_eq!(req.uri(), "/");
|
||||
|
||||
let mut req = make_request("", "/");
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: Some(Advanced {
|
||||
rewrites: None,
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: Some(Advanced {
|
||||
rewrites: None,
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
&mut req
|
||||
)
|
||||
.is_none());
|
||||
},
|
||||
&mut req
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
assert_eq!(req.uri(), "/");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_match() {
|
||||
let mut req = make_request("example.com", "/source2/whatever");
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: Some(Advanced {
|
||||
rewrites: Some(get_rewrites()),
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: Some(Advanced {
|
||||
rewrites: Some(get_rewrites()),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
&mut req
|
||||
)
|
||||
.is_none());
|
||||
},
|
||||
&mut req
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
assert_eq!(req.uri(), "/source2/whatever");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_match() {
|
||||
let mut req = make_request("", "/source1?query");
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: Some(Advanced {
|
||||
rewrites: Some(get_rewrites()),
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: Some(Advanced {
|
||||
rewrites: Some(get_rewrites()),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
&mut req
|
||||
)
|
||||
.is_none());
|
||||
},
|
||||
&mut req
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
assert_eq!(req.uri(), "/destination1?query");
|
||||
|
||||
let mut req = make_request("", "/source2");
|
||||
@@ -265,17 +273,19 @@ mod tests {
|
||||
);
|
||||
|
||||
let mut req = make_request("example.com", "/source4/whatever?query");
|
||||
assert!(pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: Some(Advanced {
|
||||
rewrites: Some(get_rewrites()),
|
||||
assert!(
|
||||
pre_process(
|
||||
&RequestHandlerOpts {
|
||||
advanced_opts: Some(Advanced {
|
||||
rewrites: Some(get_rewrites()),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
&mut req
|
||||
)
|
||||
.is_none());
|
||||
},
|
||||
&mut req
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
assert_eq!(
|
||||
req.uri(),
|
||||
"http://example.net:1234/destination4/source4?whatever"
|
||||
|
||||
@@ -11,7 +11,7 @@ use http::header::{
|
||||
};
|
||||
use hyper::{Body, Request, Response};
|
||||
|
||||
use crate::{handler::RequestHandlerOpts, Error};
|
||||
use crate::{Error, handler::RequestHandlerOpts};
|
||||
|
||||
pub(crate) fn init(enabled: bool, handler_opts: &mut RequestHandlerOpts) {
|
||||
handler_opts.security_headers = enabled;
|
||||
|
||||
@@ -10,7 +10,7 @@ use hyper::server::Server as HyperServer;
|
||||
use listenfd::ListenFd;
|
||||
use std::net::{IpAddr, SocketAddr, TcpListener};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{watch::Receiver, Mutex};
|
||||
use tokio::sync::{Mutex, watch::Receiver};
|
||||
|
||||
use crate::handler::{RequestHandler, RequestHandlerOpts};
|
||||
|
||||
@@ -51,10 +51,10 @@ use crate::basic_auth;
|
||||
#[cfg(feature = "experimental")]
|
||||
use crate::mem_cache;
|
||||
|
||||
use crate::{Context, Result, service::RouterService};
|
||||
use crate::{
|
||||
control_headers, cors, health, helpers, log_addr, maintenance_mode, security_headers, Settings,
|
||||
Settings, control_headers, cors, health, helpers, log_addr, maintenance_mode, security_headers,
|
||||
};
|
||||
use crate::{service::RouterService, Context, Result};
|
||||
|
||||
/// Define a multi-threaded HTTP or HTTP/2 web server.
|
||||
pub struct Server {
|
||||
@@ -412,9 +412,9 @@ impl Server {
|
||||
.with_context(|| "failed to set TCP non-blocking mode")?;
|
||||
let listener = tokio::net::TcpListener::from_std(tcp_listener)
|
||||
.with_context(|| "failed to create tokio::net::TcpListener")?;
|
||||
let mut incoming = AddrIncoming::from_listener(listener).with_context(|| {
|
||||
"failed to create an AddrIncoming from the current tokio::net::TcpListener"
|
||||
})?;
|
||||
let mut incoming = AddrIncoming::from_listener(listener).with_context(
|
||||
|| "failed to create an AddrIncoming from the current tokio::net::TcpListener",
|
||||
)?;
|
||||
incoming.set_nodelay(true);
|
||||
|
||||
let http2_tls_cert = match general.http2_tls_cert {
|
||||
@@ -430,9 +430,9 @@ impl Server {
|
||||
.cert_path(&http2_tls_cert)
|
||||
.key_path(&http2_tls_key)
|
||||
.build()
|
||||
.with_context(|| {
|
||||
"failed to initialize TLS probably because invalid cert or key file"
|
||||
})?;
|
||||
.with_context(
|
||||
|| "failed to initialize TLS probably because invalid cert or key file",
|
||||
)?;
|
||||
|
||||
#[cfg(unix)]
|
||||
let signals = signals::create_signals()
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
//! The module provides a custom [Hyper service](hyper::service::Service).
|
||||
//!
|
||||
|
||||
use hyper::{service::Service, Body, Request, Response};
|
||||
use hyper::{Body, Request, Response, service::Service};
|
||||
use std::convert::Infallible;
|
||||
use std::future::{ready, Future, Ready};
|
||||
use std::future::{Future, Ready, ready};
|
||||
use std::net::SocketAddr;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use crate::{handler::RequestHandler, transport::Transport, Error};
|
||||
use crate::{Error, handler::RequestHandler, transport::Transport};
|
||||
|
||||
/// It defines the router service which is the main entry point for Hyper Server.
|
||||
pub struct RouterService {
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::directory_listing::DirListFmt;
|
||||
#[cfg(feature = "directory-listing-download")]
|
||||
use crate::directory_listing_download::DirDownloadFmt;
|
||||
|
||||
use crate::{helpers, Context, Result};
|
||||
use crate::{Context, Result, helpers};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
|
||||
@@ -13,7 +13,7 @@ use hyper::StatusCode;
|
||||
use regex::Regex;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::{helpers, logger, Context, Result};
|
||||
use crate::{Context, Result, helpers, logger};
|
||||
|
||||
pub mod cli;
|
||||
#[doc(hidden)]
|
||||
@@ -105,20 +105,19 @@ impl Settings {
|
||||
/// It also takes care to initialize the logging system with its level
|
||||
/// once the `general` settings are determined.
|
||||
pub fn get(log_init: bool) -> Result<Settings> {
|
||||
Self::read(log_init, true)
|
||||
Self::parse_from(log_init, None)
|
||||
}
|
||||
|
||||
/// Reads CLI/Env and config file options returning the server settings
|
||||
/// without parsing arguments useful for testing.
|
||||
pub fn get_unparsed(log_init: bool) -> Result<Settings> {
|
||||
Self::read(log_init, false)
|
||||
pub fn get_unparsed(log_init: bool, args: &[&str]) -> Result<Settings> {
|
||||
Self::parse_from(log_init, Some(args))
|
||||
}
|
||||
|
||||
fn read(log_init: bool, parse_args: bool) -> Result<Settings> {
|
||||
let opts = if parse_args {
|
||||
General::parse()
|
||||
} else {
|
||||
General::parse_from([""])
|
||||
fn parse_from(log_init: bool, args: Option<&[&str]>) -> Result<Settings> {
|
||||
let opts = match args {
|
||||
Some(v) => General::parse_from(v),
|
||||
None => General::parse(),
|
||||
};
|
||||
|
||||
// Define the general CLI/file options
|
||||
@@ -226,7 +225,9 @@ impl Settings {
|
||||
|
||||
let to_use_config_file = match Path::new("./config.toml").is_file() {
|
||||
true => {
|
||||
eprintln!("Deprecated: 'config.toml' found, rename it to 'sws.toml' to prepare for future releases");
|
||||
eprintln!(
|
||||
"Deprecated: 'config.toml' found, rename it to 'sws.toml' to prepare for future releases"
|
||||
);
|
||||
PathBuf::from("./config.toml")
|
||||
}
|
||||
false => opts.config_file.clone(),
|
||||
@@ -717,9 +718,9 @@ fn read_file_settings(config_file: &Path) -> Result<Option<(FileSettings, PathBu
|
||||
.canonicalize()
|
||||
.with_context(|| "unable to resolve toml config file path")?;
|
||||
|
||||
let settings = FileSettings::read(&file_path_resolved).with_context(|| {
|
||||
"unable to read toml config file because has invalid format or unsupported options"
|
||||
})?;
|
||||
let settings = FileSettings::read(&file_path_resolved).with_context(
|
||||
|| "unable to read toml config file because has invalid format or unsupported options",
|
||||
)?;
|
||||
|
||||
return Ok(Some((settings, file_path_resolved)));
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
//!
|
||||
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{watch::Receiver, Mutex};
|
||||
use tokio::time::{sleep, Duration};
|
||||
use tokio::sync::{Mutex, watch::Receiver};
|
||||
use tokio::time::{Duration, sleep};
|
||||
|
||||
#[cfg(unix)]
|
||||
use {
|
||||
|
||||
@@ -10,17 +10,17 @@
|
||||
// https://github.com/seanmonstar/warp/blob/master/src/filters/fs.rs
|
||||
|
||||
use headers::{AcceptRanges, HeaderMap, HeaderMapExt, HeaderValue};
|
||||
use hyper::{header::CONTENT_ENCODING, header::CONTENT_LENGTH, Body, Method, Response, StatusCode};
|
||||
use hyper::{Body, Method, Response, StatusCode, header::CONTENT_ENCODING, header::CONTENT_LENGTH};
|
||||
use std::fs::{File, Metadata};
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::conditional_headers::ConditionalHeaders;
|
||||
use crate::fs::meta::{try_metadata, try_metadata_with_html_suffix, FileMetadata};
|
||||
use crate::fs::path::{sanitize_path, PathExt};
|
||||
use crate::http_ext::{MethodExt, HTTP_SUPPORTED_METHODS};
|
||||
use crate::response::response_body;
|
||||
use crate::Result;
|
||||
use crate::conditional_headers::ConditionalHeaders;
|
||||
use crate::fs::meta::{FileMetadata, try_metadata, try_metadata_with_html_suffix};
|
||||
use crate::fs::path::{PathExt, sanitize_path};
|
||||
use crate::http_ext::{HTTP_SUPPORTED_METHODS, MethodExt};
|
||||
use crate::response::response_body;
|
||||
|
||||
#[cfg(feature = "experimental")]
|
||||
use crate::mem_cache::{cache, cache::MemCacheOpts};
|
||||
@@ -42,7 +42,7 @@ use crate::{
|
||||
|
||||
#[cfg(feature = "directory-listing-download")]
|
||||
use crate::directory_listing_download::{
|
||||
archive_reply, DirDownloadFmt, DirDownloadOpts, DOWNLOAD_PARAM_KEY,
|
||||
DOWNLOAD_PARAM_KEY, DirDownloadFmt, DirDownloadOpts, archive_reply,
|
||||
};
|
||||
|
||||
const DEFAULT_INDEX_FILES: &[&str; 1] = &["index.html"];
|
||||
@@ -441,7 +441,7 @@ fn get_composed_file_metadata<'a>(
|
||||
metadata: new_meta,
|
||||
is_dir: false,
|
||||
precompressed_variant: None,
|
||||
})
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
// Last pre-compressed variant check or the suffixed file not found
|
||||
|
||||
@@ -12,10 +12,10 @@ pub mod fixtures {
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
handler::{RequestHandler, RequestHandlerOpts},
|
||||
settings::cli::General,
|
||||
settings::Advanced,
|
||||
Settings,
|
||||
handler::{RequestHandler, RequestHandlerOpts},
|
||||
settings::Advanced,
|
||||
settings::cli::General,
|
||||
};
|
||||
|
||||
/// Testing Remote address
|
||||
@@ -25,8 +25,11 @@ pub mod fixtures {
|
||||
pub fn fixture_settings(fixture_toml: &str) -> Settings {
|
||||
// Replace default config file and load the fixture TOML settings
|
||||
let f = PathBuf::from("tests/fixtures").join(fixture_toml);
|
||||
std::env::set_var("SERVER_CONFIG_FILE", f);
|
||||
Settings::get_unparsed(false).unwrap()
|
||||
Settings::get_unparsed(
|
||||
false,
|
||||
&["static-web-server", "--config-file", f.to_str().unwrap()],
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Create a `RequestHandlerOpts` from the given options (fixture).
|
||||
|
||||
@@ -20,7 +20,7 @@ use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use tokio_rustls::rustls::{pki_types::PrivateKeyDer, Error as TlsError, ServerConfig};
|
||||
use tokio_rustls::rustls::{Error as TlsError, ServerConfig, pki_types::PrivateKeyDer};
|
||||
|
||||
use crate::transport::Transport;
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
//! Module that allows to determine a virtual hostname.
|
||||
//!
|
||||
|
||||
use hyper::header::HOST;
|
||||
use hyper::Request;
|
||||
use hyper::header::HOST;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::settings::VirtualHosts;
|
||||
|
||||
@@ -22,7 +22,7 @@ use windows_service::{
|
||||
service_manager::{ServiceManager, ServiceManagerAccess},
|
||||
};
|
||||
|
||||
use crate::{helpers, Context, Result, Server, Settings};
|
||||
use crate::{Context, Result, Server, Settings, helpers};
|
||||
|
||||
const SERVICE_NAME: &str = "static-web-server";
|
||||
const SERVICE_TYPE: ServiceType = ServiceType::OWN_PROCESS;
|
||||
|
||||
@@ -19,7 +19,7 @@ pub mod tests {
|
||||
use static_web_server::{
|
||||
settings::cli::General,
|
||||
testing::fixtures::{
|
||||
fixture_req_handler, fixture_req_handler_opts, fixture_settings, REMOTE_ADDR,
|
||||
REMOTE_ADDR, fixture_req_handler, fixture_req_handler_opts, fixture_settings,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ mod tests {
|
||||
use static_web_server::{
|
||||
settings::cli::General,
|
||||
testing::fixtures::{
|
||||
fixture_req_handler, fixture_req_handler_opts, fixture_settings, REMOTE_ADDR,
|
||||
REMOTE_ADDR, fixture_req_handler, fixture_req_handler_opts, fixture_settings,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ mod tests {
|
||||
use static_web_server::cors;
|
||||
use static_web_server::http_ext::MethodExt;
|
||||
use static_web_server::testing::fixtures::{
|
||||
fixture_req_handler, fixture_req_handler_opts, fixture_settings, REMOTE_ADDR,
|
||||
REMOTE_ADDR, fixture_req_handler, fixture_req_handler_opts, fixture_settings,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -25,7 +25,7 @@ mod tests {
|
||||
static_files::{self, HandleOpts},
|
||||
};
|
||||
|
||||
use static_web_server::directory_listing_download::{DirDownloadFmt, DOWNLOAD_PARAM_KEY};
|
||||
use static_web_server::directory_listing_download::{DOWNLOAD_PARAM_KEY, DirDownloadFmt};
|
||||
|
||||
const METHODS: [Method; 8] = [
|
||||
Method::CONNECT,
|
||||
@@ -148,10 +148,12 @@ mod tests {
|
||||
let mut res = result.resp;
|
||||
assert_eq!(res.status(), 200);
|
||||
assert_eq!(res.headers()["content-type"], "application/gzip");
|
||||
assert!(res.headers()["content-disposition"]
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.starts_with("attachment"));
|
||||
assert!(
|
||||
res.headers()["content-disposition"]
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.starts_with("attachment")
|
||||
);
|
||||
|
||||
let body = hyper::body::to_bytes(res.body_mut())
|
||||
.await
|
||||
@@ -218,10 +220,12 @@ mod tests {
|
||||
let mut res = result.resp;
|
||||
assert_eq!(res.status(), 200);
|
||||
assert_eq!(res.headers()["content-type"], "application/gzip");
|
||||
assert!(res.headers()["content-disposition"]
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.starts_with("attachment"));
|
||||
assert!(
|
||||
res.headers()["content-disposition"]
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.starts_with("attachment")
|
||||
);
|
||||
|
||||
let body = hyper::body::to_bytes(res.body_mut())
|
||||
.await
|
||||
@@ -230,10 +234,12 @@ mod tests {
|
||||
if method == Method::GET {
|
||||
let mut prefix = base_path.clone();
|
||||
prefix.pop();
|
||||
assert!(!inspect_tarball_content(prefix, &body, false)
|
||||
.await
|
||||
.iter()
|
||||
.any(|path| path.file_name().unwrap() == ".dotfile"));
|
||||
assert!(
|
||||
!inspect_tarball_content(prefix, &body, false)
|
||||
.await
|
||||
.iter()
|
||||
.any(|path| path.file_name().unwrap() == ".dotfile")
|
||||
);
|
||||
} else {
|
||||
assert!(body.is_empty());
|
||||
}
|
||||
@@ -275,10 +281,12 @@ mod tests {
|
||||
let mut res = result.resp;
|
||||
assert_eq!(res.status(), 200);
|
||||
assert_eq!(res.headers()["content-type"], "application/gzip");
|
||||
assert!(res.headers()["content-disposition"]
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.starts_with("attachment"));
|
||||
assert!(
|
||||
res.headers()["content-disposition"]
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.starts_with("attachment")
|
||||
);
|
||||
|
||||
let body = hyper::body::to_bytes(res.body_mut())
|
||||
.await
|
||||
@@ -344,10 +352,11 @@ mod tests {
|
||||
let res = result.resp;
|
||||
assert_eq!(res.status(), 200);
|
||||
assert_eq!(res.headers()["content-type"], "text/html; charset=utf-8");
|
||||
assert!(!res
|
||||
.headers()
|
||||
.iter()
|
||||
.any(|(k, _v)| *k == "content-disposition"));
|
||||
assert!(
|
||||
!res.headers()
|
||||
.iter()
|
||||
.any(|(k, _v)| *k == "content-disposition")
|
||||
);
|
||||
}
|
||||
Err(status) => {
|
||||
assert!(method != Method::GET && method != Method::HEAD);
|
||||
|
||||
@@ -9,7 +9,7 @@ pub mod tests {
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use static_web_server::testing::fixtures::{
|
||||
fixture_req_handler, fixture_req_handler_opts, fixture_settings, REMOTE_ADDR,
|
||||
REMOTE_ADDR, fixture_req_handler, fixture_req_handler_opts, fixture_settings,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -11,7 +11,7 @@ pub mod tests {
|
||||
|
||||
use static_web_server::http_ext::MethodExt;
|
||||
use static_web_server::testing::fixtures::{
|
||||
fixture_req_handler, fixture_req_handler_opts, fixture_settings, REMOTE_ADDR,
|
||||
REMOTE_ADDR, fixture_req_handler, fixture_req_handler_opts, fixture_settings,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -9,7 +9,7 @@ pub mod tests {
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use static_web_server::testing::fixtures::{
|
||||
fixture_req_handler, fixture_req_handler_opts, fixture_settings, REMOTE_ADDR,
|
||||
REMOTE_ADDR, fixture_req_handler, fixture_req_handler_opts, fixture_settings,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -9,7 +9,7 @@ pub mod tests {
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use static_web_server::testing::fixtures::{
|
||||
fixture_req_handler, fixture_req_handler_opts, fixture_settings, REMOTE_ADDR,
|
||||
REMOTE_ADDR, fixture_req_handler, fixture_req_handler_opts, fixture_settings,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
Reference in New Issue
Block a user