♻️ 使用 Actix RS 重构 #8

Merged
LittleSheep merged 7 commits from refactor/actix-rs into refactor/rust 2024-02-13 12:39:08 +00:00
5 changed files with 67 additions and 27 deletions
Showing only changes of commit e27023c130 - Show all commits

1
Cargo.lock generated
View File

@ -1277,6 +1277,7 @@ dependencies = [
"actix-web-httpauth", "actix-web-httpauth",
"awc", "awc",
"config", "config",
"derive_more",
"lazy_static", "lazy_static",
"mime", "mime",
"percent-encoding", "percent-encoding",

View File

@ -31,3 +31,4 @@ toml = "0.8.8"
tracing = "0.1.40" tracing = "0.1.40"
tracing-subscriber = "0.3.18" tracing-subscriber = "0.3.18"
wildmatch = "2.3.0" wildmatch = "2.3.0"
derive_more = "0.99.17"

View File

@ -1,7 +1,9 @@
use actix_web::http::header::HeaderMap; use actix_web::http::header::{ContentType, HeaderMap};
use actix_web::http::{Method, Uri}; use actix_web::http::{Method, StatusCode, Uri};
use regex::Regex; use regex::Regex;
use wildmatch::WildMatch; use wildmatch::WildMatch;
use actix_web::{error, HttpResponse};
use derive_more::{Display};
use crate::warden::WardenInstance; use crate::warden::WardenInstance;
@ -16,6 +18,46 @@ pub mod metrics;
pub mod responder; pub mod responder;
pub mod route; pub mod route;
#[derive(Debug, Display)]
pub enum ProxyError {
#[display(fmt = "Remote gateway issue")]
BadGateway,
#[display(fmt = "No configured able to process this request")]
NoGateway,
#[display(fmt = "Not found")]
NotFound,
#[display(fmt = "Only accepts method GET")]
MethodGetOnly,
#[display(fmt = "Invalid request path")]
InvalidRequestPath,
#[display(fmt = "Upstream does not support protocol you used")]
NotImplemented,
}
impl error::ResponseError for ProxyError {
fn status_code(&self) -> StatusCode {
match *self {
ProxyError::BadGateway => StatusCode::BAD_GATEWAY,
ProxyError::NoGateway => StatusCode::NOT_FOUND,
ProxyError::NotFound => StatusCode::NOT_FOUND,
ProxyError::MethodGetOnly => StatusCode::METHOD_NOT_ALLOWED,
ProxyError::InvalidRequestPath => StatusCode::BAD_REQUEST,
ProxyError::NotImplemented => StatusCode::NOT_IMPLEMENTED,
}
}
fn error_response(&self) -> HttpResponse {
HttpResponse::build(self.status_code())
.insert_header(ContentType::html())
.body(self.to_string())
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct RoadInstance { pub struct RoadInstance {
pub regions: Vec<Region>, pub regions: Vec<Region>,

View File

@ -7,12 +7,14 @@ use actix_proxy::IntoHttpResponse;
use actix_web::{HttpRequest, HttpResponse, web}; use actix_web::{HttpRequest, HttpResponse, web};
use actix_web::http::Method; use actix_web::http::Method;
use awc::Client; use awc::Client;
use tracing::log::warn;
use crate::proxies::ProxyError;
pub async fn respond_hypertext( pub async fn respond_hypertext(
uri: String, uri: String,
req: HttpRequest, req: HttpRequest,
client: web::Data<Client>, client: web::Data<Client>,
) -> Result<HttpResponse, HttpResponse> { ) -> Result<HttpResponse, ProxyError> {
let ip = req.peer_addr().unwrap().ip().to_string(); let ip = req.peer_addr().unwrap().ip().to_string();
let proto = req.uri().scheme_str().unwrap(); let proto = req.uri().scheme_str().unwrap();
let host = req.uri().host().unwrap(); let host = req.uri().host().unwrap();
@ -40,8 +42,8 @@ pub async fn respond_hypertext(
} }
Err(error) => { Err(error) => {
Err(HttpResponse::BadGateway() warn!("Proxy got a upstream issue... {:?}", error);
.body(format!("Something went wrong... {:}", error))) Err(ProxyError::BadGateway)
} }
}; };
} }
@ -59,10 +61,9 @@ pub struct StaticResponderConfig {
pub async fn respond_static( pub async fn respond_static(
cfg: StaticResponderConfig, cfg: StaticResponderConfig,
req: HttpRequest, req: HttpRequest,
) -> Result<HttpResponse, HttpResponse> { ) -> Result<HttpResponse, ProxyError> {
if req.method() != Method::GET { if req.method() != Method::GET {
return Err(HttpResponse::MethodNotAllowed() return Err(ProxyError::MethodGetOnly);
.body("This destination only support GET request."));
} }
let path = req let path = req
@ -74,7 +75,7 @@ pub async fn respond_static(
let path = match percent_encoding::percent_decode_str(path).decode_utf8() { let path = match percent_encoding::percent_decode_str(path).decode_utf8() {
Ok(val) => val, Ok(val) => val,
Err(_) => { Err(_) => {
return Err(HttpResponse::NotFound().body("Not found.")); return Err(ProxyError::NotFound);
} }
}; };
@ -91,8 +92,7 @@ pub async fn respond_static(
} }
if !file_path.starts_with(cfg.uri) { if !file_path.starts_with(cfg.uri) {
return Err(HttpResponse::Forbidden() return Err(ProxyError::InvalidRequestPath);
.body("Unexpected path."));
} }
if !file_path.exists() { if !file_path.exists() {
@ -116,7 +116,7 @@ pub async fn respond_static(
} }
} }
return Err(HttpResponse::NotFound().body("Not found.")); return Err(ProxyError::NotFound);
} }
return if file_path.is_file() { return if file_path.is_file() {
@ -129,6 +129,6 @@ pub async fn respond_static(
} }
} }
Err(HttpResponse::NotFound().body("Not found.")) return Err(ProxyError::NotFound);
}; };
} }

