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}