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        use crate::http::VERSION;
262
263        let request_allocation_size = connection.request_size;
264        let client = connection.client.clone();
265        let client_addr = SocketAddr::new(IpAddr::from_str(client.ip.as_str()).unwrap(), client.port as u16);
266
267        let max_body_size = crate::entry_point::get_max_body_size();
268
269        // Bytes read from the connection but not consumed yet: the rest of a
270        // request head, a body, or the next pipelined request.
271        let mut pending: Vec<u8> = Vec::new();
272        let mut read_buffer = vec![0u8; request_allocation_size as usize];
273
274        'session: loop {
275            // Read until the request head is complete — it may span several reads.
276            let head_len = loop {
277                framing::skip_leading_empty_lines(&mut pending);
278                match framing::scan_head(&pending) {
279                    framing::HeadScan::Complete(n) => break Some(n),
280                    framing::HeadScan::TooLarge => {
281                        let _ = stream.write_all(&Server::header_fields_too_large_response());
282                        let _ = stream.flush();
283                        return Err("request head too large".to_string());
284                    }
285                    framing::HeadScan::Incomplete => {}
286                }
287                match stream.read(&mut read_buffer) {
288                    // timeout or client closed — normal end of keep-alive session
289                    Ok(0) | Err(_) => break None,
290                    Ok(n) => pending.extend_from_slice(&read_buffer[..n]),
291                }
292            };
293            let Some(head_len) = head_len else { break };
294
295            let parsed = Request::parse(&pending[..head_len])
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 = {
390                let conn_hdr = request.get_header(Header::_CONNECTION.to_string());
391                match conn_hdr {
392                    Some(h) => h.value.to_lowercase() != "close",
393                    None => request.http_version == VERSION.http_1_1,
394                }
395            };
396
397            let mut response = match app.execute(&request, &connection) {
398                Ok(r) => r,
399                Err(message) => {
400                    let raw_response = Server::bad_request_response(message.clone());
401                    let boxed_stream = stream.write(raw_response.borrow());
402                    if boxed_stream.is_ok() { stream.flush().unwrap(); }
403                    return Err(message);
404                }
405            };
406
407            crate::metrics::record_request();
408            crate::compression::apply_gzip(&request, &mut response);
409
410            response.headers.push(Header {
411                name: Header::_CONNECTION.to_string(),
412                value: if keep_alive { "keep-alive".to_string() } else { "close".to_string() },
413            });
414
415            Log::log_access(&request, &response, &client_addr);
416
417            if let Some(reader) = response.stream_pipe.take() {
418                if let Err(e) = Server::pipe_stream(&mut stream, response, request, reader) {
419                    return Err(e);
420                }
421            } else if let Some(ref filepath) = response.stream_file.clone() {
422                if let Err(e) = Server::write_chunked_file(&mut stream, response, request, filepath) {
423                    return Err(e);
424                }
425            } else {
426                let raw_response = Response::generate_response(response, request);
427                // write_all: a single write() may accept only part of a large response.
428                if let Err(e) = stream.write_all(raw_response.borrow()) {
429                    return Err(e.to_string());
430                }
431                stream.flush().unwrap();
432            }
433
434            if !keep_alive { break; }
435        }
436
437        Ok(())
438    }
439
440    /// Streams a file to `stream` using HTTP/1.1 chunked transfer encoding.
441    /// The response headers are written first, then the file is read and written in 64 KB chunks.
442    pub(crate) fn write_chunked_file(
443        stream: &mut impl Write,
444        response: Response,
445        request: Request,
446        filepath: &str,
447    ) -> Result<(), String> {
448        use std::fs::File;
449        use std::io::Read as _;
450
451        let (head, chunked) = Server::stream_file_head(response);
452        stream.write_all(&head).map_err(|e| e.to_string())?;
453
454        if request.method != METHOD.head && request.method != METHOD.options {
455            let mut file = File::open(filepath).map_err(|e| e.to_string())?;
456            let mut buf = vec![0u8; 65536];
457            loop {
458                let n = file.read(&mut buf).map_err(|e| e.to_string())?;
459                if n == 0 { break; }
460                if chunked {
461                    // chunk header: hex size + CRLF
462                    stream.write_all(format!("{:x}\r\n", n).as_bytes()).map_err(|e| e.to_string())?;
463                    stream.write_all(&buf[..n]).map_err(|e| e.to_string())?;
464                    stream.write_all(b"\r\n").map_err(|e| e.to_string())?;
465                } else {
466                    stream.write_all(&buf[..n]).map_err(|e| e.to_string())?;
467                }
468            }
469            if chunked {
470                // terminal chunk
471                stream.write_all(b"0\r\n\r\n").map_err(|e| e.to_string())?;
472            }
473        }
474
475        stream.flush().map_err(|e| e.to_string())
476    }
477
478    /// The status line and headers of a `stream_file` response, and whether
479    /// its body is sent chunked. With a known Content-Length (static files
480    /// set it) the body goes out as-is; Transfer-Encoding: chunked is only
481    /// for an unknown length. Sending both is forbidden (RFC 9112 §6.1) —
482    /// mismatched handling of the two by intermediaries is how request
483    /// smuggling happens.
484    pub(crate) fn stream_file_head(mut response: Response) -> (Vec<u8>, bool) {
485        let chunked = !response.headers.iter().any(|h| h.name.eq_ignore_ascii_case(Header::_CONTENT_LENGTH));
486        if chunked {
487            response.headers.push(Header {
488                name: Header::_TRANSFER_ENCODING.to_string(),
489                value: "chunked".to_string(),
490            });
491        }
492
493        // build status line + headers (no body)
494        let status = [
495            response.http_version.clone(),
496            response.status_code.to_string(),
497            response.reason_phrase.clone(),
498        ].join(SYMBOL.whitespace);
499
500        let mut headers_str = SYMBOL.new_line_carriage_return.to_string();
501        for header in &response.headers {
502            headers_str.push_str(&header.name);
503            headers_str.push_str(Header::NAME_VALUE_SEPARATOR);
504            headers_str.push_str(&header.value);
505            headers_str.push_str(SYMBOL.new_line_carriage_return);
506        }
507        let head = format!("{}{}{}", status, headers_str, SYMBOL.new_line_carriage_return);
508        (head.into_bytes(), chunked)
509    }
510
511    /// Streams a `Read` source to `stream` using HTTP/1.1 chunked transfer encoding.
512    ///
513    /// Called when `response.stream_pipe` is set (proxy passthrough for SSE, AI token
514    /// streams, and large downloads). The response headers are written first (with
515    /// `Transfer-Encoding: chunked` injected and any `Content-Length` stripped), then
516    /// bytes are read from `reader` and forwarded in 8 KB chunks. Each chunk is flushed
517    /// immediately so the client sees data as it arrives.
518    pub(crate) fn pipe_stream(
519        stream: &mut impl Write,
520        mut response: Response,
521        request: Request,
522        mut reader: Box<dyn std::io::Read + Send>,
523    ) -> Result<(), String> {
524        // If the backend already uses chunked encoding we pass the raw chunk
525        // frames through; otherwise we wrap the raw bytes in our own chunked
526        // framing so the client can receive data incrementally.
527        let backend_is_chunked = response.headers.iter().any(|h| {
528            h.name.eq_ignore_ascii_case("transfer-encoding")
529                && h.value.to_lowercase().contains("chunked")
530        });
531
532        response.headers.retain(|h| !h.name.eq_ignore_ascii_case("content-length"));
533        if !backend_is_chunked {
534            response.headers.push(Header {
535                name: Header::_TRANSFER_ENCODING.to_string(),
536                value: "chunked".to_string(),
537            });
538        }
539
540        let status = [
541            response.http_version.clone(),
542            response.status_code.to_string(),
543            response.reason_phrase.clone(),
544        ].join(SYMBOL.whitespace);
545
546        let mut head = format!("{}\r\n", status);
547        for header in &response.headers {
548            head.push_str(&header.name);
549            head.push_str(Header::NAME_VALUE_SEPARATOR);
550            head.push_str(&header.value);
551            head.push_str(SYMBOL.new_line_carriage_return);
552        }
553        head.push_str(SYMBOL.new_line_carriage_return);
554        stream.write_all(head.as_bytes()).map_err(|e| e.to_string())?;
555
556        if request.method != METHOD.head && request.method != METHOD.options {
557            let mut buf = [0u8; 8192];
558            if backend_is_chunked {
559                // Passthrough: forward raw chunk frames as-is; the client
560                // decodes them itself (avoids double-chunking).
561                loop {
562                    match reader.read(&mut buf) {
563                        Ok(0) | Err(_) => break,
564                        Ok(n) => {
565                            stream.write_all(&buf[..n]).map_err(|e| e.to_string())?;
566                            stream.flush().map_err(|e| e.to_string())?;
567                        }
568                    }
569                }
570            } else {
571                // Re-encode: wrap the raw byte stream in chunked framing so
572                // the client can receive each fragment as it arrives (SSE,
573                // large downloads).
574                loop {
575                    match reader.read(&mut buf) {
576                        Ok(0) | Err(_) => break,
577                        Ok(n) => {
578                            stream
579                                .write_all(format!("{:x}\r\n", n).as_bytes())
580                                .map_err(|e| e.to_string())?;
581                            stream.write_all(&buf[..n]).map_err(|e| e.to_string())?;
582                            stream.write_all(b"\r\n").map_err(|e| e.to_string())?;
583                            stream.flush().map_err(|e| e.to_string())?;
584                        }
585                    }
586                }
587                stream.write_all(b"0\r\n\r\n").map_err(|e| e.to_string())?;
588            }
589        }
590
591        stream.flush().map_err(|e| e.to_string())
592    }
593
594    /// Reads configuration (IP, port, thread count, TLS paths) from the layered config system
595    /// and returns a bound `TcpListener` and a sized `ThreadPool`. Call once at startup.
596    pub fn setup() -> Result<(TcpListener, ThreadPool), String> {
597        let info = Log::info("Rust Web Server");
598        println!("{}", info);
599
600        let usage_info = Log::usage_information();
601        println!("{}", usage_info);
602
603
604        println!("RWS Configuration Start: \n");
605
606        set_default_values();
607        bootstrap();
608
609        #[cfg(feature = "secrets")]
610        crate::secrets::resolve_env_vars().map_err(|e| e.to_string())?;
611
612        println!("\nRWS Configuration End\n\n");
613
614
615        let (ip, port, thread_count) = get_ip_port_thread_count();
616
617
618        let mut ip_readable = ip.to_string();
619
620        if ip.contains(":") {
621            ip_readable = [SYMBOL.opening_square_bracket, &ip, SYMBOL.closing_square_bracket].join("");
622        }
623
624        let bind_addr = [ip_readable, SYMBOL.colon.to_string(), port.to_string()].join(SYMBOL.empty_string);
625
626        #[cfg(feature = "http2")]
627        let protocol = {
628            let cert = std::env::var(crate::entry_point::Config::RWS_CONFIG_TLS_CERT_FILE).unwrap_or_default();
629            if cert.is_empty() { "http" } else { "https" }
630        };
631        #[cfg(not(feature = "http2"))]
632        let protocol = "http";
633
634        println!("Setting up {}://{}...", protocol, &bind_addr);
635
636        let boxed_listener = TcpListener::bind(&bind_addr);
637        if boxed_listener.is_err() {
638            let message = format!("unable to set up TCP listener: {}", boxed_listener.err().unwrap());
639            return Err(message);
640        }
641
642        let listener = boxed_listener.unwrap();
643        let pool = ThreadPool::new(thread_count as usize);
644
645
646        let server_url_thread_count = Log::server_url_thread_count(protocol, &bind_addr, thread_count);
647        println!("{}", server_url_thread_count);
648
649        Ok((listener, pool))
650    }
651
652    /// Accepts TCP connections in a loop and dispatches each to the thread pool.
653    ///
654    /// When built with the `http1` feature, Ctrl+C and SIGTERM stop the accept
655    /// loop gracefully: `SERVER_READY` is cleared and the pool drains all
656    /// in-flight connections before returning.
657    ///
658    /// For TLS/HTTP2/HTTP3 use [`Server::run_tls`].
659    pub fn run(listener: TcpListener,
660               pool: ThreadPool,
661               app: impl Application + Send + 'static + Clone) {
662        #[cfg(feature = "http1")]
663        {
664            use std::sync::Arc;
665            use std::sync::atomic::{AtomicBool, Ordering};
666
667            let shutdown = Arc::new(AtomicBool::new(false));
668            let s = shutdown.clone();
669            if let Err(e) = ctrlc::set_handler(move || {
670                s.store(true, Ordering::SeqCst);
671            }) {
672                eprintln!("unable to install signal handler: {}", e);
673            }
674            crate::config_reload::install_sighup_handler();
675            if let Err(e) = listener.set_nonblocking(true) {
676                eprintln!("unable to set non-blocking listener: {}", e);
677            }
678
679            loop {
680                if shutdown.load(Ordering::SeqCst) {
681                    break;
682                }
683                if crate::config_reload::RELOAD_REQUESTED
684                    .compare_exchange(true, false, Ordering::SeqCst, Ordering::Relaxed)
685                    .is_ok()
686                {
687                    crate::config_reload::reload();
688                }
689                match listener.accept() {
690                    Ok((stream, peer_addr)) => {
691                        Server::dispatch_connection(stream, peer_addr, &pool, app.clone());
692                    }
693                    Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
694                        std::thread::sleep(Duration::from_millis(10));
695                    }
696                    Err(e) => {
697                        eprintln!("accept error: {}", e);
698                        break;
699                    }
700                }
701            }
702
703            crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
704            println!("Shutting down — waiting for in-flight connections to finish");
705            pool.join();
706            println!("Server stopped");
707        }
708
709        #[cfg(not(feature = "http1"))]
710        {
711            for boxed_stream in listener.incoming() {
712                match boxed_stream {
713                    Err(e) => {
714                        eprintln!("unable to get TCP stream: {}", e);
715                        return;
716                    }
717                    Ok(stream) => {
718                        let peer_addr = match stream.peer_addr() {
719                            Ok(a) => a,
720                            Err(e) => {
721                                eprintln!("unable to read peer addr: {}", e);
722                                return;
723                            }
724                        };
725                        Server::dispatch_connection(stream, peer_addr, &pool, app.clone());
726                    }
727                }
728            }
729        }
730    }
731
732    fn dispatch_connection(
733        stream: std::net::TcpStream,
734        peer_addr: std::net::SocketAddr,
735        pool: &ThreadPool,
736        app: impl Application + Send + 'static + Clone,
737    ) {
738        print!("Connection established, ");
739        if let Ok(local) = stream.local_addr() {
740            print!("local addr: {}", local);
741        }
742        println!(", peer addr: {}", peer_addr);
743
744        let (server_ip, server_port, _thread_count) = get_ip_port_thread_count();
745        let connection = ConnectionInfo {
746            client: Address {
747                ip: peer_addr.ip().to_string(),
748                port: peer_addr.port() as i32,
749            },
750            server: Address {
751                ip: server_ip,
752                port: server_port,
753            },
754            request_size: get_request_allocation_size(),
755            sni_hostname: None,
756        };
757
758        if let Err(e) = stream.set_read_timeout(Some(Duration::from_secs(30))) {
759            eprintln!("failed to set read timeout: {}", e);
760        }
761
762        pool.execute(move || {
763            crate::metrics::connection_open();
764            let result = Server::process(stream, connection, app);
765            crate::metrics::connection_close();
766            if let Err(msg) = result {
767                crate::metrics::record_error();
768                eprintln!("{}", msg);
769            }
770        });
771    }
772
773}
774
775/// Network context for the current connection, passed into every [`Controller`](crate::controller::Controller).
776#[derive(Clone)]
777pub struct ConnectionInfo {
778    /// Client (peer) address.
779    pub client: Address,
780    /// Server (local) address.
781    pub server: Address,
782    /// Bytes allocated for reading the request.
783    pub request_size: i64,
784    /// SNI hostname sent by the client during the TLS handshake, if any.
785    /// `None` for plain-HTTP connections or when the client omits SNI.
786    pub sni_hostname: Option<String>,
787}
788
789/// IP address and port pair.
790#[derive(Clone)]
791pub struct Address {
792    pub ip: String,
793    pub port: i32
794}
795
796impl ConnectionInfo {
797    /// Parse the client address into a [`std::net::SocketAddr`], if the stored
798    /// IP and port are valid. Returns `None` if parsing fails.
799    pub fn peer_addr(&self) -> Option<std::net::SocketAddr> {
800        self.client.to_socket_addr()
801    }
802}
803
804impl Address {
805    /// Parse this address into a [`std::net::SocketAddr`]. Returns `None` if
806    /// the IP string or port value cannot be converted.
807    pub fn to_socket_addr(&self) -> Option<std::net::SocketAddr> {
808        let ip: std::net::IpAddr = self.ip.parse().ok()?;
809        let port = u16::try_from(self.port).ok()?;
810        Some(std::net::SocketAddr::new(ip, port))
811    }
812}
813
814/// Resolves when SIGTERM is received on Unix, or never on other platforms.
815/// Enables a single `select!` branch to handle both SIGTERM and Ctrl+C.
816#[cfg(feature = "http2")]
817async fn sigterm() {
818    #[cfg(unix)]
819    {
820        if let Ok(mut s) = tokio::signal::unix::signal(
821            tokio::signal::unix::SignalKind::terminate()
822        ) {
823            s.recv().await;
824        } else {
825            std::future::pending::<()>().await
826        }
827    }
828    #[cfg(not(unix))]
829    std::future::pending::<()>().await
830}
831
832/// Returns a stream that fires on each SIGHUP on Unix; never fires elsewhere.
833#[cfg(feature = "http2")]
834async fn sighup() {
835    #[cfg(unix)]
836    {
837        if let Ok(mut s) = tokio::signal::unix::signal(
838            tokio::signal::unix::SignalKind::hangup()
839        ) {
840            s.recv().await;
841        } else {
842            std::future::pending::<()>().await
843        }
844    }
845    #[cfg(not(unix))]
846    std::future::pending::<()>().await
847}
848
849/// Bind `port` for both IPv4 and IPv6 when `ip` is the wildcard `0.0.0.0`
850/// (otherwise just `ip`). Let's Encrypt validates HTTP-01 over IPv6 when a
851/// domain has an AAAA record, so a v4-only challenge/redirect listener fails
852/// validation on dual-stack hosts. On Linux/macOS `[::]` already accepts
853/// IPv4 too and the second bind reports "address in use", which is fine; on
854/// Windows (`IPV6_V6ONLY` by default) both binds succeed.
855#[cfg(feature = "http2")]
856pub(crate) async fn bind_all_families(ip: &str, port: u16) -> Result<Vec<tokio::net::TcpListener>, String> {
857    use tokio::net::TcpListener;
858    if ip != "0.0.0.0" {
859        let addr = if ip.contains(':') { format!("[{ip}]:{port}") } else { format!("{ip}:{port}") };
860        return TcpListener::bind(&addr).await.map(|l| vec![l]).map_err(|e| format!("{addr}: {e}"));
861    }
862    let mut listeners = Vec::new();
863    let mut errors = Vec::new();
864    for addr in [format!("[::]:{port}"), format!("0.0.0.0:{port}")] {
865        match TcpListener::bind(&addr).await {
866            Ok(l) => listeners.push(l),
867            Err(e) => errors.push(format!("{addr}: {e}")),
868        }
869    }
870    if listeners.is_empty() {
871        return Err(errors.join("; "));
872    }
873    Ok(listeners)
874}
875
876#[cfg(feature = "http2")]
877impl Server {
878    pub async fn run_tls(
879        listener: TcpListener,
880        pool: ThreadPool,
881        app: impl Application + Send + 'static + Clone,
882    ) {
883        use crate::tls::create_tls_acceptor_from_vhosts;
884        use crate::h2_handler;
885
886        let cert_path = std::env::var(crate::entry_point::Config::RWS_CONFIG_TLS_CERT_FILE)
887            .unwrap_or_default();
888        let key_path = std::env::var(crate::entry_point::Config::RWS_CONFIG_TLS_KEY_FILE)
889            .unwrap_or_default();
890
891        if cert_path.is_empty() || key_path.is_empty() {
892            println!("No TLS certificate configured — serving plain HTTP/1.1.");
893            drop(pool);
894            Server::run_plain(listener, app).await;
895            return;
896        }
897
898        let vhosts = crate::entry_point::get_virtual_hosts();
899        let mut tls_acceptor = match create_tls_acceptor_from_vhosts(&vhosts, &cert_path, &key_path) {
900            Ok(a) => a,
901            Err(e) => {
902                eprintln!("TLS setup failed: {}", e);
903                return;
904            }
905        };
906
907        listener
908            .set_nonblocking(true)
909            .expect("failed to set TCP listener to non-blocking");
910        let tokio_listener = tokio::net::TcpListener::from_std(listener)
911            .expect("failed to convert TCP listener to tokio");
912
913        println!("Listening for TLS connections (HTTP/1.1 + HTTP/2)...");
914
915        loop {
916            tokio::select! {
917                result = tokio_listener.accept() => {
918                    match result {
919                        Ok((tcp_stream, peer_addr)) => {
920                            let _ = tcp_stream.set_nodelay(true);
921                            let acceptor = tls_acceptor.clone();
922                            let app = app.clone();
923                            tokio::spawn(async move {
924                                let handshake = tokio::time::timeout(
925                                    http1_async::TLS_HANDSHAKE_TIMEOUT,
926                                    acceptor.accept(tcp_stream),
927                                );
928                                match handshake.await {
929                                    Err(_) => {} // client never finished the handshake
930                                    Ok(Err(e)) => eprintln!("TLS handshake failed: {}", e),
931                                    Ok(Ok(tls_stream)) => {
932                                        let server_conn = tls_stream.get_ref().1;
933                                        let sni = server_conn.server_name().map(|s| s.to_string());
934                                        let protocol = server_conn
935                                            .alpn_protocol()
936                                            .map(|p| p.to_vec());
937
938                                        match protocol.as_deref() {
939                                            Some(b"h2") => {
940                                                if let Err(e) =
941                                                    h2_handler::handle_connection(tls_stream, peer_addr, sni, app)
942                                                        .await
943                                                {
944                                                    eprintln!("H2 connection error: {}", e);
945                                                }
946                                            }
947                                            _ => {
948                                                crate::metrics::connection_open();
949                                                let result = http1_async::serve(tls_stream, peer_addr, sni, true, app).await;
950                                                crate::metrics::connection_close();
951                                                if let Err(e) = result {
952                                                    crate::metrics::record_error();
953                                                    eprintln!("H1 TLS error: {}", e);
954                                                }
955                                            }
956                                        }
957                                    }
958                                }
959                            });
960                        }
961                        Err(e) => eprintln!("TCP accept error: {}", e),
962                    }
963                }
964                _ = tokio::signal::ctrl_c() => {
965                    crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
966                    println!("\nShutting down gracefully (SIGINT).");
967                    break;
968                }
969                _ = sigterm() => {
970                    crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
971                    println!("\nShutting down gracefully (SIGTERM).");
972                    break;
973                }
974                _ = sighup() => {
975                    crate::config_reload::reload();
976                    let vhosts = crate::entry_point::get_virtual_hosts();
977                    if let Ok(new_acceptor) = create_tls_acceptor_from_vhosts(&vhosts, &cert_path, &key_path) {
978                        tls_acceptor = new_acceptor;
979                        println!("[TLS] Certificates reloaded ({} virtual hosts).", vhosts.len());
980                    }
981                }
982            }
983        }
984    }
985
986    /// Plain-HTTP accept loop of the async builds (used by `run_tls` when no
987    /// certificate is configured): one tokio task per connection via
988    /// `http1_async::serve`, so idle or slow clients can't exhaust a thread
989    /// pool. An accept error (e.g. out of file descriptors) backs off briefly
990    /// and keeps serving instead of ending the loop.
991    pub async fn run_plain(listener: TcpListener, app: impl Application + Send + 'static + Clone) {
992        listener
993            .set_nonblocking(true)
994            .expect("failed to set TCP listener to non-blocking");
995        let tokio_listener = tokio::net::TcpListener::from_std(listener)
996            .expect("failed to convert TCP listener to tokio");
997
998        loop {
999            tokio::select! {
1000                result = tokio_listener.accept() => {
1001                    match result {
1002                        Ok((tcp_stream, peer_addr)) => {
1003                            // Head and body go out as separate writes: don't let
1004                            // Nagle hold the body back waiting for an ACK.
1005                            let _ = tcp_stream.set_nodelay(true);
1006                            let app = app.clone();
1007                            tokio::spawn(async move {
1008                                crate::metrics::connection_open();
1009                                let result = http1_async::serve(tcp_stream, peer_addr, None, false, app).await;
1010                                crate::metrics::connection_close();
1011                                if let Err(e) = result {
1012                                    crate::metrics::record_error();
1013                                    eprintln!("{}", e);
1014                                }
1015                            });
1016                        }
1017                        Err(e) => {
1018                            eprintln!("TCP accept error: {}", e);
1019                            tokio::time::sleep(std::time::Duration::from_millis(50)).await;
1020                        }
1021                    }
1022                }
1023                _ = tokio::signal::ctrl_c() => {
1024                    crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
1025                    println!("\nShutting down gracefully (SIGINT).");
1026                    break;
1027                }
1028                _ = sigterm() => {
1029                    crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
1030                    println!("\nShutting down gracefully (SIGTERM).");
1031                    break;
1032                }
1033                _ = sighup() => {
1034                    crate::config_reload::reload();
1035                }
1036            }
1037        }
1038    }
1039
1040    /// Binds a plain-HTTP listener on the port in `RWS_CONFIG_HTTP_REDIRECT_PORT` and sends
1041    /// `301 Moved Permanently` to the HTTPS equivalent of every incoming URL.
1042    /// Returns immediately if TLS is not configured or the redirect port is not set.
1043    pub async fn run_redirect() {
1044        use std::env;
1045
1046        let cert_path = env::var(crate::entry_point::Config::RWS_CONFIG_TLS_CERT_FILE)
1047            .unwrap_or_default();
1048        if cert_path.is_empty() {
1049            return;
1050        }
1051
1052        let redirect_port_str = env::var(crate::entry_point::Config::RWS_CONFIG_HTTP_REDIRECT_PORT)
1053            .unwrap_or_default();
1054        if redirect_port_str.is_empty() {
1055            return;
1056        }
1057
1058        let redirect_port: u16 = match redirect_port_str.parse() {
1059            Ok(p) => p,
1060            Err(_) => {
1061                eprintln!("Invalid RWS_CONFIG_HTTP_REDIRECT_PORT: {}", redirect_port_str);
1062                return;
1063            }
1064        };
1065
1066        let (server_ip, server_port, _) = get_ip_port_thread_count();
1067        let listeners = match bind_all_families(&server_ip, redirect_port).await {
1068            Ok(l) => l,
1069            Err(e) => {
1070                eprintln!("HTTP redirect listener error on port {}: {}", redirect_port, e);
1071                return;
1072            }
1073        };
1074
1075        println!("HTTP→HTTPS redirect listening on http://{}:{}", server_ip, redirect_port);
1076        #[cfg(feature = "acme")]
1077        crate::acme::note_redirect_listener(redirect_port);
1078
1079        for listener in listeners {
1080            tokio::spawn(async move {
1081                loop {
1082                    match listener.accept().await {
1083                        Ok((stream, _peer)) => {
1084                            tokio::spawn(Server::handle_redirect_connection(stream, server_port));
1085                        }
1086                        Err(e) => eprintln!("HTTP redirect accept error: {}", e),
1087                    }
1088                }
1089            });
1090        }
1091
1092        loop {
1093            tokio::select! {
1094                _ = tokio::signal::ctrl_c() => {
1095                    println!("\nShutting down HTTP redirect listener (SIGINT).");
1096                    break;
1097                }
1098                _ = sigterm() => {
1099                    println!("\nShutting down HTTP redirect listener (SIGTERM).");
1100                    break;
1101                }
1102                _ = sighup() => {
1103                    crate::config_reload::reload();
1104                }
1105            }
1106        }
1107    }
1108
1109    /// One redirect-listener connection: answer a pending ACME HTTP-01
1110    /// challenge, or `301` to the HTTPS equivalent of the requested URL.
1111    pub(crate) async fn handle_redirect_connection(mut stream: tokio::net::TcpStream, https_port: i32) {
1112        use tokio::io::{AsyncReadExt, AsyncWriteExt};
1113        let mut buf = vec![0u8; 4096];
1114        let n = match stream.read(&mut buf).await {
1115            Ok(n) => n,
1116            Err(_) => return,
1117        };
1118        let text = String::from_utf8_lossy(&buf[..n]);
1119
1120        let uri = text.lines()
1121            .next()
1122            .and_then(|line| line.split_whitespace().nth(1))
1123            .unwrap_or("/")
1124            .to_string();
1125
1126        // ACME HTTP-01 challenges must be answered over
1127        // plain HTTP, not redirected.
1128        #[cfg(feature = "acme")]
1129        if let Some(challenge) = crate::acme::challenge_http_response(&uri) {
1130            let _ = stream.write_all(challenge.as_bytes()).await;
1131            return;
1132        }
1133
1134        let host_header = text.lines()
1135            .find(|l| l.to_lowercase().starts_with("host:"))
1136            .map(|l| l[5..].trim().to_string());
1137
1138        let location = match host_header {
1139            Some(h) => {
1140                // strip existing port from Host header
1141                let h_no_port = if h.starts_with('[') {
1142                    // IPv6: [::1] or [::1]:port
1143                    h.find(']')
1144                        .map(|i| h[..=i].to_string())
1145                        .unwrap_or(h.clone())
1146                } else {
1147                    h.rfind(':')
1148                        .map(|i| h[..i].to_string())
1149                        .unwrap_or(h.clone())
1150                };
1151                if https_port == 443 {
1152                    format!("https://{}{}", h_no_port, uri)
1153                } else {
1154                    format!("https://{}:{}{}", h_no_port, https_port, uri)
1155                }
1156            }
1157            None => format!("https://localhost:{}{}", https_port, uri),
1158        };
1159
1160        let response = format!(
1161            "HTTP/1.1 301 Moved Permanently\r\nLocation: {}\r\nContent-Length: 0\r\nConnection: close\r\n\r\n",
1162            location
1163        );
1164        let _ = stream.write_all(response.as_bytes()).await;
1165    }
1166
1167}
1168
1169#[cfg(feature = "http3")]
1170impl Server {
1171    pub async fn run_quic(
1172        app: impl Application + Send + 'static + Clone,
1173    ) {
1174        use crate::tls::create_quinn_server_config_from_vhosts;
1175        use crate::h3_handler;
1176
1177        let cert_path = std::env::var(crate::entry_point::Config::RWS_CONFIG_TLS_CERT_FILE)
1178            .unwrap_or_default();
1179        let key_path = std::env::var(crate::entry_point::Config::RWS_CONFIG_TLS_KEY_FILE)
1180            .unwrap_or_default();
1181
1182        if cert_path.is_empty() || key_path.is_empty() {
1183            return;
1184        }
1185
1186        let vhosts = crate::entry_point::get_virtual_hosts();
1187        let server_config = match create_quinn_server_config_from_vhosts(&vhosts, &cert_path, &key_path) {
1188            Ok(c) => c,
1189            Err(e) => {
1190                eprintln!("QUIC TLS setup failed: {}", e);
1191                return;
1192            }
1193        };
1194
1195        let (server_ip, server_port, _) = get_ip_port_thread_count();
1196        let bind_addr = format!("{}:{}", server_ip, server_port);
1197        let addr: std::net::SocketAddr = match bind_addr.parse() {
1198            Ok(a) => a,
1199            Err(e) => {
1200                eprintln!("Invalid QUIC bind address '{}': {}", bind_addr, e);
1201                return;
1202            }
1203        };
1204
1205        let endpoint = match quinn::Endpoint::server(server_config, addr) {
1206            Ok(e) => e,
1207            Err(e) => {
1208                eprintln!("QUIC endpoint error: {}", e);
1209                return;
1210            }
1211        };
1212
1213        println!("Listening for QUIC/HTTP3 on UDP {}:{}", server_ip, server_port);
1214
1215        loop {
1216            tokio::select! {
1217                maybe = endpoint.accept() => {
1218                    match maybe {
1219                        Some(incoming) => {
1220                            let app = app.clone();
1221                            tokio::spawn(async move {
1222                                match incoming.await {
1223                                    Ok(conn) => {
1224                                        let peer_addr = conn.remote_address();
1225                                        if let Err(e) = h3_handler::handle_connection(conn, peer_addr, app).await {
1226                                            eprintln!("H3 connection error: {}", e);
1227                                        }
1228                                    }
1229                                    Err(e) => eprintln!("QUIC connection error: {}", e),
1230                                }
1231                            });
1232                        }
1233                        None => break,
1234                    }
1235                }
1236                _ = tokio::signal::ctrl_c() => {
1237                    crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
1238                    println!("\nShutting down QUIC (SIGINT).");
1239                    endpoint.close(0u32.into(), b"shutdown");
1240                    break;
1241                }
1242                _ = sigterm() => {
1243                    crate::metrics::SERVER_READY.store(false, std::sync::atomic::Ordering::SeqCst);
1244                    println!("\nShutting down QUIC (SIGTERM).");
1245                    endpoint.close(0u32.into(), b"shutdown");
1246                    break;
1247                }
1248                _ = sighup() => {
1249                    crate::config_reload::reload();
1250                }
1251            }
1252        }
1253    }
1254}
1255
1256