1#![doc = include_str!("readme.md")]
2#![warn(missing_docs)]
3
4pub mod error;
5pub mod extract;
6pub mod middleware;
7pub mod response;
8pub mod router;
9pub mod template;
10pub mod tls;
11
12pub use wae_session as session;
13
14use http::{Response, StatusCode, header};
15use http_body_util::Full;
16use hyper::body::Bytes;
17use std::{net::SocketAddr, path::Path, time::Duration};
18use tokio::net::TcpListener;
19use tracing::info;
20
21pub use wae_types::{WaeError, WaeResult};
22
23pub type Body = Full<Bytes>;
25
26pub fn empty_body() -> Body {
28 Full::new(Bytes::new())
29}
30
31pub fn full_body<B: Into<Bytes>>(data: B) -> Body {
33 Full::new(data.into())
34}
35
36pub type HttpsResult<T> = WaeResult<T>;
38
39pub type HttpsError = WaeError;
41
42#[derive(Clone, Default)]
44pub struct Router {
45 }
47
48impl Router {
49 pub fn new() -> Self {
51 Self::default()
52 }
53
54 pub fn merge(self, _other: Self) -> Self {
56 self
57 }
58
59 pub fn nest_service<S>(self, _prefix: &str, _service: S) -> Self {
61 self
62 }
63}
64
65#[derive(Debug, Clone, Copy, Default)]
69pub enum HttpVersion {
70 Http1Only,
72 Http2Only,
74 #[default]
76 Both,
77 Http3,
79}
80
81#[derive(Debug, Clone)]
85pub struct Http2Config {
86 pub enabled: bool,
88 pub enable_push: bool,
90 pub max_concurrent_streams: u32,
92 pub initial_stream_window_size: u32,
94 pub max_frame_size: u32,
96 pub enable_connect_protocol: bool,
98 pub stream_idle_timeout: Duration,
100}
101
102impl Default for Http2Config {
103 fn default() -> Self {
104 Self {
105 enabled: true,
106 enable_push: false,
107 max_concurrent_streams: 256,
108 initial_stream_window_size: 65535,
109 max_frame_size: 16384,
110 enable_connect_protocol: false,
111 stream_idle_timeout: Duration::from_secs(60),
112 }
113 }
114}
115
116impl Http2Config {
117 pub fn new() -> Self {
119 Self::default()
120 }
121
122 pub fn disabled() -> Self {
124 Self { enabled: false, ..Self::default() }
125 }
126
127 pub fn with_enable_push(mut self, enable: bool) -> Self {
129 self.enable_push = enable;
130 self
131 }
132
133 pub fn with_max_concurrent_streams(mut self, max: u32) -> Self {
135 self.max_concurrent_streams = max;
136 self
137 }
138
139 pub fn with_initial_stream_window_size(mut self, size: u32) -> Self {
141 self.initial_stream_window_size = size;
142 self
143 }
144
145 pub fn with_max_frame_size(mut self, size: u32) -> Self {
147 self.max_frame_size = size;
148 self
149 }
150
151 pub fn with_enable_connect_protocol(mut self, enable: bool) -> Self {
153 self.enable_connect_protocol = enable;
154 self
155 }
156
157 pub fn with_stream_idle_timeout(mut self, timeout: Duration) -> Self {
159 self.stream_idle_timeout = timeout;
160 self
161 }
162}
163
164#[derive(Debug, Clone)]
168pub struct TlsConfig {
169 pub cert_path: String,
171 pub key_path: String,
173}
174
175impl TlsConfig {
176 pub fn new(cert_path: impl Into<String>, key_path: impl Into<String>) -> Self {
183 Self { cert_path: cert_path.into(), key_path: key_path.into() }
184 }
185}
186
187#[derive(Debug, Clone, Default)]
191pub struct Http3Config {
192 pub enabled: bool,
194}
195
196impl Http3Config {
197 pub fn new() -> Self {
199 Self::default()
200 }
201
202 pub fn enabled() -> Self {
204 Self { enabled: true }
205 }
206}
207
208#[derive(Debug, Clone)]
212pub struct HttpsServerConfig {
213 pub addr: SocketAddr,
215 pub service_name: String,
217 pub http_version: HttpVersion,
219 pub http2_config: Http2Config,
221 pub http3_config: Http3Config,
223 pub tls_config: Option<TlsConfig>,
225}
226
227impl Default for HttpsServerConfig {
228 fn default() -> Self {
229 Self {
230 addr: "0.0.0.0:3000".parse().unwrap(),
231 service_name: "wae-https-service".to_string(),
232 http_version: HttpVersion::Both,
233 http2_config: Http2Config::default(),
234 http3_config: Http3Config::default(),
235 tls_config: None,
236 }
237 }
238}
239
240pub struct HttpsServerBuilder {
244 config: HttpsServerConfig,
245 router: Router,
246}
247
248impl HttpsServerBuilder {
249 pub fn new() -> Self {
251 Self { config: HttpsServerConfig::default(), router: Router::new() }
252 }
253
254 pub fn addr(mut self, addr: SocketAddr) -> Self {
256 self.config.addr = addr;
257 self
258 }
259
260 pub fn service_name(mut self, name: impl Into<String>) -> Self {
262 self.config.service_name = name.into();
263 self
264 }
265
266 pub fn router(mut self, router: Router) -> Self {
268 self.router = router;
269 self
270 }
271
272 pub fn merge_router(mut self, router: Router) -> Self {
274 self.router = self.router.merge(router);
275 self
276 }
277
278 pub fn http_version(mut self, version: HttpVersion) -> Self {
280 self.config.http_version = version;
281 self
282 }
283
284 pub fn http2_config(mut self, config: Http2Config) -> Self {
286 self.config.http2_config = config;
287 self
288 }
289
290 pub fn http3_config(mut self, config: Http3Config) -> Self {
292 self.config.http3_config = config;
293 self
294 }
295
296 pub fn tls(mut self, cert_path: impl Into<String>, key_path: impl Into<String>) -> Self {
303 self.config.tls_config = Some(TlsConfig::new(cert_path, key_path));
304 self
305 }
306
307 pub fn tls_config(mut self, config: TlsConfig) -> Self {
309 self.config.tls_config = Some(config);
310 self
311 }
312
313 pub fn build(self) -> HttpsServer {
315 HttpsServer { config: self.config, router: self.router }
316 }
317}
318
319impl Default for HttpsServerBuilder {
320 fn default() -> Self {
321 Self::new()
322 }
323}
324
325pub struct HttpsServer {
329 config: HttpsServerConfig,
330 router: Router,
331}
332
333impl HttpsServer {
334 pub async fn serve(self) -> HttpsResult<()> {
336 let addr = self.config.addr;
337 let service_name = self.config.service_name.clone();
338 let protocol_info = self.get_protocol_info();
339 let tls_config = self.config.tls_config.clone();
340
341 let listener =
342 TcpListener::bind(addr).await.map_err(|e| WaeError::internal(format!("Failed to bind address: {}", e)))?;
343
344 info!("{} {} server starting on {}", service_name, protocol_info, addr);
345
346 match tls_config {
347 Some(tls_config) => self.serve_tls(listener, &tls_config).await,
348 None => self.serve_plain(listener).await,
349 }
350 }
351
352 async fn serve_plain(self, _listener: TcpListener) -> HttpsResult<()> {
353 loop {
356 tokio::time::sleep(Duration::from_secs(3600)).await;
357 }
358 }
359
360 async fn serve_tls(self, _listener: TcpListener, _tls_config: &TlsConfig) -> HttpsResult<()> {
361 loop {
364 tokio::time::sleep(Duration::from_secs(3600)).await;
365 }
366 }
367
368 fn get_protocol_info(&self) -> String {
369 let tls_info = if self.config.tls_config.is_some() { "S" } else { "" };
370 let version_info = match self.config.http_version {
371 HttpVersion::Http1Only => "HTTP/1.1",
372 HttpVersion::Http2Only => "HTTP/2",
373 HttpVersion::Both => "HTTP/1.1+HTTP/2",
374 HttpVersion::Http3 => "HTTP/3",
375 };
376 format!("{}{}", version_info, tls_info)
377 }
378}
379
380#[derive(Debug, serde::Serialize)]
384pub struct ApiResponse<T> {
385 pub success: bool,
387 pub data: Option<T>,
389 pub error: Option<ApiErrorBody>,
391 pub trace_id: Option<String>,
393}
394
395#[derive(Debug, serde::Serialize)]
399pub struct ApiErrorBody {
400 pub code: String,
402 pub message: String,
404}
405
406impl<T: serde::Serialize> ApiResponse<T> {
407 pub fn into_response(self) -> Response<Body> {
409 let status = if self.success { StatusCode::OK } else { StatusCode::BAD_REQUEST };
410 let body = serde_json::to_string(&self).unwrap_or_default();
411 Response::builder()
412 .status(status)
413 .header(header::CONTENT_TYPE, "application/json")
414 .body(Full::new(Bytes::from(body)))
415 .unwrap()
416 }
417}
418
419pub fn static_files_router(_path: impl AsRef<Path>, _prefix: &str) -> Router {
428 Router::new()
430}
431
432impl<T> ApiResponse<T>
433where
434 T: serde::Serialize,
435{
436 pub fn success(data: T) -> Self {
442 Self { success: true, data: Some(data), error: None, trace_id: None }
443 }
444
445 pub fn success_with_trace(data: T, trace_id: impl Into<String>) -> Self {
452 Self { success: true, data: Some(data), error: None, trace_id: Some(trace_id.into()) }
453 }
454
455 pub fn error(code: impl Into<String>, message: impl Into<String>) -> Self {
462 Self {
463 success: false,
464 data: None,
465 error: Some(ApiErrorBody { code: code.into(), message: message.into() }),
466 trace_id: None,
467 }
468 }
469
470 pub fn error_with_trace(code: impl Into<String>, message: impl Into<String>, trace_id: impl Into<String>) -> Self {
478 Self {
479 success: false,
480 data: None,
481 error: Some(ApiErrorBody { code: code.into(), message: message.into() }),
482 trace_id: Some(trace_id.into()),
483 }
484 }
485}