1#![deny(
26 clippy::unwrap_used,
27 clippy::panic,
28 clippy::expect_used,
29 unused_must_use
30)]
31#![warn(clippy::pedantic)]
32#![allow(clippy::missing_errors_doc, clippy::module_name_repetitions)]
33
34pub use http::{self, HttpRequest, HttpResponse, request, response};
35pub mod config;
36
37pub mod handler;
38
39#[doc(hidden)]
40pub mod prelude {
41 pub use http::{request::HttpRequest, response::*, *};
42
43 pub use crate::{
44 HttpServer,
45 config::*,
46 handler::{self, AuthConfig, Handler},
47 };
48}
49use prelude::*;
50
51pub type Result<T> = std::result::Result<T, HttpError>;
55
56use std::{
57 net::{TcpListener, TcpStream},
58 sync::Arc,
59 time::{Duration, Instant},
60};
61
62pub use config::ServerConfig;
63use http::HttpStream;
64use pool::ThreadPool;
65
66mod log;
67use log::prelude::*;
68
69pub struct HttpServer {
90 listener: TcpListener,
91 pool: ThreadPool,
92 handler: Option<Handler>,
93 config: ServerConfig,
94}
95
96fn peek_stream(stream: &HttpStream, duration: Duration) -> bool {
97 if stream.set_read_timeout(Some(duration)).is_err() {
98 return false;
99 }
100 let mut buf: [u8; 1] = [0];
101 let result = match stream.peek(&mut buf) {
102 Ok(n) => n > 0,
103 Err(_) => false,
104 };
105 if stream.set_read_timeout(None).is_err() {
106 return false;
107 }
108 result
109}
110
111fn handle_connection(
112 stream: TcpStream,
113 handlers: &Handler,
114 keep_alive_timeout: Duration,
115 keep_alive_requests: u16,
116) -> Result<()> {
117 let mut req = HttpRequest::parse(stream)?;
118 handlers.handle(&mut req)?;
119 let connection = req.header("Connection");
120 let keep_alive = keep_alive_timeout.as_millis() > 0;
121 if connection.is_some_and(|conn| conn == "keep-alive") && keep_alive {
122 let start = Instant::now();
123 let mut n = 1;
124 while start.elapsed() < keep_alive_timeout && n < keep_alive_requests {
125 let offset = keep_alive_timeout - start.elapsed();
126 if !peek_stream(req.stream(), offset) {
127 break;
128 }
129
130 req = req.keep_alive()?;
131 handlers.handle(&mut req)?;
132 n += 1;
133
134 let connection = req.header("Connection");
135 if connection.is_some_and(|conn| conn == "close") {
136 break;
137 }
138 }
139 }
140 Ok(())
141}
142
143#[allow(clippy::unwrap_used)]
144#[allow(clippy::expect_used)]
145#[allow(clippy::panic)]
146impl HttpServer {
147 pub fn new(config: ServerConfig) -> Result<Self> {
154 let address = format!("::0:{}", config.port);
155 let listener = TcpListener::bind(address)
156 .map_err(|err| format!("Could not bind to port {}: {}", config.port, err))?;
157 let pool =
158 ThreadPool::new(config.pool_conf).map_err(|_| "Error initializing thread pool")?;
159 let handler = Some(Handler::new());
160 let srv = Self {
161 listener,
162 pool,
163 handler,
164 config,
165 };
166 Ok(srv)
167 }
168 #[allow(clippy::missing_panics_doc)]
170 pub fn run(mut self) {
171 let handler = Arc::new(self.handler.take().unwrap());
172 println!("Sever listening on port {}", self.config.port);
173 for stream in self.listener.incoming().flatten() {
174 let handler = Arc::clone(&handler);
175 let timeout = self.config.keep_alive_timeout;
176 let req = self.config.keep_alive_requests;
177 self.pool.execute(move || {
178 handle_connection(stream, &handler, timeout, req).unwrap_or_else(|err| {
179 log_error!("{err}");
180 });
181 });
182 }
183 println!("Shutting down.");
184 }
185 pub fn set_handler(&mut self, handler: Handler) {
187 self.handler = Some(handler);
188 }
189}
190
191impl Default for HttpServer {
192 fn default() -> Self {
200 let conf = ServerConfig::default();
201 #[allow(clippy::expect_used)]
202 let mut srv = Self::new(conf)
203 .expect("Fatal error: HttpServer failed to initialize with default config");
204 let handler = Handler::default();
205 srv.set_handler(handler);
206 srv
207 }
208}