use super::config::ProxyDirective;
use trillium::Conn;
use trillium_proxy::{
Url,
upstream::{ConnectionCounting, RandomSelector, RoundRobin, UpstreamSelector},
};
#[derive(Debug, Clone)]
struct Base(Url);
impl UpstreamSelector for Base {
fn determine_upstream(&self, conn: &mut Conn) -> Option<Url> {
let mut url = self.0.clone();
let base_path = url.path().trim_end_matches('/');
let rest = conn.path().trim_start_matches('/');
url.set_path(&format!("{base_path}/{rest}"));
let query = conn.querystring();
url.set_query((!query.is_empty()).then_some(query));
Some(url)
}
}
pub fn build_selector(proxy: &ProxyDirective) -> Box<dyn UpstreamSelector> {
let bases: Vec<Base> = proxy
.upstreams
.iter()
.map(|u| {
Base(
u.url
.parse()
.unwrap_or_else(|e| panic!("invalid upstream url {:?}: {e}", u.url)),
)
})
.collect();
assert!(
!bases.is_empty(),
"proxy directive requires at least one `upstream`"
);
match proxy.strategy.as_deref().unwrap_or("round-robin") {
"round-robin" => RoundRobin::new(bases).boxed(),
"random" => RandomSelector::new(bases).boxed(),
"connection-counting" | "least-conn" => ConnectionCounting::new(bases).boxed(),
other => panic!(
"unknown proxy strategy {other:?}; use round-robin, random, or connection-counting"
),
}
}