1#[cfg(test)]
2pub mod tests;
3#[cfg(test)]
4mod example;
5
6use std::io::prelude::*;
7use std::borrow::Borrow;
8use std::net::{IpAddr, SocketAddr, TcpListener};
9use std::str::FromStr;
10use std::time::Duration;
11
12use crate::request::{METHOD, Request};
13use crate::response::{Response, STATUS_CODE_REASON_PHRASE};
14use crate::app::App;
15use crate::application::Application;
16use crate::core::{New};
17use crate::entry_point::{bootstrap, get_ip_port_thread_count, get_request_allocation_size, set_default_values};
18use crate::header::Header;
19use crate::log::Log;
20use crate::mime_type::MimeType;
21use crate::range::{ContentRange, Range};
22use crate::symbol::SYMBOL;
23use crate::thread_pool::ThreadPool;
24
25pub struct Server {}
26impl Server {
27 pub fn process_request(mut stream: impl Read + Write + Unpin, peer_addr: SocketAddr) -> Vec<u8> {
28 let request_allocation_size = get_request_allocation_size();
29 let mut buffer = vec![0; request_allocation_size as usize];
30 let boxed_read = stream.read(&mut buffer);
31 if boxed_read.is_err() {
32 let message = boxed_read.err().unwrap().to_string();
33 eprintln!("unable to read TCP stream {}", &message);
34
35 let raw_response = Server::bad_request_response(message);
36 let boxed_stream = stream.write(raw_response.borrow());
37 if boxed_stream.is_ok() {
38 stream.flush().unwrap();
39 };
40 return raw_response;
41 }
42
43 boxed_read.unwrap();
44 let request : &[u8] = &buffer;
45
46 let boxed_request = Request::parse_request(request);
51 if boxed_request.is_err() {
52 let message = boxed_request.err().unwrap();
53 eprintln!("unable to parse request: {}", &message);
54
55 let raw_response = Server::bad_request_response(message);
56 let boxed_stream = stream.write(raw_response.borrow());
57 if boxed_stream.is_ok() {
58 stream.flush().unwrap();
59 };
60 return raw_response;
61 }
62
63
64 let request: Request = boxed_request.unwrap();
65 let (response, request) = App::handle_request(request);
66
67
68 let log_request_response = Log::combined(&request, &response, &peer_addr);
69 println!("{}", log_request_response);
70 let raw_response = Response::generate_response(response, request);
71
72 let boxed_stream = stream.write(raw_response.borrow());
73 if boxed_stream.is_ok() {
74 stream.flush().unwrap();
75 };
76
77 raw_response
78 }
79
80 pub fn bad_request_response(message: String) -> Vec<u8> {
81 let error_request = Request {
82 method: METHOD.get.to_string(),
83 request_uri: "".to_string(),
84 http_version: "".to_string(),
85 headers: vec![],
86 body: vec![],
87 };
88
89 let size = message.chars().count() as u64;
90 let content_range = ContentRange {
91 unit: Range::BYTES.to_string(),
92 range: Range { start: 0, end: size },
93 size: size.to_string(),
94 body: Vec::from(message.as_bytes()),
95 content_type: MimeType::TEXT_PLAIN.to_string(),
96 };
97
98 let header_list = Header::get_header_list(&error_request);
99 let error_response: Response = Response::get_response(
100 STATUS_CODE_REASON_PHRASE.n400_bad_request,
101 Some(header_list),
102 Some(vec![content_range])
103 );
104
105 let response = Response::generate_response(error_response, error_request);
106 return response;
107 }
108
109 pub fn process(mut stream: impl Read + Write + Unpin,
110 connection: ConnectionInfo,
111 app: impl Application) -> Result<(), String> {
112 use crate::http::VERSION;
113
114 let request_allocation_size = connection.request_size;
115 let client = connection.client.clone();
116 let client_addr = SocketAddr::new(IpAddr::from_str(client.ip.as_str()).unwrap(), client.port as u16);
117
118 loop {
119 let mut buffer = vec![0; request_allocation_size as usize];
120 let boxed_read = stream.read(&mut buffer);
121 if boxed_read.is_err() {
122 break;
124 }
125 if boxed_read.unwrap() == 0 {
126 break;
127 }
128
129 let request = match Request::parse(&buffer) {
130 Ok(r) => r,
131 Err(message) => {
132 let raw_response = Server::bad_request_response(message.clone());
133 let boxed_stream = stream.write(raw_response.borrow());
134 if boxed_stream.is_ok() { stream.flush().unwrap(); }
135 return Err(message);
136 }
137 };
138
139 let keep_alive = {
140 let conn_hdr = request.get_header(Header::_CONNECTION.to_string());
141 match conn_hdr {
142 Some(h) => h.value.to_lowercase() != "close",
143 None => request.http_version == VERSION.http_1_1,
144 }
145 };
146
147 let mut response = match app.execute(&request, &connection) {
148 Ok(r) => r,
149 Err(message) => {
150 let raw_response = Server::bad_request_response(message.clone());
151 let boxed_stream = stream.write(raw_response.borrow());
152 if boxed_stream.is_ok() { stream.flush().unwrap(); }
153 return Err(message);
154 }
155 };
156
157 crate::metrics::record_request();
158 crate::compression::apply_gzip(&request, &mut response);
159
160 response.headers.push(Header {
161 name: Header::_CONNECTION.to_string(),
162 value: if keep_alive { "keep-alive".to_string() } else { "close".to_string() },
163 });
164
165 Log::log_access(&request, &response, &client_addr);
166
167 if let Some(ref filepath) = response.stream_file.clone() {
168 if let Err(e) = Server::write_chunked_file(&mut stream, response, request, filepath) {
169 return Err(e);
170 }
171 } else {
172 let raw_response = Response::generate_response(response, request);
173 if let Err(e) = stream.write(raw_response.borrow()) {
174 return Err(e.to_string());
175 }
176 stream.flush().unwrap();
177 }
178
179 if !keep_alive { break; }
180 }
181
182 Ok(())
183 }
184
185 pub(crate) fn write_chunked_file(
188 stream: &mut impl Write,
189 mut response: Response,
190 request: Request,
191 filepath: &str,
192 ) -> Result<(), String> {
193 use std::fs::File;
194 use std::io::Read as _;
195
196 response.headers.push(Header {
197 name: Header::_TRANSFER_ENCODING.to_string(),
198 value: "chunked".to_string(),
199 });
200
201 let status = [
203 response.http_version.clone(),
204 response.status_code.to_string(),
205 response.reason_phrase.clone(),
206 ].join(SYMBOL.whitespace);
207
208 let mut headers_str = SYMBOL.new_line_carriage_return.to_string();
209 for header in &response.headers {
210 headers_str.push_str(&header.name);
211 headers_str.push_str(Header::NAME_VALUE_SEPARATOR);
212 headers_str.push_str(&header.value);
213 headers_str.push_str(SYMBOL.new_line_carriage_return);
214 }
215 let head = format!("{}{}{}", status, headers_str, SYMBOL.new_line_carriage_return);
216
217 stream.write_all(head.as_bytes()).map_err(|e| e.to_string())?;
218
219 if request.method != METHOD.head && request.method != METHOD.options {
220 let mut file = File::open(filepath).map_err(|e| e.to_string())?;
221 let mut buf = vec![0u8; 65536];
222 loop {
223 let n = file.read(&mut buf).map_err(|e| e.to_string())?;
224 if n == 0 { break; }
225 stream.write_all(format!("{:x}\r\n", n).as_bytes()).map_err(|e| e.to_string())?;
227 stream.write_all(&buf[..n]).map_err(|e| e.to_string())?;
228 stream.write_all(b"\r\n").map_err(|e| e.to_string())?;
229 }
230 stream.write_all(b"0\r\n\r\n").map_err(|e| e.to_string())?;
232 }
233
234 stream.flush().map_err(|e| e.to_string())
235 }
236
237 pub fn setup() -> Result<(TcpListener, ThreadPool), String> {
240 let info = Log::info("Rust Web Server");
241 println!("{}", info);
242
243 let usage_info = Log::usage_information();
244 println!("{}", usage_info);
245
246
247 println!("RWS Configuration Start: \n");
248
249 set_default_values();
250 bootstrap();
251
252 println!("\nRWS Configuration End\n\n");
253
254
255 let (ip, port, thread_count) = get_ip_port_thread_count();
256
257
258 let mut ip_readable = ip.to_string();
259
260 if ip.contains(":") {
261 ip_readable = [SYMBOL.opening_square_bracket, &ip, SYMBOL.closing_square_bracket].join("");
262 }
263
264 let bind_addr = [ip_readable, SYMBOL.colon.to_string(), port.to_string()].join(SYMBOL.empty_string);
265
266 #[cfg(feature = "http2")]
267 let protocol = {
268 let cert = std::env::var(crate::entry_point::Config::RWS_CONFIG_TLS_CERT_FILE).unwrap_or_default();
269 if cert.is_empty() { "http" } else { "https" }
270 };
271 #[cfg(not(feature = "http2"))]
272 let protocol = "http";
273
274 println!("Setting up {}://{}...", protocol, &bind_addr);
275
276 let boxed_listener = TcpListener::bind(&bind_addr);
277 if boxed_listener.is_err() {
278 let message = format!("unable to set up TCP listener: {}", boxed_listener.err().unwrap());
279 return Err(message);
280 }
281
282 let listener = boxed_listener.unwrap();
283 let pool = ThreadPool::new(thread_count as usize);
284
285
286 let server_url_thread_count = Log::server_url_thread_count(protocol, &bind_addr, thread_count);
287 println!("{}", server_url_thread_count);
288
289 Ok((listener, pool))
290 }
291
292 pub fn run(listener: TcpListener,
300 pool: ThreadPool,
301 app: impl Application + New + Send + 'static + Copy) {
302 #[cfg(feature = "http1")]
303 {
304 use std::sync::Arc;
305 use std::sync::atomic::{AtomicBool, Ordering};
306
307 let shutdown = Arc::new(AtomicBool::new(false));
308 let s = shutdown.clone();
309 if let Err(e) = ctrlc::set_handler(move || {
310 s.store(true, Ordering::SeqCst);
311 }) {
312 eprintln!("unable to install signal handler: {}", e);
313 }
314 if let Err(e) = listener.set_nonblocking(true) {
315 eprintln!("unable to set non-blocking listener: {}", e);
316 }
317
318 loop {
319 if shutdown.load(Ordering::SeqCst) {
320 break;
321 }
322 match listener.accept() {
323 Ok((stream, peer_addr)) => {
324 Server::dispatch_connection(stream, peer_addr, &pool, app);
325 }
326 Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
327 std::thread::sleep(Duration::from_millis(10));
328 }
329 Err(e) => {
330 eprintln!("accept error: {}", e);
331 break;
332 }
333 }
334 }
335
336 crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
337 println!("Shutting down — waiting for in-flight connections to finish");
338 pool.join();
339 println!("Server stopped");
340 }
341
342 #[cfg(not(feature = "http1"))]
343 {
344 for boxed_stream in listener.incoming() {
345 match boxed_stream {
346 Err(e) => {
347 eprintln!("unable to get TCP stream: {}", e);
348 return;
349 }
350 Ok(stream) => {
351 let peer_addr = match stream.peer_addr() {
352 Ok(a) => a,
353 Err(e) => {
354 eprintln!("unable to read peer addr: {}", e);
355 return;
356 }
357 };
358 Server::dispatch_connection(stream, peer_addr, &pool, app);
359 }
360 }
361 }
362 }
363 }
364
365 fn dispatch_connection(
366 stream: std::net::TcpStream,
367 peer_addr: std::net::SocketAddr,
368 pool: &ThreadPool,
369 app: impl Application + New + Send + 'static + Copy,
370 ) {
371 print!("Connection established, ");
372 if let Ok(local) = stream.local_addr() {
373 print!("local addr: {}", local);
374 }
375 println!(", peer addr: {}", peer_addr);
376
377 let (server_ip, server_port, _thread_count) = get_ip_port_thread_count();
378 let connection = ConnectionInfo {
379 client: Address {
380 ip: peer_addr.ip().to_string(),
381 port: peer_addr.port() as i32,
382 },
383 server: Address {
384 ip: server_ip,
385 port: server_port,
386 },
387 request_size: get_request_allocation_size(),
388 };
389
390 if let Err(e) = stream.set_read_timeout(Some(Duration::from_secs(30))) {
391 eprintln!("failed to set read timeout: {}", e);
392 }
393
394 pool.execute(move || {
395 crate::metrics::connection_open();
396 let result = Server::process(stream, connection, app);
397 crate::metrics::connection_close();
398 if let Err(msg) = result {
399 crate::metrics::record_error();
400 eprintln!("{}", msg);
401 }
402 });
403 }
404
405}
406
407#[derive(Clone)]
409pub struct ConnectionInfo {
410 pub client: Address,
412 pub server: Address,
414 pub request_size: i64
416}
417
418#[derive(Clone)]
420pub struct Address {
421 pub ip: String,
422 pub port: i32
423}
424
425impl ConnectionInfo {
426 pub fn peer_addr(&self) -> Option<std::net::SocketAddr> {
429 self.client.to_socket_addr()
430 }
431}
432
433impl Address {
434 pub fn to_socket_addr(&self) -> Option<std::net::SocketAddr> {
437 let ip: std::net::IpAddr = self.ip.parse().ok()?;
438 let port = u16::try_from(self.port).ok()?;
439 Some(std::net::SocketAddr::new(ip, port))
440 }
441}
442
443#[cfg(feature = "http2")]
446async fn sigterm() {
447 #[cfg(unix)]
448 {
449 if let Ok(mut s) = tokio::signal::unix::signal(
450 tokio::signal::unix::SignalKind::terminate()
451 ) {
452 s.recv().await;
453 } else {
454 std::future::pending::<()>().await
455 }
456 }
457 #[cfg(not(unix))]
458 std::future::pending::<()>().await
459}
460
461#[cfg(feature = "http2")]
462impl Server {
463 pub async fn run_tls(
464 listener: TcpListener,
465 pool: ThreadPool,
466 app: impl Application + New + Send + 'static + Copy,
467 ) {
468 use crate::tls::create_tls_acceptor;
469 use crate::h2_handler;
470
471 let cert_path = std::env::var(crate::entry_point::Config::RWS_CONFIG_TLS_CERT_FILE)
472 .unwrap_or_default();
473 let key_path = std::env::var(crate::entry_point::Config::RWS_CONFIG_TLS_KEY_FILE)
474 .unwrap_or_default();
475
476 if cert_path.is_empty() || key_path.is_empty() {
477 println!("No TLS certificate configured — serving plain HTTP/1.1.");
478 tokio::task::block_in_place(|| Server::run(listener, pool, app));
479 return;
480 }
481
482 let tls_acceptor = match create_tls_acceptor(&cert_path, &key_path) {
483 Ok(a) => a,
484 Err(e) => {
485 eprintln!("TLS setup failed: {}", e);
486 return;
487 }
488 };
489
490 listener
491 .set_nonblocking(true)
492 .expect("failed to set TCP listener to non-blocking");
493 let tokio_listener = tokio::net::TcpListener::from_std(listener)
494 .expect("failed to convert TCP listener to tokio");
495
496 println!("Listening for TLS connections (HTTP/1.1 + HTTP/2)...");
497
498 loop {
499 tokio::select! {
500 result = tokio_listener.accept() => {
501 match result {
502 Ok((tcp_stream, peer_addr)) => {
503 let acceptor = tls_acceptor.clone();
504 tokio::spawn(async move {
505 match acceptor.accept(tcp_stream).await {
506 Ok(tls_stream) => {
507 let protocol = tls_stream
508 .get_ref()
509 .1
510 .alpn_protocol()
511 .map(|p| p.to_vec());
512
513 match protocol.as_deref() {
514 Some(b"h2") => {
515 if let Err(e) =
516 h2_handler::handle_connection(tls_stream, peer_addr, app)
517 .await
518 {
519 eprintln!("H2 connection error: {}", e);
520 }
521 }
522 _ => {
523 if let Err(e) =
524 Server::process_h1_tls(tls_stream, peer_addr, app).await
525 {
526 eprintln!("H1 TLS error: {}", e);
527 }
528 }
529 }
530 }
531 Err(e) => eprintln!("TLS handshake failed: {}", e),
532 }
533 });
534 }
535 Err(e) => eprintln!("TCP accept error: {}", e),
536 }
537 }
538 _ = tokio::signal::ctrl_c() => {
539 crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
540 println!("\nShutting down gracefully (SIGINT).");
541 break;
542 }
543 _ = sigterm() => {
544 crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
545 println!("\nShutting down gracefully (SIGTERM).");
546 break;
547 }
548 }
549 }
550 }
551
552 pub async fn run_redirect() {
556 use std::env;
557 use tokio::io::{AsyncReadExt, AsyncWriteExt};
558 use tokio::net::TcpListener as TokioListener;
559
560 let cert_path = env::var(crate::entry_point::Config::RWS_CONFIG_TLS_CERT_FILE)
561 .unwrap_or_default();
562 if cert_path.is_empty() {
563 return;
564 }
565
566 let redirect_port_str = env::var(crate::entry_point::Config::RWS_CONFIG_HTTP_REDIRECT_PORT)
567 .unwrap_or_default();
568 if redirect_port_str.is_empty() {
569 return;
570 }
571
572 let redirect_port: u16 = match redirect_port_str.parse() {
573 Ok(p) => p,
574 Err(_) => {
575 eprintln!("Invalid RWS_CONFIG_HTTP_REDIRECT_PORT: {}", redirect_port_str);
576 return;
577 }
578 };
579
580 let (server_ip, server_port, _) = get_ip_port_thread_count();
581 let bind_addr = format!("{}:{}", server_ip, redirect_port);
582
583 let listener = match TokioListener::bind(&bind_addr).await {
584 Ok(l) => l,
585 Err(e) => {
586 eprintln!("HTTP redirect listener error on {}: {}", bind_addr, e);
587 return;
588 }
589 };
590
591 println!("HTTP→HTTPS redirect listening on http://{}:{}", server_ip, redirect_port);
592
593 loop {
594 tokio::select! {
595 result = listener.accept() => {
596 match result {
597 Ok((mut stream, _peer)) => {
598 let https_port = server_port;
599 tokio::spawn(async move {
600 let mut buf = vec![0u8; 4096];
601 let n = match stream.read(&mut buf).await {
602 Ok(n) => n,
603 Err(_) => return,
604 };
605 let text = String::from_utf8_lossy(&buf[..n]);
606
607 let uri = text.lines()
608 .next()
609 .and_then(|line| line.split_whitespace().nth(1))
610 .unwrap_or("/")
611 .to_string();
612
613 let host_header = text.lines()
614 .find(|l| l.to_lowercase().starts_with("host:"))
615 .map(|l| l[5..].trim().to_string());
616
617 let location = match host_header {
618 Some(h) => {
619 let h_no_port = if h.starts_with('[') {
621 h.find(']')
623 .map(|i| h[..=i].to_string())
624 .unwrap_or(h.clone())
625 } else {
626 h.rfind(':')
627 .map(|i| h[..i].to_string())
628 .unwrap_or(h.clone())
629 };
630 if https_port == 443 {
631 format!("https://{}{}", h_no_port, uri)
632 } else {
633 format!("https://{}:{}{}", h_no_port, https_port, uri)
634 }
635 }
636 None => format!("https://localhost:{}{}", https_port, uri),
637 };
638
639 let response = format!(
640 "HTTP/1.1 301 Moved Permanently\r\nLocation: {}\r\nContent-Length: 0\r\nConnection: close\r\n\r\n",
641 location
642 );
643 let _ = stream.write_all(response.as_bytes()).await;
644 });
645 }
646 Err(e) => eprintln!("HTTP redirect accept error: {}", e),
647 }
648 }
649 _ = tokio::signal::ctrl_c() => {
650 println!("\nShutting down HTTP redirect listener (SIGINT).");
651 break;
652 }
653 _ = sigterm() => {
654 println!("\nShutting down HTTP redirect listener (SIGTERM).");
655 break;
656 }
657 }
658 }
659 }
660
661 async fn process_h1_tls(
662 mut stream: tokio_rustls::server::TlsStream<tokio::net::TcpStream>,
663 peer_addr: std::net::SocketAddr,
664 app: impl Application,
665 ) -> Result<(), String> {
666 use tokio::io::{AsyncReadExt, AsyncWriteExt};
667
668 let (server_ip, server_port, _) = get_ip_port_thread_count();
669 let request_allocation_size = get_request_allocation_size();
670
671 let mut buffer = vec![0u8; request_allocation_size as usize];
672 if let Err(e) = stream.read(&mut buffer).await {
673 let raw = Server::bad_request_response(e.to_string());
674 let _ = stream.write_all(&raw).await;
675 return Ok(());
676 }
677
678 let request = match Request::parse(&buffer) {
679 Ok(r) => r,
680 Err(message) => {
681 let raw = Server::bad_request_response(message);
682 let _ = stream.write_all(&raw).await;
683 return Ok(());
684 }
685 };
686
687 let connection = ConnectionInfo {
688 client: Address {
689 ip: peer_addr.ip().to_string(),
690 port: peer_addr.port() as i32,
691 },
692 server: Address {
693 ip: server_ip,
694 port: server_port,
695 },
696 request_size: request_allocation_size,
697 };
698
699 let mut response = match app.execute(&request, &connection) {
700 Ok(r) => r,
701 Err(message) => {
702 let raw = Server::bad_request_response(message);
703 let _ = stream.write_all(&raw).await;
704 return Ok(());
705 }
706 };
707
708 crate::metrics::record_request();
709 crate::compression::apply_gzip(&request, &mut response);
710 response.headers.push(Header::get_hsts_header());
711
712 #[cfg(feature = "http3")]
713 response.headers.push(Header {
714 name: Header::_ALT_SVC.to_string(),
715 value: format!("h3=\":{}\"", server_port),
716 });
717 #[cfg(not(feature = "http3"))]
718 response.headers.push(Header {
719 name: Header::_ALT_SVC.to_string(),
720 value: format!("h2=\":{}\"", server_port),
721 });
722
723 Log::log_access(&request, &response, &peer_addr);
724
725 let raw = Response::generate_response(response, request);
726 stream
727 .write_all(&raw)
728 .await
729 .map_err(|e| e.to_string())?;
730 stream.flush().await.map_err(|e| e.to_string())?;
731
732 Ok(())
733 }
734}
735
736#[cfg(feature = "http3")]
737impl Server {
738 pub async fn run_quic(
739 app: impl Application + New + Send + 'static + Copy,
740 ) {
741 use crate::tls::create_quinn_server_config;
742 use crate::h3_handler;
743
744 let cert_path = std::env::var(crate::entry_point::Config::RWS_CONFIG_TLS_CERT_FILE)
745 .unwrap_or_default();
746 let key_path = std::env::var(crate::entry_point::Config::RWS_CONFIG_TLS_KEY_FILE)
747 .unwrap_or_default();
748
749 if cert_path.is_empty() || key_path.is_empty() {
750 return;
751 }
752
753 let server_config = match create_quinn_server_config(&cert_path, &key_path) {
754 Ok(c) => c,
755 Err(e) => {
756 eprintln!("QUIC TLS setup failed: {}", e);
757 return;
758 }
759 };
760
761 let (server_ip, server_port, _) = get_ip_port_thread_count();
762 let bind_addr = format!("{}:{}", server_ip, server_port);
763 let addr: std::net::SocketAddr = match bind_addr.parse() {
764 Ok(a) => a,
765 Err(e) => {
766 eprintln!("Invalid QUIC bind address '{}': {}", bind_addr, e);
767 return;
768 }
769 };
770
771 let endpoint = match quinn::Endpoint::server(server_config, addr) {
772 Ok(e) => e,
773 Err(e) => {
774 eprintln!("QUIC endpoint error: {}", e);
775 return;
776 }
777 };
778
779 println!("Listening for QUIC/HTTP3 on UDP {}:{}", server_ip, server_port);
780
781 loop {
782 tokio::select! {
783 maybe = endpoint.accept() => {
784 match maybe {
785 Some(incoming) => {
786 tokio::spawn(async move {
787 match incoming.await {
788 Ok(conn) => {
789 let peer_addr = conn.remote_address();
790 if let Err(e) = h3_handler::handle_connection(conn, peer_addr, app).await {
791 eprintln!("H3 connection error: {}", e);
792 }
793 }
794 Err(e) => eprintln!("QUIC connection error: {}", e),
795 }
796 });
797 }
798 None => break,
799 }
800 }
801 _ = tokio::signal::ctrl_c() => {
802 crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
803 println!("\nShutting down QUIC (SIGINT).");
804 endpoint.close(0u32.into(), b"shutdown");
805 break;
806 }
807 _ = sigterm() => {
808 crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
809 println!("\nShutting down QUIC (SIGTERM).");
810 endpoint.close(0u32.into(), b"shutdown");
811 break;
812 }
813 }
814 }
815 }
816}
817
818