1.0.0-rc.4 add readme and 403 connection close bug fix

This commit is contained in:
Tim-Paik 2021-08-25 20:41:59 +08:00
parent dda85e927a
commit 2cda2c0a45
3 changed files with 115 additions and 84 deletions

View File

@ -1 +1,35 @@
# Srv Dev Server # 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

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

View File

@ -1,7 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public /* 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 * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#[macro_use] #[macro_use]
extern crate clap; extern crate clap;
#[macro_use] #[macro_use]
@ -599,83 +599,81 @@ async fn main() -> std::io::Result<()> {
addr addr
}; };
env_logger::Builder::from_env( env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
env_logger::Env::default().default_filter_or("info"), .format(|buf, record| {
) let data = record.args().to_string();
.format(|buf, record| { let mut style = buf.style();
let data = record.args().to_string(); let blue = style.set_color(Color::Rgb(52, 152, 219));
let mut style = buf.style(); let mut style = buf.style();
let blue = style.set_color(Color::Rgb(52, 152, 219)); let red = style.set_color(Color::Rgb(231, 76, 60));
let mut style = buf.style(); let mut style = buf.style();
let red = style.set_color(Color::Rgb(231, 76, 60)); let green = style.set_color(Color::Rgb(76, 175, 80));
let mut style = buf.style(); if record.target() == "actix_web::middleware::logger" {
let green = style.set_color(Color::Rgb(76, 175, 80)); let data: Vec<&str> = data.splitn(5, "^").collect();
if record.target() == "actix_web::middleware::logger" { let time = blue.value(
let data: Vec<&str> = data.splitn(5, "^").collect(); chrono::NaiveDateTime::from_str(data[0])
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)
.unwrap() .unwrap()
.get(1) .format("%Y/%m/%d %H:%M:%S")
.map_or("", |m| m.as_str()); .to_string(),
let data = format!( );
"[INFO] Serving {} on {}", let ipaddr = blue.value(data[1]);
var("ROOT").unwrap_or(".".to_string()), let status_code = data[2].parse().unwrap_or(500);
addr let status_code = if status_code < 400 {
); green.value(status_code)
return writeln!(buf, "\r{}", green.value(data)); } 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]")
if data.starts_with("[ERROR]") || data.starts_with("TLS alert")
|| data.starts_with("TLS alert") || data.starts_with("Failed")
|| data.starts_with("Failed") {
{ writeln!(buf, "\r{}", red.value(data))
writeln!(buf, "\r{}", red.value(data)) } else {
} else { writeln!(buf, "\r{}", green.value(data))
writeln!(buf, "\r{}", green.value(data)) }
} })
}) .init();
.init();
let server = HttpServer::new(move || { let server = HttpServer::new(move || {
let compress = if var("COMPRESS").unwrap_or("false".to_string()) == "true" { 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 http::header::ContentEncoding::Identity
}; };
let app = App::new() 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| { .wrap_fn(|req, srv| {
let paths = PathBuf::from_str(req.path()).unwrap_or(PathBuf::default()); let paths = PathBuf::from_str(req.path()).unwrap_or(PathBuf::default());
let mut isdotfile = false; let mut isdotfile = false;
@ -699,7 +692,7 @@ async fn main() -> std::io::Result<()> {
} }
let fut = srv.call(req); let fut = srv.call(req);
async move { 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" { if var("NOCACHE").unwrap_or("false".to_string()) == "true" {
head.headers_mut().insert( head.headers_mut().insert(
http::header::CACHE_CONTROL, http::header::CACHE_CONTROL,
@ -717,13 +710,17 @@ async fn main() -> std::io::Result<()> {
{ {
head.status = http::StatusCode::FORBIDDEN; head.status = http::StatusCode::FORBIDDEN;
*head.headers_mut() = http::HeaderMap::new(); *head.headers_mut() = http::HeaderMap::new();
let _ = body.take_body(); return dev::ResponseBody::Other(actix_web::body::Body::None);
head.set_connection_type(http::ConnectionType::Close);
} }
body 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")); .wrap(middleware::Logger::new("%t^%a^%s^%D^%r"));
let files = fs::Files::new("/", var("ROOT").unwrap_or(".".to_string())) let files = fs::Files::new("/", var("ROOT").unwrap_or(".".to_string()))
.use_hidden_files() .use_hidden_files()