1pub mod forward;
20pub mod optimize;
21pub mod routes;
22pub mod state;
23
24use axum::Router;
25use std::net::SocketAddr;
26use tokio::net::TcpListener;
27
28pub use state::{ProxyConfig, ProxyState};
29
30pub fn create_app(config: ProxyConfig) -> Router {
32 let state = ProxyState::new(config);
33 routes::build_router(state)
34}
35
36pub async fn start_server(config: ProxyConfig) -> anyhow::Result<()> {
42 let filter = if config.verbose {
45 "shift_proxy=debug,tower_http=debug"
46 } else {
47 "shift_proxy=warn"
48 };
49 tracing_subscriber::fmt()
50 .with_env_filter(
51 tracing_subscriber::EnvFilter::try_from_default_env()
52 .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new(filter)),
53 )
54 .with_target(false)
55 .init();
56
57 let port = config.port;
58 let verbose = config.verbose;
59 let app = create_app(config);
60
61 let addr = SocketAddr::from(([127, 0, 0, 1], port));
62 let listener = TcpListener::bind(addr).await?;
63
64 if verbose {
65 tracing::info!("shift proxy listening on http://{}", addr);
66 }
67 eprintln!("[shift] proxy listening on http://{}", addr);
68
69 axum::serve(listener, app)
70 .with_graceful_shutdown(shutdown_signal())
71 .await?;
72
73 Ok(())
74}
75
76async fn shutdown_signal() {
77 let ctrl_c = async {
78 tokio::signal::ctrl_c()
79 .await
80 .expect("failed to install Ctrl+C handler");
81 };
82
83 #[cfg(unix)]
84 let terminate = async {
85 tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
86 .expect("failed to install SIGTERM handler")
87 .recv()
88 .await;
89 };
90
91 #[cfg(not(unix))]
92 let terminate = std::future::pending::<()>();
93
94 tokio::select! {
95 _ = ctrl_c => {},
96 _ = terminate => {},
97 }
98}