Skip to main content

torus_http/
server.rs

1//! The actual http server on which you define your routes
2use std::{
3    collections::HashMap,
4    io::{Read, Write},
5    net::{TcpListener, ToSocketAddrs},
6    str::{FromStr, Utf8Error, from_utf8},
7    sync::Arc,
8};
9
10use crate::{method::HttpMethod, request::HttpRequest, response::Response};
11
12/// A generic trait to allow many different types of handlers to be passed into our http server
13pub trait HandlerFn: Send + Sync {
14    fn call(&self, req: HttpRequest) -> Box<dyn Response>;
15}
16
17impl<F, T> HandlerFn for F
18where
19    F: Fn(HttpRequest) -> T + Send + Sync,
20    T: Response + 'static,
21{
22    fn call(&self, req: HttpRequest) -> Box<dyn Response> {
23        Box::new(self(req))
24    }
25}
26
27pub type MiddleWareFn = fn(HttpRequest) -> HttpRequest;
28pub type Handler = Box<dyn HandlerFn + Send + Sync>;
29/// The struct to initialise your http server and finally listen on some port
30///
31/// # Example usage:
32///
33/// ```no_run
34/// use torus_http::server::HttpServer;
35/// HttpServer::new().listen(("127.0.0.1", 8080)); // no_op http server listening on port 8080
36/// ```
37#[derive(Default)]
38pub struct HttpServer {
39    handlers: HashMap<(String, HttpMethod), Handler>,
40    middle_ware: Option<MiddleWareFn>,
41    state: Option<Box<dyn Send + Sync>>,
42}
43
44impl HttpServer {
45    /// Initialise an http server on an address
46    #[must_use]
47    pub fn new() -> Self {
48        Self {
49            handlers: HashMap::new(),
50            middle_ware: None,
51            state: None,
52        }
53    }
54
55    /// Initialises middleware or replaces if there was already some added
56    ///
57    /// subject to change
58    ///
59    /// # Example usage:
60    ///
61    /// ```rust
62    /// use torus_http::server::HttpServer;
63    /// HttpServer::new().add_middleware(|req| {
64    ///     println!("we got request: {req:#?}");
65    ///     req
66    /// });
67    /// ```
68    #[must_use]
69    pub fn add_middleware(mut self, f: fn(req: HttpRequest) -> HttpRequest) -> Self {
70        self.middle_ware.replace(f);
71        self
72    }
73
74    /// Register a custom route
75    ///
76    /// # Example usage:
77    ///
78    /// ```rust
79    /// use torus_http::server::HttpServer;
80    /// use torus_http::method::HttpMethod;
81    /// HttpServer::new().route("/some_path", HttpMethod::other("custom"), |_| {"hi"});
82    /// ```
83    #[must_use]
84    pub fn route<F: HandlerFn + 'static>(
85        mut self,
86        path: impl Into<String>,
87        method: HttpMethod,
88        f: F,
89    ) -> Self {
90        self.handlers.insert((path.into(), method), Box::new(f));
91        self
92    }
93
94    /// Register a **GET** method
95    ///
96    /// # Example usage:
97    ///
98    /// ```rust
99    /// use torus_http::server::HttpServer;
100    /// use torus_http::request::HttpRequest;
101    /// use torus_http::response::Response;
102    /// fn home_method(_req: HttpRequest) -> impl Response {
103    ///     "hello, world"
104    /// }
105    /// HttpServer::new().get("/home", home_method);
106    /// ```
107    ///
108    /// ## Note:
109    ///
110    /// I drop the body for get requests as that is apparently standard
111    #[must_use]
112    pub fn get<F: HandlerFn + 'static>(self, path: impl Into<String>, f: F) -> Self {
113        self.route(path, HttpMethod::Get, f)
114    }
115
116    /// Register a **POST** method
117    ///
118    /// # Example usage:
119    ///
120    /// ```rust
121    /// use torus_http::server::HttpServer;
122    /// use torus_http::request::HttpRequest;
123    /// use torus_http::response::Response;
124    /// fn my_post(_req: HttpRequest) -> impl Response {
125    ///     // ... Super complex DB activity
126    ///     "I'll keep you posted"
127    /// }
128    /// HttpServer::new().post("/drop/prod/db", my_post);
129    /// ```
130    #[must_use]
131    pub fn post<F: HandlerFn + 'static>(self, path: impl Into<String>, f: F) -> Self {
132        self.route(path, HttpMethod::Post, f)
133    }
134
135    /// Register a **DELETE** method
136    ///
137    /// # Example usage:
138    ///
139    /// ```rust
140    /// use torus_http::server::HttpServer;
141    /// use torus_http::request::HttpRequest;
142    /// use torus_http::response::Response;
143    /// fn my_delete(_req: HttpRequest) -> impl Response {
144    ///     // delete browser history ...
145    ///     "Yeah I don't use the internet bro trust me..."
146    /// }
147    /// HttpServer::new().delete("/homework", my_delete);
148    /// ```
149    #[must_use]
150    pub fn delete<F: HandlerFn + 'static>(self, path: impl Into<String>, f: F) -> Self {
151        self.route(path, HttpMethod::Delete, f)
152    }
153
154    /// Register an **UPDATE** method
155    ///
156    /// # Example usage:
157    ///
158    /// ```rust
159    /// use torus_http::server::HttpServer;
160    /// use torus_http::request::HttpRequest;
161    /// use torus_http::response::Response;
162    /// fn im_getting_tired_of_writing_these(_req: HttpRequest) -> impl Response {
163    ///     // just read the others like .get() and .post() bro
164    ///     "Yeah I don't use the internet bro trust me..."
165    /// }
166    /// HttpServer::new().delete("/homework", im_getting_tired_of_writing_these);
167    /// ```
168    #[must_use]
169    pub fn update<F: HandlerFn + 'static>(self, path: impl Into<String>, f: F) -> Self {
170        self.route(path, HttpMethod::Update, f)
171    }
172
173    /// Register a **PUT** method
174    ///
175    /// # Example usage:
176    ///
177    /// ```rust
178    /// use torus_http::request::HttpRequest;
179    /// use torus_http::server::HttpServer;
180    /// use torus_http::response::Response;
181    /// fn im_getting_tired_of_writing_these(_req: HttpRequest) -> impl Response {
182    ///     "WHY THE HECK DID I ADD SO MANY OF THESE THINGS"
183    /// }
184    /// HttpServer::new().delete("/us-east1", im_getting_tired_of_writing_these);
185    /// ```
186    #[must_use]
187    pub fn put<F: HandlerFn + 'static>(self, path: impl Into<String>, f: F) -> Self {
188        self.route(path, HttpMethod::Put, f)
189    }
190
191    /// like `.post()` but patch
192    #[must_use]
193    pub fn patch<F: HandlerFn + 'static>(self, path: impl Into<String>, f: F) -> Self {
194        self.route(path, HttpMethod::Patch, f)
195    }
196
197    /// I just took this one from hoppscotch I never heard of the head method before
198    /// read `.post()` and stuff for documentation
199    #[must_use]
200    pub fn head<F: HandlerFn + 'static>(self, path: impl Into<String>, f: F) -> Self {
201        self.route(path, HttpMethod::Head, f)
202    }
203
204    /// Shoutout to chatgpt for this one:
205    /// Register an **OPTIONS** method
206    ///
207    /// This attaches a handler to the given path that responds to http `OPTIONS`
208    /// requests. Typically used for capability discovery, CORS preflight checks,
209    /// or politely telling browsers what they are allowed to do.
210    ///
211    /// # Example usage:
212    ///
213    /// ```rust
214    /// use torus_http::server::HttpServer;
215    /// use torus_http::request::HttpRequest;
216    /// use torus_http::response::Response;
217    /// fn options_method(_req: HttpRequest) -> impl Response {
218    ///     ""
219    /// }
220    ///
221    /// HttpServer::new().options("/home", options_method);
222    /// ```
223    ///
224    /// ## Note:
225    ///
226    /// `OPTIONS` requests are generally expected to return headers describing
227    /// allowed methods and behaviors. A response body is usually unnecessary and
228    /// often ignored, but nothing is stopping you from adding one if you enjoy
229    /// disappointing strict HTTP purists.
230    #[must_use]
231    pub fn options<F: HandlerFn + 'static>(self, path: impl Into<String>, f: F) -> Self {
232        self.route(path, HttpMethod::Options, f)
233    }
234
235    #[must_use]
236    pub fn set_state<T: Send + Sync + 'static>(mut self, state: T) -> Self {
237        self.state.replace(Box::new(state));
238        self
239    }
240
241    /// Start your http server
242    ///
243    /// # Errors
244    ///
245    /// - Failed binding listener to address
246    /// - Failed reading the stream to the buffer
247    /// - Failed getting the stream
248    /// - Failed parsing the request
249    /// - Failed flushing to the stream
250    pub fn listen(self, address: impl ToSocketAddrs) -> Result<(), ServerError> {
251        let listener = TcpListener::bind(address)?;
252        let handlers = Arc::new(self.handlers);
253        let middle_ware = Arc::new(self.middle_ware);
254
255        for stream in listener.incoming() {
256            let stream = stream?;
257            let middle_ware = middle_ware.clone();
258            let handlers = handlers.clone();
259
260            let job = move || _ = handle_connection(&handlers, &middle_ware, stream);
261            std::thread::spawn(job);
262        }
263        Ok(())
264    }
265}
266
267fn handle_connection(
268    // server: &Arc<HttpServer<T>>,
269    handlers: &Arc<HashMap<(String, HttpMethod), Handler>>,
270    middle_ware: &Arc<Option<MiddleWareFn>>,
271    mut stream: std::net::TcpStream,
272) -> Result<(), ServerError> {
273    let mut buf = [0; 4096 * 4];
274    let n = stream.read(&mut buf)?;
275    let request = {
276        let request = HttpRequest::from_str(from_utf8(&buf[..n])?)?;
277        if let Some(middle_ware) = **middle_ware {
278            middle_ware(request)
279        } else {
280            request
281        }
282    };
283    let path = request.path.clone();
284    let method = request.method.clone();
285    if let Some(handler) = handlers.get(&(path, method)) {
286        let ret = handler.call(request);
287        stream.write_all(ret.to_response().into_bytes().as_slice())?;
288    } else {
289        stream.write_all(&"no method found".to_response().into_bytes())?;
290    }
291    Ok(())
292}
293#[derive(Debug)]
294pub enum ServerError {
295    Utf8Conversion(Utf8Error),
296    IoError(std::io::Error),
297}
298
299impl From<Utf8Error> for ServerError {
300    fn from(value: Utf8Error) -> Self {
301        Self::Utf8Conversion(value)
302    }
303}
304impl From<std::io::Error> for ServerError {
305    fn from(value: std::io::Error) -> Self {
306        Self::IoError(value)
307    }
308}