1#[cfg(test)]
22mod tests;
23
24use std::sync::Arc;
25use std::net::SocketAddr;
26use std::convert::Infallible;
27use tokio::sync::RwLock;
28use hyper::body::Incoming;
29use hyper::{Request, Response};
30use hyper_util::server::conn::auto::Builder as AutoBuilder;
31use hyper_util::rt::TokioExecutor;
32use hyper::service::service_fn;
33use hyper_util::rt::TokioIo;
34use bytes::Bytes;
35use futures_util::TryStreamExt;
36use http_body_util::{BodyExt, Full};
37use reqwest::Body;
38use serde::{Serialize, Deserialize};
39use log::{debug, info, warn, error, trace};
40use tokio::signal;
41use tokio::task::{Id, JoinSet};
42use crate::core::{ProxyCore, ProxyRequest, ProxyResponse, ProxyError, HttpMethod, RequestContext};
43use std::collections::HashMap;
44use tokio::sync::oneshot;
45use tokio::task::JoinHandle;
46
47#[cfg(unix)]
48use tokio::signal::unix::{signal, SignalKind};
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct ServerConfig {
53 #[serde(default = "default_host")]
55 pub host: String,
56
57 #[serde(default = "default_port")]
59 pub port: u16,
60}
61
62fn default_host() -> String {
63 "127.0.0.1".to_string()
64}
65
66fn default_port() -> u16 {
67 8080
68}
69
70impl Default for ServerConfig {
71 fn default() -> Self {
72 Self {
73 host: default_host(),
74 port: default_port(),
75 }
76 }
77}
78
79#[derive(Debug, Clone)]
81pub struct ProxyServer {
82 config: ServerConfig,
84 core: Arc<ProxyCore>,
86 shutdown_senders: Arc<RwLock<HashMap<Id, oneshot::Sender<()>>>>,
88}
89
90impl ProxyServer {
91 pub fn new(config: ServerConfig, core: Arc<ProxyCore>) -> Self {
93 Self {
94 config,
95 core,
96 shutdown_senders: Arc::new(RwLock::new(HashMap::new())),
97 }
98 }
99
100 pub async fn start(&self) -> Result<(), ProxyError> {
102 let addr = format!("{}:{}", self.config.host, self.config.port)
103 .parse::<SocketAddr>()
104 .map_err(|e| ProxyError::Other(format!("Invalid server address: {}", e)))?;
105
106 let listener = tokio::net::TcpListener::bind(addr)
107 .await
108 .map_err(|e| ProxyError::Other(format!("Failed to bind: {}", e)))?;
109
110 info!("Foxy proxy listening on http://{}", addr);
111
112 let ctrl_c = signal::ctrl_c();
114
115 #[cfg(unix)]
117 let mut term_stream = signal(SignalKind::terminate())
118 .map_err(|e| ProxyError::Other(format!("Cannot install SIGTERM handler: {}", e)))?;
119
120 #[cfg(unix)]
122 let sigterm = term_stream.recv();
123 #[cfg(not(unix))]
124 let sigterm = std::future::pending();
125
126 tokio::pin!(ctrl_c);
128 tokio::pin!(sigterm);
129
130 let shutdown_senders = self.shutdown_senders.clone();
132
133 let mut join_set = JoinSet::new();
135 let core = self.core.clone();
136
137 let shutdown_initiated = Arc::new(std::sync::atomic::AtomicBool::new(false));
139 let shutdown_initiated_clone = shutdown_initiated.clone();
140
141 loop {
142 tokio::select! {
143 _ = &mut ctrl_c => {
144 info!("Received Ctrl-C; initiating graceful shutdown");
145 shutdown_initiated_clone.store(true, std::sync::atomic::Ordering::SeqCst);
146 break;
147 }
148 _ = &mut sigterm => {
149 info!("Received SIGTERM; initiating graceful shutdown");
150 shutdown_initiated_clone.store(true, std::sync::atomic::Ordering::SeqCst);
151 break;
152 }
153 accept = listener.accept() => {
154 match accept {
155 Ok((stream, remote_addr)) => {
156 if shutdown_initiated.load(std::sync::atomic::Ordering::SeqCst) {
158 info!("Rejecting new connection during shutdown");
159 continue;
160 }
161
162 let core = core.clone();
163 let client_ip = remote_addr.ip().to_string();
164 let (tx, rx) = oneshot::channel();
165
166 let handle = join_set.spawn(async move {
167 let service = service_fn(move |req: Request<Incoming>| {
168 debug!("Incoming over {:?}", req.version());
169 handle_request(req, core.clone(), client_ip.clone())
170 });
171 let io = TokioIo::new(stream);
172
173 let builder = {
175 let mut b = AutoBuilder::new(TokioExecutor::new());
176 b.http1();
177 b.http2();
178 b
179 };
180
181 let graceful_shutdown = async {
183 let _ = rx.await;
185 debug!("Connection received shutdown signal");
186 };
187
188 let connection = builder.serve_connection(io, service);
190
191 tokio::select! {
193 res = connection => {
194 if let Err(e) = res {
195 error!("Connection error: {}", e);
196 }
197 }
198 _ = graceful_shutdown => {
199 debug!("Connection shutting down gracefully");
200 }
201 }
202 });
203
204 shutdown_senders.write().await.insert(handle.id(), tx);
206 }
207 Err(e) => error!("Accept error: {}", e),
208 }
209 }
210 }
211 }
212
213 info!("Shutting down; waiting for {} connection(s)", join_set.len());
215
216 {
218 let mut senders = shutdown_senders.write().await;
219 for (_, sender) in senders.drain() {
220 let _ = sender.send(());
221 }
222 }
223
224 let shutdown_timeout = tokio::time::Duration::from_secs(30);
226 let shutdown_future = async {
227 while let Some(res) = join_set.join_next().await {
228 if let Err(e) = res {
229 error!("Connection task failed: {}", e);
230 }
231 }
232 };
233
234 match tokio::time::timeout(shutdown_timeout, shutdown_future).await {
235 Ok(_) => info!("All connections drained gracefully"),
236 Err(_) => warn!("Shutdown timed out after {} seconds", shutdown_timeout.as_secs()),
237 }
238
239 info!("Shutdown complete");
240 Ok(())
241 }
242}
243
244async fn convert_hyper_request(
246 req: Request<Incoming>,
247 client_ip: String,
248) -> Result<ProxyRequest, ProxyError> {
249 let method = HttpMethod::from(req.method());
250 let uri = req.uri().clone();
251 let path = uri.path().to_owned();
252 let query = uri.query().map(|q| q.to_owned());
253 let headers = req.headers().clone();
254
255 let hyper_stream = req.into_body().into_data_stream();
257 let byte_stream = hyper_stream.map_ok(Bytes::from);
258 let body = reqwest::Body::wrap_stream(byte_stream);
259
260 Ok(ProxyRequest {
261 method,
262 path,
263 query,
264 headers,
265 body,
266 context: Arc::new(RwLock::new(RequestContext {
267 client_ip: Some(client_ip),
268 start_time: Some(std::time::Instant::now()),
269 attributes: std::collections::HashMap::new(),
270 })),
271 })
272}
273
274fn convert_proxy_response(resp: ProxyResponse) -> Result<Response<Body>, ProxyError> {
276 let stream = resp
277 .body
278 .into_data_stream()
279 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e));
280
281 let body = Body::wrap_stream(stream);
282
283 let mut builder = Response::builder().status(resp.status);
284 *builder
285 .headers_mut()
286 .ok_or_else(|| ProxyError::Other("unable to set headers".into()))? = resp.headers;
287
288 Ok(builder
289 .body(body)
290 .map_err(|e| ProxyError::Other(e.to_string()))?)
291}
292
293async fn handle_request(
295 req: Request<Incoming>,
296 core: Arc<ProxyCore>,
297 client_ip: String,
298) -> Result<Response<Body>, Infallible> {
299 let proxy_req = match convert_hyper_request(req, client_ip).await {
301 Ok(r) => r,
302 Err(e) => {
303 error!("convert request: {e}");
304 return Ok(Response::builder()
305 .status(500)
306 .body(Body::from("Internal Server Error"))
307 .unwrap());
308 }
309 };
310
311 match core.process_request(proxy_req).await {
313 Ok(proxy_resp) => match convert_proxy_response(proxy_resp) {
314 Ok(resp) => Ok(resp),
315 Err(e) => {
316 error!("convert response: {e}");
317 Ok(Response::builder()
318 .status(500)
319 .body(Body::from("Internal Server Error"))
320 .unwrap())
321 }
322 },
323 Err(e) => {
324 error!("proxy error: {e}");
325 let (status, msg) = match e {
326 ProxyError::Timeout(d) => (504, format!("Gateway Timeout after {d:?}")),
327 ProxyError::RoutingError(_) => (404, "Route not found".into()),
328 _ => (500, "Internal Server Error".into()),
329 };
330 Ok(Response::builder()
331 .status(status)
332 .body(Body::from(msg))
333 .unwrap())
334 }
335 }
336}