1#![deny(
26 clippy::unwrap_used,
27 clippy::panic,
28 clippy::expect_used,
29 unused_must_use
30)]
31#![warn(clippy::pedantic)]
32#![allow(
33 clippy::missing_errors_doc,
34 clippy::module_name_repetitions,
35 clippy::unchecked_time_subtraction
36)]
37
38pub use http::{self, HttpRequest, HttpResponse, request, response};
39pub mod config;
40
41pub mod handler;
42
43#[doc(hidden)]
44pub mod prelude {
45 pub use http::{request::HttpRequest, response::*, *};
46
47 pub use crate::{
48 HttpServer,
49 config::*,
50 handler::{self, AuthConfig, Handler},
51 };
52}
53use prelude::*;
54
55pub type Result<T> = std::result::Result<T, HttpError>;
59
60use std::{
61 io::{self, BufRead, BufReader},
62 net::{TcpListener, TcpStream},
63 sync::Arc,
64 thread,
65 time::{Duration, Instant},
66};
67
68pub use config::ServerConfig;
69use http::HttpStream;
70use pool::ThreadPool;
71
72pub mod log;
73use log::prelude::*;
74
75pub struct HttpServer {
96 listener: TcpListener,
97 pool: ThreadPool,
98 handler: Option<Handler>,
99 config: ServerConfig,
100}
101
102fn peek_stream(
103 stream: &mut BufReader<Box<dyn HttpStream>>,
104 duration: Duration,
105) -> io::Result<bool> {
106 stream.get_mut().set_non_blocking(duration)?;
107 let result = match stream.fill_buf() {
108 Ok(b) => !b.is_empty(),
109 Err(err) => match err.kind() {
110 io::ErrorKind::WouldBlock => false,
111 _ => return Err(err),
112 },
113 };
114 stream.get_mut().set_blocking()?;
115 Ok(result)
116}
117
118fn handle_connection(
119 stream: TcpStream,
120 handlers: &Handler,
121 keep_alive_timeout: Duration,
122 keep_alive_requests: u16,
123
124 #[cfg(feature = "tls")] tls_config: Option<&Arc<rustls::ServerConfig>>,
125) -> Result<()> {
126 #[cfg(feature = "tls")]
127 let mut req = match tls_config {
128 Some(config) => {
129 let conn = rustls::ServerConnection::new(Arc::clone(config))
130 .map_err(|err| format!("TLS error: {err}"))?;
131 let tls_stream = rustls::StreamOwned::new(conn, stream);
132 HttpRequest::parse(tls_stream)?
133 }
134 None => HttpRequest::parse(stream)?,
135 };
136
137 #[cfg(not(feature = "tls"))]
138 let mut req = HttpRequest::parse(stream)?;
139
140 handlers.handle(&mut req)?;
141
142 let connection = req.header("Connection");
143 let keep_alive = keep_alive_timeout.as_millis() > 0;
144 if connection.is_some_and(|conn| conn == "keep-alive") && keep_alive {
145 let start = Instant::now();
146 let mut n = 1;
147 log_info!("[{:?}] Start keep alive", thread::current().id());
148 while start.elapsed() < keep_alive_timeout && n < keep_alive_requests {
149 let offset = keep_alive_timeout - start.elapsed();
150
151 match peek_stream(req.stream_mut(), offset) {
152 Ok(false) => break,
153 Err(err) => {
154 log_error!("Error on peek_stream: {err}");
155 break;
156 }
157 _ => {}
158 }
159
160 req = req.keep_alive()?;
161 handlers.handle(&mut req)?;
162 n += 1;
163
164 let connection = req.header("Connection");
165 if connection.is_some_and(|conn| conn == "close") {
166 break;
167 }
168 }
169 log_info!("[{:?}] End keep alive", thread::current().id());
170 }
171 Ok(())
172}
173
174#[allow(clippy::unwrap_used)]
175#[allow(clippy::expect_used)]
176#[allow(clippy::panic)]
177impl HttpServer {
178 pub fn new(config: ServerConfig) -> Result<Self> {
185 let address = format!("::0:{}", config.port);
186 let listener = TcpListener::bind(address)
187 .map_err(|err| format!("Could not bind to port {}: {}", config.port, err))?;
188 let pool =
189 ThreadPool::new(config.pool_conf).map_err(|_| "Error initializing thread pool")?;
190 let handler = Some(Handler::new());
191 let srv = Self {
192 listener,
193 pool,
194 handler,
195 config,
196 };
197 Ok(srv)
198 }
199 #[allow(clippy::missing_panics_doc)]
201 pub fn run(self) {
202 let Self {
203 listener,
204 pool,
205 handler,
206 config,
207 } = self;
208 let handler = handler.unwrap();
209 let timeout = config.keep_alive_timeout;
210 let req = config.keep_alive_requests;
211
212 #[cfg(feature = "tls")]
213 let tls_config = config.tls_config.as_ref();
214
215 println!("Sever listening on port {}", config.port);
216
217 pool.scope(|scope| {
218 for stream in listener.incoming().flatten() {
219 scope.execute(|| {
220 handle_connection(
221 stream,
222 &handler,
223 timeout,
224 req,
225 #[cfg(feature = "tls")]
226 tls_config,
227 )
228 .unwrap_or_else(|err| {
229 log_error!("{err}");
230 });
231 });
232 }
233 });
234
235 println!("Shutting down.");
236 }
237 pub fn set_handler(&mut self, handler: Handler) {
239 self.handler = Some(handler);
240 }
241}
242
243impl Default for HttpServer {
244 fn default() -> Self {
252 let conf = ServerConfig::default();
253 #[allow(clippy::expect_used)]
254 let mut srv = Self::new(conf)
255 .expect("Fatal error: HttpServer failed to initialize with default config");
256 let handler = Handler::default();
257 srv.set_handler(handler);
258 srv
259 }
260}