Rewrote and optimized healtchecks

This commit is contained in:
Ara Sadoyan
2026-08-05 18:51:07 +02:00
parent 600cb602e3
commit b63d822577
10 changed files with 214 additions and 132 deletions
Generated
+49 -32
View File
@@ -127,14 +127,14 @@ checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
[[package]] [[package]]
name = "aralez" name = "aralez"
version = "0.92.16" version = "0.92.17"
dependencies = [ dependencies = [
"ahash", "ahash",
"arc-swap", "arc-swap",
"async-trait", "async-trait",
"axum", "axum",
"base16ct 1.0.0", "base16ct 1.0.0",
"base64", "base64 0.23.1",
"bytes", "bytes",
"dashmap", "dashmap",
"http-body-util", "http-body-util",
@@ -230,13 +230,13 @@ dependencies = [
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.89" version = "0.1.91"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.118", "syn 3.0.3",
] ]
[[package]] [[package]]
@@ -360,6 +360,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
[[package]] [[package]]
name = "base64ct" name = "base64ct"
version = "1.8.3" version = "1.8.3"
@@ -1425,9 +1431,9 @@ dependencies = [
[[package]] [[package]]
name = "http-body-util" name = "http-body-util"
version = "0.1.3" version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-core", "futures-core",
@@ -1471,9 +1477,9 @@ dependencies = [
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "1.10.1" version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72"
dependencies = [ dependencies = [
"atomic-waker", "atomic-waker",
"bytes", "bytes",
@@ -1514,7 +1520,7 @@ version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
dependencies = [ dependencies = [
"base64", "base64 0.22.1",
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-util", "futures-util",
@@ -1716,7 +1722,7 @@ checksum = "9f05ad37c421b962354c358d347d4a6130151df9407978372d3ad7f0c8f71a64"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"aws-lc-rs", "aws-lc-rs",
"base64", "base64 0.22.1",
"bytes", "bytes",
"http", "http",
"http-body", "http-body",
@@ -1736,9 +1742,9 @@ dependencies = [
[[package]] [[package]]
name = "ipnet" name = "ipnet"
version = "2.12.0" version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78"
[[package]] [[package]]
name = "is_terminal_polyfill" name = "is_terminal_polyfill"
@@ -1824,11 +1830,11 @@ dependencies = [
[[package]] [[package]]
name = "jsonwebtoken" name = "jsonwebtoken"
version = "10.4.0" version = "11.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eba32bfb4ffdeaca3e34431072faf01745c9b26d25504aa7a6cf5684334fc4fc" checksum = "881733cbc631fc9e472e24447ce32a64bedf2da498d6d8570b08edc87de71f65"
dependencies = [ dependencies = [
"base64", "base64 0.22.1",
"ed25519-dalek", "ed25519-dalek",
"getrandom 0.2.17", "getrandom 0.2.17",
"hmac", "hmac",
@@ -2206,9 +2212,9 @@ dependencies = [
[[package]] [[package]]
name = "noyalib" name = "noyalib"
version = "0.0.15" version = "0.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9014fb34654ac4f54a4ff52d5207b1e4a764604fced568c3a1d71ee99d1fc300" checksum = "ccc67d13e91577493ff12d930fcdc92a342b46e2299210089d90d2caed837f60"
dependencies = [ dependencies = [
"indexmap 2.14.0", "indexmap 2.14.0",
"itoa", "itoa",
@@ -2451,7 +2457,7 @@ version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
dependencies = [ dependencies = [
"base64", "base64 0.22.1",
"serde_core", "serde_core",
] ]
@@ -3113,7 +3119,7 @@ version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3"
dependencies = [ dependencies = [
"base64", "base64 0.22.1",
"bytes", "bytes",
"encoding_rs", "encoding_rs",
"futures-channel", "futures-channel",
@@ -3441,9 +3447,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.228" version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [ dependencies = [
"serde_core", "serde_core",
"serde_derive", "serde_derive",
@@ -3461,22 +3467,22 @@ dependencies = [
[[package]] [[package]]
name = "serde_core" name = "serde_core"
version = "1.0.228" version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.228" version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.118", "syn 3.0.3",
] ]
[[package]] [[package]]
@@ -3491,9 +3497,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.150" version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
@@ -3544,7 +3550,7 @@ version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fa1f336066b758b7c9df34ed049c0e693a426afe2b27ff7d5b14f410ab1a132" checksum = "3fa1f336066b758b7c9df34ed049c0e693a426afe2b27ff7d5b14f410ab1a132"
dependencies = [ dependencies = [
"base64", "base64 0.22.1",
"indexmap 2.14.0", "indexmap 2.14.0",
"rust_decimal", "rust_decimal",
] ]
@@ -3741,6 +3747,17 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "syn"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]] [[package]]
name = "sync_wrapper" name = "sync_wrapper"
version = "1.0.2" version = "1.0.2"
@@ -3927,9 +3944,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.52.3" version = "1.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed"
dependencies = [ dependencies = [
"bytes", "bytes",
"libc", "libc",
+10 -10
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "aralez" name = "aralez"
version = "0.92.16" version = "0.92.17"
edition = "2021" edition = "2021"
license = "Apache-2.0" license = "Apache-2.0"
description = "Reverse proxy built on top of Cloudflare's Pingora" description = "Reverse proxy built on top of Cloudflare's Pingora"
@@ -15,7 +15,7 @@ panic = "abort"
strip = true strip = true
[dependencies] [dependencies]
tokio = { version = "1.52.3", features = ["full"] } tokio = { version = "1.53.1", features = ["full"] }
pingora = { version = "0.8.1", features = ["lb", "openssl"] } # openssl, rustls, boringssl pingora = { version = "0.8.1", features = ["lb", "openssl"] } # openssl, rustls, boringssl
pingora-core = "0.8.1" pingora-core = "0.8.1"
pingora-proxy = "0.8.1" pingora-proxy = "0.8.1"
@@ -23,16 +23,16 @@ pingora-http = "0.8.1"
pingora-limits = "0.8.1" pingora-limits = "0.8.1"
pingora-cache = "0.8.1" pingora-cache = "0.8.1"
dashmap = "7.0.0-rc2" dashmap = "7.0.0-rc2"
async-trait = "0.1.89" async-trait = "0.1.91"
log = "0.4.33" log = "0.4.33"
notify = "9.0.0-rc.4" notify = "9.0.0-rc.4"
axum = { version = "0.8.9" } axum = { version = "0.8.9" }
reqwest = { version = "0.13.4", features = ["json", "stream", "blocking"] } reqwest = { version = "0.13.4", features = ["json", "stream", "blocking"] }
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.229", features = ["derive"] }
noyalib = { version = "0.0.15", features = ["compat-serde-yaml"] } noyalib = { version = "0.0.18", features = ["compat-serde-yaml"] }
rand = "0.10.2" rand = "0.10.2"
base64 = "0.22.1" base64 = "0.23.1"
jsonwebtoken = { version = "10.4.0", default-features = false, features = ["use_pem", "rust_crypto"] } jsonwebtoken = { version = "11.0.0", default-features = false, features = ["use_pem", "rust_crypto"] }
sha2 = { version = "0.11.0-rc.5", default-features = false } sha2 = { version = "0.11.0-rc.5", default-features = false }
base16ct = { version = "1.0.0", features = ["alloc"] } base16ct = { version = "1.0.0", features = ["alloc"] }
urlencoding = "2.1.3" urlencoding = "2.1.3"
@@ -42,7 +42,7 @@ x509-parser = "0.18.1"
rustls-pemfile = "2.2.0" rustls-pemfile = "2.2.0"
tower-http = { version = "0.7.0", features = ["fs"] } tower-http = { version = "0.7.0", features = ["fs"] }
privdrop = "0.5.6" privdrop = "0.5.6"
serde_json = "1.0.150" serde_json = "1.0.151"
subtle = "2.6.1" subtle = "2.6.1"
moka = { version = "0.12.15", features = ["sync"] } moka = { version = "0.12.15", features = ["sync"] }
ahash = "0.8.12" ahash = "0.8.12"
@@ -54,8 +54,8 @@ signal-hook = "0.4.4"
sd-notify = "0.5.0" sd-notify = "0.5.0"
libc = "0.2.186" libc = "0.2.186"
tokio-native-tls = "0.3.1" tokio-native-tls = "0.3.1"
hyper = "1.10.1" hyper = "1.11.0"
hyper-util = "0.1.20" hyper-util = "0.1.20"
http-body-util = "0.1.3" http-body-util = "0.1.4"
bytes = "1.12.1" bytes = "1.12.1"
+1 -4
View File
@@ -1,17 +1,14 @@
use crate::tls::acme::account::get_account; use crate::tls::acme::account::get_account;
use crate::utils::lazylock::CHALLENGES;
use crate::utils::parceyaml::DOMAINS; use crate::utils::parceyaml::DOMAINS;
use instant_acme::{ChallengeType, Identifier, NewOrder, RetryPolicy}; use instant_acme::{ChallengeType, Identifier, NewOrder, RetryPolicy};
use log::{error, info}; use log::{error, info};
use pingora::prelude::sleep; use pingora::prelude::sleep;
use rcgen::{CertificateParams, DistinguishedName, KeyPair}; use rcgen::{CertificateParams, DistinguishedName, KeyPair};
use std::collections::HashMap;
use std::fs; use std::fs;
use std::sync::{LazyLock, RwLock};
use std::time::Duration; use std::time::Duration;
use x509_parser::prelude::*; use x509_parser::prelude::*;
pub static CHALLENGES: LazyLock<RwLock<HashMap<String, String>>> = LazyLock::new(|| RwLock::new(HashMap::new()));
pub async fn refresh_order(certs_dir: String, autoconf_dir: String) { pub async fn refresh_order(certs_dir: String, autoconf_dir: String) {
let credsfile = autoconf_dir + "/acme_credentials.json"; let credsfile = autoconf_dir + "/acme_credentials.json";
loop { loop {
+1
View File
@@ -3,6 +3,7 @@ pub mod discovery;
mod filewatch; mod filewatch;
pub mod fordebug; pub mod fordebug;
pub mod grpc; pub mod grpc;
pub mod hcclient;
pub mod healthcheck; pub mod healthcheck;
pub mod httpclient; pub mod httpclient;
pub mod jwt; pub mod jwt;
+20 -19
View File
@@ -1,5 +1,6 @@
use crate::utils::jwt::{check_jwt, JWT_TOKEN}; use crate::utils::jwt::{check_jwt, JWT_TOKEN};
use crate::utils::structs::InnerAuth; use crate::utils::structs::InnerAuth;
pub(crate) use crate::utils::tools::split_host_port;
use axum::http::StatusCode; use axum::http::StatusCode;
use base64::engine::general_purpose::STANDARD; use base64::engine::general_purpose::STANDARD;
use base64::Engine; use base64::Engine;
@@ -224,22 +225,22 @@ pub fn get_query_param(session: &mut Session, key: &str) -> Option<String> {
params.get(key).and_then(|v| decode(v).ok()).map(|s| s.to_string()) params.get(key).and_then(|v| decode(v).ok()).map(|s| s.to_string())
} }
#[allow(clippy::needless_return)] // #[allow(clippy::needless_return)]
fn split_host_port(addr: &str, tls: bool) -> Option<(&str, u16, bool, &str)> { // pub fn split_host_port(addr: &str, tls: bool) -> Option<(&str, u16, bool, &str)> {
match addr.split_once(':') { // match addr.split_once(':') {
Some((h, p)) => match p.parse::<u16>() { // Some((h, p)) => match p.parse::<u16>() {
Ok(port) => return Some((h, port, tls, h)), // Ok(port) => return Some((h, port, tls, h)),
Err(_) => { // Err(_) => {
log::warn!("ForwardAuth: invalid port in {}", addr); // log::warn!("ForwardAuth: invalid port in {}", addr);
return None; // return None;
} // }
}, // },
None => { // None => {
if tls { // if tls {
return Some((addr, 443u16, tls, addr)); // return Some((addr, 443u16, tls, addr));
} else { // } else {
return Some((addr, 80u16, tls, addr)); // return Some((addr, 80u16, tls, addr));
} // }
} // }
}; // };
} // }
+93
View File
@@ -0,0 +1,93 @@
use crate::utils::grpc::ping_grpc;
use bytes::Bytes;
use pingora_core::connectors::http::Connector;
use pingora_core::listeners::ALPN;
use pingora_core::prelude::HttpPeer;
use pingora_http::RequestHeader;
use std::sync::LazyLock;
pub static HC_CONNECTOR: LazyLock<Connector> = LazyLock::new(|| Connector::new(None));
pub async fn httpclient(method: &str, tls: bool, host: &str, path: &str, address: &str, port: u16, url: String) -> (bool, bool) {
let method = match method {
"HEAD" => "HEAD",
"GET" => "GET",
"POST" => "POST",
_ => "GET",
};
let mut peer = HttpPeer::new((address, port), tls, host.to_string());
if tls {
peer.options.verify_cert = false;
peer.options.verify_hostname = false;
}
let mut is_h2 = false;
peer.options.alpn = ALPN::H2H1;
let (mut http_session, _) = match HC_CONNECTOR.get_http_session(&peer).await {
Ok(s) => s,
Err(e) => {
if let Some(msg) = e.context {
log::warn!("{}, type: {}", msg.as_str(), e.etype.as_str());
} else {
log::warn!("Fail to connect to addr: {}, tls {}, host {}", address, tls, host);
}
return (false, false);
}
};
match http_session.as_http2() {
Some(_) => {
peer.options.alpn = ALPN::H2;
is_h2 = true;
}
None => {
if ping_grpc(url.as_str()).await {
return (true, true);
}
}
}
let mut req = match RequestHeader::build(method, path.as_bytes(), None) {
Ok(r) => r,
Err(e) => {
log::warn!("Failed to build request: {}", e);
return (false, false);
}
};
req.insert_header("Host", host).ok();
if let Err(e) = http_session.write_request_header(Box::new(req)).await {
log::warn!("Write header failed: {}", e);
return (false, false);
}
let status = if is_h2 {
if let Err(e) = http_session.write_request_body(Bytes::new(), true).await {
log::warn!("Write body failed: {}", e);
return (false, false);
}
match http_session.read_response_header().await {
Ok(_) => http_session.response_header().map(|r| r.status.as_u16()).unwrap_or(500),
Err(e) => {
if ping_grpc(url.as_str()).await {
return (true, true);
}
log::warn!("Health Check H2 read failed: {} - {}", host, e);
return (false, false);
}
}
} else {
match http_session.read_response_header().await {
Ok(_) => http_session.response_header().map(|r| r.status.as_u16()).unwrap_or(500),
Err(e) => {
log::warn!("Health Check H1 read failed: {} - {}", e, host);
return (false, false);
}
}
};
HC_CONNECTOR.release_http_session(http_session, &peer, None).await;
((200..500).contains(&status), is_h2)
}
+16 -61
View File
@@ -1,10 +1,8 @@
use crate::utils::grpc::ping_grpc; use crate::utils::hcclient::httpclient;
use crate::utils::lazylock::REVERSE_STORE; use crate::utils::lazylock::REVERSE_STORE;
use crate::utils::structs::{InnerMap, UpstreamsDashMap, UpstreamsIdMap}; use crate::utils::structs::{InnerMap, UpstreamsDashMap, UpstreamsIdMap};
use crate::utils::tools::*; use crate::utils::tools::*;
use dashmap::DashMap; use dashmap::DashMap;
use log::{error, warn};
use reqwest::{Client, Version};
use std::sync::atomic::AtomicUsize; use std::sync::atomic::AtomicUsize;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
@@ -14,12 +12,10 @@ use tokio_native_tls::native_tls::TlsConnector;
pub async fn hc2(upslist: Arc<UpstreamsDashMap>, fullist: Arc<UpstreamsDashMap>, idlist: Arc<UpstreamsIdMap>, params: (&str, u64)) { pub async fn hc2(upslist: Arc<UpstreamsDashMap>, fullist: Arc<UpstreamsDashMap>, idlist: Arc<UpstreamsIdMap>, params: (&str, u64)) {
let mut period = interval(Duration::from_secs(params.1)); let mut period = interval(Duration::from_secs(params.1));
let client = Client::builder().timeout(Duration::from_secs(params.1)).danger_accept_invalid_certs(true).build().unwrap();
loop { loop {
tokio::select! { tokio::select! {
_ = period.tick() => { _ = period.tick() => {
// populate_upstreams(&upslist, &fullist, &idlist, params, &client).await; let totest = build_upstreams(&fullist, params.0).await;
let totest = build_upstreams(&fullist, params.0, &client).await;
if !compare_dashmaps(&totest, &upslist) { if !compare_dashmaps(&totest, &upslist) {
clone_dashmap_into(&totest, &upslist); clone_dashmap_into(&totest, &upslist);
clone_idmap_into(&totest, &idlist); clone_idmap_into(&totest, &idlist);
@@ -31,11 +27,10 @@ pub async fn hc2(upslist: Arc<UpstreamsDashMap>, fullist: Arc<UpstreamsDashMap>,
} }
pub async fn initiate_upstreams(fullist: UpstreamsDashMap) -> UpstreamsDashMap { pub async fn initiate_upstreams(fullist: UpstreamsDashMap) -> UpstreamsDashMap {
let client = Client::builder().timeout(Duration::from_secs(2)).danger_accept_invalid_certs(true).build().unwrap(); build_upstreams(&fullist, "HEAD").await
build_upstreams(&fullist, "HEAD", &client).await
} }
async fn build_upstreams(fullist: &UpstreamsDashMap, method: &str, client: &Client) -> UpstreamsDashMap { async fn build_upstreams(fullist: &UpstreamsDashMap, method: &str) -> UpstreamsDashMap {
let totest: UpstreamsDashMap = DashMap::new(); let totest: UpstreamsDashMap = DashMap::new();
let fclone = clone_dashmap(fullist); let fclone = clone_dashmap(fullist);
for val in fclone.iter() { for val in fclone.iter() {
@@ -48,15 +43,15 @@ async fn build_upstreams(fullist: &UpstreamsDashMap, method: &str, client: &Clie
for upstream in path_entry.value().0.iter() { for upstream in path_entry.value().0.iter() {
let tls = if upstream.healthcheck.unwrap_or(true) { let tls = if upstream.healthcheck.unwrap_or(true) {
detect_tls(upstream.address.as_ref(), &upstream.port, client).await detect_tls(upstream.address.as_ref(), &upstream.port, host).await
} else { } else {
(false, None) false
}; };
let mut scheme = InnerMap { let mut scheme = InnerMap {
address: upstream.address.clone(), address: upstream.address.clone(),
port: upstream.port, port: upstream.port,
is_ssl: tls.0, is_ssl: tls,
is_http2: matches!(tls.1, Some(Version::HTTP_2)), is_http2: false,
to_https: upstream.to_https, to_https: upstream.to_https,
rate_limit: upstream.rate_limit, rate_limit: upstream.rate_limit,
x4xx_limit: upstream.x4xx_limit, x4xx_limit: upstream.x4xx_limit,
@@ -66,21 +61,17 @@ async fn build_upstreams(fullist: &UpstreamsDashMap, method: &str, client: &Clie
}; };
if scheme.healthcheck.unwrap_or(true) { if scheme.healthcheck.unwrap_or(true) {
let link = if tls.0 { let link = if tls {
format!("https://{}:{}{}", upstream.address, upstream.port, path) format!("https://{}:{}{}", upstream.address, upstream.port, path)
} else { } else {
format!("http://{}:{}{}", upstream.address, upstream.port, path) format!("http://{}:{}{}", upstream.address, upstream.port, path)
}; };
let resp = httpclient(method, tls, host, path, upstream.address.as_ref(), upstream.port, link).await;
let resp = http_request(&link, method, "", client).await;
if resp.0 { if resp.0 {
if resp.1 { if resp.1 {
scheme.is_http2 = resp.1; scheme.is_http2 = resp.1;
} }
innervec.push(Arc::from(scheme)); innervec.push(Arc::from(scheme));
} else {
warn!("Dead Upstream : {}", link);
} }
} else { } else {
innervec.push(Arc::from(scheme)); innervec.push(Arc::from(scheme));
@@ -93,33 +84,12 @@ async fn build_upstreams(fullist: &UpstreamsDashMap, method: &str, client: &Clie
totest totest
} }
async fn http_request(url: &str, method: &str, payload: &str, client: &Client) -> (bool, bool) { async fn detect_tls(ip: &str, port: &u16, sni: &str) -> bool {
if !["POST", "GET", "HEAD"].contains(&method) { let addr = format!("{ip}:{port}");
error!("Method {} not supported. Only GET|POST|HEAD are supported ", method);
return (false, false);
}
async fn send_request(client: &Client, method: &str, url: &str, payload: &str) -> Option<reqwest::Response> {
match method {
"POST" => client.post(url).body(payload.to_owned()).send().await.ok(),
"GET" => client.get(url).send().await.ok(),
"HEAD" => client.head(url).send().await.ok(),
_ => None,
}
}
match send_request(client, method, url, payload).await { let stream = match TcpStream::connect(&addr).await {
Some(response) => { Ok(s) => s,
let status = response.status().as_u16(); Err(_) => return false,
((99..499).contains(&status), false)
}
None => (ping_grpc(url).await, true),
}
}
async fn detect_tls(ip: &str, port: &u16, client: &Client) -> (bool, Option<Version>) {
let addr = format!("{}:{}", ip, port);
let Ok(stream) = TcpStream::connect(&addr).await else {
return (false, Some(Version::HTTP_11));
}; };
let connector = tokio_native_tls::TlsConnector::from( let connector = tokio_native_tls::TlsConnector::from(
@@ -129,20 +99,5 @@ async fn detect_tls(ip: &str, port: &u16, client: &Client) -> (bool, Option<Vers
.build() .build()
.unwrap(), .unwrap(),
); );
connector.connect(sni, stream).await.is_ok()
let tls = connector.connect(ip, stream).await.is_ok();
if tls {
let vers = detect_version(ip, *port, true, client).await;
return (tls, vers);
}
(tls, Some(Version::HTTP_11))
}
async fn detect_version(ip: &str, port: u16, is_tls: bool, client: &Client) -> Option<Version> {
let scheme = if is_tls { "https" } else { "http" };
let url = format!("{}://{}:{}", scheme, ip, port);
match client.get(&url).send().await.ok().map(|r| r.version()) {
Some(version) => Some(version),
None => Some(Version::HTTP_11),
}
} }
+3 -1
View File
@@ -4,9 +4,10 @@ use pingora_cache::eviction::simple_lru::Manager;
use pingora_cache::lock::CacheLock; use pingora_cache::lock::CacheLock;
use pingora_cache::MemCache; use pingora_cache::MemCache;
use pingora_limits::rate::Rate; use pingora_limits::rate::Rate;
use std::collections::HashMap;
use std::net::IpAddr; use std::net::IpAddr;
use std::sync::OnceLock;
use std::sync::{Arc, LazyLock}; use std::sync::{Arc, LazyLock};
use std::sync::{OnceLock, RwLock};
use std::time::Duration; use std::time::Duration;
pub static REVERSE_STORE: LazyLock<DashMap<String, String>> = LazyLock::new(DashMap::new); pub static REVERSE_STORE: LazyLock<DashMap<String, String>> = LazyLock::new(DashMap::new);
@@ -17,3 +18,4 @@ pub static MEM_CACHE: LazyLock<MemCache> = LazyLock::new(MemCache::new);
pub static CACHE_LOCK: LazyLock<CacheLock> = LazyLock::new(|| CacheLock::new(Duration::from_secs(30))); pub static CACHE_LOCK: LazyLock<CacheLock> = LazyLock::new(|| CacheLock::new(Duration::from_secs(30)));
pub static EVICTION: OnceLock<Manager> = OnceLock::new(); pub static EVICTION: OnceLock<Manager> = OnceLock::new();
pub static CACHE_TTL: OnceLock<u64> = OnceLock::new(); pub static CACHE_TTL: OnceLock<u64> = OnceLock::new();
pub static CHALLENGES: LazyLock<RwLock<HashMap<String, String>>> = LazyLock::new(|| RwLock::new(HashMap::new()));
+20
View File
@@ -377,3 +377,23 @@ pub fn write_pid_file(path: &str) -> std::io::Result<()> {
file.write_all(process::id().to_string().as_bytes())?; file.write_all(process::id().to_string().as_bytes())?;
Ok(()) Ok(())
} }
#[allow(clippy::needless_return)]
pub fn split_host_port(addr: &str, tls: bool) -> Option<(&str, u16, bool, &str)> {
match addr.split_once(':') {
Some((h, p)) => match p.parse::<u16>() {
Ok(port) => return Some((h, port, tls, h)),
Err(_) => {
log::warn!("ForwardAuth: invalid port in {}", addr);
return None;
}
},
None => {
if tls {
return Some((addr, 443u16, tls, addr));
} else {
return Some((addr, 80u16, tls, addr));
}
}
};
}
+1 -5
View File
@@ -1,5 +1,5 @@
use crate::tls::acme::order::CHALLENGES;
use crate::tls::acme::{account, order}; use crate::tls::acme::{account, order};
use crate::utils::lazylock::CHALLENGES;
use axum::body::Body; use axum::body::Body;
use axum::extract::State; use axum::extract::State;
use axum::http::{Response, StatusCode}; use axum::http::{Response, StatusCode};
@@ -44,10 +44,6 @@ pub async fn acme_order(State(state): State<crate::web::webserver::AppState>, ax
} }
pub async fn http01_challenge(axum::extract::Path(token): axum::extract::Path<String>) -> impl IntoResponse { pub async fn http01_challenge(axum::extract::Path(token): axum::extract::Path<String>) -> impl IntoResponse {
if let Ok(challenges) = CHALLENGES.read() { if let Ok(challenges) = CHALLENGES.read() {
// for k in challenges.iter() {
// println!(" ==> {} : {}", k.0, k.1);
// }
if let Some(key_authorization) = challenges.get(&token) { if let Some(key_authorization) = challenges.get(&token) {
return Response::builder() return Response::builder()
.status(StatusCode::OK) .status(StatusCode::OK)