✨ Metrics
This commit is contained in:
		
							
								
								
									
										149
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										149
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -526,6 +526,25 @@ version = "0.28.1" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "h2" | ||||||
|  | version = "0.3.23" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7" | ||||||
|  | dependencies = [ | ||||||
|  |  "bytes", | ||||||
|  |  "fnv", | ||||||
|  |  "futures-core", | ||||||
|  |  "futures-sink", | ||||||
|  |  "futures-util", | ||||||
|  |  "http 0.2.11", | ||||||
|  |  "indexmap", | ||||||
|  |  "slab", | ||||||
|  |  "tokio", | ||||||
|  |  "tokio-util", | ||||||
|  |  "tracing", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "h2" | name = "h2" | ||||||
| version = "0.4.1" | version = "0.4.1" | ||||||
| @@ -537,7 +556,7 @@ dependencies = [ | |||||||
|  "futures-core", |  "futures-core", | ||||||
|  "futures-sink", |  "futures-sink", | ||||||
|  "futures-util", |  "futures-util", | ||||||
|  "http", |  "http 1.0.0", | ||||||
|  "indexmap", |  "indexmap", | ||||||
|  "slab", |  "slab", | ||||||
|  "tokio", |  "tokio", | ||||||
| @@ -569,7 +588,7 @@ dependencies = [ | |||||||
|  "base64 0.21.7", |  "base64 0.21.7", | ||||||
|  "bytes", |  "bytes", | ||||||
|  "headers-core", |  "headers-core", | ||||||
|  "http", |  "http 1.0.0", | ||||||
|  "httpdate", |  "httpdate", | ||||||
|  "mime", |  "mime", | ||||||
|  "sha1", |  "sha1", | ||||||
| @@ -581,7 +600,7 @@ version = "0.3.0" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" | checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "http", |  "http 1.0.0", | ||||||
| ] | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| @@ -608,6 +627,17 @@ dependencies = [ | |||||||
|  "digest", |  "digest", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "http" | ||||||
|  | version = "0.2.11" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" | ||||||
|  | dependencies = [ | ||||||
|  |  "bytes", | ||||||
|  |  "fnv", | ||||||
|  |  "itoa", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "http" | name = "http" | ||||||
| version = "1.0.0" | version = "1.0.0" | ||||||
| @@ -619,6 +649,17 @@ dependencies = [ | |||||||
|  "itoa", |  "itoa", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "http-body" | ||||||
|  | version = "0.4.6" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" | ||||||
|  | dependencies = [ | ||||||
|  |  "bytes", | ||||||
|  |  "http 0.2.11", | ||||||
|  |  "pin-project-lite", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "http-body" | name = "http-body" | ||||||
| version = "1.0.0" | version = "1.0.0" | ||||||
| @@ -626,7 +667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||||
| checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" | checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bytes", |  "bytes", | ||||||
|  "http", |  "http 1.0.0", | ||||||
| ] | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| @@ -637,8 +678,8 @@ checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" | |||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bytes", |  "bytes", | ||||||
|  "futures-util", |  "futures-util", | ||||||
|  "http", |  "http 1.0.0", | ||||||
|  "http-body", |  "http-body 1.0.0", | ||||||
|  "pin-project-lite", |  "pin-project-lite", | ||||||
| ] | ] | ||||||
|  |  | ||||||
| @@ -654,6 +695,30 @@ version = "1.0.3" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "hyper" | ||||||
|  | version = "0.14.28" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" | ||||||
|  | dependencies = [ | ||||||
|  |  "bytes", | ||||||
|  |  "futures-channel", | ||||||
|  |  "futures-core", | ||||||
|  |  "futures-util", | ||||||
|  |  "h2 0.3.23", | ||||||
|  |  "http 0.2.11", | ||||||
|  |  "http-body 0.4.6", | ||||||
|  |  "httparse", | ||||||
|  |  "httpdate", | ||||||
|  |  "itoa", | ||||||
|  |  "pin-project-lite", | ||||||
|  |  "socket2", | ||||||
|  |  "tokio", | ||||||
|  |  "tower-service", | ||||||
|  |  "tracing", | ||||||
|  |  "want", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "hyper" | name = "hyper" | ||||||
| version = "1.1.0" | version = "1.1.0" | ||||||
| @@ -663,9 +728,9 @@ dependencies = [ | |||||||
|  "bytes", |  "bytes", | ||||||
|  "futures-channel", |  "futures-channel", | ||||||
|  "futures-util", |  "futures-util", | ||||||
|  "h2", |  "h2 0.4.1", | ||||||
|  "http", |  "http 1.0.0", | ||||||
|  "http-body", |  "http-body 1.0.0", | ||||||
|  "httparse", |  "httparse", | ||||||
|  "httpdate", |  "httpdate", | ||||||
|  "itoa", |  "itoa", | ||||||
| @@ -682,7 +747,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" | |||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bytes", |  "bytes", | ||||||
|  "http-body-util", |  "http-body-util", | ||||||
|  "hyper", |  "hyper 1.1.0", | ||||||
|  "hyper-util", |  "hyper-util", | ||||||
|  "native-tls", |  "native-tls", | ||||||
|  "tokio", |  "tokio", | ||||||
| @@ -699,9 +764,9 @@ dependencies = [ | |||||||
|  "bytes", |  "bytes", | ||||||
|  "futures-channel", |  "futures-channel", | ||||||
|  "futures-util", |  "futures-util", | ||||||
|  "http", |  "http 1.0.0", | ||||||
|  "http-body", |  "http-body 1.0.0", | ||||||
|  "hyper", |  "hyper 1.1.0", | ||||||
|  "pin-project-lite", |  "pin-project-lite", | ||||||
|  "socket2", |  "socket2", | ||||||
|  "tokio", |  "tokio", | ||||||
| @@ -897,7 +962,7 @@ dependencies = [ | |||||||
|  "bytes", |  "bytes", | ||||||
|  "encoding_rs", |  "encoding_rs", | ||||||
|  "futures-util", |  "futures-util", | ||||||
|  "http", |  "http 1.0.0", | ||||||
|  "httparse", |  "httparse", | ||||||
|  "log", |  "log", | ||||||
|  "memchr", |  "memchr", | ||||||
| @@ -1187,10 +1252,10 @@ dependencies = [ | |||||||
|  "cookie", |  "cookie", | ||||||
|  "futures-util", |  "futures-util", | ||||||
|  "headers", |  "headers", | ||||||
|  "http", |  "http 1.0.0", | ||||||
|  "http-body-util", |  "http-body-util", | ||||||
|  "httpdate", |  "httpdate", | ||||||
|  "hyper", |  "hyper 1.1.0", | ||||||
|  "hyper-util", |  "hyper-util", | ||||||
|  "mime", |  "mime", | ||||||
|  "mime_guess", |  "mime_guess", | ||||||
| @@ -1202,6 +1267,7 @@ dependencies = [ | |||||||
|  "poem-derive", |  "poem-derive", | ||||||
|  "quick-xml", |  "quick-xml", | ||||||
|  "regex", |  "regex", | ||||||
|  |  "reqwest 0.11.23 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "rfc7239", |  "rfc7239", | ||||||
|  "serde", |  "serde", | ||||||
|  "serde_json", |  "serde_json", | ||||||
| @@ -1265,7 +1331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||||
| checksum = "d5d485fb9cc4ca9a8364beedd4ea81294b1f028d459c8fd7bb352e38f87f8ffa" | checksum = "d5d485fb9cc4ca9a8364beedd4ea81294b1f028d459c8fd7bb352e38f87f8ffa" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "darling", |  "darling", | ||||||
|  "http", |  "http 1.0.0", | ||||||
|  "indexmap", |  "indexmap", | ||||||
|  "mime", |  "mime", | ||||||
|  "proc-macro-crate", |  "proc-macro-crate", | ||||||
| @@ -1418,6 +1484,41 @@ version = "0.8.2" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "reqwest" | ||||||
|  | version = "0.11.23" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" | ||||||
|  | dependencies = [ | ||||||
|  |  "base64 0.21.7", | ||||||
|  |  "bytes", | ||||||
|  |  "encoding_rs", | ||||||
|  |  "futures-core", | ||||||
|  |  "futures-util", | ||||||
|  |  "h2 0.3.23", | ||||||
|  |  "http 0.2.11", | ||||||
|  |  "http-body 0.4.6", | ||||||
|  |  "hyper 0.14.28", | ||||||
|  |  "ipnet", | ||||||
|  |  "js-sys", | ||||||
|  |  "log", | ||||||
|  |  "mime", | ||||||
|  |  "once_cell", | ||||||
|  |  "percent-encoding", | ||||||
|  |  "pin-project-lite", | ||||||
|  |  "serde", | ||||||
|  |  "serde_json", | ||||||
|  |  "serde_urlencoded", | ||||||
|  |  "system-configuration", | ||||||
|  |  "tokio", | ||||||
|  |  "tower-service", | ||||||
|  |  "url", | ||||||
|  |  "wasm-bindgen", | ||||||
|  |  "wasm-bindgen-futures", | ||||||
|  |  "web-sys", | ||||||
|  |  "winreg", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "reqwest" | name = "reqwest" | ||||||
| version = "0.11.23" | version = "0.11.23" | ||||||
| @@ -1428,11 +1529,11 @@ dependencies = [ | |||||||
|  "encoding_rs", |  "encoding_rs", | ||||||
|  "futures-core", |  "futures-core", | ||||||
|  "futures-util", |  "futures-util", | ||||||
|  "h2", |  "h2 0.4.1", | ||||||
|  "http", |  "http 1.0.0", | ||||||
|  "http-body", |  "http-body 1.0.0", | ||||||
|  "http-body-util", |  "http-body-util", | ||||||
|  "hyper", |  "hyper 1.1.0", | ||||||
|  "hyper-tls", |  "hyper-tls", | ||||||
|  "hyper-util", |  "hyper-util", | ||||||
|  "ipnet", |  "ipnet", | ||||||
| @@ -1472,7 +1573,7 @@ version = "0.1.0" | |||||||
| dependencies = [ | dependencies = [ | ||||||
|  "config", |  "config", | ||||||
|  "futures-util", |  "futures-util", | ||||||
|  "http", |  "http 1.0.0", | ||||||
|  "hyper-util", |  "hyper-util", | ||||||
|  "lazy_static", |  "lazy_static", | ||||||
|  "mime", |  "mime", | ||||||
| @@ -1482,7 +1583,7 @@ dependencies = [ | |||||||
|  "queryst", |  "queryst", | ||||||
|  "rand", |  "rand", | ||||||
|  "regex", |  "regex", | ||||||
|  "reqwest", |  "reqwest 0.11.23 (git+https://github.com/seanmonstar/reqwest.git?branch=hyper-v1)", | ||||||
|  "serde", |  "serde", | ||||||
|  "serde_json", |  "serde_json", | ||||||
|  "tokio", |  "tokio", | ||||||
| @@ -2123,7 +2224,7 @@ dependencies = [ | |||||||
|  "byteorder", |  "byteorder", | ||||||
|  "bytes", |  "bytes", | ||||||
|  "data-encoding", |  "data-encoding", | ||||||
|  "http", |  "http 1.0.0", | ||||||
|  "httparse", |  "httparse", | ||||||
|  "log", |  "log", | ||||||
|  "rand", |  "rand", | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ hyper-util = { version = "0.1.2", features = ["full"] } | |||||||
| lazy_static = "1.4.0" | lazy_static = "1.4.0" | ||||||
| mime = "0.3.17" | mime = "0.3.17" | ||||||
| percent-encoding = "2.3.1" | percent-encoding = "2.3.1" | ||||||
| poem = { version = "2.0.0", features = ["tokio-metrics", "websocket", "static-files"] } | poem = { version = "2.0.0", features = ["tokio-metrics", "websocket", "static-files", "reqwest"] } | ||||||
| poem-openapi = { version = "4.0.0", features = ["swagger-ui"] } | poem-openapi = { version = "4.0.0", features = ["swagger-ui"] } | ||||||
| queryst = "3.0.0" | queryst = "3.0.0" | ||||||
| rand = "0.8.5" | rand = "0.8.5" | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| use std::sync::RwLock; |  | ||||||
|  |  | ||||||
| use config::Config; | use config::Config; | ||||||
| use lazy_static::lazy_static; | use lazy_static::lazy_static; | ||||||
|  | use tokio::sync::RwLock; | ||||||
|  |  | ||||||
| use crate::config::loader::load_settings; | use crate::config::loader::load_settings; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										33
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								src/main.rs
									
									
									
									
									
								
							| @@ -2,11 +2,28 @@ mod config; | |||||||
| mod proxies; | mod proxies; | ||||||
| mod sideload; | mod sideload; | ||||||
|  |  | ||||||
| use poem::{listener::TcpListener, EndpointExt, Route, Server}; | use std::collections::VecDeque; | ||||||
|  |  | ||||||
|  | use lazy_static::lazy_static; | ||||||
|  | use poem::{listener::TcpListener, Route, Server}; | ||||||
| use poem_openapi::OpenApiService; | use poem_openapi::OpenApiService; | ||||||
|  | use proxies::RoadInstance; | ||||||
|  | use tokio::sync::Mutex; | ||||||
| use tracing::{error, info, Level}; | use tracing::{error, info, Level}; | ||||||
|  |  | ||||||
| use crate::proxies::route; | use crate::proxies::{metrics::RoadMetrics, route}; | ||||||
|  |  | ||||||
|  | lazy_static! { | ||||||
|  |     static ref ROAD: Mutex<RoadInstance> = Mutex::new(RoadInstance { | ||||||
|  |         regions: vec![], | ||||||
|  |         metrics: RoadMetrics { | ||||||
|  |             requests_count: 0, | ||||||
|  |             failures_count: 0, | ||||||
|  |             recent_successes: VecDeque::new(), | ||||||
|  |             recent_errors: VecDeque::new(), | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  |  | ||||||
| #[tokio::main] | #[tokio::main] | ||||||
| async fn main() -> Result<(), std::io::Error> { | async fn main() -> Result<(), std::io::Error> { | ||||||
| @@ -19,19 +36,17 @@ async fn main() -> Result<(), std::io::Error> { | |||||||
|         .init(); |         .init(); | ||||||
|  |  | ||||||
|     // Prepare all the stuff |     // Prepare all the stuff | ||||||
|     let mut instance = proxies::Instance::new(); |  | ||||||
|  |  | ||||||
|     info!("Loading proxy regions..."); |     info!("Loading proxy regions..."); | ||||||
|     match proxies::loader::scan_regions( |     match proxies::loader::scan_regions( | ||||||
|         config::C |         config::C | ||||||
|             .read() |             .read() | ||||||
|             .unwrap() |             .await | ||||||
|             .get_string("regions") |             .get_string("regions") | ||||||
|             .unwrap_or("./regions".to_string()), |             .unwrap_or("./regions".to_string()), | ||||||
|     ) { |     ) { | ||||||
|         Err(_) => error!("Loading proxy regions... failed"), |         Err(_) => error!("Loading proxy regions... failed"), | ||||||
|         Ok((regions, count)) => { |         Ok((regions, count)) => { | ||||||
|             instance.regions = regions; |             ROAD.lock().await.regions = regions; | ||||||
|             info!(count, "Loading proxy regions... done") |             info!(count, "Loading proxy regions... done") | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| @@ -40,11 +55,11 @@ async fn main() -> Result<(), std::io::Error> { | |||||||
|     let proxies_server = Server::new(TcpListener::bind( |     let proxies_server = Server::new(TcpListener::bind( | ||||||
|         config::C |         config::C | ||||||
|             .read() |             .read() | ||||||
|             .unwrap() |             .await | ||||||
|             .get_string("listen.proxies") |             .get_string("listen.proxies") | ||||||
|             .unwrap_or("0.0.0.0:80".to_string()), |             .unwrap_or("0.0.0.0:80".to_string()), | ||||||
|     )) |     )) | ||||||
|     .run(route::handle.data(instance)); |     .run(route::handle); | ||||||
|  |  | ||||||
|     // Sideload |     // Sideload | ||||||
|     let sideload = OpenApiService::new(sideload::SideloadApi, "Sideload API", "1.0") |     let sideload = OpenApiService::new(sideload::SideloadApi, "Sideload API", "1.0") | ||||||
| @@ -54,7 +69,7 @@ async fn main() -> Result<(), std::io::Error> { | |||||||
|     let sideload_server = Server::new(TcpListener::bind( |     let sideload_server = Server::new(TcpListener::bind( | ||||||
|         config::C |         config::C | ||||||
|             .read() |             .read() | ||||||
|             .unwrap() |             .await | ||||||
|             .get_string("listen.sideload") |             .get_string("listen.sideload") | ||||||
|             .unwrap_or("0.0.0.0:81".to_string()), |             .unwrap_or("0.0.0.0:81".to_string()), | ||||||
|     )) |     )) | ||||||
|   | |||||||
							
								
								
									
										80
									
								
								src/proxies/metrics.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/proxies/metrics.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | |||||||
|  | use std::collections::VecDeque; | ||||||
|  |  | ||||||
|  | use poem_openapi::Object; | ||||||
|  | use serde::{Deserialize, Serialize}; | ||||||
|  |  | ||||||
|  | use super::config::{Destination, Location, Region}; | ||||||
|  |  | ||||||
|  | #[derive(Debug, Object, Clone, Serialize, Deserialize, PartialEq)] | ||||||
|  | pub struct RoadTrace { | ||||||
|  |     pub region: String, | ||||||
|  |     pub location: String, | ||||||
|  |     pub destination: String, | ||||||
|  |     pub error: Option<String>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl RoadTrace { | ||||||
|  |     pub fn from_structs(reg: Region, loc: Location, end: Destination) -> RoadTrace { | ||||||
|  |         RoadTrace { | ||||||
|  |             region: reg.id, | ||||||
|  |             location: loc.id, | ||||||
|  |             destination: end.id, | ||||||
|  |             error: None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn from_structs_with_error( | ||||||
|  |         reg: Region, | ||||||
|  |         loc: Location, | ||||||
|  |         end: Destination, | ||||||
|  |         err: String, | ||||||
|  |     ) -> RoadTrace { | ||||||
|  |         RoadTrace { | ||||||
|  |             region: reg.id, | ||||||
|  |             location: loc.id, | ||||||
|  |             destination: end.id, | ||||||
|  |             error: Some(err), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub struct RoadMetrics { | ||||||
|  |     pub requests_count: u64, | ||||||
|  |     pub failures_count: u64, | ||||||
|  |  | ||||||
|  |     pub recent_successes: VecDeque<RoadTrace>, | ||||||
|  |     pub recent_errors: VecDeque<RoadTrace>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl RoadMetrics { | ||||||
|  |     pub fn get_success_rate(&self) -> f64 { | ||||||
|  |         if self.requests_count > 0 { | ||||||
|  |             (self.requests_count - self.failures_count) as f64 / self.requests_count as f64 | ||||||
|  |         } else { | ||||||
|  |             0.0 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn add_success_request(&mut self, reg: Region, loc: Location, end: Destination) { | ||||||
|  |         self.requests_count += 1; | ||||||
|  |         self.recent_successes | ||||||
|  |             .push_back(RoadTrace::from_structs(reg, loc, end)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn add_faliure_request( | ||||||
|  |         &mut self, | ||||||
|  |         reg: Region, | ||||||
|  |         loc: Location, | ||||||
|  |         end: Destination, | ||||||
|  |         err: String, // For some reason error is rarely clonable, so we use preformatted message | ||||||
|  |     ) { | ||||||
|  |         self.requests_count += 1; | ||||||
|  |         self.failures_count += 1; | ||||||
|  |         self.recent_errors | ||||||
|  |             .push_back(RoadTrace::from_structs_with_error(reg, loc, end, err)); | ||||||
|  |         if self.recent_errors.len() > 10 { | ||||||
|  |             self.recent_errors.pop_front(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,30 +1,35 @@ | |||||||
| use http::Method; | use http::Method; | ||||||
| use poem::http::{HeaderMap, Uri}; | use poem::http::{HeaderMap, Uri}; | ||||||
| use regex::Regex; | use regex::Regex; | ||||||
| use serde::{Deserialize, Serialize}; |  | ||||||
| use wildmatch::WildMatch; | use wildmatch::WildMatch; | ||||||
|  |  | ||||||
| use self::config::{Location, Region}; | use self::{ | ||||||
|  |     config::{Location, Region}, | ||||||
|  |     metrics::RoadMetrics, | ||||||
|  | }; | ||||||
|  |  | ||||||
| pub mod browser; | pub mod browser; | ||||||
| pub mod config; | pub mod config; | ||||||
| pub mod loader; | pub mod loader; | ||||||
|  | pub mod metrics; | ||||||
| pub mod responder; | pub mod responder; | ||||||
| pub mod route; | pub mod route; | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Serialize, Deserialize)] | #[derive(Debug, Clone)] | ||||||
| pub struct Instance { | pub struct RoadInstance { | ||||||
|     pub regions: Vec<Region>, |     pub regions: Vec<Region>, | ||||||
|  |     pub metrics: RoadMetrics, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Instance { | impl RoadInstance { | ||||||
|     pub fn new() -> Instance { |     pub fn filter( | ||||||
|         Instance { regions: vec![] } |         &self, | ||||||
|     } |         uri: &Uri, | ||||||
|  |         method: Method, | ||||||
|     pub fn filter(&self, uri: &Uri, method: Method, headers: &HeaderMap) -> Option<&Location> { |         headers: &HeaderMap, | ||||||
|  |     ) -> Option<(&Region, &Location)> { | ||||||
|         self.regions.iter().find_map(|region| { |         self.regions.iter().find_map(|region| { | ||||||
|             region.locations.iter().find(|location| { |             let location = region.locations.iter().find(|location| { | ||||||
|                 let mut hosts = location.hosts.iter(); |                 let mut hosts = location.hosts.iter(); | ||||||
|                 if !hosts.any(|item| { |                 if !hosts.any(|item| { | ||||||
|                     WildMatch::new(item.as_str()).matches(uri.host().unwrap_or("localhost")) |                     WildMatch::new(item.as_str()).matches(uri.host().unwrap_or("localhost")) | ||||||
| @@ -64,7 +69,9 @@ impl Instance { | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 true |                 true | ||||||
|             }) |             }); | ||||||
|  |  | ||||||
|  |             location.map(|location| (region, location)) | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,27 +1,30 @@ | |||||||
|  | use http::Method; | ||||||
| use poem::{ | use poem::{ | ||||||
|     handler, |     handler, | ||||||
|     http::{HeaderMap, StatusCode, Uri}, |     http::{HeaderMap, StatusCode, Uri}, | ||||||
|     web::{websocket::WebSocket, Data}, |     web::websocket::WebSocket, | ||||||
|     Body, Error, FromRequest, IntoResponse, Request, Response, Result, |     Body, Error, FromRequest, IntoResponse, Request, Response, Result, | ||||||
| }; | }; | ||||||
| use rand::seq::SliceRandom; | use rand::seq::SliceRandom; | ||||||
| use reqwest::Method; |  | ||||||
|  |  | ||||||
| use crate::proxies::{ | use crate::{ | ||||||
|  |     proxies::{ | ||||||
|         config::{Destination, DestinationType}, |         config::{Destination, DestinationType}, | ||||||
|         responder, |         responder, | ||||||
|  |     }, | ||||||
|  |     ROAD, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #[handler] | #[handler] | ||||||
| pub async fn handle( | pub async fn handle( | ||||||
|     app: Data<&super::Instance>, |  | ||||||
|     req: &Request, |     req: &Request, | ||||||
|     uri: &Uri, |     uri: &Uri, | ||||||
|     headers: &HeaderMap, |     headers: &HeaderMap, | ||||||
|     method: Method, |     method: Method, | ||||||
|     body: Body, |     body: Body, | ||||||
| ) -> Result<impl IntoResponse, Error> { | ) -> Result<impl IntoResponse, Error> { | ||||||
|     let location = match app.filter(uri, method.clone(), headers) { |     let readable_app = ROAD.lock().await; | ||||||
|  |     let (region, location) = match readable_app.filter(uri, method.clone(), headers) { | ||||||
|         Some(val) => val, |         Some(val) => val, | ||||||
|         None => { |         None => { | ||||||
|             return Err(Error::from_string( |             return Err(Error::from_string( | ||||||
| @@ -93,5 +96,27 @@ pub async fn handle( | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     forward(destination, req, uri, headers, method, body).await |     let reg = region.clone(); | ||||||
|  |     let loc = location.clone(); | ||||||
|  |     let end = destination.clone(); | ||||||
|  |  | ||||||
|  |     match forward(&end, req, uri, headers, method, body).await { | ||||||
|  |         Ok(resp) => { | ||||||
|  |             tokio::spawn(async move { | ||||||
|  |                 let writable_app = &mut ROAD.lock().await; | ||||||
|  |                 writable_app.metrics.add_success_request(reg, loc, end); | ||||||
|  |             }); | ||||||
|  |             Ok(resp) | ||||||
|  |         } | ||||||
|  |         Err(err) => { | ||||||
|  |             let message = format!("{:}", err); | ||||||
|  |             tokio::spawn(async move { | ||||||
|  |                 let writable_app = &mut ROAD.lock().await; | ||||||
|  |                 writable_app | ||||||
|  |                     .metrics | ||||||
|  |                     .add_faliure_request(reg, loc, end, message); | ||||||
|  |             }); | ||||||
|  |             Err(err) | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,14 +1,81 @@ | |||||||
| use poem_openapi::{param::Query, payload::PlainText, OpenApi}; | use poem_openapi::{payload::Json, ApiResponse, Object, OpenApi}; | ||||||
|  |  | ||||||
|  | use crate::{ | ||||||
|  |     proxies::{ | ||||||
|  |         config::{Destination, Location}, | ||||||
|  |         metrics::RoadTrace, | ||||||
|  |     }, | ||||||
|  |     ROAD, | ||||||
|  | }; | ||||||
|  |  | ||||||
| use super::SideloadApi; | use super::SideloadApi; | ||||||
|  |  | ||||||
|  | #[derive(ApiResponse)] | ||||||
|  | enum OverviewResponse { | ||||||
|  |     /// Return the overview data. | ||||||
|  |     #[oai(status = 200)] | ||||||
|  |     Ok(Json<OverviewData>), | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Object, Clone, PartialEq)] | ||||||
|  | struct OverviewData { | ||||||
|  |     /// Loaded regions count | ||||||
|  |     #[oai(read_only)] | ||||||
|  |     regions: usize, | ||||||
|  |     /// Loaded locations count | ||||||
|  |     #[oai(read_only)] | ||||||
|  |     locations: usize, | ||||||
|  |     /// Loaded destnations count | ||||||
|  |     #[oai(read_only)] | ||||||
|  |     destinations: usize, | ||||||
|  |     /// Recent requests count | ||||||
|  |     requests_count: u64, | ||||||
|  |     /// Recent requests success count | ||||||
|  |     faliures_count: u64, | ||||||
|  |     /// Recent requests falied count | ||||||
|  |     successes_count: u64, | ||||||
|  |     /// Recent requests success rate | ||||||
|  |     success_rate: f64, | ||||||
|  |     /// Recent successes | ||||||
|  |     recent_successes: Vec<RoadTrace>, | ||||||
|  |     /// Recent errors | ||||||
|  |     recent_errors: Vec<RoadTrace>, | ||||||
|  | } | ||||||
|  |  | ||||||
| #[OpenApi] | #[OpenApi] | ||||||
| impl SideloadApi { | impl SideloadApi { | ||||||
|     #[oai(path = "/hello", method = "get")] |     #[oai(path = "/", method = "get")] | ||||||
|     async fn index(&self, name: Query<Option<String>>) -> PlainText<String> { |     async fn index(&self) -> OverviewResponse { | ||||||
|         match name.0 { |         let locked_app = ROAD.lock().await; | ||||||
|             Some(name) => PlainText(format!("hello, {name}!")), |         let regions = locked_app.regions.clone(); | ||||||
|             None => PlainText("hello!".to_string()), |         let locations = regions | ||||||
|         } |             .iter() | ||||||
|  |             .flat_map(|item| item.locations.clone()) | ||||||
|  |             .collect::<Vec<Location>>(); | ||||||
|  |         let destinations = locations | ||||||
|  |             .iter() | ||||||
|  |             .flat_map(|item| item.destinations.clone()) | ||||||
|  |             .collect::<Vec<Destination>>(); | ||||||
|  |         OverviewResponse::Ok(Json(OverviewData { | ||||||
|  |             regions: regions.len(), | ||||||
|  |             locations: locations.len(), | ||||||
|  |             destinations: destinations.len(), | ||||||
|  |             requests_count: locked_app.metrics.requests_count, | ||||||
|  |             successes_count: locked_app.metrics.requests_count - locked_app.metrics.failures_count, | ||||||
|  |             faliures_count: locked_app.metrics.failures_count, | ||||||
|  |             success_rate: locked_app.metrics.get_success_rate(), | ||||||
|  |             recent_successes: locked_app | ||||||
|  |                 .metrics | ||||||
|  |                 .recent_successes | ||||||
|  |                 .clone() | ||||||
|  |                 .into_iter() | ||||||
|  |                 .collect::<Vec<_>>(), | ||||||
|  |             recent_errors: locked_app | ||||||
|  |                 .metrics | ||||||
|  |                 .recent_errors | ||||||
|  |                 .clone() | ||||||
|  |                 .into_iter() | ||||||
|  |                 .collect::<Vec<_>>(), | ||||||
|  |         })) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user