@zap-js/server 0.0.2 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/server.rs DELETED
@@ -1,597 +0,0 @@
1
- //! Core ZapServer implementation
2
-
3
- use std::net::SocketAddr;
4
- use std::sync::Arc;
5
- use std::time::Duration;
6
-
7
- use hyper::server::conn::http1;
8
- use hyper::service::service_fn;
9
- use hyper::{body::Incoming, Request as HyperRequest, Response as HyperResponse};
10
- use hyper_util::rt::TokioIo;
11
- use serde::Serialize;
12
- use tokio::net::TcpListener;
13
- use tracing::{debug, error, info};
14
-
15
- use zap_core::{
16
- HttpParser, Method, MiddlewareChain, Request, Router,
17
- };
18
-
19
- use crate::config::{ServerConfig, ZapConfig};
20
- use crate::error::{ZapError, ZapResult};
21
- use crate::handler::{AsyncHandler, BoxedHandler, Handler, SimpleHandler};
22
- use crate::proxy::ProxyHandler;
23
- use crate::reliability::{HealthChecker, HealthStatus};
24
- use crate::request::RequestData;
25
- use crate::response::{Json, ZapResponse};
26
- use crate::r#static::{handle_static_files, StaticHandler, StaticOptions};
27
- use crate::utils::convert_method;
28
-
29
- /// Main Zap server - the entry point for building high-performance web applications
30
- pub struct Zap {
31
- /// Server configuration
32
- config: ServerConfig,
33
- /// HTTP router for handling requests
34
- router: Router<BoxedHandler>,
35
- /// Middleware chain
36
- middleware: MiddlewareChain,
37
- /// Static file handlers
38
- static_handlers: Vec<StaticHandler>,
39
- }
40
-
41
- impl Zap {
42
- /// Create a new Zap server instance
43
- pub fn new() -> Self {
44
- Self {
45
- config: ServerConfig::default(),
46
- router: Router::new(),
47
- middleware: MiddlewareChain::new(),
48
- static_handlers: Vec::new(),
49
- }
50
- }
51
-
52
- /// Set the server port
53
- pub fn port(mut self, port: u16) -> Self {
54
- self.config.port = port;
55
- self
56
- }
57
-
58
- /// Set the server hostname
59
- pub fn hostname<S: Into<String>>(mut self, hostname: S) -> Self {
60
- self.config.hostname = hostname.into();
61
- self
62
- }
63
-
64
- /// Set keep-alive timeout
65
- pub fn keep_alive_timeout(mut self, timeout: Duration) -> Self {
66
- self.config.keep_alive_timeout = timeout;
67
- self
68
- }
69
-
70
- /// Set maximum request body size
71
- pub fn max_request_body_size(mut self, size: usize) -> Self {
72
- self.config.max_request_body_size = size;
73
- self
74
- }
75
-
76
- /// Set request timeout
77
- pub fn request_timeout(mut self, timeout: Duration) -> Self {
78
- self.config.request_timeout = timeout;
79
- self
80
- }
81
-
82
- /// Add middleware to the chain
83
- pub fn use_middleware<M>(mut self, middleware: M) -> Self
84
- where
85
- M: zap_core::Middleware + 'static,
86
- {
87
- self.middleware = self.middleware.use_middleware(middleware);
88
- self
89
- }
90
-
91
- /// Register a GET route
92
- pub fn get<H>(mut self, path: &str, handler: H) -> Self
93
- where
94
- H: Handler + Send + Sync + 'static,
95
- {
96
- self.router
97
- .insert(Method::GET, path, Box::new(handler))
98
- .unwrap_or_else(|e| panic!("Failed to register GET route '{}': {}", path, e));
99
- self
100
- }
101
-
102
- /// Register a GET route with a simple closure
103
- pub fn get_simple<F>(mut self, path: &str, handler: F) -> Self
104
- where
105
- F: Fn() -> String + Send + Sync + 'static,
106
- {
107
- self.router
108
- .insert(Method::GET, path, Box::new(SimpleHandler::new(handler)))
109
- .unwrap_or_else(|e| panic!("Failed to register GET route '{}': {}", path, e));
110
- self
111
- }
112
-
113
- /// Register a GET route with an async handler
114
- pub fn get_async<F, Fut>(mut self, path: &str, handler: F) -> Self
115
- where
116
- F: Fn(RequestData) -> Fut + Send + Sync + 'static,
117
- Fut: std::future::Future<Output = ZapResponse> + Send + 'static,
118
- {
119
- self.router
120
- .insert(Method::GET, path, Box::new(AsyncHandler::new(handler)))
121
- .unwrap_or_else(|e| panic!("Failed to register GET route '{}': {}", path, e));
122
- self
123
- }
124
-
125
- /// Register a POST route
126
- pub fn post<H>(mut self, path: &str, handler: H) -> Self
127
- where
128
- H: Handler + Send + Sync + 'static,
129
- {
130
- self.router
131
- .insert(Method::POST, path, Box::new(handler))
132
- .unwrap_or_else(|e| panic!("Failed to register POST route '{}': {}", path, e));
133
- self
134
- }
135
-
136
- /// Register a POST route with an async handler
137
- pub fn post_async<F, Fut>(mut self, path: &str, handler: F) -> Self
138
- where
139
- F: Fn(RequestData) -> Fut + Send + Sync + 'static,
140
- Fut: std::future::Future<Output = ZapResponse> + Send + 'static,
141
- {
142
- self.router
143
- .insert(Method::POST, path, Box::new(AsyncHandler::new(handler)))
144
- .unwrap_or_else(|e| panic!("Failed to register POST route '{}': {}", path, e));
145
- self
146
- }
147
-
148
- /// Register a PUT route
149
- pub fn put<H>(mut self, path: &str, handler: H) -> Self
150
- where
151
- H: Handler + Send + Sync + 'static,
152
- {
153
- self.router
154
- .insert(Method::PUT, path, Box::new(handler))
155
- .unwrap_or_else(|e| panic!("Failed to register PUT route '{}': {}", path, e));
156
- self
157
- }
158
-
159
- /// Register a PUT route with an async handler
160
- pub fn put_async<F, Fut>(mut self, path: &str, handler: F) -> Self
161
- where
162
- F: Fn(RequestData) -> Fut + Send + Sync + 'static,
163
- Fut: std::future::Future<Output = ZapResponse> + Send + 'static,
164
- {
165
- self.router
166
- .insert(Method::PUT, path, Box::new(AsyncHandler::new(handler)))
167
- .unwrap_or_else(|e| panic!("Failed to register PUT route '{}': {}", path, e));
168
- self
169
- }
170
-
171
- /// Register a PATCH route
172
- pub fn patch<H>(mut self, path: &str, handler: H) -> Self
173
- where
174
- H: Handler + Send + Sync + 'static,
175
- {
176
- self.router
177
- .insert(Method::PATCH, path, Box::new(handler))
178
- .unwrap_or_else(|e| panic!("Failed to register PATCH route '{}': {}", path, e));
179
- self
180
- }
181
-
182
- /// Register a DELETE route
183
- pub fn delete<H>(mut self, path: &str, handler: H) -> Self
184
- where
185
- H: Handler + Send + Sync + 'static,
186
- {
187
- self.router
188
- .insert(Method::DELETE, path, Box::new(handler))
189
- .unwrap_or_else(|e| panic!("Failed to register DELETE route '{}': {}", path, e));
190
- self
191
- }
192
-
193
- /// Register an OPTIONS route
194
- pub fn options<H>(mut self, path: &str, handler: H) -> Self
195
- where
196
- H: Handler + Send + Sync + 'static,
197
- {
198
- self.router
199
- .insert(Method::OPTIONS, path, Box::new(handler))
200
- .unwrap_or_else(|e| panic!("Failed to register OPTIONS route '{}': {}", path, e));
201
- self
202
- }
203
-
204
- /// Register a HEAD route
205
- pub fn head<H>(mut self, path: &str, handler: H) -> Self
206
- where
207
- H: Handler + Send + Sync + 'static,
208
- {
209
- self.router
210
- .insert(Method::HEAD, path, Box::new(handler))
211
- .unwrap_or_else(|e| panic!("Failed to register HEAD route '{}': {}", path, e));
212
- self
213
- }
214
-
215
- /// Register routes for all HTTP methods
216
- pub fn all<H>(mut self, path: &str, handler: H) -> Self
217
- where
218
- H: Handler + Send + Sync + Clone + 'static,
219
- {
220
- for method in [
221
- Method::GET,
222
- Method::POST,
223
- Method::PUT,
224
- Method::PATCH,
225
- Method::DELETE,
226
- Method::OPTIONS,
227
- Method::HEAD,
228
- ] {
229
- self.router
230
- .insert(method, path, Box::new(handler.clone()))
231
- .unwrap_or_else(|e| {
232
- panic!("Failed to register {} route '{}': {}", method, path, e)
233
- });
234
- }
235
- self
236
- }
237
-
238
- /// Serve static files from a directory
239
- pub fn static_files<P: Into<std::path::PathBuf>>(mut self, prefix: &str, directory: P) -> Self {
240
- self.static_handlers.push(StaticHandler::new(prefix, directory));
241
- self
242
- }
243
-
244
- /// Serve static files with custom options
245
- pub fn static_files_with_options<P: Into<std::path::PathBuf>>(
246
- mut self,
247
- prefix: &str,
248
- directory: P,
249
- options: StaticOptions,
250
- ) -> Self {
251
- self.static_handlers.push(StaticHandler::new_with_options(prefix, directory, options));
252
- self
253
- }
254
-
255
- /// Register a JSON API endpoint with automatic serialization
256
- pub fn json_get<F, T>(self, path: &str, handler: F) -> Self
257
- where
258
- F: Fn(RequestData) -> T + Send + Sync + 'static,
259
- T: Serialize + Send + 'static,
260
- {
261
- self.get_async(path, move |req| {
262
- let result = handler(req);
263
- async move { Json(result).into() }
264
- })
265
- }
266
-
267
- /// Register a JSON POST endpoint
268
- pub fn json_post<F, T>(self, path: &str, handler: F) -> Self
269
- where
270
- F: Fn(RequestData) -> T + Send + Sync + 'static,
271
- T: Serialize + Send + 'static,
272
- {
273
- self.post_async(path, move |req| {
274
- let result = handler(req);
275
- async move { Json(result).into() }
276
- })
277
- }
278
-
279
- /// Add CORS middleware with permissive settings
280
- pub fn cors(self) -> Self {
281
- self.use_middleware(zap_core::CorsMiddleware::permissive())
282
- }
283
-
284
- /// Add logging middleware
285
- pub fn logging(self) -> Self {
286
- self.use_middleware(zap_core::LoggerMiddleware::new())
287
- }
288
-
289
- /// Simple health check endpoint (backwards compatible)
290
- pub fn health_check(self, path: &str) -> Self {
291
- self.get(path, || "OK")
292
- }
293
-
294
- /// Enhanced liveness probe (Kubernetes-style)
295
- /// Returns 200 if the process is alive and can respond
296
- pub fn health_live(self, path: &str) -> Self {
297
- let checker = Arc::new(HealthChecker::new(env!("CARGO_PKG_VERSION").to_string()));
298
- self.get_async(path, move |_req| {
299
- let checker = checker.clone();
300
- async move {
301
- let response = checker.liveness();
302
- let status_code = match response.status {
303
- HealthStatus::Healthy => 200,
304
- HealthStatus::Degraded => 200,
305
- HealthStatus::Unhealthy => 503,
306
- };
307
- ZapResponse::JsonWithStatus(
308
- serde_json::from_str(&response.to_json()).unwrap_or_default(),
309
- status_code,
310
- )
311
- }
312
- })
313
- }
314
-
315
- /// Enhanced readiness probe (Kubernetes-style)
316
- /// Returns 200 if the server can handle requests
317
- pub fn health_ready(self, path: &str) -> Self {
318
- let checker = Arc::new(HealthChecker::new(env!("CARGO_PKG_VERSION").to_string()));
319
- self.get_async(path, move |_req| {
320
- let checker = checker.clone();
321
- async move {
322
- let response = checker.readiness().await;
323
- let status_code = match response.status {
324
- HealthStatus::Healthy => 200,
325
- HealthStatus::Degraded => 200,
326
- HealthStatus::Unhealthy => 503,
327
- };
328
- ZapResponse::JsonWithStatus(
329
- serde_json::from_str(&response.to_json()).unwrap_or_default(),
330
- status_code,
331
- )
332
- }
333
- })
334
- }
335
-
336
- /// Register all health endpoints at once
337
- /// - /health/live - Liveness probe
338
- /// - /health/ready - Readiness probe
339
- pub fn health_endpoints(self) -> Self {
340
- self.health_live("/health/live")
341
- .health_ready("/health/ready")
342
- }
343
-
344
- /// Metrics endpoint (basic)
345
- pub fn metrics(self, path: &str) -> Self {
346
- self.get_async(path, |_req| async move {
347
- let metrics = serde_json::json!({
348
- "status": "healthy",
349
- "timestamp": chrono::Utc::now().to_rfc3339(),
350
- "memory_usage": "TODO",
351
- "uptime": "TODO"
352
- });
353
- Json(metrics).into()
354
- })
355
- }
356
-
357
- /// Start the server and listen for connections
358
- pub async fn listen(self) -> Result<(), ZapError> {
359
- let addr = self.config.socket_addr();
360
- let socket_addr: SocketAddr = addr.parse().map_err(|e| {
361
- ZapError::http(format!("Invalid address '{}': {}", addr, e))
362
- })?;
363
-
364
- let listener = TcpListener::bind(socket_addr).await?;
365
-
366
- info!("🚀 Zap server listening on http://{}", addr);
367
- info!("📊 Router contains {} routes", self.router.total_routes());
368
-
369
- let server = Arc::new(self);
370
-
371
- loop {
372
- let (stream, remote_addr) = listener.accept().await?;
373
- let server = server.clone();
374
-
375
- tokio::spawn(async move {
376
- let io = TokioIo::new(stream);
377
-
378
- let service = service_fn(move |req| {
379
- let server = server.clone();
380
- async move {
381
- server.handle_request(req, remote_addr).await
382
- }
383
- });
384
-
385
- if let Err(err) = http1::Builder::new()
386
- .serve_connection(io, service)
387
- .await
388
- {
389
- error!("Error serving connection: {:?}", err);
390
- }
391
- });
392
- }
393
- }
394
-
395
- /// Handle an individual HTTP request
396
- async fn handle_request(
397
- &self,
398
- hyper_req: HyperRequest<Incoming>,
399
- remote_addr: SocketAddr,
400
- ) -> Result<HyperResponse<String>, hyper::Error> {
401
- let response = match self.process_request(hyper_req, remote_addr).await {
402
- Ok(zap_response) => zap_response.to_hyper_response(),
403
- Err(error) => {
404
- error!("Request processing error: {}", error);
405
- hyper::Response::builder()
406
- .status(500)
407
- .body("Internal Server Error".to_string())
408
- .unwrap()
409
- }
410
- };
411
-
412
- Ok(response)
413
- }
414
-
415
- /// Process the request through our complete pipeline
416
- async fn process_request(
417
- &self,
418
- hyper_req: HyperRequest<Incoming>,
419
- _remote_addr: SocketAddr,
420
- ) -> Result<ZapResponse, ZapError> {
421
- use http_body_util::BodyExt;
422
-
423
- // Step 1: Convert Hyper request to raw bytes
424
- let (parts, body) = hyper_req.into_parts();
425
-
426
- // Collect the body bytes
427
- let body_bytes = body.collect().await
428
- .map_err(|e| ZapError::http(format!("Failed to read request body: {}", e)))?
429
- .to_bytes()
430
- .to_vec();
431
-
432
- // Convert method
433
- let method = convert_method(&parts.method)?;
434
-
435
- // Step 2: Reconstruct HTTP request bytes for our parser
436
- let mut request_bytes = Vec::new();
437
- request_bytes.extend_from_slice(format!("{} {} {:?}\r\n", parts.method, parts.uri, parts.version).as_bytes());
438
-
439
- for (name, value) in &parts.headers {
440
- request_bytes.extend_from_slice(name.as_str().as_bytes());
441
- request_bytes.extend_from_slice(b": ");
442
- request_bytes.extend_from_slice(value.as_bytes());
443
- request_bytes.extend_from_slice(b"\r\n");
444
- }
445
- request_bytes.extend_from_slice(b"\r\n");
446
- request_bytes.extend_from_slice(&body_bytes);
447
-
448
- // Step 3: Parse using our fast HTTP parser
449
- let parser = HttpParser::new();
450
- let parsed = parser.parse_request(&request_bytes)
451
- .map_err(|e| ZapError::http(format!("HTTP parsing failed: {:?}", e)))?;
452
-
453
- // Step 4: Check for static file handlers first
454
- let path_for_routing = parsed.path.split('?').next().unwrap_or(parsed.path);
455
-
456
- // Check static handlers
457
- if let Some(static_response) = handle_static_files(&self.static_handlers, path_for_routing).await? {
458
- return Ok(static_response);
459
- }
460
-
461
- // Step 5: Route the request using our fast router
462
- let (handler, route_params) = self.router.at(method, path_for_routing)
463
- .ok_or_else(|| ZapError::route_not_found(path_for_routing))?;
464
-
465
- // Step 6: Create Request object
466
- let body_start = &request_bytes[parsed.body_offset..];
467
- let request = Request::new(&parsed, body_start, route_params);
468
-
469
- // Step 7: Execute the handler (middleware is handled separately in a real implementation)
470
- let response = handler.handle(request).await
471
- .map_err(|e| ZapError::handler(format!("Handler execution failed: {}", e)))?;
472
-
473
- Ok(response)
474
- }
475
-
476
- /// Get router reference for testing
477
- pub fn router(&self) -> &Router<BoxedHandler> {
478
- &self.router
479
- }
480
-
481
- /// Get config reference for testing
482
- pub fn config(&self) -> &ServerConfig {
483
- &self.config
484
- }
485
-
486
- /// Get static handlers reference for testing
487
- pub fn static_handlers(&self) -> &[StaticHandler] {
488
- &self.static_handlers
489
- }
490
-
491
- /// Create a Zap server from comprehensive configuration
492
- ///
493
- /// This method is used by the binary entry point to load configuration
494
- /// from JSON and build the complete server with all routes and middleware.
495
- pub async fn from_config(config: ZapConfig) -> ZapResult<Self> {
496
- info!("🔧 Building Zap server from configuration");
497
-
498
- let mut server = Self {
499
- config: ServerConfig::new()
500
- .port(config.port)
501
- .hostname(config.hostname.clone())
502
- .max_request_body_size(config.max_request_body_size)
503
- .request_timeout(Duration::from_secs(config.request_timeout_secs))
504
- .keep_alive_timeout(Duration::from_secs(config.keepalive_timeout_secs)),
505
- router: Router::new(),
506
- middleware: MiddlewareChain::new(),
507
- static_handlers: Vec::new(),
508
- };
509
-
510
- // Add middleware
511
- if config.middleware.enable_cors {
512
- info!("✓ CORS middleware enabled");
513
- server = server.cors();
514
- }
515
-
516
- if config.middleware.enable_logging {
517
- info!("✓ Logging middleware enabled");
518
- server = server.logging();
519
- }
520
-
521
- // Register all routes from configuration
522
- for route_cfg in &config.routes {
523
- let method = route_cfg.method.to_uppercase();
524
- let method_enum = match method.as_str() {
525
- "GET" => Method::GET,
526
- "POST" => Method::POST,
527
- "PUT" => Method::PUT,
528
- "DELETE" => Method::DELETE,
529
- "PATCH" => Method::PATCH,
530
- "HEAD" => Method::HEAD,
531
- "OPTIONS" => Method::OPTIONS,
532
- _ => {
533
- return Err(ZapError::config(format!(
534
- "Unknown HTTP method: {}",
535
- method
536
- )))
537
- }
538
- };
539
-
540
- if route_cfg.is_typescript {
541
- // TypeScript handler - use proxy
542
- let proxy = ProxyHandler::with_timeout(
543
- route_cfg.handler_id.clone(),
544
- config.ipc_socket_path.clone(),
545
- config.request_timeout_secs,
546
- );
547
- server.router.insert(method_enum, &route_cfg.path, Box::new(proxy))
548
- .map_err(|e| ZapError::config(format!(
549
- "Failed to register route {}: {}",
550
- route_cfg.path, e
551
- )))?;
552
- debug!("✓ Registered {} {} -> {} (TypeScript)", method, route_cfg.path, route_cfg.handler_id);
553
- }
554
- // Rust handlers would be added here if needed
555
- }
556
-
557
- // Register static files
558
- for static_cfg in &config.static_files {
559
- server = server.static_files(&static_cfg.prefix, &static_cfg.directory);
560
- info!(
561
- "✓ Static files: {} -> {}",
562
- static_cfg.prefix, static_cfg.directory
563
- );
564
- }
565
-
566
- // Note: RPC endpoint (/__zap_rpc) is registered by the application binary
567
-
568
- // Add health check
569
- if !config.health_check_path.is_empty() {
570
- server = server.health_check(&config.health_check_path);
571
- info!("✓ Health check: {}", config.health_check_path);
572
- }
573
-
574
- // Add metrics endpoint if configured
575
- if let Some(metrics_path) = config.metrics_path {
576
- server = server.metrics(&metrics_path);
577
- info!("✓ Metrics endpoint: {}", metrics_path);
578
- }
579
-
580
- info!("✅ Server configured with {} routes", server.router.total_routes());
581
-
582
- Ok(server)
583
- }
584
-
585
- /// Graceful shutdown of the server
586
- pub async fn shutdown(&self) -> ZapResult<()> {
587
- info!("🛑 Initiating graceful shutdown");
588
- // Cleanup can be added here if needed
589
- Ok(())
590
- }
591
- }
592
-
593
- impl Default for Zap {
594
- fn default() -> Self {
595
- Self::new()
596
- }
597
- }