diff --git a/Cargo.lock b/Cargo.lock index 20c8f8d..0f01a68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1277,6 +1277,7 @@ dependencies = [ "actix-web-httpauth", "awc", "config", + "derive_more", "lazy_static", "mime", "percent-encoding", diff --git a/Cargo.toml b/Cargo.toml index d41485f..95438ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,3 +31,4 @@ toml = "0.8.8" tracing = "0.1.40" tracing-subscriber = "0.3.18" wildmatch = "2.3.0" +derive_more = "0.99.17" diff --git a/src/proxies/mod.rs b/src/proxies/mod.rs index 2dd3fe7..7ecf9e9 100644 --- a/src/proxies/mod.rs +++ b/src/proxies/mod.rs @@ -1,7 +1,9 @@ -use actix_web::http::header::HeaderMap; -use actix_web::http::{Method, Uri}; +use actix_web::http::header::{ContentType, HeaderMap}; +use actix_web::http::{Method, StatusCode, Uri}; use regex::Regex; use wildmatch::WildMatch; +use actix_web::{error, HttpResponse}; +use derive_more::{Display}; use crate::warden::WardenInstance; @@ -16,6 +18,46 @@ pub mod metrics; pub mod responder; 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)] pub struct RoadInstance { pub regions: Vec, diff --git a/src/proxies/responder.rs b/src/proxies/responder.rs index 54c4e64..3213695 100644 --- a/src/proxies/responder.rs +++ b/src/proxies/responder.rs @@ -7,12 +7,14 @@ use actix_proxy::IntoHttpResponse; use actix_web::{HttpRequest, HttpResponse, web}; use actix_web::http::Method; use awc::Client; +use tracing::log::warn; +use crate::proxies::ProxyError; pub async fn respond_hypertext( uri: String, req: HttpRequest, client: web::Data, -) -> Result { +) -> Result { let ip = req.peer_addr().unwrap().ip().to_string(); let proto = req.uri().scheme_str().unwrap(); let host = req.uri().host().unwrap(); @@ -40,8 +42,8 @@ pub async fn respond_hypertext( } Err(error) => { - Err(HttpResponse::BadGateway() - .body(format!("Something went wrong... {:}", error))) + warn!("Proxy got a upstream issue... {:?}", error); + Err(ProxyError::BadGateway) } }; } @@ -59,10 +61,9 @@ pub struct StaticResponderConfig { pub async fn respond_static( cfg: StaticResponderConfig, req: HttpRequest, -) -> Result { +) -> Result { if req.method() != Method::GET { - return Err(HttpResponse::MethodNotAllowed() - .body("This destination only support GET request.")); + return Err(ProxyError::MethodGetOnly); } let path = req @@ -74,7 +75,7 @@ pub async fn respond_static( let path = match percent_encoding::percent_decode_str(path).decode_utf8() { Ok(val) => val, 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) { - return Err(HttpResponse::Forbidden() - .body("Unexpected path.")); + return Err(ProxyError::InvalidRequestPath); } 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() { @@ -129,6 +129,6 @@ pub async fn respond_static( } } - Err(HttpResponse::NotFound().body("Not found.")) + return Err(ProxyError::NotFound); }; } diff --git a/src/proxies/route.rs b/src/proxies/route.rs index 4f968ef..3e37b5f 100644 --- a/src/proxies/route.rs +++ b/src/proxies/route.rs @@ -1,4 +1,4 @@ -use actix_web::{HttpRequest, HttpResponse, web}; +use actix_web::{HttpRequest, HttpResponse, ResponseError, web}; use actix_web::http::header; use awc::Client; use rand::seq::SliceRandom; @@ -10,14 +10,14 @@ use crate::{ }, ROAD, }; +use crate::proxies::ProxyError; pub async fn handle(req: HttpRequest, client: web::Data) -> HttpResponse { let readable_app = ROAD.lock().await; let (region, location) = match readable_app.filter(req.uri(), req.method(), req.headers()) { Some(val) => val, None => { - return HttpResponse::NotFound() - .body("There are no region be able to respone this request."); + return ProxyError::NoGateway.error_response(); } }; @@ -30,29 +30,24 @@ pub async fn handle(req: HttpRequest, client: web::Data) -> HttpResponse end: &Destination, req: HttpRequest, client: web::Data, - ) -> Result { + ) -> Result { // Handle normal web request match end.get_type() { DestinationType::Hypertext => { let Ok(uri) = end.get_hypertext_uri() else { - return Err(HttpResponse::NotImplemented() - .body("This destination was not support web requests.")); + return Err(ProxyError::NotImplemented); }; responder::respond_hypertext(uri, req, client).await } DestinationType::StaticFiles => { let Ok(cfg) = end.get_static_config() else { - return Err(HttpResponse::NotImplemented() - .body("This destination was not support static files.")); + return Err(ProxyError::NotImplemented); }; responder::respond_static(cfg, req).await } - _ => { - return Err(HttpResponse::NotImplemented() - .body("Unsupported destination protocol.")); - } + _ => Err(ProxyError::NotImplemented) } } @@ -78,13 +73,14 @@ pub async fn handle(req: HttpRequest, client: web::Data) -> HttpResponse resp } Err(resp) => { + let message = resp.to_string(); tokio::spawn(async move { let writable_app = &mut ROAD.lock().await; writable_app .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() } } }