litellm_rs/server/
utils.rs1use crate::server::HttpServer;
6use crate::utils::error::gateway_error::GatewayError;
7use tracing::{info, warn};
8
9impl HttpServer {
10 pub async fn shutdown_signal() {
14 let ctrl_c = async {
15 match tokio::signal::ctrl_c().await {
16 Ok(()) => info!("Received Ctrl+C signal, shutting down gracefully"),
17 Err(e) => warn!("Failed to install Ctrl+C handler: {}", e),
18 }
19 };
20
21 #[cfg(unix)]
22 let terminate = async {
23 match tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) {
24 Ok(mut signal) => {
25 signal.recv().await;
26 info!("Received terminate signal, shutting down gracefully");
27 }
28 Err(e) => {
29 warn!("Failed to install SIGTERM handler: {}", e);
30 std::future::pending::<()>().await;
32 }
33 }
34 };
35
36 #[cfg(not(unix))]
37 let terminate = std::future::pending::<()>();
38
39 tokio::select! {
40 _ = ctrl_c => {},
41 _ = terminate => {},
42 }
43 }
44
45 pub(crate) fn format_bind_error(
47 error: std::io::Error,
48 bind_addr: &str,
49 port: u16,
50 ) -> GatewayError {
51 let error_str = error.to_string();
52
53 if error_str.contains("Address already in use")
55 || error_str.contains("os error 48")
56 || error_str.contains("os error 98")
57 {
58 let message = format!(
59 r#"
60┌─────────────────────────────────────────────────────────────────┐
61│ ❌ Error: Port {} is already in use
62├─────────────────────────────────────────────────────────────────┤
63│ Possible solutions:
64│
65│ 1. Kill the existing process:
66│ lsof -ti:{} | xargs kill -9
67│
68│ 2. Use a different port:
69│ --port {} or PORT={}
70│
71│ 3. Check what's using it:
72│ lsof -i:{}
73└─────────────────────────────────────────────────────────────────┘
74"#,
75 port,
76 port,
77 port + 1,
78 port + 1,
79 port
80 );
81 GatewayError::server(message)
82 } else if error_str.contains("Permission denied") || error_str.contains("os error 13") {
83 let message = format!(
84 r#"
85┌─────────────────────────────────────────────────────────────────┐
86│ ❌ Error: Permission denied for port {}
87├─────────────────────────────────────────────────────────────────┤
88│ Possible solutions:
89│
90│ 1. Use a port >= 1024 (non-privileged):
91│ --port 8000 or PORT=8000
92│
93│ 2. Run with elevated privileges (not recommended):
94│ sudo ./gateway
95└─────────────────────────────────────────────────────────────────┘
96"#,
97 port
98 );
99 GatewayError::server(message)
100 } else {
101 GatewayError::server(format!("Failed to bind to {}: {}", bind_addr, error))
102 }
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use std::io::{Error, ErrorKind};
110
111 #[test]
114 fn test_format_bind_error_address_in_use() {
115 let error = Error::new(ErrorKind::AddrInUse, "Address already in use");
116 let result = HttpServer::format_bind_error(error, "0.0.0.0:8080", 8080);
117
118 let error_msg = result.to_string();
119 assert!(error_msg.contains("8080"));
120 assert!(error_msg.contains("already in use"));
121 assert!(error_msg.contains("8081")); }
123
124 #[test]
125 fn test_format_bind_error_os_error_48() {
126 let error = Error::other("os error 48");
127 let result = HttpServer::format_bind_error(error, "0.0.0.0:3000", 3000);
128
129 let error_msg = result.to_string();
130 assert!(error_msg.contains("3000"));
131 assert!(error_msg.contains("3001")); }
133
134 #[test]
135 fn test_format_bind_error_os_error_98() {
136 let error = Error::other("os error 98");
137 let result = HttpServer::format_bind_error(error, "127.0.0.1:9000", 9000);
138
139 let error_msg = result.to_string();
140 assert!(error_msg.contains("9000"));
141 }
142
143 #[test]
144 fn test_format_bind_error_permission_denied() {
145 let error = Error::new(ErrorKind::PermissionDenied, "Permission denied");
146 let result = HttpServer::format_bind_error(error, "0.0.0.0:80", 80);
147
148 let error_msg = result.to_string();
149 assert!(error_msg.contains("80"));
150 assert!(error_msg.contains("Permission denied"));
151 assert!(error_msg.contains("1024")); }
153
154 #[test]
155 fn test_format_bind_error_os_error_13() {
156 let error = Error::other("os error 13");
157 let result = HttpServer::format_bind_error(error, "0.0.0.0:443", 443);
158
159 let error_msg = result.to_string();
160 assert!(error_msg.contains("443"));
161 assert!(error_msg.contains("Permission denied"));
162 }
163
164 #[test]
165 fn test_format_bind_error_generic() {
166 let error = Error::other("Network unreachable");
167 let result = HttpServer::format_bind_error(error, "192.168.1.1:8080", 8080);
168
169 let error_msg = result.to_string();
170 assert!(error_msg.contains("Failed to bind"));
171 assert!(error_msg.contains("192.168.1.1:8080"));
172 assert!(error_msg.contains("Network unreachable"));
173 }
174
175 #[test]
176 fn test_format_bind_error_suggested_port_8000() {
177 let error = Error::new(ErrorKind::AddrInUse, "Address already in use");
178 let result = HttpServer::format_bind_error(error, "0.0.0.0:8000", 8000);
179 assert!(result.to_string().contains("8001"));
180 }
181
182 #[test]
183 fn test_format_bind_error_suggested_port_3000() {
184 let error = Error::new(ErrorKind::AddrInUse, "Address already in use");
185 let result = HttpServer::format_bind_error(error, "0.0.0.0:3000", 3000);
186 assert!(result.to_string().contains("3001"));
187 }
188
189 #[test]
190 fn test_format_bind_error_contains_emoji() {
191 let error = Error::new(ErrorKind::AddrInUse, "Address already in use");
192 let result = HttpServer::format_bind_error(error, "0.0.0.0:8080", 8080);
193
194 let error_msg = result.to_string();
195 assert!(error_msg.contains("❌")); }
197
198 #[test]
199 fn test_format_bind_error_contains_lsof_command() {
200 let error = Error::new(ErrorKind::AddrInUse, "Address already in use");
201 let result = HttpServer::format_bind_error(error, "0.0.0.0:8080", 8080);
202
203 let error_msg = result.to_string();
204 assert!(error_msg.contains("lsof")); }
206
207 #[test]
208 fn test_format_bind_error_privileged_port() {
209 let error = Error::new(ErrorKind::PermissionDenied, "Permission denied");
210 let result = HttpServer::format_bind_error(error, "0.0.0.0:22", 22);
211
212 let error_msg = result.to_string();
213 assert!(error_msg.contains("22"));
214 assert!(error_msg.contains("8000")); }
216
217 #[test]
218 fn test_format_bind_error_connection_refused() {
219 let error = Error::new(ErrorKind::ConnectionRefused, "Connection refused");
220 let result = HttpServer::format_bind_error(error, "localhost:8080", 8080);
221
222 let error_msg = result.to_string();
223 assert!(error_msg.contains("Connection refused"));
225 }
226
227 #[test]
228 fn test_format_bind_error_invalid_input() {
229 let error = Error::new(ErrorKind::InvalidInput, "Invalid address format");
230 let result = HttpServer::format_bind_error(error, "invalid:addr", 0);
231
232 let error_msg = result.to_string();
233 assert!(error_msg.contains("Failed to bind"));
234 }
235
236 #[test]
239 fn test_format_bind_error_returns_gateway_error() {
240 let error = Error::new(ErrorKind::AddrInUse, "Address already in use");
241 let result = HttpServer::format_bind_error(error, "0.0.0.0:8080", 8080);
242
243 let error_str = result.to_string();
245 assert!(!error_str.is_empty());
246 assert!(error_str.contains("8080"));
248 }
249}