1#![allow(
6 dead_code,
7 unused_imports,
8 unused_variables,
9 unused_mut
10)]
11
12mod utils;
14mod thread_handler;
15pub mod response;
16pub mod request;
17pub mod errors;
18pub mod stream;
19pub mod prelude;
20
21use crate::response::ResponseType;
23use errors::ConfigError;
24use request::info::{ RequestInfo, Method };
25pub use response::{ Respond, not_found };
26pub use stream::Stream;
27use lazy_static::lazy_static;
28use std::{
29 net::{
30 TcpStream,
31 TcpListener
32 },
33 io::{
34 Read, Write,
35 },
36 path::{Path, PathBuf},
37 collections::HashMap,
38 fs, sync::Mutex,
39};
40
41const _DATA_BUF_INIT:usize = 1024usize;
43const DATA_BUF_POST_INIT:usize = 65536usize;
44
45lazy_static! {
47 pub static ref FILE_CACHE:Mutex<HashMap<String, Vec<u8>>> = Mutex::new(HashMap::new());
48}
49
50#[derive(Clone, Copy)]
52enum FileCacheType {
53 All,
54 Selection(&'static [&'static str]),
55}
56
57#[derive(Clone, Copy)]
58pub struct Server {
63 addr: Option<&'static str>,
65
66 port: Option<u16>,
68
69 num_threads:u16,
71
72 serve: Option<&'static str>,
74
75 not_found: Option<&'static str>,
77
78 routes: &'static [Route],
80
81 init_buf: Option<usize>,
83
84 cache: Option<FileCacheType>,
86
87 logs: bool,
89
90 cors: bool,
92}
93
94pub enum Route {
116 Stack(
118 &'static str,
119 &'static [Route]
120 ),
121
122 ControlledStack(
126 fn(&mut Stream) -> bool,
127 &'static str,
128 &'static [Route]
129 ),
130
131 Get(
133 &'static str,
134 fn(&mut Stream) -> ()
135 ),
136
137 Post(
139 &'static str,
140 fn(&mut Stream) -> ()
141 ),
142
143 File(
145 &'static str,
146 &'static str
147 )
148}
149
150fn handle_req(tcp_stream:TcpStream, config:&Server) {
152 let buffer:&mut Vec<u8> = &mut vec![0u8; config.init_buf.unwrap_or(DATA_BUF_POST_INIT)];
154 let mut stream = Stream::from(tcp_stream);
155
156 if config.cors {
158 stream.cors = true;
159 };
160
161 match stream.get_mut_inner_ref().read(buffer) {
163 Ok(data) => data,
164 Err(_) => return
165 };
166
167 let request:String = String::from_utf8_lossy(
169 &buffer[..buffer.iter().position(|&r| r == 0).unwrap_or(buffer.len())]
171 ).to_string();
172 let headers:HashMap<&str, &str> = utils::headers::parse_headers(&request);
173
174 let mut body:String = String::new();
176 let info:RequestInfo = match RequestInfo::parse_req(&request) {
177 Ok(e) => e,
178 Err(_) => return
179 };
180
181 if info.method == Method::POST {
185 body = request.split("\r\n\r\n").last().unwrap_or("").to_string();
186 }
188 let mut full_path:String = String::new();
189 stream.set_body(body);
190 stream.set_headers(headers);
191
192 for route in config.routes {
194 match call_endpoint(&route, info, &mut full_path, &mut stream) {
195 Ok(_) => return,
196 Err(optional_status) => {
197 if let Some(status) = optional_status {
198 return stream.respond_status(status);
199 }
200 },
201 };
202 };
203
204 if let Some(static_path) = config.serve {
207 match serve_static_dir(static_path, info.path, &mut stream) {
208 Ok(_) => (),
209 Err(_) => {
210 not_found(&mut stream, *config);
213 },
214 };
215 }else {
216 not_found(&mut stream, *config);
217 };
218}
219
220fn call_endpoint(
222 routes:&Route,
223 info:RequestInfo,
224 full_path:&mut String,
225
226 stream: &mut Stream
228) -> Result<(), Option<u16>> {
229
230 if let Route::ControlledStack(_, pathname, next_routes) | Route::Stack(pathname, next_routes) = routes {
234 if let Route::ControlledStack(fnc, _, _) = routes {
235 if fnc(stream) == false { return Err(None); };
239 }
240
241 let mut tail_found:bool = false;
243
244 'tail_search: for route in next_routes.iter() {
246
247 let mut possible_full_path = full_path.clone();
249 possible_full_path.push_str(pathname);
250 possible_full_path.push('/');
251
252 match call_endpoint(route, info, &mut possible_full_path, stream) {
254 Ok(_) => {
255 tail_found = true;
256
257 full_path.push_str(pathname);
259 full_path.push('/');
260 break 'tail_search;
261 },
262 Err(_) => continue
263 };
264 };
265
266 if tail_found { return Ok(()); }
268 else { return Err(None); }
269 }
270
271 match routes {
273 Route::Post(pathname, function_ptr)
274 | Route::Get(pathname, function_ptr) => {
275
276 let mut params:HashMap<String, String> = HashMap::new();
279
280 let mut possible_full_path = full_path.clone();
282 possible_full_path.push_str(pathname);
283
284 let final_subpaths:Vec<&str> = get_subpaths(&possible_full_path);
286 let mut final_check_url:String = possible_full_path.clone();
287
288 for (index, request_path) in get_subpaths(info.path).iter().enumerate() {
290
291 let subp:&str = match final_subpaths.get(index) {
293 Some(e) => e,
294 None => return Err(None)
295 };
296
297 match is_url_param(subp) {
298 Some(param_name) => {
299 params.insert(param_name.into(), request_path.to_string());
300
301 final_check_url = final_check_url.replace(subp, request_path);
303 continue;
304 },
305 None => {
306 if request_path != &subp {
307 return Err(None);
308 }else {
309 continue;
310 };
311 },
312 }
313 }
314
315 if trim(final_check_url) == trim(info.path.to_string()) {
317
318 match &info.method {
320 Method::GET => {
321 stream.set_params(params);
323 function_ptr(stream);
324
325 Ok(())
327 },
328 Method::POST => {
329 stream.set_params(params);
331 function_ptr(stream);
332
333 Ok(())
335 },
336 Method::UNKNOWN => Err(Some(405u16)), Method::OPTIONS => {
338 stream.respond_status(200u16);
339 Ok(())
340 },
341 _ => {
342 stream.set_params(params);
344 function_ptr(stream);
345
346 Ok(())
348 }
349 }
350 }else {
351 Err(None)
352 }
353 },
354 Route::File(endpoint_path, file_path) => {
355 println!("{endpoint_path} {file_path}");
356 let mut possible_full_path = full_path.clone();
358 possible_full_path.push_str(&endpoint_path);
359
360 dbg!(&possible_full_path);
361 dbg!(info.path);
362
363 if trim(possible_full_path) == trim(info.path.to_string()) {
364 stream.respond_file(200u16, file_path);
365 Ok(())
366 }else {
367 Err(None)
368 }
369 }
370 _ => Err(Some(405u16)) }
372}
373
374pub fn trim(input:String) -> String {
376 let mut output = input.clone();
377 if output.ends_with('/') { output.pop(); };
378 if output.starts_with('/') { output.remove(0); };
379 output
380}
381
382fn get_subpaths(path:&str) -> Vec<&str> {
384 let mut subpaths:Vec<&str> = Vec::new();
385
386 for subpath in path.split('/') {
388 if !subpath.is_empty() { subpaths.push(subpath); };
389 };
390
391 subpaths
393}
394
395fn is_url_param(path:&str) -> Option<&str> {
397 if path.starts_with(':') && path.ends_with(':') {
398 Some(&path[1..path.len()-1])
399 }else {
400 None
401 }
402}
403
404fn serve_static_dir(dir:&str, request_path:&str, stream:&mut Stream) -> Result<(), ()> {
406
407 let path = &[dir, request_path].concat();
409 let file_path:&Path = Path::new(path);
410
411 if let Ok(fc) = FILE_CACHE.lock() {
413 match fc.get(path) {
414 Some(buf) => {
415 stream.respond(
416 200,
417 Respond::new().content(
418 &String::from_utf8_lossy(&buf),
419 ResponseType::guess(file_path)
420 )
421 );
422 return Ok(())
423 },
424 None => ()
425 }
426 };
427
428 match file_path.is_file() {
430 true => (),
431 false => return Err(())
432 };
433
434 match fs::File::open(file_path) {
436 Ok(_) => {
437 let mut file_content:String = match fs::read_to_string(file_path) {
439 Ok(e) => e,
440 Err(_) => {
441 return Err(());
443 }
444 };
445 let res:Respond = Respond {
446 response_type: ResponseType::guess(file_path),
447 content: Some(file_content),
448 additional_headers: None
449 };
450
451 stream.respond(
453 200u16,
454 res
455 );
456 },
457 Err(_) => return Err(())
458 }
459
460 Ok(())
461}
462
463impl<'f> Server {
465 pub fn new() -> Server {
466 Server {
467 addr: None,
468 port: None,
469 num_threads: 1,
470 serve: None,
471 not_found: None,
472 routes: &[],
473 init_buf: None,
474 cache: None,
475 logs: true,
476 cors: false
477 }
478 }
479 pub fn port(&mut self, port:u16) -> &mut Self { self.port = Some(port); self }
481
482 pub fn serve(&mut self, serve:&'static str) -> &mut Self { self.serve = Some(serve); self }
484
485 pub fn routes(&mut self, routes:&'static [Route]) -> &mut Self { self.routes = routes; self }
487
488 pub fn address(&mut self, addr:&'static str) -> &mut Self { self.addr = Some(addr); self }
490
491 pub fn threads(&mut self, num_threads:u16) -> &mut Self { self.num_threads = num_threads; self }
493
494 pub fn not_found(&mut self, not_found:&'static str) -> &mut Self { self.not_found = Some(not_found); self }
496
497 pub fn init_buf_size(&mut self, buf_size:usize) -> &mut Self { self.init_buf = Some(buf_size); self }
499
500 pub fn cache_serve_dir(&mut self) -> &mut Self { self.cache = Some(FileCacheType::All); self }
502
503 pub fn cache_selected(&mut self, selection:&'static [&'static str]) -> &mut Self { self.cache = Some(FileCacheType::Selection(selection)); self }
505
506 pub fn no_logs(&mut self) -> &mut Self { self.logs = false; self }
508
509 pub fn cors(&mut self) -> &mut Self { self.cors = true; self }
511
512 pub fn start(self) -> Result<(), ConfigError> {
530
531 let bind_to = &format!(
533 "{}:{}",
534 match self.addr {
535 Some(e) => e,
536 None => return Err(errors::ConfigError::MissingHost)
537 },
538 match self.port {
539 Some(e) => e,
540 None => return Err(errors::ConfigError::MissingPort)
541 }
542 );
543
544 if let Some(cache) = self.cache {
546 match cache {
547 FileCacheType::All => {
548 load_files_cache(
549 self.logs,
550 get_list_dir(self.serve.expect("Calling .cache_serve_dir() requires .serve(dir) to be set"))
551 )
552 },
553 FileCacheType::Selection(selection) => {
554 load_files_cache(self.logs, selection.to_owned().iter().map(|e| e.to_string()).collect::<Vec<String>>())
555 }
556 }
557 };
558
559 let stream = match TcpListener::bind(bind_to) {
561 Ok(listener) => listener,
562
563 Err(_) => return Err(ConfigError::HostPortBindingFail)
565 };
566
567 if self.logs { println!("http://{bind_to}") };
569
570 let thread_handler = thread_handler::MainThreadHandler::new(self.num_threads);
572
573 for request in stream.incoming() {
575
576 thread_handler.exec(move || {
578 handle_req(match request {
580 Ok(req) => req,
581 Err(_) => return,
582 }, &self);
583 });
584 };
585
586 Ok(())
588 }
589}
590
591fn get_list_dir<'a>(dir:&str) -> Vec<String> {
593 let mut files:Vec<String> = Vec::new();
594
595 for entry in match fs::read_dir(dir) { Ok(e) => e, Err(_) => return files } {
597 let entry = match entry {
598 Ok(e) => e,
599 Err(_) => continue
600 };
601 let path = entry.path();
602
603 if path.is_file() {
605 files.push(
606 match path.to_str() {
607 Some(e) => e.to_string(),
608 None => continue
609 }
610 );
611 };
612 };
613
614 files
615}
616
617fn load_files_cache(logs:bool, files:Vec<String>) {
619 let files_len = files.len();
620 let mut index = 0;
621 let mut stdout = std::io::stdout();
622 for file in files {
623 index += 1;
624 if logs { print!("\rLoading file: {} / {}", index, files_len) };
625 let mut file_ = match std::fs::File::open(file.clone()) {
626 Ok(e) => e,
627 Err(_) => continue
628 };
629 let mut buf = Vec::new();
630 match file_.read_to_end(&mut buf) {
631 Ok(e) => e,
632 Err(_) => continue
633 };
634 match FILE_CACHE.lock() {
635 Ok(e) => e,
636 Err(_) => continue
637 }.insert(Path::new(&file).canonicalize().unwrap_or(PathBuf::from("")).display().to_string(), buf);
638 stdout.flush().unwrap_or_default();
639 };
640 if logs {
641 println!();
642 println!("Loaded {} files into memory", files_len);
643 };
644}