at_jet/server.rs
1//! HTTP Server implementation for AT-Jet
2
3use {crate::error::{JetError,
4 Result},
5 axum::Router,
6 std::net::SocketAddr,
7 tokio::net::TcpListener,
8 tower_http::{compression::CompressionLayer,
9 cors::{Any,
10 CorsLayer},
11 trace::TraceLayer},
12 tracing::info};
13
14/// AT-Jet HTTP Server
15///
16/// A high-performance HTTP server optimized for Protobuf APIs.
17///
18/// # Example
19///
20/// ```rust,no_run
21/// use at_jet::prelude::*;
22///
23/// #[tokio::main]
24/// async fn main() -> anyhow::Result<()> {
25/// let server = JetServer::new()
26/// .route("/api/health", get(health_check))
27/// .with_cors()
28/// .with_compression()
29/// .with_tracing();
30///
31/// server.serve("0.0.0.0:8080").await?;
32/// Ok(())
33/// }
34///
35/// async fn health_check() -> &'static str {
36/// "OK"
37/// }
38/// ```
39pub struct JetServer {
40 router: Router,
41 cors_layer: Option<CorsLayer>,
42 compression_enabled: bool,
43 tracing_enabled: bool,
44}
45
46impl JetServer {
47 /// Create a new JetServer instance
48 pub fn new() -> Self {
49 Self {
50 router: Router::new(),
51 cors_layer: None,
52 compression_enabled: false,
53 tracing_enabled: false,
54 }
55 }
56
57 /// Add a route to the server
58 ///
59 /// # Example
60 ///
61 /// ```rust,ignore
62 /// use at_jet::prelude::*;
63 ///
64 /// let server = JetServer::new()
65 /// .route("/api/users", get(list_users).post(create_user))
66 /// .route("/api/users/:id", get(get_user).delete(delete_user));
67 /// ```
68 pub fn route(mut self, path: &str, method_router: axum::routing::MethodRouter) -> Self {
69 self.router = self.router.route(path, method_router);
70 self
71 }
72
73 /// Merge another router into this server
74 pub fn merge(mut self, router: Router) -> Self {
75 self.router = self.router.merge(router);
76 self
77 }
78
79 /// Nest a router under a path prefix
80 ///
81 /// # Example
82 ///
83 /// ```rust,ignore
84 /// use at_jet::prelude::*;
85 ///
86 /// let api_routes = Router::new()
87 /// .route("/users", get(list_users))
88 /// .route("/posts", get(list_posts));
89 ///
90 /// let server = JetServer::new()
91 /// .nest("/api/v1", api_routes);
92 /// ```
93 pub fn nest(mut self, path: &str, router: Router) -> Self {
94 self.router = self.router.nest(path, router);
95 self
96 }
97
98 /// Enable CORS with permissive defaults (suitable for development)
99 ///
100 /// This allows any origin, method, and header. For production,
101 /// use `with_cors_config()` for fine-grained control.
102 ///
103 /// # Example
104 ///
105 /// ```rust,ignore
106 /// let server = JetServer::new()
107 /// .route("/api/users", get(list_users))
108 /// .with_cors(); // Allow all origins (development only)
109 /// ```
110 pub fn with_cors(mut self) -> Self {
111 self.cors_layer = Some(CorsLayer::new().allow_origin(Any).allow_methods(Any).allow_headers(Any));
112 self
113 }
114
115 /// Enable CORS with custom configuration (recommended for production)
116 ///
117 /// # Example
118 ///
119 /// ```rust,ignore
120 /// use tower_http::cors::{CorsLayer, AllowOrigin};
121 /// use http::{HeaderValue, Method};
122 ///
123 /// let cors = CorsLayer::new()
124 /// .allow_origin([
125 /// "https://app.example.com".parse::<HeaderValue>().unwrap(),
126 /// "https://admin.example.com".parse::<HeaderValue>().unwrap(),
127 /// ])
128 /// .allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE])
129 /// .allow_headers([http::header::CONTENT_TYPE, http::header::AUTHORIZATION])
130 /// .allow_credentials(true)
131 /// .max_age(Duration::from_secs(3600));
132 ///
133 /// let server = JetServer::new()
134 /// .route("/api/users", get(list_users))
135 /// .with_cors_config(cors);
136 /// ```
137 pub fn with_cors_config(mut self, cors: CorsLayer) -> Self {
138 self.cors_layer = Some(cors);
139 self
140 }
141
142 /// Enable response compression (gzip)
143 pub fn with_compression(mut self) -> Self {
144 self.compression_enabled = true;
145 self
146 }
147
148 /// Enable request/response tracing
149 ///
150 /// This adds detailed logging for HTTP requests including:
151 /// - Request method, URI, and version
152 /// - Response status code
153 /// - Request duration
154 ///
155 /// Requires a tracing subscriber to be initialized.
156 ///
157 /// # Example
158 ///
159 /// ```rust,ignore
160 /// use tracing_subscriber::EnvFilter;
161 ///
162 /// // Initialize tracing subscriber
163 /// tracing_subscriber::fmt()
164 /// .with_env_filter(EnvFilter::from_default_env())
165 /// .init();
166 ///
167 /// let server = JetServer::new()
168 /// .route("/api/users", get(list_users))
169 /// .with_tracing();
170 /// ```
171 pub fn with_tracing(mut self) -> Self {
172 self.tracing_enabled = true;
173 self
174 }
175
176 /// Add a custom middleware layer to the server
177 ///
178 /// This method allows you to add any tower-compatible middleware layer.
179 ///
180 /// # Example - Request Timeout
181 ///
182 /// ```rust,ignore
183 /// use std::time::Duration;
184 /// use tower_http::timeout::TimeoutLayer;
185 ///
186 /// let server = JetServer::new()
187 /// .route("/api/users", get(list_users))
188 /// .layer(TimeoutLayer::new(Duration::from_secs(30)));
189 /// ```
190 ///
191 /// # Example - Custom Authentication Middleware
192 ///
193 /// ```rust,ignore
194 /// use axum::{middleware, http::Request, response::Response};
195 ///
196 /// async fn auth_middleware<B>(
197 /// request: Request<B>,
198 /// next: axum::middleware::Next<B>,
199 /// ) -> Response {
200 /// // Check authentication here
201 /// next.run(request).await
202 /// }
203 ///
204 /// let server = JetServer::new()
205 /// .route("/api/users", get(list_users))
206 /// .layer(middleware::from_fn(auth_middleware));
207 /// ```
208 pub fn layer<L>(mut self, layer: L) -> Self
209 where
210 L: tower::Layer<axum::routing::Route> + Clone + Send + 'static,
211 L::Service: tower::Service<
212 axum::extract::Request,
213 Response = axum::response::Response,
214 Error = std::convert::Infallible,
215 > + Clone
216 + Send
217 + 'static,
218 <L::Service as tower::Service<axum::extract::Request>>::Future: Send + 'static, {
219 self.router = self.router.layer(layer);
220 self
221 }
222
223 /// Get the router for advanced customization
224 pub fn into_router(self) -> Router {
225 self.router
226 }
227
228 /// Start the server
229 ///
230 /// # Arguments
231 ///
232 /// * `addr` - Address to bind to (e.g., "0.0.0.0:8080")
233 ///
234 /// # Example
235 ///
236 /// ```rust,no_run
237 /// use at_jet::prelude::*;
238 ///
239 /// #[tokio::main]
240 /// async fn main() -> anyhow::Result<()> {
241 /// let server = JetServer::new()
242 /// .route("/health", get(|| async { "OK" }));
243 ///
244 /// server.serve("0.0.0.0:8080").await?;
245 /// Ok(())
246 /// }
247 /// ```
248 pub async fn serve(self, addr: &str) -> Result<()> {
249 let mut router = self.router;
250
251 // Apply middleware layers
252 if let Some(cors) = self.cors_layer {
253 router = router.layer(cors);
254 }
255
256 if self.compression_enabled {
257 router = router.layer(CompressionLayer::new());
258 }
259
260 if self.tracing_enabled {
261 router = router.layer(TraceLayer::new_for_http());
262 }
263
264 let addr: SocketAddr = addr
265 .parse()
266 .map_err(|e| JetError::ServerBind(format!("Invalid address: {}", e)))?;
267
268 let listener = TcpListener::bind(addr)
269 .await
270 .map_err(|e| JetError::ServerBind(format!("Failed to bind: {}", e)))?;
271
272 info!("AT-Jet server listening on {}", addr);
273
274 axum::serve(listener, router)
275 .await
276 .map_err(|e| JetError::ServerBind(format!("Server error: {}", e)))?;
277
278 Ok(())
279 }
280}
281
282impl Default for JetServer {
283 fn default() -> Self {
284 Self::new()
285 }
286}