rastapi/
lib.rs

1#![allow(non_snake_case)]
2//! # RastAPI
3//!
4//! RastAPI is a easy-to-use Rust library for creating low-boilerplate, efficient RESTfull APIs, inspired by FastAPI (Python).
5//! You define route handler (functions) and map them with their corresponding url and you have a functioning route.
6//! RastAPI follows a **Non Blocking IO** structure for efficient and conccurent handling of requests.
7//! We achieve Non blocking IO by following a **Threadpool** architecture. We can set the number of threads in the pool before
8//! starting the application.
9//! RastAPI also has a very efficient LFU-LRU based file caching system.It's a multi threaded cache system, so we used efficient lock
10//! stripping techniques to reduce lock contentions.
11//!
12//! ## Features
13//!  - Minimal boilerplate for defining routes and handlers.
14//!  - High performance leveraging Rust's coccurency and safety.
15//!  - Very efficient file uploads and downloads.
16//!
17//! ## Example
18//! ### Text or Json Response
19//!
20//! ```no_run
21//!     use rastapi::RastAPI;
22//!     use rastapi::Request::HttpRequest;
23//!     use rastapi::Response::{HttpResponse,create_response};
24//!     use rastapi::utils::ContentType;
25//!     use std::collections::HashMap;
26//!     
27//!     // Define a Function/Route handler.
28//!     // With this exact fucnction signature.
29//!     // i.e. fn function_name(request_obj:&HttpRequest,path_params:HashMap<String,String>)->HttpResponse
30//!     // request_obj is parameter where all the details of the incoming request is stored.
31//!     // path_params is a parameter where url path params are stored. Not confuse it with url query parameters.
32//!     // query params are stored in request_obj.params.
33//!
34//!     fn json(request_obj:&HttpRequest,path_params:HashMap<String,String>)->HttpResponse{
35//!
36//!         let json_content=r#"{
37//!         "name" : "Rony",
38//!         "batch" : 2024,
39//!         "sem" : 3,
40//!         "subjects" : ["OS","Networking","Algorithms"],
41//!         "grade" : {
42//!         "OS" : "C",
43//!         "Cryptography" : "C",
44//!         "Algorithms" : "C"
45//!                    }           
46//!         }"#;
47//!
48//!         let resp=create_response(&json_content, 200,ContentType::JSON,true).unwrap(); //create_response(string_content,HTTP_CODE,
49//!         return resp;                                                                  // ContentType,keep_alive_flag)
50//!     }
51//!     fn main(){
52//!         let mut app=RastAPI::new();
53//!         // Set number of workers. It indicates total number of simultaneous requests it can serve.
54//!         app.set_total_workers(5);
55//!         // Map the function/route handler with corresponding url route.
56//!         app.register_route("/json",vec!["GET"],json);
57//!         app.run("0.0.0.0",5000);
58//!     }
59//!```
60//! ### File response
61//!
62//! One of the most powerfull features of rastapi is It's very efficient file uploads downloads. If a file is Not found on system
63//! then the server automatically sends 404 Not Found response.
64//!
65//! ```no_run
66//!     use rastapi::RastAPI;
67//!     use rastapi::Request::HttpRequest;
68//!     use rastapi::Response::{HttpResponse,send_file};
69//!     use rastapi::utils::FileType;
70//!     use std::collections::HashMap;
71//!
72//!     fn file(req:&HttpRequest,path_params:HashMap<String,String>)->HttpResponse{
73//!     let mut resp=send_file("file_path/file_name.ext",Some("file_name.ext".to_string()),FileType::MP4,200,true).unwrap(); // send_file0(file_path,file_name
74//!     resp.add_header("Header-Name","Header-Value");                                              //  file_type,HTTP_CODE,keep_alive_flag)
75//!     return resp;
76//!     }
77//!     fn main(){
78//!         let mut app=RastAPI::new();
79//!         app.set_total_workers(5);
80//!         app.register_route("/file",vec!["GET"],file);
81//!         app.run("127.0.0.1",5000);
82//!      }
83//! ```
84//! ### Load local enviornment variables and access them from std::env module.
85//! ```no_run
86//!     use rastapi::RastAPI;
87//!     use rastapi::Request::HttpRequest;
88//!     use rastapi::Response::{HttpResponse,send_file};
89//!     use rastapi::utils::FileType;
90//!     use rastapi::utils::load_env;
91//!     use std::collections::HashMap;
92//!     use std::env;
93//!     fn file(req:&HttpRequest,path_params:HashMap<String,String>)->HttpResponse{
94//!     let mut resp=send_file("file_path/file_name.ext",Some("file_name.ext".to_string()),FileType::MP4,200,true).unwrap(); // send_file0(file_path,file_name
95//!     resp.add_header("Header-Name","Header-Value");                                              //  file_type,HTTP_CODE,keep_alive_flag)
96//!     return resp;
97//!     }
98//!     fn main(){
99//!         load_env::load_env(); // Loads all local enviornment variables in .env file.
100//!         let mut app=RastAPI::new();
101//!         app.set_total_workers(5);
102//!         app.register_route("/file",vec!["GET"],file);
103//!         let port:u16=env::var("PORT").unwrap().parse().unwrap();
104//!         app.run(env::var("HOST").unwrap().as_str(),port);
105//!      }
106//! ```
107//! ## Cache
108//! RastAPI uses a slightly tweaked version of a standard LFU-LRU (LFU for eviction and LRU when there is a tie between frequencies of two entity) cache. It only caches files for now.
109//! It's a multi threaded cache So for syncronization we use locks (Mutex). We cann't use RwLock as for LRU-LFU cache as every read qyery is a write query.
110//! So there is a issue of Lock contention. To mitigate lock contention we devided the cache into multiple parts (CacheStore) and lock them individually, thus reducing load on
111//! single cache store.
112//! We can set total cache size and total number of cache stores.
113//!
114//! ```no_run
115//!     use rastapi::RastAPI;
116//!     fn main(){
117//!     let mut app=RastAPI::new();
118//!     #[cfg(feature="cachung")]
119//!      app.set_cache_config(500,10); // Total cache size is set to 500 MB and
120//!                                    // total number of cache stores set to 10
121//! }
122//! ```
123//! 
124//! ## Persistent Connections
125//!  RastAPI supports persistent connections. If you want a route to be persistent then set the `keep_alive` flag in `create_response` or `send_file` as `true`.
126//!  You can tweak the keep alive behabiour like maximum number of keep alive requests i.e. total number of request response cycle on a signle connection can be set,
127//!  default value is 10. You can also set the keep alive timeout, i.e. for how much time we wait for next request after sending a response on persistent connection.
128//!  If we don't recieve new request for this duration then we drop the connection. Default value is 5 seconds.
129//! 
130//! ```no_run
131//!     use rastapi::RastAPI;
132//!     fn main(){
133//!     let mut app=RastAPI::new();
134//!     app.set_keep_alive_time_out(3);
135//!     app.set_maximum_keep_alive_requests(10);
136//!     app.run("127.0.0.1",5000);
137//! }
138//! ```
139//! ## Stop the app
140//!  To stop the app gracefully you need to send SIGINT (CTRL + C) or SIGTERM. SIGTSTP(CTRL + Z) is ignored.
141mod App;
142mod File;
143pub mod Request;
144pub mod Response;
145mod cache;
146mod macros;
147pub mod utils;
148use cache::Cache;
149use libc::{
150    sigaction as sigaction_syscall, sigaction as sigaction_struct, sigaddset, SA_RESTART, SIGINT,
151    SIGTERM, SIGTSTP,getsockopt,SOL_SOCKET,SO_SNDBUF
152};
153use std::{
154    ffi::c_void, io, net::{IpAddr, TcpListener, TcpStream, UdpSocket}, os::fd::AsRawFd, path::PathBuf, process, ptr::NonNull, sync::Arc, time::Duration
155};
156use utils::{threadpool::ThreadPool, Method};
157use App::{client::Client, AppEnv, Route, RouteFunction, URLRouter};
158
159// Signal handling ctrl+c & ctrl + z
160static mut SIG_FLAG: bool = false;
161static mut PORT: u16 = 0;
162extern "C" fn handle_shutdown_signal(_: i32) {
163    unsafe {
164        SIG_FLAG = true;
165        let addr = format!("127.0.0.1:{}", PORT);
166        let _dummy_stream = match TcpStream::connect(&addr) {
167            Ok(c) => c,
168            Err(_) => {
169                process::exit(1);
170            }
171        };
172    }
173}
174extern "C" fn handle_sigtstp(_: i32) {
175    log_info!("CTRL + Z is ignored. If want to terminate the server press CTRL + C");
176}
177fn get_send_buffer_len(sock_fd:i32)->usize{
178    let mut buf_sz=1<<14;
179    unsafe {
180        let n_ptr=Box::into_raw(Box::new(0_usize)) as * mut c_void;
181        let m_ptr=Box::into_raw(Box::new(std::mem::size_of::<usize>() as u32));
182        let res=getsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, n_ptr, m_ptr);
183        if res==0{
184            buf_sz=*(n_ptr as * const usize);
185        }
186        let _=Box::from_raw(n_ptr as * mut usize);
187        let _=Box::from_raw(m_ptr);
188    }
189    if buf_sz>(1<<16){
190        buf_sz=1<<14;
191    }
192    buf_sz as usize
193}
194/// This is the main API struct. Before doing anything we need to initialize it.
195/// ## Example
196/// ```no_run
197/// use rastapi::RastAPI;
198///     use rastapi::Request::HttpRequest;
199///     use rastapi::Response::{HttpResponse,send_file};
200///     use rastapi::utils::FileType;
201///     use rastapi::utils::load_env;
202///     use std::collections::HashMap;
203///
204///     fn route_handler(req:&HttpRequest,path_params:HashMap<String,String>)->HttpResponse{
205///     let mut resp=send_file("file_path/file_name.ext",Some("file_name.ext".to_string()),FileType::MP4,200,true).unwrap(); // send_files(file_path,file_name
206///     resp.add_header("Header-Name","Header-Value");                                              //  file_type,HTTP_CODE,keep_alive_flag)
207///     return resp;
208///      }
209///
210/// fn main(){
211///     let mut app=RastAPI::new();
212///     app.set_total_workers(5);
213///     app.set_maximum_payload_size(100);
214///     app.set_read_time_out(5);
215///     app.set_write_time_out(5);
216///     app.set_keep_alive_time_out(5);
217///     app.set_maximum_keep_alive_requests(10);
218///     app.register_route("/route",vec!["GET"],route_handler);
219///     app.run("localhost",5000);
220/// }
221///    
222///
223/// ```
224pub struct RastAPI {
225    /// Routes of our app.
226    pub(crate) routes: NonNull<URLRouter>,
227    /// Total number of workers. i.e. total threads in our threadpool. It's system threads NOT green threads. Default 10 workers.
228    pub total_workers: usize,
229    /// Maximum size of payload to accept on each request. If it exceeds for any request our API will automatically send *413 Payload too larrge*. Default 512 MB.
230    pub payload_maximum_size_in_MB: usize,
231    /// If no packet recieved for this duration then we stop reading from stream and drop the connection. Default 5 secs.
232    pub read_time_out: Duration,
233    /// If we couldn't write on the stream for this duration we stop sending request and drop the connection. Default 5 secs.
234    pub write_time_out: Duration,
235    /// Maximum amount of time we keep a persistent connection alive after sending a response. Default 5 secs.
236    pub keep_alive_time_out: Duration,
237    /// Maximum number of request-response cycle on a persistent connection. Default 10 request-response cycle.
238    pub keep_alive_max_count: u8,
239    /// A LFU-LRU cache for file caching. Default size 400 MB, devided among 10 Cache Stores.
240    pub(crate) cache: NonNull<Cache<PathBuf>>,
241    /// Name of the directory where incoming files are stored. i.e. files coming in request bodies. Default name is `input_files`.
242    pub file_upload_directory_name:String
243}
244impl RastAPI {
245    /// Initializes a RastAPI struct with default configurations.
246    pub fn new() -> Self {
247        let total_workers: usize = 10;
248        let default_read_time_out = Duration::from_secs(5);
249        let deafault_write_time_out = Duration::from_secs(5);
250        let deafault_keep_alive_time_out = Duration::from_secs(5);
251        Self {
252            routes: unsafe { NonNull::new_unchecked(Box::into_raw(Box::new(URLRouter::new()))) },
253            total_workers,
254            payload_maximum_size_in_MB: 512 as usize,
255            read_time_out: default_read_time_out,
256            write_time_out: deafault_write_time_out,
257            keep_alive_time_out: deafault_keep_alive_time_out,
258            keep_alive_max_count: 10,
259            cache: unsafe { NonNull::new_unchecked(Box::into_raw(Box::new(Cache::new(10, 40)))) },
260            file_upload_directory_name:String::from("input_files")
261        }
262    }
263
264    /// Sets the cache configuration.
265    /// ## Parameters
266    ///  - `total_cache_size` : Total size of the cache.
267    ///  - `total_no_of_cache_stores` : Total number of cache stores. each cache store will contain part of the cache.
268    ///   Maximum size of a cache store will be `toatal_cache_size`/`total_no_of_cache_stores`.
269    ///
270    /// Make sure `total_cache_size` is divisible by `total_no_of_cache_stores`.
271    ///
272    #[cfg(feature="caching")]
273    pub fn set_cache_config(&mut self, total_cache_size: usize, total_no_of_cache_stores: usize) {
274        assert!(
275            total_cache_size % total_no_of_cache_stores == 0,
276            "TOTAL CACHE SIZE MUST BE DIVISIBLE BY TOTAL NUMBER OF CACHE STORES"
277        );
278        let _ = unsafe { Box::from_raw(self.cache.as_ptr()) };
279        let eache_cache_store_size = total_cache_size / total_no_of_cache_stores;
280        self.cache = unsafe {
281            NonNull::new_unchecked(Box::into_raw(Box::new(Cache::new(
282                total_no_of_cache_stores,
283                eache_cache_store_size,
284            ))))
285        };
286    }
287
288    /// Register a route i.e. Map a url with it's corresponding handler.
289    /// ## Parameters
290    ///   - `url` : The url we want to map.
291    ///   - `methods`: Vector of HTTP methods allowed on this route.
292    ///   -`func` : Name of the route handler.
293    ///
294    /// Can return error if supplied method is not implimented.
295    ///
296    pub fn register_route(
297        &mut self,
298        url: &str,
299        methods: Vec<&str>,
300        func: RouteFunction,
301    ) -> Result<(), io::Error> {
302        let mut method_list: Vec<Method> = Vec::new();
303        for m in methods {
304            if let Some(_m_) = Method::from_string(m) {
305                method_list.push(_m_);
306            } else {
307                error!(
308                    "No http method named {}. Try using only uppercase letters like GET,POST",
309                    m
310                );
311                return Err(io::ErrorKind::InvalidInput.into());
312            }
313        }
314
315        let route = Route::new(func, method_list);
316        // Routes is not a NULL pointer
317        unsafe { self.routes.as_mut().add_route(url, route) };
318        Ok(())
319    }
320    // Get the local ipv4 address.
321    fn server_wl01_addr() -> Option<String> {
322        let udp_socket = match UdpSocket::bind("0.0.0.0:0") {
323            Ok(udps) => udps,
324            Err(_) => {
325                return None;
326            }
327        };
328        let _ = match udp_socket.connect("8.8.8.8:80") {
329            Ok(_) => (),
330            Err(_) => {
331                return None;
332            }
333        };
334        if let Ok(local_addr) = udp_socket.local_addr() {
335            if let IpAddr::V4(ipv4_addr) = local_addr.ip() {
336                return Some(ipv4_addr.to_string());
337            } else {
338                return None;
339            }
340        }
341        return None;
342    }
343    /// Set total number of threads in threadpool.
344    pub fn set_total_workers(&mut self, n: usize) -> () {
345        self.total_workers = n;
346    }
347    /// Set maximum payload size. If it exceeded in any request then automatic 413 Payload too large is sent.
348    pub fn set_maximum_payload_size(&mut self, size_in_MB: usize) -> () {
349        self.payload_maximum_size_in_MB = size_in_MB;
350    }
351    /// Set the read time out. If no packet recieved for this duration then we stop reading from stream and drop the connection.
352    pub fn set_read_time_out(&mut self, time_in_secs: u8) -> () {
353        self.read_time_out = Duration::from_secs(time_in_secs as u64);
354    }
355    /// Set the write time out. If we couldn't write on the stream for this duration we stop sending request and drop the connection.
356    pub fn set_write_time_out(&mut self, time_in_secs: u8) -> () {
357        self.write_time_out = Duration::from_secs(time_in_secs as u64);
358    }
359    /// set the maximum amount of time we keep a persistent connection alive after sending a response. Default 5 secs.
360    pub fn set_keep_alive_time_out(&mut self, time_in_secs: u8) {
361        self.keep_alive_time_out = Duration::from_secs(time_in_secs as u64);
362    }
363    /// set the maximum number of request-response cycle on a persistent connection. Default 10 request-response cycle.
364    pub fn set_maximum_keep_alive_requests(&mut self, n_requests: u8) {
365        self.keep_alive_max_count = n_requests;
366    }
367    /// Set the name of the directory where incoming files are stored. i.e. files coming in request bodies. Default name is `input_files`.
368    pub fn set_incoming_files_directory_name(&mut self,directory_name:&str){
369        self.file_upload_directory_name=String::from(directory_name);
370    }
371
372    /// Run the application.
373    ///
374    /// ## Parameters
375    ///   - `host` : IP address of the API. Either It can be `127.0.0.1` then it can only be accessed from your local machine
376    /// or `0.0.0.0` then It can be accessed from all devices in your LAN.
377    ///   - `port` : Port on which the API listens to. If It's zero then a random available port is assigned.
378    pub fn run(&mut self, host: &str, port: u16) -> () {
379        //termination and interuption signal handling
380        unsafe {
381            let mut sig_int_act: sigaction_struct = std::mem::zeroed();
382            let mut sig_term_act: sigaction_struct = std::mem::zeroed();
383            let mut sig_tstp_act: sigaction_struct = std::mem::zeroed();
384            sig_int_act.sa_sigaction = handle_shutdown_signal as usize;
385            sig_term_act.sa_sigaction = handle_shutdown_signal as usize;
386            sig_tstp_act.sa_sigaction = handle_sigtstp as usize;
387            sigaddset(&mut sig_int_act.sa_mask, SIGTERM);
388            sigaddset(&mut sig_int_act.sa_mask, SIGTSTP);
389            sigaddset(&mut sig_term_act.sa_mask, SIGINT);
390            sigaddset(&mut sig_term_act.sa_mask, SIGTSTP);
391            sigaddset(&mut sig_tstp_act.sa_mask, SIGINT);
392            sigaddset(&mut sig_tstp_act.sa_mask, SIGTERM);
393            sig_int_act.sa_flags = SA_RESTART;
394            sig_term_act.sa_flags = SA_RESTART;
395            sig_tstp_act.sa_flags = SA_RESTART;
396            sigaction_syscall(SIGINT, &sig_int_act, std::ptr::null_mut());
397            sigaction_syscall(SIGTERM, &sig_term_act, std::ptr::null_mut());
398            sigaction_syscall(SIGTSTP, &sig_tstp_act, std::ptr::null_mut());
399        }
400        let mut addr = String::from(host);
401        addr.push(':');
402        addr.push_str(&port.to_string());
403        let listner = match TcpListener::bind(&addr) {
404            Ok(l) => l,
405            Err(e) => {
406                eprintln!("{}", e);
407                return;
408            }
409        };
410        let send_buf_sz=get_send_buffer_len(listner.as_raw_fd());
411        let mut app_env = AppEnv::new(host, port, &self,send_buf_sz);
412        if let Ok(server_addr) = listner.local_addr() {
413            if server_addr.ip().is_unspecified() {
414                if let Some(wl10) = Self::server_wl01_addr() {
415                    addr = wl10.clone();
416                    addr.push(':');
417                    addr.push_str(server_addr.port().to_string().as_str());
418                    app_env.host = wl10;
419                    app_env.port = server_addr.port();
420                }
421            } else {
422                addr = String::from("127.0.0.1");
423                app_env.host = addr.clone();
424                addr.push(':');
425                addr.push_str(server_addr.port().to_string().as_str());
426                app_env.port = server_addr.port();
427            }
428        }
429        log_info!("SERVER STARTED");
430        log_info!("Listening to {}", &addr);
431        log_info!("Press CTRL + C to stop the server.");
432        log_info!("Process PID : {}", process::id());
433        unsafe {
434            PORT = app_env.port;
435        }
436        let pool = ThreadPool::new(self.total_workers);
437        let max_keep_alive_count = app_env.keep_alive_max_count;
438        let app_env_arc = Arc::new(app_env);
439        for stream in listner.incoming().flatten() {
440            unsafe {
441                if SIG_FLAG {
442                    break;
443                }
444            }
445            let stream = Arc::new(stream);
446            let app_env_cloned = Arc::clone(&app_env_arc);
447            let cnt = max_keep_alive_count;
448            pool.execute(move || {
449                let _ = Client(stream, app_env_cloned, cnt, true);
450            });
451        }
452    }
453}
454#[cfg(test)]
455mod apitest {
456    use super::*;
457    use io::Read;
458    use reqwest::{
459        blocking::Client,
460        header::{self, HeaderMap, HeaderValue}
461    };
462    use std::{collections::HashMap, fs};
463    use std::thread;
464    use utils::{ContentType, FileType};
465    use Request::HttpRequest;
466    use Response::{create_response, send_file, HttpResponse};
467    enum TestResult {
468        PASSED,
469        FAILED(String),
470        
471    }
472    fn json_header_path_params(
473        _req: &HttpRequest,
474        _path_params: HashMap<String, String>,
475    ) -> HttpResponse {
476        let resp_json = r#"{
477        "Foo" : "Bar",
478        "Dummy" : 5
479        }"#;
480        let mut resp = create_response(&resp_json, 200, ContentType::JSON, false).unwrap();
481        for (key, val) in _req.headers.iter() {
482            resp.add_header(&key, &val);
483        }
484        for (key, val) in _path_params.iter() {
485            resp.add_header(&key, &val);
486        }
487        resp
488    }
489    fn file_download(_req:&HttpRequest,_path_params:HashMap<String,String>)->HttpResponse{
490        let resp=send_file("src/test/test.jpg", None,FileType::JPEG,200,false).unwrap();
491        return resp;
492    }
493    fn file_upload(req:&HttpRequest,_path_params:HashMap<String,String>)->HttpResponse{
494        if let Some(file_path) =&req.body_location{
495          let mut uploaded_file=match fs::File::open(file_path){
496            Ok(f)=>f,
497            Err(e)=>{
498                let mut s=String::from("FAILED TO OPEN UPLOADED FILE.\n");
499                s.push_str(e.to_string().as_str());
500                let resp=create_response(&s, 400, ContentType::TEXT, false).unwrap();
501                return resp;
502            }
503          };
504          let mut uploaded_file_buf=Vec::<u8>::new();
505          let _=match uploaded_file.read_to_end(&mut uploaded_file_buf){
506            Ok(_)=>(),
507            Err(e)=>{
508                let mut s=String::from("FAILED TO READ UPLOADED FILE.\n");
509                s.push_str(e.to_string().as_str());
510                let resp=create_response(&s, 400, ContentType::TEXT, false).unwrap();
511                return resp;
512            }
513          };
514          let mut orignal_file=match fs::File::open("src/test/test.jpg"){
515            Ok(f)=>f,
516            Err(e)=>{
517                let mut s=String::from("FAILED TO OPEN ORIGNAL FILE.\n");
518                s.push_str(e.to_string().as_str());
519                let resp=create_response(&s, 400, ContentType::TEXT, false).unwrap();
520                return resp;
521            }
522          };
523          let mut orignal_file_buf=Vec::<u8>::new();
524          let _=match orignal_file.read_to_end(&mut orignal_file_buf){
525            Ok(_)=>(),
526            Err(e)=>{
527                let mut s=String::from("FAILED TO READ ORIGNAL FILE.\n");
528                s.push_str(e.to_string().as_str());
529                let resp=create_response(&s, 400, ContentType::TEXT, false).unwrap();
530                return resp;
531            }
532          };
533          if uploaded_file_buf.eq(&orignal_file_buf){
534            let resp=create_response("SUCCESS", 200, ContentType::TEXT, false).unwrap();
535            return resp;
536          }
537          else {
538            let resp=create_response("ORIGNAL FILE AND UPLOADED FILE DOESN'T MATCH", 200, ContentType::TEXT, false).unwrap();
539            return resp;
540          }
541        }
542        else {
543            
544            let resp=create_response("BODY LOCATION IS NONE.", 400, ContentType::TEXT, false).unwrap();
545            return resp;
546        }
547    }
548    fn run_server(){
549        let mut app = RastAPI::new();
550        let _ = app.register_route("/json/{id}/{name}", vec!["GET"], json_header_path_params).expect("FAILED TO REGISTER 1");
551        let _ = app.register_route("/download", vec!["GET"], file_download).expect("FAILED TO REGISTER 2");
552        let _=app.register_route("/upload", vec!["POST"], file_upload);
553        app.run("127.0.0.1", 5000);
554    }
555    #[test]
556    fn json_header_path_params_test() {
557        let _handle1 = thread::spawn(|| {
558                run_server();
559        });
560        thread::sleep(std::time::Duration::from_secs(1));
561        let handle2 = thread::spawn(|| {
562            let mut headers = header::HeaderMap::new();
563            headers.insert("X-api-key", HeaderValue::from_static("abcdef12"));
564            let response = match Client::new()
565                .get("http://127.0.0.1:5000/json/5/rony")
566                .headers(headers)
567                .send(){
568                    Ok(R)=>R,
569                    Err(e)=>{
570                        let mut s=String::from("FAILED TO SEND REQUEST. REASON :\n");
571                        s.push_str(e.to_string().as_str());
572                        return TestResult::FAILED(s);
573                    }
574                };
575            let resp_json = r#"{
576        "Foo" : "Bar",
577        "Dummy" : 5
578        }"#;
579            let resp_json_vec = resp_json.as_bytes().to_vec();
580            let h = response.headers();
581            if !(h.get("id").map(|hv| hv.to_str().unwrap()).eq(&Some("5"))){
582                return TestResult::FAILED("FAILED PATH PARAM 1".to_string());
583            }
584            if !(h.get("name").map(|hv| hv.to_str().unwrap()).eq(&Some("rony"))){
585                return TestResult::FAILED("FAILED PATH PARAM 2".to_string());
586            }
587            if !(h.get("X-api-key").map(|hv| hv.to_str().unwrap()).eq(&Some("abcdef12"))){
588                return TestResult::FAILED("FAILED HEADER".to_string());
589            }
590            let body = match response.bytes(){
591                Ok(b)=>b.to_vec(),
592                Err(e)=>{
593                    let mut s=String::from("FAILED TO RECIEVE BYTES FROM SERVER. REASON :\n");
594                    s.push_str(e.to_string().as_str());
595                    return TestResult::FAILED(s);
596                }
597            };
598            if !(body.eq(&resp_json_vec)){
599                return TestResult::FAILED(String::from("BODY MISMATCH"));
600            }
601            TestResult::PASSED
602        });
603        let res=handle2.join().expect("FAILED TO JOIN");
604        match res {
605            TestResult::FAILED(S)=>{
606                assert!(false,"{}",S);
607            },
608            TestResult::PASSED=>()
609        };
610    }
611    #[test]
612    fn file_download_test(){
613            let _handle1 = thread::spawn(|| {
614                run_server();
615            });
616        let _=thread::sleep(std::time::Duration::from_secs(1));
617        let handle2=thread::spawn(||{
618            let resp=match Client::new().get("http://127.0.0.1:5000/download").send(){
619                Ok(R)=>R,
620                Err(e)=>{
621                    let mut s=String::from("FAILED TO SEND REQUEST. REASON :\n");
622                    s.push_str(e.to_string().as_str());
623                    return TestResult::FAILED(s);
624                }
625            };
626            let mut file=match fs::File::open("src/test/test.jpg"){
627                Ok(F)=>F,
628                Err(e)=>{
629                    let mut s=String::from("FAILED TO OPEN ORIGNAL FILE. REASON :\n");
630                    s.push_str(e.to_string().as_str());
631                    return TestResult::FAILED(s);
632                }
633            };
634            let mut orignal_buf=Vec::<u8>::new();
635            let _=match file.read_to_end(&mut orignal_buf){
636                Ok(_)=>(),
637                Err(e)=>{
638                    let mut s=String::from("FAILED TO LOAD ORIGNAL FILE. REASON :\n");
639                    s.push_str(e.to_string().as_str());
640                    return TestResult::FAILED(s);
641                }
642            };
643            let downloaded_buf=match resp.bytes(){
644                Ok(b)=>b.to_vec(),
645                Err(e)=>{
646                    let mut s=String::from("FAILED TO RECIEVE BYTES FROM SERVER. REASON :\n");
647                    s.push_str(e.to_string().as_str());
648                    return TestResult::FAILED(s);
649                }
650            };
651            if !(orignal_buf.eq(&downloaded_buf)){
652                return TestResult::FAILED(String::from("ORIGNAL FILE AND DOWNLOADED FILE MISMATCH"));
653            }
654            TestResult::PASSED
655        });
656        let res=handle2.join().expect("FAILED TO JOIN");
657        match res {
658            TestResult::FAILED(S)=>{
659                assert!(false,"{}",S);
660            },
661            TestResult::PASSED=>()
662        };
663    }
664    #[test]
665    fn file_upload_test(){
666        let _handle1=thread::spawn(||{
667            run_server();
668        });
669        let handle2=thread::spawn(||{
670            let test_file=match fs::File::open("src/test/test.jpg"){
671                Ok(f)=>f,
672                Err(e)=>{
673                    let mut s=String::from("FAILED TO OPEN TEST FILE. REASON :\n");
674                    s.push_str(e.to_string().as_str());
675                    return TestResult::FAILED(s);
676                }
677            };
678            let mut headers=HeaderMap::new();
679            headers.insert("Content-Type", HeaderValue::from_static("image/jpeg"));
680            let resp=match Client::new().post("http://127.0.0.1:5000/upload").headers(headers).body(test_file).send(){
681                Ok(R)=>R,
682                Err(e)=>{
683                    let mut s=String::from("FAILED TO SEND REQUEST. REASON :\n");
684                    s.push_str(e.to_string().as_str());
685                    return TestResult::FAILED(s);
686                }
687            };
688            if resp.status().as_u16()!=200_u16{
689                let content=match resp.text(){
690                    Ok(s)=>s,
691                    Err(e)=>{
692                        let mut s=String::from("FAILED TO GET RESPONSE TEXT. REASON :\n");
693                        s.push_str(e.to_string().as_str());
694                        return TestResult::FAILED(s);
695                    }
696                };
697                return TestResult::FAILED(content);
698            }
699            TestResult::PASSED
700        });
701        let res=handle2.join().expect("FAILED TO JOIN");
702        match res {
703            TestResult::FAILED(s)=>{
704                assert!(false,"{}",s);
705            },
706            TestResult::PASSED=>()
707        }
708        
709    }
710}