-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
334 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
use std::sync::Arc; | ||
|
||
use axum::{response::IntoResponse, routing::get, Router}; | ||
use http::StatusCode; | ||
|
||
use crate::app::{api::AppState, ThreadSafeDNSResolver}; | ||
|
||
#[derive(Clone)] | ||
struct DNSState { | ||
resolver: ThreadSafeDNSResolver, | ||
} | ||
|
||
pub fn routes(resolver: ThreadSafeDNSResolver) -> Router<Arc<AppState>> { | ||
let state = DNSState { resolver }; | ||
Router::new() | ||
.route("/dns", get(query_dns)) | ||
.with_state(state) | ||
} | ||
|
||
async fn query_dns() -> impl IntoResponse { | ||
StatusCode::NOT_IMPLEMENTED | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
pub mod config; | ||
pub mod connection; | ||
pub mod dns; | ||
pub mod hello; | ||
pub mod log; | ||
pub mod provider; | ||
pub mod proxy; | ||
pub mod rule; | ||
pub mod version; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
use std::{collections::HashMap, sync::Arc, time::Duration}; | ||
|
||
use axum::{ | ||
extract::{Path, Query, State}, | ||
http::Request, | ||
http::StatusCode, | ||
middleware::{self, Next}, | ||
response::{IntoResponse, Response}, | ||
routing::get, | ||
Extension, Router, | ||
}; | ||
use serde::Deserialize; | ||
|
||
use crate::app::{api::AppState, outbound::manager::ThreadSafeOutboundManager}; | ||
use crate::{ | ||
app::proxy_manager::providers::proxy_provider::ThreadSafeProxyProvider, | ||
proxy::AnyOutboundHandler, | ||
}; | ||
#[derive(Clone)] | ||
struct ProviderState { | ||
outbound_manager: ThreadSafeOutboundManager, | ||
} | ||
|
||
pub fn routes(outbound_manager: ThreadSafeOutboundManager) -> Router<Arc<AppState>> { | ||
let state = ProviderState { outbound_manager }; | ||
Router::new() | ||
.route("/", get(get_providers)) | ||
.nest( | ||
"/:provider_name", | ||
Router::new() | ||
.route("/", get(get_provider).put(update_provider)) | ||
.route("/healthcheck", get(provider_healthcheck)) | ||
.nest( | ||
"/:proxy_name", | ||
Router::new() | ||
.route("/", get(get_proxy)) | ||
.route("/healthcheck", get(get_proxy_delay)) | ||
.layer(middleware::from_fn_with_state( | ||
state.clone(), | ||
find_provider_proxy_by_name, | ||
)) | ||
.with_state(state.clone()), | ||
) | ||
.layer(middleware::from_fn_with_state( | ||
state.clone(), | ||
find_proxy_provider_by_name, | ||
)) | ||
.with_state(state.clone()), | ||
) | ||
.with_state(state) | ||
} | ||
|
||
async fn get_providers(State(state): State<ProviderState>) -> impl IntoResponse { | ||
let outbound_manager = state.outbound_manager.read().await; | ||
let mut res = HashMap::new(); | ||
|
||
let mut providers = HashMap::new(); | ||
|
||
for (name, p) in outbound_manager.get_proxy_providers() { | ||
let m = p.lock().await.as_map().await; | ||
providers.insert(name, m); | ||
} | ||
|
||
res.insert("providers".to_owned(), providers); | ||
axum::response::Json(res) | ||
} | ||
|
||
async fn find_proxy_provider_by_name<B>( | ||
State(state): State<ProviderState>, | ||
Path(name): Path<String>, | ||
mut req: Request<B>, | ||
next: Next<B>, | ||
) -> Response { | ||
let outbound_manager = state.outbound_manager.read().await; | ||
if let Some(provider) = outbound_manager.get_proxy_provider(&name) { | ||
req.extensions_mut().insert(provider); | ||
next.run(req).await | ||
} else { | ||
( | ||
StatusCode::NOT_FOUND, | ||
format!("proxy provider {} not found", name), | ||
) | ||
.into_response() | ||
} | ||
} | ||
|
||
async fn get_provider( | ||
Extension(provider): Extension<ThreadSafeProxyProvider>, | ||
) -> impl IntoResponse { | ||
let provider = provider.lock().await; | ||
axum::response::Json(provider.as_map().await) | ||
} | ||
|
||
async fn update_provider( | ||
Extension(provider): Extension<ThreadSafeProxyProvider>, | ||
) -> impl IntoResponse { | ||
let provider = provider.lock().await; | ||
match provider.update().await { | ||
Ok(_) => (StatusCode::ACCEPTED, "provider update started").into_response(), | ||
Err(err) => ( | ||
StatusCode::INTERNAL_SERVER_ERROR, | ||
format!( | ||
"update proxy provider {} failed with error {}", | ||
provider.name(), | ||
err.to_string() | ||
), | ||
) | ||
.into_response(), | ||
} | ||
} | ||
|
||
async fn provider_healthcheck( | ||
Extension(provider): Extension<ThreadSafeProxyProvider>, | ||
) -> impl IntoResponse { | ||
let provider = provider.lock().await; | ||
provider.healthcheck().await; | ||
(StatusCode::ACCEPTED, "provider healthcheck started") | ||
} | ||
|
||
async fn find_provider_proxy_by_name<B>( | ||
Extension(provider): Extension<ThreadSafeProxyProvider>, | ||
Path(params): Path<HashMap<String, String>>, | ||
mut req: Request<B>, | ||
next: Next<B>, | ||
) -> Response { | ||
let proxy = provider.lock().await.proxies().await; | ||
let proxy = proxy | ||
.iter() | ||
.find(|x| Some(&x.name().to_string()) == params.get("proxy_name")); | ||
|
||
if let Some(proxy) = proxy { | ||
req.extensions_mut().insert(proxy.clone()); | ||
next.run(req).await | ||
} else { | ||
( | ||
StatusCode::NOT_FOUND, | ||
format!( | ||
"proxy {} not found in provider {}", | ||
params.get("proxy_name").unwrap(), | ||
provider.lock().await.name() | ||
), | ||
) | ||
.into_response() | ||
} | ||
} | ||
|
||
async fn get_proxy( | ||
Extension(proxy): Extension<AnyOutboundHandler>, | ||
State(state): State<ProviderState>, | ||
) -> impl IntoResponse { | ||
let outbound_manager = state.outbound_manager.read().await; | ||
axum::response::Json(outbound_manager.get_proxy(&proxy).await) | ||
} | ||
|
||
#[derive(Deserialize)] | ||
struct DelayRequest { | ||
url: String, | ||
timeout: u16, | ||
} | ||
async fn get_proxy_delay( | ||
State(state): State<ProviderState>, | ||
Extension(proxy): Extension<AnyOutboundHandler>, | ||
Query(q): Query<DelayRequest>, | ||
) -> impl IntoResponse { | ||
let outbound_manager = state.outbound_manager.read().await; | ||
let timeout = Duration::from_millis(q.timeout.into()); | ||
let n = proxy.name().to_owned(); | ||
match outbound_manager.url_test(proxy, &q.url, timeout).await { | ||
Ok((delay, mean_delay)) => { | ||
let mut r = HashMap::new(); | ||
r.insert("delay".to_owned(), delay); | ||
r.insert("meanDelay".to_owned(), mean_delay); | ||
axum::response::Json(delay).into_response() | ||
} | ||
Err(err) => ( | ||
StatusCode::BAD_REQUEST, | ||
format!("get delay for {} failed with error: {}", n, err), | ||
) | ||
.into_response(), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.