@zap-js/server 0.0.1 → 0.0.4
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/index.js +18 -14
- package/package.json +5 -10
- package/src/bin/zap.rs +0 -154
- package/src/config.rs +0 -253
- package/src/connection_pool.rs +0 -404
- package/src/error.rs +0 -380
- package/src/handler.rs +0 -89
- package/src/ipc.js +0 -10
- package/src/ipc.rs +0 -499
- package/src/lib.rs +0 -433
- package/src/metrics.rs +0 -264
- package/src/proxy.rs +0 -436
- package/src/reliability.rs +0 -917
- package/src/request.rs +0 -60
- package/src/request_id.rs +0 -97
- package/src/response.rs +0 -182
- package/src/rpc.js +0 -14
- package/src/server.rs +0 -597
- package/src/static.rs +0 -572
- package/src/types.js +0 -21
- package/src/utils.rs +0 -18
- package/src/websocket.rs +0 -429
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
|
-
}
|