mirror of https://github.com/Tim-Paik/srv.git
1.0.0-rc.4 add readme and 403 connection close bug fix
This commit is contained in:
parent
dda85e927a
commit
2cda2c0a45
36
README.md
36
README.md
|
@ -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
|
Binary file not shown.
After Width: | Height: | Size: 164 KiB |
163
src/main.rs
163
src/main.rs
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue