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}