use ::pingora::server::Server;
use anyhow::anyhow;
use bytes::Bytes;
use clap::crate_version;
use config::{load, Config, LogFormat, RouteHeaderAdd, RouteHeaderRemove, RoutePlugin};
use tracing_subscriber::{fmt::writer::MakeWriterExt, EnvFilter};
use std::{borrow::Cow, sync::Arc};
use pingora::{listeners::TlsSettings, proxy::http_proxy_service, server::configuration::Opt};
use proxy_server::cert_store::CertStore;
use services::BackgroundFunctionService;
use tracing_appender::{
non_blocking::{NonBlocking, WorkerGuard},
rolling::{RollingFileAppender, Rotation},
};
mod cache;
mod channel;
mod config;
mod plugins;
mod proxy_server;
mod server;
mod services;
mod stores;
mod tools;
#[derive(Clone, Default)]
pub struct MsgRoute {
host: Cow<'static, str>,
upstreams: Vec<String>,
path_matchers: Vec<String>,
host_headers_add: Vec<RouteHeaderAdd>,
host_headers_remove: Vec<RouteHeaderRemove>,
plugins: Vec<RoutePlugin>,
self_signed_certs: bool,
}
#[derive(Clone)]
pub struct MsgCert {
_cert: Bytes,
_key: Bytes,
}
#[derive(Clone)]
pub enum MsgProxy {
NewRoute(MsgRoute),
NewCertificate(MsgCert),
ConfigUpdate(()),
}
fn get_non_blocking_writer(config: &Config) -> (NonBlocking, WorkerGuard) {
if let Some(path) = config.logging.path.clone() {
let appender = match config.logging.rotation {
config::LogRotation::Daily => Rotation::DAILY,
config::LogRotation::Hourly => Rotation::HOURLY,
config::LogRotation::Minutely => Rotation::MINUTELY,
config::LogRotation::Never => Rotation::NEVER,
};
let file_appender = RollingFileAppender::builder()
.rotation(appender) .filename_prefix("proksi") .filename_suffix("log") .build(path) .expect("initializing rolling file appender failed");
return tracing_appender::non_blocking(file_appender);
}
tracing_appender::non_blocking(std::io::stdout())
}
#[deny(
clippy::all,
clippy::pedantic,
clippy::perf,
clippy::correctness,
clippy::style,
clippy::suspicious,
clippy::complexity
)]
fn main() -> Result<(), anyhow::Error> {
let proxy_config = Arc::new(
load("/etc/proksi/configs").map_err(|e| anyhow!("Failed to load configuration: {}", e))?,
);
let (sender, mut _receiver) = tokio::sync::broadcast::channel::<MsgProxy>(10);
let (appender, _guard) = get_non_blocking_writer(&proxy_config);
let cfg = proxy_config.clone();
let appender = appender.with_filter(move |meta| {
if !cfg.logging.access_logs_enabled && meta.fields().field("access_log").is_some() {
return false;
}
if !cfg.logging.error_logs_enabled && meta.level() == &tracing::Level::ERROR {
return false;
}
true
});
if proxy_config.logging.format == LogFormat::Json {
tracing_subscriber::fmt()
.json()
.with_env_filter(EnvFilter::from_default_env())
.with_max_level(&proxy_config.logging.level)
.with_writer(appender)
.init();
} else {
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.with_max_level(&proxy_config.logging.level)
.with_ansi(proxy_config.logging.path.is_none())
.with_writer(appender)
.init();
};
let pingora_opts = Opt {
daemon: proxy_config.daemon,
upgrade: proxy_config.upgrade,
conf: None,
nocapture: false,
test: false,
};
let mut pingora_server = Server::new(Some(pingora_opts))?;
pingora_server.bootstrap();
let mut http_public_service = http_proxy_service(
&pingora_server.configuration,
proxy_server::http_proxy::HttpLB {},
);
let router = proxy_server::https_proxy::Router {};
let mut https_secure_service = http_proxy_service(&pingora_server.configuration, router);
http_public_service.add_tcp("0.0.0.0:80");
https_secure_service.threads = proxy_config.worker_threads;
let cert_store = CertStore::new();
let mut tls_settings = TlsSettings::with_callbacks(Box::new(cert_store)).unwrap();
tls_settings.enable_h2();
tls_settings.set_servername_callback(move |ssl_ref, _| CertStore::sni_callback(ssl_ref));
tls_settings.set_min_proto_version(Some(pingora::tls::ssl::SslVersion::TLS1_2))?;
tls_settings.set_max_proto_version(Some(pingora::tls::ssl::SslVersion::TLS1_3))?;
https_secure_service.add_tls_with_settings("0.0.0.0:443", None, tls_settings);
pingora_server.add_service(BackgroundFunctionService::new(proxy_config.clone(), sender));
pingora_server.add_service(http_public_service);
pingora_server.add_service(https_secure_service);
tracing::info!(
version = crate_version!(),
workers = proxy_config.worker_threads,
"running on :443 and :80"
);
pingora_server.run_forever();
}