Skip to main content

rust_web_server/server/
mod.rs

1#[cfg(all(test, not(target_arch = "wasm32")))]
2pub mod tests;
3#[cfg(all(test, not(target_arch = "wasm32")))]
4mod example;
5#[cfg(feature = "http2")]
6pub(crate) mod http1_async;
7#[cfg(feature = "http2")]
8pub(crate) mod body_stream;
9#[cfg(feature = "http2")]
10pub(crate) mod sendfile;
11pub(crate) mod framing;
12
13// The TCP/thread-pool accept loop below (`Server::setup`/`run`/`process`/…) needs
14// `std::net::TcpListener` and OS threads, neither available on `wasm32-wasip2`.
15// `ConnectionInfo`/`Address` further down have no socket dependency and stay
16// compiled on every target — see spec/WASM_SHIM.md for the guest-adapter that
17// replaces this accept loop under WASI.
18#[cfg(not(target_arch = "wasm32"))]
19use std::io::prelude::*;
20#[cfg(not(target_arch = "wasm32"))]
21use std::borrow::Borrow;
22#[cfg(not(target_arch = "wasm32"))]
23use std::net::{IpAddr, SocketAddr, TcpListener};
24#[cfg(not(target_arch = "wasm32"))]
25use std::str::FromStr;
26#[cfg(not(target_arch = "wasm32"))]
27use std::time::Duration;
28
29#[cfg(not(target_arch = "wasm32"))]
30use crate::request::{METHOD, Request};
31#[cfg(not(target_arch = "wasm32"))]
32use crate::response::{Response, STATUS_CODE_REASON_PHRASE};
33#[cfg(not(target_arch = "wasm32"))]
34use crate::app::App;
35#[cfg(not(target_arch = "wasm32"))]
36use crate::application::Application;
37#[cfg(not(target_arch = "wasm32"))]
38use crate::entry_point::{bootstrap, get_ip_port_thread_count, get_request_allocation_size, set_default_values};
39#[cfg(not(target_arch = "wasm32"))]
40use crate::header::Header;
41#[cfg(not(target_arch = "wasm32"))]
42use crate::log::Log;
43#[cfg(not(target_arch = "wasm32"))]
44use crate::mime_type::MimeType;
45#[cfg(not(target_arch = "wasm32"))]
46use crate::range::{ContentRange, Range};
47#[cfg(not(target_arch = "wasm32"))]
48use crate::symbol::SYMBOL;
49#[cfg(not(target_arch = "wasm32"))]
50use crate::thread_pool::ThreadPool;
51
52#[cfg(not(target_arch = "wasm32"))]
53pub struct Server {}
54#[cfg(not(target_arch = "wasm32"))]
55impl Server {
56    pub fn process_request(mut stream: impl Read + Write + Unpin, peer_addr: SocketAddr) -> Vec<u8> {
57        let request_allocation_size = get_request_allocation_size();
58        let mut buffer = vec![0; request_allocation_size as usize];
59        let boxed_read = stream.read(&mut buffer);
60        if boxed_read.is_err() {
61            let message = boxed_read.err().unwrap().to_string();
62            eprintln!("unable to read TCP stream {}", &message);
63
64            let raw_response = Server::bad_request_response(message);
65            let boxed_stream = stream.write(raw_response.borrow());
66            if boxed_stream.is_ok() {
67                stream.flush().unwrap();
68            };
69            return raw_response;
70        }
71
72        boxed_read.unwrap();
73        let request : &[u8] = &buffer;
74
75        // let raw_request = String::from_utf8(Vec::from(request)).unwrap();
76        // println!("\n\n______{}______\n\n", raw_request);
77
78
79        let boxed_request = Request::parse_request(request);
80        if boxed_request.is_err() {
81            let message = boxed_request.err().unwrap();
82            eprintln!("unable to parse request: {}", &message);
83
84            let raw_response = Server::bad_request_response(message);
85            let boxed_stream = stream.write(raw_response.borrow());
86            if boxed_stream.is_ok() {
87                stream.flush().unwrap();
88            };
89            return raw_response;
90        }
91
92
93        let request: Request = boxed_request.unwrap();
94        let (response, request) = App::handle_request(request);
95
96
97        let log_request_response = Log::combined(&request, &response, &peer_addr);
98        println!("{}", log_request_response);
99        let raw_response = Response::generate_response(response, request);
100
101        let boxed_stream = stream.write(raw_response.borrow());
102        if boxed_stream.is_ok() {
103            stream.flush().unwrap();
104        };
105
106        raw_response
107    }
108
109    pub fn bad_request_response(message: String) -> Vec<u8> {
110        let error_request = Request {
111            method: METHOD.get.to_string(),
112            request_uri: "".to_string(),
113            http_version: "".to_string(),
114            headers: vec![],
115            body: vec![],
116        };
117
118        let size = message.chars().count() as u64;
119        let content_range = ContentRange {
120            unit: Range::BYTES.to_string(),
121            range: Range { start: 0, end: size },
122            size: size.to_string(),
123            body: Vec::from(message.as_bytes()),
124            content_type: MimeType::TEXT_PLAIN.to_string(),
125        };
126
127        let header_list = Header::get_header_list(&error_request);
128        let error_response: Response = Response::get_response(
129            STATUS_CODE_REASON_PHRASE.n400_bad_request,
130            Some(header_list),
131            Some(vec![content_range])
132        );
133
134        let response = Response::generate_response(error_response, error_request);
135        return response;
136    }
137
138    /// Builds a `413 Payload Too Large` raw response for a request whose declared
139    /// `Content-Length` exceeds `RWS_CONFIG_MAX_BODY_SIZE_IN_BYTES`. The connection
140    /// is always closed after this is sent (see [`Server::process`]) — the client's
141    /// unread body bytes are still queued on the socket, so keep-alive framing
142    /// can't be trusted.
143    pub fn payload_too_large_response(message: String) -> Vec<u8> {
144        let error_request = Request {
145            method: METHOD.get.to_string(),
146            request_uri: "".to_string(),
147            http_version: "".to_string(),
148            headers: vec![],
149            body: vec![],
150        };
151
152        let size = message.chars().count() as u64;
153        let content_range = ContentRange {
154            unit: Range::BYTES.to_string(),
155            range: Range { start: 0, end: size },
156            size: size.to_string(),
157            body: Vec::from(message.as_bytes()),
158            content_type: MimeType::TEXT_PLAIN.to_string(),
159        };
160
161        let mut header_list = Header::get_header_list(&error_request);
162        header_list.push(Header { name: Header::_CONNECTION.to_string(), value: "close".to_string() });
163        let error_response: Response = Response::get_response(
164            STATUS_CODE_REASON_PHRASE.n413_payload_too_large,
165            Some(header_list),
166            Some(vec![content_range])
167        );
168
169        Response::generate_response(error_response, error_request)
170    }
171
172    /// Builds a `431 Request Header Fields Too Large` raw response for a
173    /// request head longer than [`framing::MAX_HEAD_SIZE`]. The connection is
174    /// closed after it: the rest of the oversized head is still unread.
175    pub fn header_fields_too_large_response() -> Vec<u8> {
176        let message = format!(
177            "431 Request Header Fields Too Large: the request head exceeds {} bytes",
178            framing::MAX_HEAD_SIZE
179        );
180        let error_request = Request {
181            method: METHOD.get.to_string(),
182            request_uri: "".to_string(),
183            http_version: "".to_string(),
184            headers: vec![],
185            body: vec![],
186        };
187
188        let size = message.chars().count() as u64;
189        let content_range = ContentRange {
190            unit: Range::BYTES.to_string(),
191            range: Range { start: 0, end: size },
192            size: size.to_string(),
193            body: Vec::from(message.as_bytes()),
194            content_type: MimeType::TEXT_PLAIN.to_string(),
195        };
196
197        let mut header_list = Header::get_header_list(&error_request);
198        header_list.push(Header { name: Header::_CONNECTION.to_string(), value: "close".to_string() });
199        let error_response: Response = Response::get_response(
200            STATUS_CODE_REASON_PHRASE.n431_request_header_fields_too_large,
201            Some(header_list),
202            Some(vec![content_range])
203        );
204
205        Response::generate_response(error_response, error_request)
206    }
207
208    /// Builds the `100 Continue` interim response (RFC 9110 §15.2.1) — no
209    /// headers, no body, just the status line. Sent immediately after parsing
210    /// a request with `Expect: 100-continue`, before reading its body, so the
211    /// client (which is waiting for this before it uploads the body) doesn't
212    /// stall or time out. See [`Server::process`] for where this is sent from.
213    pub fn continue_response() -> Vec<u8> {
214        let dummy_request = Request {
215            method: METHOD.get.to_string(),
216            request_uri: "".to_string(),
217            http_version: "".to_string(),
218            headers: vec![],
219            body: vec![],
220        };
221        let response = Response::get_response(STATUS_CODE_REASON_PHRASE.n100_continue, None, None);
222        Response::generate_response(response, dummy_request)
223    }
224
225    /// Builds a `417 Expectation Failed` raw response for an `Expect` header
226    /// value other than `100-continue` (RFC 9110 §10.1.1) — the only
227    /// expectation this server understands. Sent instead of reading the
228    /// request body, since the expectation can't be met either way.
229    pub fn expectation_failed_response(message: String) -> Vec<u8> {
230        let error_request = Request {
231            method: METHOD.get.to_string(),
232            request_uri: "".to_string(),
233            http_version: "".to_string(),
234            headers: vec![],
235            body: vec![],
236        };
237
238        let size = message.chars().count() as u64;
239        let content_range = ContentRange {
240            unit: Range::BYTES.to_string(),
241            range: Range { start: 0, end: size },
242            size: size.to_string(),
243            body: Vec::from(message.as_bytes()),
244            content_type: MimeType::TEXT_PLAIN.to_string(),
245        };
246
247        let mut header_list = Header::get_header_list(&error_request);
248        header_list.push(Header { name: Header::_CONNECTION.to_string(), value: "close".to_string() });
249        let error_response: Response = Response::get_response(
250            STATUS_CODE_REASON_PHRASE.n417_expectation_failed,
251            Some(header_list),
252            Some(vec![content_range])
253        );
254
255        Response::generate_response(error_response, error_request)
256    }
257
258    pub fn process(mut stream: impl Read + Write + Unpin,
259                   connection: ConnectionInfo,
260                   app: impl Application) -> Result<(), String> {
261
262        let request_allocation_size = connection.request_size;
263        let client = connection.client.clone();
264        let client_addr = SocketAddr::new(IpAddr::from_str(client.ip.as_str()).unwrap(), client.port as u16);
265
266        let max_body_size = crate::entry_point::get_max_body_size();
267
268        // Bytes read from the connection but not consumed yet: the rest of a
269        // request head, a body, or the next pipelined request.
270        let mut pending: Vec<u8> = Vec::new();
271        let mut read_buffer = vec![0u8; request_allocation_size as usize];
272
273        'session: loop {
274            // Read until the request head is complete — it may span several reads.
275            let head_len = loop {
276                framing::skip_leading_empty_lines(&mut pending);
277                match framing::scan_head(&pending) {
278                    framing::HeadScan::Complete(n) => break Some(n),
279                    framing::HeadScan::TooLarge => {
280                        let _ = stream.write_all(&Server::header_fields_too_large_response());
281                        let _ = stream.flush();
282                        return Err("request head too large".to_string());
283                    }
284                    framing::HeadScan::Incomplete => {}
285                }
286                match stream.read(&mut read_buffer) {
287                    // timeout or client closed — normal end of keep-alive session
288                    Ok(0) | Err(_) => break None,
289                    Ok(n) => pending.extend_from_slice(&read_buffer[..n]),
290                }
291            };
292            let Some(head_len) = head_len else { break };
293
294            let parsed = Request::parse(&pending[..head_len])
295                .and_then(|request| framing::validate_head(&pending[..head_len], &request).map(|_| request))
296                .and_then(|request| framing::body_framing(&request).map(|f| (request, f)));
297            pending.drain(..head_len);
298            let (mut request, body_framing) = match parsed {
299                Ok(parsed) => parsed,
300                Err(message) => {
301                    let _ = stream.write_all(&Server::bad_request_response(message.clone()));
302                    let _ = stream.flush();
303                    return Err(message);
304                }
305            };
306
307            // RFC 9110 §10.1.1 `Expect` — the only expectation this server
308            // understands is `100-continue`; anything else fails fast with a
309            // final status instead of reading a body we can't act on.
310            let expects_continue = match request.get_header(Header::_EXPECT.to_string()) {
311                Some(h) if h.value.trim().eq_ignore_ascii_case("100-continue") => true,
312                Some(h) => {
313                    let raw_response = Server::expectation_failed_response(format!(
314                        "417 Expectation Failed: unsupported Expect value '{}'", h.value.trim()
315                    ));
316                    let _ = stream.write_all(&raw_response);
317                    let _ = stream.flush();
318                    break;
319                }
320                None => false,
321            };
322
323            match body_framing {
324                framing::BodyFraming::None => {}
325                framing::BodyFraming::Length(declared_len) => {
326                    if max_body_size > 0 && declared_len > max_body_size {
327                        let raw_response = Server::payload_too_large_response(format!(
328                            "413 Payload Too Large: declared Content-Length {} exceeds the {} byte limit",
329                            declared_len, max_body_size
330                        ));
331                        let _ = stream.write_all(&raw_response);
332                        let _ = stream.flush();
333                        break;
334                    }
335
336                    // Tell the client to go ahead and send the body now, before we
337                    // block waiting for it below.
338                    if expects_continue && (pending.len() as u64) < declared_len {
339                        if stream.write_all(&Server::continue_response()).and_then(|_| stream.flush()).is_err() {
340                            break;
341                        }
342                    }
343
344                    while (pending.len() as u64) < declared_len {
345                        match stream.read(&mut read_buffer) {
346                            Ok(0) | Err(_) => break 'session, // closed or stalled mid-body
347                            Ok(k) => pending.extend_from_slice(&read_buffer[..k]),
348                        }
349                    }
350                    request.body = pending.drain(..declared_len as usize).collect();
351                }
352                framing::BodyFraming::Chunked => {
353                    if expects_continue && pending.is_empty() {
354                        if stream.write_all(&Server::continue_response()).and_then(|_| stream.flush()).is_err() {
355                            break;
356                        }
357                    }
358
359                    let mut decoder = framing::ChunkedBody::new(max_body_size);
360                    loop {
361                        match decoder.feed(&pending) {
362                            Ok((consumed, done)) => {
363                                pending.drain(..consumed);
364                                if done { break; }
365                            }
366                            Err(framing::ChunkError::TooLarge) => {
367                                let raw_response = Server::payload_too_large_response(format!(
368                                    "413 Payload Too Large: chunked body exceeds the {} byte limit", max_body_size
369                                ));
370                                let _ = stream.write_all(&raw_response);
371                                let _ = stream.flush();
372                                break 'session;
373                            }
374                            Err(framing::ChunkError::Malformed(message)) => {
375                                let _ = stream.write_all(&Server::bad_request_response(message.clone()));
376                                let _ = stream.flush();
377                                return Err(message);
378                            }
379                        }
380                        match stream.read(&mut read_buffer) {
381                            Ok(0) | Err(_) => break 'session,
382                            Ok(k) => pending.extend_from_slice(&read_buffer[..k]),
383                        }
384                    }
385                    request.body = decoder.body;
386                }
387            }
388
389            let keep_alive = framing::keep_alive(&request);
390
391            let mut response = match app.execute(&request, &connection) {
392                Ok(r) => r,
393                Err(message) => {
394                    let raw_response = Server::bad_request_response(message.clone());
395                    let boxed_stream = stream.write(raw_response.borrow());
396                    if boxed_stream.is_ok() { stream.flush().unwrap(); }
397                    return Err(message);
398                }
399            };
400
401            crate::metrics::record_request();
402            crate::compression::apply_gzip(&request, &mut response);
403            response.drop_content_for_status();
404
405            response.headers.push(Header {
406                name: Header::_CONNECTION.to_string(),
407                value: if keep_alive { "keep-alive".to_string() } else { "close".to_string() },
408            });
409
410            Log::log_access(&request, &response, &client_addr);
411
412            if let Some(reader) = response.stream_pipe.take() {
413                if let Err(e) = Server::pipe_stream(&mut stream, response, request, reader) {
414                    return Err(e);
415                }
416            } else if let Some(ref filepath) = response.stream_file.clone() {
417                if let Err(e) = Server::write_chunked_file(&mut stream, response, request, filepath) {
418                    return Err(e);
419                }
420            } else {
421                let raw_response = Response::generate_response(response, request);
422                // write_all: a single write() may accept only part of a large response.
423                if let Err(e) = stream.write_all(raw_response.borrow()) {
424                    return Err(e.to_string());
425                }
426                stream.flush().unwrap();
427            }
428
429            if !keep_alive { break; }
430        }
431
432        Ok(())
433    }
434
435    /// Streams a file to `stream` using HTTP/1.1 chunked transfer encoding.
436    /// The response headers are written first, then the file is read and written in 64 KB chunks.
437    pub(crate) fn write_chunked_file(
438        stream: &mut impl Write,
439        response: Response,
440        request: Request,
441        filepath: &str,
442    ) -> Result<(), String> {
443        use std::fs::File;
444        use std::io::Read as _;
445
446        let (head, chunked) = Server::stream_file_head(response);
447        stream.write_all(&head).map_err(|e| e.to_string())?;
448
449        if request.method != METHOD.head {
450            let mut file = File::open(filepath).map_err(|e| e.to_string())?;
451            let mut buf = vec![0u8; 65536];
452            loop {
453                let n = file.read(&mut buf).map_err(|e| e.to_string())?;
454                if n == 0 { break; }
455                if chunked {
456                    // chunk header: hex size + CRLF
457                    stream.write_all(format!("{:x}\r\n", n).as_bytes()).map_err(|e| e.to_string())?;
458                    stream.write_all(&buf[..n]).map_err(|e| e.to_string())?;
459                    stream.write_all(b"\r\n").map_err(|e| e.to_string())?;
460                } else {
461                    stream.write_all(&buf[..n]).map_err(|e| e.to_string())?;
462                }
463            }
464            if chunked {
465                // terminal chunk
466                stream.write_all(b"0\r\n\r\n").map_err(|e| e.to_string())?;
467            }
468        }
469
470        stream.flush().map_err(|e| e.to_string())
471    }
472
473    /// The status line and headers of a `stream_file` response, and whether
474    /// its body is sent chunked. With a known Content-Length (static files
475    /// set it) the body goes out as-is; Transfer-Encoding: chunked is only
476    /// for an unknown length. Sending both is forbidden (RFC 9112 §6.1) —
477    /// mismatched handling of the two by intermediaries is how request
478    /// smuggling happens.
479    pub(crate) fn stream_file_head(mut response: Response) -> (Vec<u8>, bool) {
480        let chunked = !response.headers.iter().any(|h| h.name.eq_ignore_ascii_case(Header::_CONTENT_LENGTH));
481        if chunked {
482            response.headers.push(Header {
483                name: Header::_TRANSFER_ENCODING.to_string(),
484                value: "chunked".to_string(),
485            });
486        }
487
488        // build status line + headers (no body)
489        let status = [
490            response.http_version.clone(),
491            response.status_code.to_string(),
492            Header::single_line(&response.reason_phrase).into_owned(),
493        ].join(SYMBOL.whitespace);
494
495        let mut headers_str = SYMBOL.new_line_carriage_return.to_string();
496        for header in &response.headers {
497            headers_str.push_str(&Header::single_line(&header.name));
498            headers_str.push_str(Header::NAME_VALUE_SEPARATOR);
499            headers_str.push_str(&Header::single_line(&header.value));
500            headers_str.push_str(SYMBOL.new_line_carriage_return);
501        }
502        let head = format!("{}{}{}", status, headers_str, SYMBOL.new_line_carriage_return);
503        (head.into_bytes(), chunked)
504    }
505
506    /// Streams a `Read` source to `stream` using HTTP/1.1 chunked transfer encoding.
507    ///
508    /// Called when `response.stream_pipe` is set (proxy passthrough for SSE, AI token
509    /// streams, and large downloads). The response headers are written first (with
510    /// `Transfer-Encoding: chunked` injected and any `Content-Length` stripped), then
511    /// bytes are read from `reader` and forwarded in 8 KB chunks. Each chunk is flushed
512    /// immediately so the client sees data as it arrives.
513    pub(crate) fn pipe_stream(
514        stream: &mut impl Write,
515        mut response: Response,
516        request: Request,
517        mut reader: Box<dyn std::io::Read + Send>,
518    ) -> Result<(), String> {
519        // If the backend already uses chunked encoding we pass the raw chunk
520        // frames through; otherwise we wrap the raw bytes in our own chunked
521        // framing so the client can receive data incrementally.
522        let backend_is_chunked = response.headers.iter().any(|h| {
523            h.name.eq_ignore_ascii_case("transfer-encoding")
524                && h.value.to_lowercase().contains("chunked")
525        });
526
527        response.headers.retain(|h| !h.name.eq_ignore_ascii_case("content-length"));
528        if !backend_is_chunked {
529            response.headers.push(Header {
530                name: Header::_TRANSFER_ENCODING.to_string(),
531                value: "chunked".to_string(),
532            });
533        }
534
535        let status = [
536            response.http_version.clone(),
537            response.status_code.to_string(),
538            Header::single_line(&response.reason_phrase).into_owned(),
539        ].join(SYMBOL.whitespace);
540
541        let mut head = format!("{}\r\n", status);
542        for header in &response.headers {
543            head.push_str(&Header::single_line(&header.name));
544            head.push_str(Header::NAME_VALUE_SEPARATOR);
545            head.push_str(&Header::single_line(&header.value));
546            head.push_str(SYMBOL.new_line_carriage_return);
547        }
548        head.push_str(SYMBOL.new_line_carriage_return);
549        stream.write_all(head.as_bytes()).map_err(|e| e.to_string())?;
550
551        if request.method != METHOD.head {
552            let mut buf = [0u8; 8192];
553            if backend_is_chunked {
554                // Passthrough: forward raw chunk frames as-is; the client
555                // decodes them itself (avoids double-chunking).
556                loop {
557                    match reader.read(&mut buf) {
558                        Ok(0) | Err(_) => break,
559                        Ok(n) => {
560                            stream.write_all(&buf[..n]).map_err(|e| e.to_string())?;
561                            stream.flush().map_err(|e| e.to_string())?;
562                        }
563                    }
564                }
565            } else {
566                // Re-encode: wrap the raw byte stream in chunked framing so
567                // the client can receive each fragment as it arrives (SSE,
568                // large downloads).
569                loop {
570                    match reader.read(&mut buf) {
571                        Ok(0) | Err(_) => break,
572                        Ok(n) => {
573                            stream
574                                .write_all(format!("{:x}\r\n", n).as_bytes())
575                                .map_err(|e| e.to_string())?;
576                            stream.write_all(&buf[..n]).map_err(|e| e.to_string())?;
577                            stream.write_all(b"\r\n").map_err(|e| e.to_string())?;
578                            stream.flush().map_err(|e| e.to_string())?;
579                        }
580                    }
581                }
582                stream.write_all(b"0\r\n\r\n").map_err(|e| e.to_string())?;
583            }
584        }
585
586        stream.flush().map_err(|e| e.to_string())
587    }
588
589    /// Reads configuration (IP, port, thread count, TLS paths) from the layered config system
590    /// and returns a bound `TcpListener` and a sized `ThreadPool`. Call once at startup.
591    pub fn setup() -> Result<(TcpListener, ThreadPool), String> {
592        let info = Log::info("Rust Web Server");
593        println!("{}", info);
594
595        let usage_info = Log::usage_information();
596        println!("{}", usage_info);
597
598
599        println!("RWS Configuration Start: \n");
600
601        set_default_values();
602        bootstrap();
603
604        #[cfg(feature = "secrets")]
605        crate::secrets::resolve_env_vars().map_err(|e| e.to_string())?;
606
607        println!("\nRWS Configuration End\n\n");
608
609
610        let (ip, port, thread_count) = get_ip_port_thread_count();
611
612
613        let mut ip_readable = ip.to_string();
614
615        if ip.contains(":") {
616            ip_readable = [SYMBOL.opening_square_bracket, &ip, SYMBOL.closing_square_bracket].join("");
617        }
618
619        let bind_addr = [ip_readable, SYMBOL.colon.to_string(), port.to_string()].join(SYMBOL.empty_string);
620
621        #[cfg(feature = "http2")]
622        let protocol = {
623            let cert = std::env::var(crate::entry_point::Config::RWS_CONFIG_TLS_CERT_FILE).unwrap_or_default();
624            if cert.is_empty() { "http" } else { "https" }
625        };
626        #[cfg(not(feature = "http2"))]
627        let protocol = "http";
628
629        println!("Setting up {}://{}...", protocol, &bind_addr);
630
631        let boxed_listener = TcpListener::bind(&bind_addr);
632        if boxed_listener.is_err() {
633            let message = format!("unable to set up TCP listener: {}", boxed_listener.err().unwrap());
634            return Err(message);
635        }
636
637        let listener = boxed_listener.unwrap();
638        let pool = ThreadPool::new(thread_count as usize);
639
640
641        let server_url_thread_count = Log::server_url_thread_count(protocol, &bind_addr, thread_count);
642        println!("{}", server_url_thread_count);
643
644        Ok((listener, pool))
645    }
646
647    /// Accepts TCP connections in a loop and dispatches each to the thread pool.
648    ///
649    /// When built with the `http1` feature, Ctrl+C and SIGTERM stop the accept
650    /// loop gracefully: `SERVER_READY` is cleared and the pool drains all
651    /// in-flight connections before returning.
652    ///
653    /// For TLS/HTTP2/HTTP3 use [`Server::run_tls`].
654    pub fn run(listener: TcpListener,
655               pool: ThreadPool,
656               app: impl Application + Send + 'static + Clone) {
657        #[cfg(feature = "http1")]
658        {
659            use std::sync::Arc;
660            use std::sync::atomic::{AtomicBool, Ordering};
661
662            let shutdown = Arc::new(AtomicBool::new(false));
663            let s = shutdown.clone();
664            if let Err(e) = ctrlc::set_handler(move || {
665                s.store(true, Ordering::SeqCst);
666            }) {
667                eprintln!("unable to install signal handler: {}", e);
668            }
669            crate::config_reload::install_sighup_handler();
670            if let Err(e) = listener.set_nonblocking(true) {
671                eprintln!("unable to set non-blocking listener: {}", e);
672            }
673
674            loop {
675                if shutdown.load(Ordering::SeqCst) {
676                    break;
677                }
678                if crate::config_reload::RELOAD_REQUESTED
679                    .compare_exchange(true, false, Ordering::SeqCst, Ordering::Relaxed)
680                    .is_ok()
681                {
682                    crate::config_reload::reload();
683                }
684                match listener.accept() {
685                    Ok((stream, peer_addr)) => {
686                        Server::dispatch_connection(stream, peer_addr, &pool, app.clone());
687                    }
688                    Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
689                        std::thread::sleep(Duration::from_millis(10));
690                    }
691                    Err(e) => {
692                        eprintln!("accept error: {}", e);
693                        break;
694                    }
695                }
696            }
697
698            crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
699            println!("Shutting down — waiting for in-flight connections to finish");
700            pool.join();
701            println!("Server stopped");
702        }
703
704        #[cfg(not(feature = "http1"))]
705        {
706            for boxed_stream in listener.incoming() {
707                match boxed_stream {
708                    Err(e) => {
709                        eprintln!("unable to get TCP stream: {}", e);
710                        return;
711                    }
712                    Ok(stream) => {
713                        let peer_addr = match stream.peer_addr() {
714                            Ok(a) => a,
715                            Err(e) => {
716                                eprintln!("unable to read peer addr: {}", e);
717                                return;
718                            }
719                        };
720                        Server::dispatch_connection(stream, peer_addr, &pool, app.clone());
721                    }
722                }
723            }
724        }
725    }
726
727    fn dispatch_connection(
728        stream: std::net::TcpStream,
729        peer_addr: std::net::SocketAddr,
730        pool: &ThreadPool,
731        app: impl Application + Send + 'static + Clone,
732    ) {
733        print!("Connection established, ");
734        if let Ok(local) = stream.local_addr() {
735            print!("local addr: {}", local);
736        }
737        println!(", peer addr: {}", peer_addr);
738
739        let (server_ip, server_port, _thread_count) = get_ip_port_thread_count();
740        let connection = ConnectionInfo {
741            client: Address {
742                ip: peer_addr.ip().to_string(),
743                port: peer_addr.port() as i32,
744            },
745            server: Address {
746                ip: server_ip,
747                port: server_port,
748            },
749            request_size: get_request_allocation_size(),
750            sni_hostname: None,
751        };
752
753        if let Err(e) = stream.set_read_timeout(Some(Duration::from_secs(30))) {
754            eprintln!("failed to set read timeout: {}", e);
755        }
756
757        pool.execute(move || {
758            crate::metrics::connection_open();
759            let result = Server::process(stream, connection, app);
760            crate::metrics::connection_close();
761            if let Err(msg) = result {
762                crate::metrics::record_error();
763                eprintln!("{}", msg);
764            }
765        });
766    }
767
768}
769
770/// Network context for the current connection, passed into every [`Controller`](crate::controller::Controller).
771#[derive(Clone)]
772pub struct ConnectionInfo {
773    /// Client (peer) address.
774    pub client: Address,
775    /// Server (local) address.
776    pub server: Address,
777    /// Bytes allocated for reading the request.
778    pub request_size: i64,
779    /// SNI hostname sent by the client during the TLS handshake, if any.
780    /// `None` for plain-HTTP connections or when the client omits SNI.
781    pub sni_hostname: Option<String>,
782}
783
784/// IP address and port pair.
785#[derive(Clone)]
786pub struct Address {
787    pub ip: String,
788    pub port: i32
789}
790
791impl ConnectionInfo {
792    /// Parse the client address into a [`std::net::SocketAddr`], if the stored
793    /// IP and port are valid. Returns `None` if parsing fails.
794    pub fn peer_addr(&self) -> Option<std::net::SocketAddr> {
795        self.client.to_socket_addr()
796    }
797}
798
799impl Address {
800    /// Parse this address into a [`std::net::SocketAddr`]. Returns `None` if
801    /// the IP string or port value cannot be converted.
802    pub fn to_socket_addr(&self) -> Option<std::net::SocketAddr> {
803        let ip: std::net::IpAddr = self.ip.parse().ok()?;
804        let port = u16::try_from(self.port).ok()?;
805        Some(std::net::SocketAddr::new(ip, port))
806    }
807}
808
809/// Resolves when SIGTERM is received on Unix, or never on other platforms.
810/// Enables a single `select!` branch to handle both SIGTERM and Ctrl+C.
811#[cfg(feature = "http2")]
812async fn sigterm() {
813    #[cfg(unix)]
814    {
815        if let Ok(mut s) = tokio::signal::unix::signal(
816            tokio::signal::unix::SignalKind::terminate()
817        ) {
818            s.recv().await;
819        } else {
820            std::future::pending::<()>().await
821        }
822    }
823    #[cfg(not(unix))]
824    std::future::pending::<()>().await
825}
826
827/// Returns a stream that fires on each SIGHUP on Unix; never fires elsewhere.
828#[cfg(feature = "http2")]
829async fn sighup() {
830    #[cfg(unix)]
831    {
832        if let Ok(mut s) = tokio::signal::unix::signal(
833            tokio::signal::unix::SignalKind::hangup()
834        ) {
835            s.recv().await;
836        } else {
837            std::future::pending::<()>().await
838        }
839    }
840    #[cfg(not(unix))]
841    std::future::pending::<()>().await
842}
843
844/// Bind `port` for both IPv4 and IPv6 when `ip` is the wildcard `0.0.0.0`
845/// (otherwise just `ip`). Let's Encrypt validates HTTP-01 over IPv6 when a
846/// domain has an AAAA record, so a v4-only challenge/redirect listener fails
847/// validation on dual-stack hosts. On Linux/macOS `[::]` already accepts
848/// IPv4 too and the second bind reports "address in use", which is fine; on
849/// Windows (`IPV6_V6ONLY` by default) both binds succeed.
850#[cfg(feature = "http2")]
851pub(crate) async fn bind_all_families(ip: &str, port: u16) -> Result<Vec<tokio::net::TcpListener>, String> {
852    use tokio::net::TcpListener;
853    if ip != "0.0.0.0" {
854        let addr = if ip.contains(':') { format!("[{ip}]:{port}") } else { format!("{ip}:{port}") };
855        return TcpListener::bind(&addr).await.map(|l| vec![l]).map_err(|e| format!("{addr}: {e}"));
856    }
857    let mut listeners = Vec::new();
858    let mut errors = Vec::new();
859    for addr in [format!("[::]:{port}"), format!("0.0.0.0:{port}")] {
860        match TcpListener::bind(&addr).await {
861            Ok(l) => listeners.push(l),
862            Err(e) => errors.push(format!("{addr}: {e}")),
863        }
864    }
865    if listeners.is_empty() {
866        return Err(errors.join("; "));
867    }
868    Ok(listeners)
869}
870
871#[cfg(feature = "http2")]
872impl Server {
873    pub async fn run_tls(
874        listener: TcpListener,
875        pool: ThreadPool,
876        app: impl Application + Send + 'static + Clone,
877    ) {
878        use crate::tls::create_tls_acceptor_from_vhosts;
879        use crate::h2_handler;
880
881        let cert_path = std::env::var(crate::entry_point::Config::RWS_CONFIG_TLS_CERT_FILE)
882            .unwrap_or_default();
883        let key_path = std::env::var(crate::entry_point::Config::RWS_CONFIG_TLS_KEY_FILE)
884            .unwrap_or_default();
885
886        if cert_path.is_empty() || key_path.is_empty() {
887            println!("No TLS certificate configured — serving plain HTTP/1.1.");
888            drop(pool);
889            Server::run_plain(listener, app).await;
890            return;
891        }
892
893        let vhosts = crate::entry_point::get_virtual_hosts();
894        let mut tls_acceptor = match create_tls_acceptor_from_vhosts(&vhosts, &cert_path, &key_path) {
895            Ok(a) => a,
896            Err(e) => {
897                eprintln!("TLS setup failed: {}", e);
898                return;
899            }
900        };
901
902        listener
903            .set_nonblocking(true)
904            .expect("failed to set TCP listener to non-blocking");
905        let tokio_listener = tokio::net::TcpListener::from_std(listener)
906            .expect("failed to convert TCP listener to tokio");
907
908        println!("Listening for TLS connections (HTTP/1.1 + HTTP/2)...");
909
910        loop {
911            tokio::select! {
912                result = tokio_listener.accept() => {
913                    match result {
914                        Ok((tcp_stream, peer_addr)) => {
915                            let _ = tcp_stream.set_nodelay(true);
916                            let acceptor = tls_acceptor.clone();
917                            let app = app.clone();
918                            tokio::spawn(async move {
919                                let handshake = tokio::time::timeout(
920                                    http1_async::TLS_HANDSHAKE_TIMEOUT,
921                                    acceptor.accept(tcp_stream),
922                                );
923                                match handshake.await {
924                                    Err(_) => {} // client never finished the handshake
925                                    Ok(Err(e)) => eprintln!("TLS handshake failed: {}", e),
926                                    Ok(Ok(tls_stream)) => {
927                                        let server_conn = tls_stream.get_ref().1;
928                                        let sni = server_conn.server_name().map(|s| s.to_string());
929                                        let protocol = server_conn
930                                            .alpn_protocol()
931                                            .map(|p| p.to_vec());
932
933                                        match protocol.as_deref() {
934                                            Some(b"h2") => {
935                                                if let Err(e) =
936                                                    h2_handler::handle_connection(tls_stream, peer_addr, sni, app)
937                                                        .await
938                                                {
939                                                    eprintln!("H2 connection error: {}", e);
940                                                }
941                                            }
942                                            _ => {
943                                                crate::metrics::connection_open();
944                                                let result = http1_async::serve(tls_stream, peer_addr, sni, true, app).await;
945                                                crate::metrics::connection_close();
946                                                if let Err(e) = result {
947                                                    crate::metrics::record_error();
948                                                    eprintln!("H1 TLS error: {}", e);
949                                                }
950                                            }
951                                        }
952                                    }
953                                }
954                            });
955                        }
956                        Err(e) => eprintln!("TCP accept error: {}", e),
957                    }
958                }
959                _ = tokio::signal::ctrl_c() => {
960                    crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
961                    println!("\nShutting down gracefully (SIGINT).");
962                    break;
963                }
964                _ = sigterm() => {
965                    crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
966                    println!("\nShutting down gracefully (SIGTERM).");
967                    break;
968                }
969                _ = sighup() => {
970                    crate::config_reload::reload();
971                    let vhosts = crate::entry_point::get_virtual_hosts();
972                    if let Ok(new_acceptor) = create_tls_acceptor_from_vhosts(&vhosts, &cert_path, &key_path) {
973                        tls_acceptor = new_acceptor;
974                        println!("[TLS] Certificates reloaded ({} virtual hosts).", vhosts.len());
975                    }
976                }
977            }
978        }
979    }
980
981    /// Plain-HTTP accept loop of the async builds (used by `run_tls` when no
982    /// certificate is configured): one tokio task per connection via
983    /// `http1_async::serve`, so idle or slow clients can't exhaust a thread
984    /// pool. An accept error (e.g. out of file descriptors) backs off briefly
985    /// and keeps serving instead of ending the loop.
986    pub async fn run_plain(listener: TcpListener, app: impl Application + Send + 'static + Clone) {
987        listener
988            .set_nonblocking(true)
989            .expect("failed to set TCP listener to non-blocking");
990        let tokio_listener = tokio::net::TcpListener::from_std(listener)
991            .expect("failed to convert TCP listener to tokio");
992
993        loop {
994            tokio::select! {
995                result = tokio_listener.accept() => {
996                    match result {
997                        Ok((tcp_stream, peer_addr)) => {
998                            // Head and body go out as separate writes: don't let
999                            // Nagle hold the body back waiting for an ACK.
1000                            let _ = tcp_stream.set_nodelay(true);
1001                            let app = app.clone();
1002                            tokio::spawn(async move {
1003                                crate::metrics::connection_open();
1004                                let result = http1_async::serve(tcp_stream, peer_addr, None, false, app).await;
1005                                crate::metrics::connection_close();
1006                                if let Err(e) = result {
1007                                    crate::metrics::record_error();
1008                                    eprintln!("{}", e);
1009                                }
1010                            });
1011                        }
1012                        Err(e) => {
1013                            eprintln!("TCP accept error: {}", e);
1014                            tokio::time::sleep(std::time::Duration::from_millis(50)).await;
1015                        }
1016                    }
1017                }
1018                _ = tokio::signal::ctrl_c() => {
1019                    crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
1020                    println!("\nShutting down gracefully (SIGINT).");
1021                    break;
1022                }
1023                _ = sigterm() => {
1024                    crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
1025                    println!("\nShutting down gracefully (SIGTERM).");
1026                    break;
1027                }
1028                _ = sighup() => {
1029                    crate::config_reload::reload();
1030                }
1031            }
1032        }
1033    }
1034
1035    /// Binds a plain-HTTP listener on the port in `RWS_CONFIG_HTTP_REDIRECT_PORT` and sends
1036    /// `301 Moved Permanently` to the HTTPS equivalent of every incoming URL.
1037    /// Returns immediately if TLS is not configured or the redirect port is not set.
1038    pub async fn run_redirect() {
1039        use std::env;
1040
1041        let cert_path = env::var(crate::entry_point::Config::RWS_CONFIG_TLS_CERT_FILE)
1042            .unwrap_or_default();
1043        if cert_path.is_empty() {
1044            return;
1045        }
1046
1047        let redirect_port_str = env::var(crate::entry_point::Config::RWS_CONFIG_HTTP_REDIRECT_PORT)
1048            .unwrap_or_default();
1049        if redirect_port_str.is_empty() {
1050            return;
1051        }
1052
1053        let redirect_port: u16 = match redirect_port_str.parse() {
1054            Ok(p) => p,
1055            Err(_) => {
1056                eprintln!("Invalid RWS_CONFIG_HTTP_REDIRECT_PORT: {}", redirect_port_str);
1057                return;
1058            }
1059        };
1060
1061        let (server_ip, server_port, _) = get_ip_port_thread_count();
1062        let listeners = match bind_all_families(&server_ip, redirect_port).await {
1063            Ok(l) => l,
1064            Err(e) => {
1065                eprintln!("HTTP redirect listener error on port {}: {}", redirect_port, e);
1066                return;
1067            }
1068        };
1069
1070        println!("HTTP→HTTPS redirect listening on http://{}:{}", server_ip, redirect_port);
1071        #[cfg(feature = "acme")]
1072        crate::acme::note_redirect_listener(redirect_port);
1073
1074        for listener in listeners {
1075            tokio::spawn(async move {
1076                loop {
1077                    match listener.accept().await {
1078                        Ok((stream, _peer)) => {
1079                            tokio::spawn(Server::handle_redirect_connection(stream, server_port));
1080                        }
1081                        Err(e) => eprintln!("HTTP redirect accept error: {}", e),
1082                    }
1083                }
1084            });
1085        }
1086
1087        loop {
1088            tokio::select! {
1089                _ = tokio::signal::ctrl_c() => {
1090                    println!("\nShutting down HTTP redirect listener (SIGINT).");
1091                    break;
1092                }
1093                _ = sigterm() => {
1094                    println!("\nShutting down HTTP redirect listener (SIGTERM).");
1095                    break;
1096                }
1097                _ = sighup() => {
1098                    crate::config_reload::reload();
1099                }
1100            }
1101        }
1102    }
1103
1104    /// One redirect-listener connection: answer a pending ACME HTTP-01
1105    /// challenge, or `301` to the HTTPS equivalent of the requested URL.
1106    pub(crate) async fn handle_redirect_connection(mut stream: tokio::net::TcpStream, https_port: i32) {
1107        use tokio::io::{AsyncReadExt, AsyncWriteExt};
1108        let mut buf = vec![0u8; 4096];
1109        let n = match stream.read(&mut buf).await {
1110            Ok(n) => n,
1111            Err(_) => return,
1112        };
1113        let text = String::from_utf8_lossy(&buf[..n]);
1114
1115        let uri = text.lines()
1116            .next()
1117            .and_then(|line| line.split_whitespace().nth(1))
1118            .unwrap_or("/")
1119            .to_string();
1120
1121        // ACME HTTP-01 challenges must be answered over
1122        // plain HTTP, not redirected.
1123        #[cfg(feature = "acme")]
1124        if let Some(challenge) = crate::acme::challenge_http_response(&uri) {
1125            let _ = stream.write_all(challenge.as_bytes()).await;
1126            return;
1127        }
1128
1129        let host_header = text.lines()
1130            .find(|l| l.to_lowercase().starts_with("host:"))
1131            .map(|l| l[5..].trim().to_string());
1132
1133        let location = match host_header {
1134            Some(h) => {
1135                // strip existing port from Host header
1136                let h_no_port = if h.starts_with('[') {
1137                    // IPv6: [::1] or [::1]:port
1138                    h.find(']')
1139                        .map(|i| h[..=i].to_string())
1140                        .unwrap_or(h.clone())
1141                } else {
1142                    h.rfind(':')
1143                        .map(|i| h[..i].to_string())
1144                        .unwrap_or(h.clone())
1145                };
1146                if https_port == 443 {
1147                    format!("https://{}{}", h_no_port, uri)
1148                } else {
1149                    format!("https://{}:{}{}", h_no_port, https_port, uri)
1150                }
1151            }
1152            None => format!("https://localhost:{}{}", https_port, uri),
1153        };
1154
1155        let response = format!(
1156            "HTTP/1.1 301 Moved Permanently\r\nLocation: {}\r\nContent-Length: 0\r\nConnection: close\r\n\r\n",
1157            location
1158        );
1159        let _ = stream.write_all(response.as_bytes()).await;
1160    }
1161
1162}
1163
1164#[cfg(feature = "http3")]
1165impl Server {
1166    pub async fn run_quic(
1167        app: impl Application + Send + 'static + Clone,
1168    ) {
1169        use crate::tls::create_quinn_server_config_from_vhosts;
1170        use crate::h3_handler;
1171
1172        let cert_path = std::env::var(crate::entry_point::Config::RWS_CONFIG_TLS_CERT_FILE)
1173            .unwrap_or_default();
1174        let key_path = std::env::var(crate::entry_point::Config::RWS_CONFIG_TLS_KEY_FILE)
1175            .unwrap_or_default();
1176
1177        if cert_path.is_empty() || key_path.is_empty() {
1178            return;
1179        }
1180
1181        let vhosts = crate::entry_point::get_virtual_hosts();
1182        let server_config = match create_quinn_server_config_from_vhosts(&vhosts, &cert_path, &key_path) {
1183            Ok(c) => c,
1184            Err(e) => {
1185                eprintln!("QUIC TLS setup failed: {}", e);
1186                return;
1187            }
1188        };
1189
1190        let (server_ip, server_port, _) = get_ip_port_thread_count();
1191        let bind_addr = format!("{}:{}", server_ip, server_port);
1192        let addr: std::net::SocketAddr = match bind_addr.parse() {
1193            Ok(a) => a,
1194            Err(e) => {
1195                eprintln!("Invalid QUIC bind address '{}': {}", bind_addr, e);
1196                return;
1197            }
1198        };
1199
1200        let endpoint = match quinn::Endpoint::server(server_config, addr) {
1201            Ok(e) => e,
1202            Err(e) => {
1203                eprintln!("QUIC endpoint error: {}", e);
1204                return;
1205            }
1206        };
1207
1208        println!("Listening for QUIC/HTTP3 on UDP {}:{}", server_ip, server_port);
1209
1210        loop {
1211            tokio::select! {
1212                maybe = endpoint.accept() => {
1213                    match maybe {
1214                        Some(incoming) => {
1215                            let app = app.clone();
1216                            tokio::spawn(async move {
1217                                match incoming.await {
1218                                    Ok(conn) => {
1219                                        let peer_addr = conn.remote_address();
1220                                        if let Err(e) = h3_handler::handle_connection(conn, peer_addr, app).await {
1221                                            eprintln!("H3 connection error: {}", e);
1222                                        }
1223                                    }
1224                                    Err(e) => eprintln!("QUIC connection error: {}", e),
1225                                }
1226                            });
1227                        }
1228                        None => break,
1229                    }
1230                }
1231                _ = tokio::signal::ctrl_c() => {
1232                    crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
1233                    println!("\nShutting down QUIC (SIGINT).");
1234                    endpoint.close(0u32.into(), b"shutdown");
1235                    break;
1236                }
1237                _ = sigterm() => {
1238                    crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
1239                    println!("\nShutting down QUIC (SIGTERM).");
1240                    endpoint.close(0u32.into(), b"shutdown");
1241                    break;
1242                }
1243                _ = sighup() => {
1244                    crate::config_reload::reload();
1245                }
1246            }
1247        }
1248    }
1249}
1250
1251