use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use tokio_util::sync::CancellationToken;
use crate::error::ProxyError;
use crate::event::{ProxyEvent, SessionId};
use crate::hook::{NoOpHook, ProxyHook};
use crate::listener::{AcceptedConn, Listener, ListenerConfig};
use crate::observer::ProxyObserver;
use crate::session::{ProxySession, ProxySessionConfig};
pub struct ProxyConfig {
pub listener: ListenerConfig,
pub session: ProxySessionConfig,
}
pub struct TransparentProxy {
config: ProxyConfig,
observer: Arc<dyn ProxyObserver>,
hook: Arc<dyn ProxyHook>,
cancel: CancellationToken,
next_session_id: AtomicU64,
}
impl TransparentProxy {
pub fn new(config: ProxyConfig, observer: Arc<dyn ProxyObserver>) -> Self {
Self {
config,
observer,
hook: Arc::new(NoOpHook),
cancel: CancellationToken::new(),
next_session_id: AtomicU64::new(1),
}
}
pub fn with_hook(
config: ProxyConfig,
observer: Arc<dyn ProxyObserver>,
hook: Arc<dyn ProxyHook>,
) -> Self {
Self {
config,
observer,
hook,
cancel: CancellationToken::new(),
next_session_id: AtomicU64::new(1),
}
}
pub fn cancel_token(&self) -> CancellationToken {
self.cancel.clone()
}
pub async fn run(&self) -> Result<(), ProxyError> {
let listener = Listener::bind(ListenerConfig {
bind_addr: self.config.listener.bind_addr,
cert_chain: self.config.listener.cert_chain.clone(),
key_der: self.config.listener.key_der.clone_key(),
})?;
loop {
tokio::select! {
result = listener.accept() => {
self.dispatch(result?);
}
_ = self.cancel.cancelled() => {
listener.close();
return Ok(());
}
}
}
}
fn dispatch(&self, accepted: AcceptedConn) {
match accepted {
AcceptedConn::Quic { conn, alpn } => {
let session_id = self.next_session_id();
let client_addr = conn.remote_address();
self.emit_session_started(session_id, client_addr, "QUIC");
let session = self.new_session(session_id, alpn);
tokio::spawn(async move {
let _ = session.run(conn).await;
});
}
#[cfg(feature = "webtransport")]
AcceptedConn::WebTransport(conn) => {
let session_id = self.next_session_id();
let client_addr = conn.remote_address();
self.emit_session_started(session_id, client_addr, "WebTransport");
let session = self.new_session(session_id, Vec::new());
tokio::spawn(async move {
let _ = session.run_webtransport(conn).await;
});
}
}
}
fn next_session_id(&self) -> SessionId {
SessionId(self.next_session_id.fetch_add(1, Ordering::Relaxed))
}
fn emit_session_started(
&self,
session_id: SessionId,
client_addr: std::net::SocketAddr,
client_transport: &str,
) {
if self.observer.wants_events() {
self.observer.on_event(&ProxyEvent::SessionStarted {
session_id,
client_addr,
client_transport: client_transport.to_string(),
});
}
}
fn new_session(&self, session_id: SessionId, client_alpn: Vec<u8>) -> ProxySession {
ProxySession::new(
session_id,
ProxySessionConfig {
draft: self.config.session.draft,
upstream_transport: self.config.session.upstream_transport.clone(),
upstream_addr: self.config.session.upstream_addr.clone(),
skip_upstream_cert_verify: self.config.session.skip_upstream_cert_verify,
upstream_ca_certs: self.config.session.upstream_ca_certs.clone(),
upstream_connect_timeout_secs: self.config.session.upstream_connect_timeout_secs,
},
client_alpn,
Arc::clone(&self.observer),
Arc::clone(&self.hook),
self.cancel.child_token(),
)
}
}