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