diff --git a/README.md b/README.md index 3408ea6..0f4f5bb 100644 --- a/README.md +++ b/README.md @@ -1 +1,35 @@ -# Srv Dev Server \ No newline at end of file +# Srv Dev Server + +This is a simple HTTP Server for use in a development environment, inspired by [simple-http-server](https://github.com/TheWaWaR/simple-http-server) and [caddy2](https://github.com/caddyserver/caddy), and it is also a practice project for me to learn rust. + +### Screenshot +![screenshot](screenshot.png) + +### Built With + +- ~~[rocket](https://github.com/SergioBenitez/Rocket)~~ Framework used in previous versions +- [clap](https://github.com/clap-rs/clap) Provide command line parameter analysis +- [tera](https://github.com/Keats/tera) Provide template support +- [actix-web](https://github.com/actix/actix-web) Main frame +- [actix-files](https://github.com/actix/actix-web/tree/master/actix-files) Provide static resources +- [actix-web-httpauth](https://github.com/actix/actix-extras/tree/master/actix-web-httpauth) Provide authentication +- [rustls](https://github.com/rustls/rustls) Provide TLS and HTTP/2 support +- [env_logger](https://github.com/env-logger-rs/env_logger) Provide log output + + +## Features + +- Automatic generation of directory listings (default enabled) +- Relative path/absolute path/support +- Brotli/Gzip/Deflate streaming compression support (default disabled, disables Content-length and segmented downloads when enabled) +- Control whether dotfiles are displayed and can be accessed (default disabled) +- HTTP cache support, 304 support, Last-Modified/ETag support, of course you can also turn off cache +- Clearly colored organized log +- Disable access logging or disable all logging support +- Automatically open default browser (default disabled) +- Single-Page Application mode (always serve /index.html when the file is not found) +- Custom listening address (default 0.0.0.0) Custom listening port number (default 8000) +- HTTP Basic Authentication Support +- TLS/SSL support, HTTP/2 support +- One click to enable CORS, custom CORS header support +- cargo doc support \ No newline at end of file diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..92c783d Binary files /dev/null and b/screenshot.png differ diff --git a/src/main.rs b/src/main.rs index 3a32131..c29781d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - + #[macro_use] extern crate clap; #[macro_use] @@ -599,83 +599,81 @@ async fn main() -> std::io::Result<()> { addr }; - env_logger::Builder::from_env( - env_logger::Env::default().default_filter_or("info"), - ) - .format(|buf, record| { - let data = record.args().to_string(); - let mut style = buf.style(); - let blue = style.set_color(Color::Rgb(52, 152, 219)); - let mut style = buf.style(); - let red = style.set_color(Color::Rgb(231, 76, 60)); - let mut style = buf.style(); - let green = style.set_color(Color::Rgb(76, 175, 80)); - if record.target() == "actix_web::middleware::logger" { - let data: Vec<&str> = data.splitn(5, "^").collect(); - let time = blue.value( - chrono::NaiveDateTime::from_str(data[0]) - .unwrap() - .format("%Y/%m/%d %H:%M:%S") - .to_string(), - ); - let ipaddr = blue.value(data[1]); - let status_code = data[2].parse().unwrap_or(500); - let status_code = if status_code < 400 { - green.value(status_code) - } else { - red.value(status_code) - }; - let process_time: Vec<&str> = data[3].splitn(2, ".").collect(); - let process_time = process_time[0].to_string() + "ms"; - let process_time = blue.value(if process_time.len() == 3 { - " ".to_string() + &process_time - } else if process_time.len() == 4 { - " ".to_string() + &process_time - } else { - process_time - }); - let content = blue.value(data[4]); - return writeln!( - buf, - "[{}] {} | {} | {} | {}", - time, ipaddr, status_code, process_time, content - ); - } else if record.target() == "actix_server::builder" { - if data.starts_with("SIGINT received, exiting") { - return writeln!(buf, "\r{}", green.value("[INFO] SIGINT received, exiting")); - // Add '\r' to remove the input ^C - } else { - let data = data.replace("actix-web-service-", ""); - let re1 = regex::Regex::new("Starting (.*) workers").unwrap(); - if re1.is_match(&data) { - return Ok(()); - } - let re2 = regex::Regex::new("Starting \"(.*)\" service on (.*)").unwrap(); - if re2.is_match(&data) { - let addr = re2 - .captures(&data) + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")) + .format(|buf, record| { + let data = record.args().to_string(); + let mut style = buf.style(); + let blue = style.set_color(Color::Rgb(52, 152, 219)); + let mut style = buf.style(); + let red = style.set_color(Color::Rgb(231, 76, 60)); + let mut style = buf.style(); + let green = style.set_color(Color::Rgb(76, 175, 80)); + if record.target() == "actix_web::middleware::logger" { + let data: Vec<&str> = data.splitn(5, "^").collect(); + let time = blue.value( + chrono::NaiveDateTime::from_str(data[0]) .unwrap() - .get(1) - .map_or("", |m| m.as_str()); - let data = format!( - "[INFO] Serving {} on {}", - var("ROOT").unwrap_or(".".to_string()), - addr - ); - return writeln!(buf, "\r{}", green.value(data)); + .format("%Y/%m/%d %H:%M:%S") + .to_string(), + ); + let ipaddr = blue.value(data[1]); + let status_code = data[2].parse().unwrap_or(500); + let status_code = if status_code < 400 { + green.value(status_code) + } else { + red.value(status_code) + }; + let process_time: Vec<&str> = data[3].splitn(2, ".").collect(); + let process_time = process_time[0].to_string() + "ms"; + let process_time = blue.value(if process_time.len() == 3 { + " ".to_string() + &process_time + } else if process_time.len() == 4 { + " ".to_string() + &process_time + } else { + process_time + }); + let content = blue.value(data[4]); + return writeln!( + buf, + "[{}] {} | {} | {} | {}", + time, ipaddr, status_code, process_time, content + ); + } else if record.target() == "actix_server::builder" { + if data.starts_with("SIGINT received, exiting") { + return writeln!(buf, "\r{}", green.value("[INFO] SIGINT received, exiting")); + // Add '\r' to remove the input ^C + } else { + let data = data.replace("actix-web-service-", ""); + let re1 = regex::Regex::new("Starting (.*) workers").unwrap(); + if re1.is_match(&data) { + return Ok(()); + } + let re2 = regex::Regex::new("Starting \"(.*)\" service on (.*)").unwrap(); + if re2.is_match(&data) { + let addr = re2 + .captures(&data) + .unwrap() + .get(1) + .map_or("", |m| m.as_str()); + let data = format!( + "[INFO] Serving {} on {}", + var("ROOT").unwrap_or(".".to_string()), + addr + ); + return writeln!(buf, "\r{}", green.value(data)); + } } } - } - if data.starts_with("[ERROR]") - || data.starts_with("TLS alert") - || data.starts_with("Failed") - { - writeln!(buf, "\r{}", red.value(data)) - } else { - writeln!(buf, "\r{}", green.value(data)) - } - }) - .init(); + if data.starts_with("[ERROR]") + || data.starts_with("TLS alert") + || data.starts_with("Failed") + { + writeln!(buf, "\r{}", red.value(data)) + } else { + writeln!(buf, "\r{}", green.value(data)) + } + }) + .init(); let server = HttpServer::new(move || { let compress = if var("COMPRESS").unwrap_or("false".to_string()) == "true" { @@ -684,11 +682,6 @@ async fn main() -> std::io::Result<()> { http::header::ContentEncoding::Identity }; let app = App::new() - .wrap(middleware::Compress::new(compress)) - .wrap(middleware::Condition::new( - var("ENABLE_AUTH").unwrap_or("false".to_string()) == "true", - actix_web_httpauth::middleware::HttpAuthentication::basic(validator), - )) .wrap_fn(|req, srv| { let paths = PathBuf::from_str(req.path()).unwrap_or(PathBuf::default()); let mut isdotfile = false; @@ -699,7 +692,7 @@ async fn main() -> std::io::Result<()> { } let fut = srv.call(req); async move { - Ok(fut.await?.map_body(|head, mut body| { + Ok(fut.await?.map_body(|head, body| { if var("NOCACHE").unwrap_or("false".to_string()) == "true" { head.headers_mut().insert( http::header::CACHE_CONTROL, @@ -717,13 +710,17 @@ async fn main() -> std::io::Result<()> { { head.status = http::StatusCode::FORBIDDEN; *head.headers_mut() = http::HeaderMap::new(); - let _ = body.take_body(); - head.set_connection_type(http::ConnectionType::Close); + return dev::ResponseBody::Other(actix_web::body::Body::None); } body })) } }) + .wrap(middleware::Compress::new(compress)) + .wrap(middleware::Condition::new( + var("ENABLE_AUTH").unwrap_or("false".to_string()) == "true", + actix_web_httpauth::middleware::HttpAuthentication::basic(validator), + )) .wrap(middleware::Logger::new("%t^%a^%s^%D^%r")); let files = fs::Files::new("/", var("ROOT").unwrap_or(".".to_string())) .use_hidden_files()