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:
Jose Quintana
2025-10-26 02:04:21 +02:00
committed by GitHub
parent a7e8fa3272
commit 57025e3321
50 changed files with 721 additions and 692 deletions

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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.

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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]

View File

@@ -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> {

View File

@@ -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>(

View File

@@ -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();

View File

@@ -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";

View File

@@ -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;

View File

@@ -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(

View File

@@ -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;

View File

@@ -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.

View File

@@ -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")]

View File

@@ -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};

View File

@@ -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()
);
}
}

View File

@@ -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;

View File

@@ -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]

View File

@@ -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.

View File

@@ -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> {

View File

@@ -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()
);
}
}

View File

@@ -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]

View File

@@ -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))
}

View File

@@ -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"

View File

@@ -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;

View File

@@ -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()

View File

@@ -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 {

View File

@@ -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")]

View File

@@ -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)));
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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).

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,
},
};

View File

@@ -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,
},
};

View File

@@ -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]

View File

@@ -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);

View File

@@ -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]

View File

@@ -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]

View File

@@ -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]

View File

@@ -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]