View File

@ -1,4 +1,4 @@
use actix_web::{HttpRequest, HttpResponse, web}; use actix_web::{HttpRequest, HttpResponse, ResponseError, web};
use actix_web::http::header; use actix_web::http::header;
use awc::Client; use awc::Client;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
@ -10,14 +10,14 @@ use crate::{
}, },
ROAD, ROAD,
}; };
use crate::proxies::ProxyError;
pub async fn handle(req: HttpRequest, client: web::Data<Client>) -> HttpResponse { pub async fn handle(req: HttpRequest, client: web::Data<Client>) -> HttpResponse {
let readable_app = ROAD.lock().await; let readable_app = ROAD.lock().await;
let (region, location) = match readable_app.filter(req.uri(), req.method(), req.headers()) { let (region, location) = match readable_app.filter(req.uri(), req.method(), req.headers()) {
Some(val) => val, Some(val) => val,
None => { None => {
return HttpResponse::NotFound() return ProxyError::NoGateway.error_response();
.body("There are no region be able to respone this request.");
} }
}; };
@ -30,29 +30,24 @@ pub async fn handle(req: HttpRequest, client: web::Data<Client>) -> HttpResponse
end: &Destination, end: &Destination,
req: HttpRequest, req: HttpRequest,
client: web::Data<Client>, client: web::Data<Client>,
) -> Result<HttpResponse, HttpResponse> { ) -> Result<HttpResponse, ProxyError> {
// Handle normal web request // Handle normal web request
match end.get_type() { match end.get_type() {
DestinationType::Hypertext => { DestinationType::Hypertext => {
let Ok(uri) = end.get_hypertext_uri() else { let Ok(uri) = end.get_hypertext_uri() else {
return Err(HttpResponse::NotImplemented() return Err(ProxyError::NotImplemented);
.body("This destination was not support web requests."));
}; };
responder::respond_hypertext(uri, req, client).await responder::respond_hypertext(uri, req, client).await
} }
DestinationType::StaticFiles => { DestinationType::StaticFiles => {
let Ok(cfg) = end.get_static_config() else { let Ok(cfg) = end.get_static_config() else {
return Err(HttpResponse::NotImplemented() return Err(ProxyError::NotImplemented);
.body("This destination was not support static files."));
}; };
responder::respond_static(cfg, req).await responder::respond_static(cfg, req).await
} }
_ => { _ => Err(ProxyError::NotImplemented)
return Err(HttpResponse::NotImplemented()
.body("Unsupported destination protocol."));
}
} }
} }
@ -78,13 +73,14 @@ pub async fn handle(req: HttpRequest, client: web::Data<Client>) -> HttpResponse
resp resp
} }
Err(resp) => { Err(resp) => {
let message = resp.to_string();
tokio::spawn(async move { tokio::spawn(async move {
let writable_app = &mut ROAD.lock().await; let writable_app = &mut ROAD.lock().await;
writable_app writable_app
.metrics .metrics
.add_failure_request(ip, ua, reg, loc, end, "TODO".to_owned()); .add_failure_request(ip, ua, reg, loc, end, message);
}); });
resp resp.error_response()
} }
} }
} }