use crate::error::FaucetResult;
use log::{info, warn};
use std::net::SocketAddr;
use tokio::net::TcpListener;
use crate::{
load_balancing,
worker::{WorkerType, Workers},
};
pub struct FaucetServer {
strategy: load_balancing::Strategy,
bind: SocketAddr,
n_workers: usize,
server_type: Option<WorkerType>,
workdir: Box<std::path::Path>,
}
impl Default for FaucetServer {
fn default() -> Self {
Self::new()
}
}
impl FaucetServer {
pub fn new() -> Self {
Self {
strategy: load_balancing::Strategy::RoundRobinIpHash,
bind: ([0, 0, 0, 0], 3000).into(),
n_workers: 1,
server_type: None,
workdir: std::env::current_dir().unwrap().into(),
}
}
pub fn strategy(mut self, strategy: load_balancing::Strategy) -> Self {
self.strategy = strategy;
self
}
pub fn bind(mut self, bind: SocketAddr) -> Self {
self.bind = bind;
self
}
pub fn workers(mut self, n: usize) -> Self {
self.n_workers = n;
self
}
pub fn server_type(mut self, server_type: WorkerType) -> Self {
self.server_type = Some(server_type);
if server_type == WorkerType::Shiny {
warn!("Using server type Shiny, switching to RoundRobinIpHash strategy");
self.strategy = load_balancing::Strategy::RoundRobinIpHash;
}
self
}
pub fn workdir(mut self, workdir: impl AsRef<std::path::Path>) -> Self {
self.workdir = workdir.as_ref().into();
self
}
pub async fn run(self) -> FaucetResult<()> {
let mut workers = Workers::new(
self.server_type.unwrap_or(WorkerType::Plumber),
self.workdir,
);
workers.spawn(self.n_workers)?;
let targets = workers.get_socket_addrs();
let load_balancer = load_balancing::LoadBalancer::new(self.strategy, targets);
let listener = TcpListener::bind(self.bind).await?;
info!("Listening on http://{}", self.bind);
loop {
let load_balancer = load_balancer.clone();
let (tcp, x) = listener.accept().await?;
tokio::task::spawn(async move {
if let Err(e) = load_balancer.bridge(tcp, x).await {
log::warn!("Dropping connection due to error: {}", e);
};
});
}
}
}