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