From 9ac52dad958ff752d613952d44253b9cfc1abfba Mon Sep 17 00:00:00 2001 From: Alberto Ruiz <17555470+Az107@users.noreply.github.com> Date: Thu, 27 Jun 2024 00:30:15 +0200 Subject: [PATCH 1/6] commit 1 --- src/hteapot.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/hteapot.rs b/src/hteapot.rs index 6d59e97..5eef57c 100644 --- a/src/hteapot.rs +++ b/src/hteapot.rs @@ -179,14 +179,14 @@ impl Hteapot { } }; let arc_action = Arc::new(action); - listener.set_nonblocking(false).expect("set_nonblocking call failed"); - let pool_clone = self.pool.clone(); thread::spawn(move || { let mut streams_to_handle = Vec::new(); loop { + println!("Processing loop begining"); { if streams_to_handle.is_empty() { + println!("no streams"); let (lock, cvar) = &*pool_clone; let mut pool = lock.lock().expect("Error locking pool"); @@ -199,14 +199,18 @@ impl Hteapot { } } streams_to_handle.retain(|stream| { + println!("Processing stream"); let action_clone = arc_action.clone(); Hteapot::handle_client(stream, move |request| { action_clone(request) }) }); + println!("Processing loop end"); + } }); + listener.set_nonblocking(false).expect("Error set nodelay to listener"); let pool_clone = self.pool.clone(); for stream in listener.incoming() { println!("new Stream"); @@ -214,15 +218,16 @@ impl Hteapot { println!("error stream! {:?}",stream.err()); continue; } - let stream = stream.unwrap(); + let mut stream = stream.unwrap(); println!("Getting lock"); let (lock, cvar) = &*pool_clone; - stream.set_nodelay(true).expect("Error set nodelay to stream"); let mut pool = lock.lock().expect("Error locking pool"); println!("Getted!"); pool.push(stream); - cvar.notify_one(); // Notify one waiting thread + cvar.notify_one(); + println!("Loop end!"); + // Notify one waiting thread } } @@ -245,6 +250,8 @@ impl Hteapot { let mut response = Vec::new(); response.extend_from_slice(response_header.as_bytes()); response.extend_from_slice(content); + response.push(0x0D); // Carriage Return + response.push(0x0A); // Line Feed response } @@ -368,8 +375,7 @@ impl Hteapot { eprintln!("Error: {}", r.err().unwrap()); } - let r = reader.read(&mut [0; 1]); - r.is_err() + false } } From 075a3a2d8101c8e0e4c9b51af0791b981d999376 Mon Sep 17 00:00:00 2001 From: Alberto Ruiz <17555470+Az107@users.noreply.github.com> Date: Thu, 27 Jun 2024 14:08:32 +0200 Subject: [PATCH 2/6] fix 27 --- .idea/.gitignore | 5 +++ src/hteapot.rs | 113 +++++++++++++++++++++++++---------------------- src/main.rs | 2 +- 3 files changed, 67 insertions(+), 53 deletions(-) create mode 100644 .idea/.gitignore diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..b58b603 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/src/hteapot.rs b/src/hteapot.rs index 5eef57c..2bf6fc3 100644 --- a/src/hteapot.rs +++ b/src/hteapot.rs @@ -2,10 +2,13 @@ // This is the HTTP server module, it will handle the requests and responses // Also provide utilities to parse the requests and build the responses -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use std::hash::Hash; use std::io::{self, BufReader, BufWriter, Read, Write}; -use std::net::{TcpListener, TcpStream}; +use std::net::{Shutdown, TcpListener, TcpStream}; +use std::os::fd::AsFd; +use std::thread::sleep; +use std::time::Duration; use std::{str, thread}; use std::sync::{Arc, Mutex, Condvar}; @@ -150,7 +153,7 @@ pub struct Hteapot { port: u16, address: String, //cache: HashMap, - pool: Arc<(Mutex>, Condvar)>, + //pool: Arc<(Mutex>, Condvar)>, } @@ -162,71 +165,76 @@ impl Hteapot { port: port, address: address.to_string(), //cache: HashMap::new(), - pool: Arc::new((Mutex::new(Vec::new()), Condvar::new())), } } // Start the server - pub fn listen(&mut self, action: impl Fn(HttpRequest) -> Vec + Send + Sync + 'static ){ + pub fn listen(&self, action: impl Fn(HttpRequest) -> Vec + Send + Sync + 'static ){ let addr = format!("{}:{}", self.address, self.port); let listener = TcpListener::bind(addr); let listener = match listener { Ok(listener) => listener, Err(e) => { - eprintln!("Error: {}", e); + eprintln!("Error L: {}", e); return; - } + } }; + let pool: Arc<(Mutex>, Condvar)> = Arc::new((Mutex::new(VecDeque::new()), Condvar::new())); let arc_action = Arc::new(action); - let pool_clone = self.pool.clone(); - thread::spawn(move || { - let mut streams_to_handle = Vec::new(); - loop { - println!("Processing loop begining"); - { - if streams_to_handle.is_empty() { - println!("no streams"); + + + for tn in 0..1 { + let pool_clone = pool.clone(); + let action_clone = arc_action.clone(); + thread::spawn(move || { + let mut streams_to_handle = Vec::new(); + loop { + { let (lock, cvar) = &*pool_clone; let mut pool = lock.lock().expect("Error locking pool"); - - while pool.is_empty(){ - pool = cvar.wait(pool).expect("Error waiting on cvar"); + if streams_to_handle.is_empty() { + println!("no streams"); + pool = cvar.wait_while(pool, |pool| pool.is_empty()).expect("Error waiting on cvar"); + println!("New streams!!!"); + } - - // Movemos los streams fuera del mutex - streams_to_handle.append(&mut *pool); + + if !pool.is_empty() { + streams_to_handle.push(pool.pop_back().unwrap()); + } + } - } - streams_to_handle.retain(|stream| { - println!("Processing stream"); - let action_clone = arc_action.clone(); - Hteapot::handle_client(stream, move |request| { - action_clone(request) - }) - }); - println!("Processing loop end"); - - } - }); + + streams_to_handle.retain(|mut stream| { + //println!("Handling request by {}", tn); + let action_clone = action_clone.clone(); + Hteapot::handle_client(stream, move |request| { + action_clone(request) + }) + }); + } + }); + } - listener.set_nonblocking(false).expect("Error set nodelay to listener"); - let pool_clone = self.pool.clone(); - for stream in listener.incoming() { - println!("new Stream"); + let pool_clone = pool.clone(); + loop { + println!("Waiting for connection"); + let stream = listener.accept(); + println!("New connection"); if stream.is_err() { - println!("error stream! {:?}",stream.err()); continue; } - let mut stream = stream.unwrap(); - println!("Getting lock"); - let (lock, cvar) = &*pool_clone; - let mut pool = lock.lock().expect("Error locking pool"); - println!("Getted!"); - - pool.push(stream); - cvar.notify_one(); - println!("Loop end!"); + let (stream, _) = stream.unwrap(); + stream.set_nonblocking(true).expect("Error seting non blocking"); + stream.set_nodelay(true).expect("Error seting no delay"); + { + let (lock, cvar) = &*pool_clone; + let mut pool = lock.lock().expect("Error locking pool"); + + pool.push_front(stream); + cvar.notify_one(); + } // Notify one waiting thread } } @@ -342,13 +350,14 @@ impl Hteapot { return true; }, _ => { + println!("{:?}",e); return false; }, } }, Ok(m) => { if m == 0 { - return false; + break; } }, }; @@ -358,23 +367,23 @@ impl Hteapot { } let request_string = String::from_utf8(request_buffer).unwrap(); + // let request_string = "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n".to_string(); let request = Self::request_parser(request_string); if request.is_err() { eprintln!("{}", request.err().unwrap()); return false; } let request = request.unwrap(); - let response = action(request); let r = writer.write_all(&response); if r.is_err() { - eprintln!("Error: {}", r.err().unwrap()); + eprintln!("Error1: {}", r.err().unwrap()); } let r = writer.flush(); if r.is_err() { - eprintln!("Error: {}", r.err().unwrap()); + eprintln!("Error2: {}", r.err().unwrap()); } - + let _ = stream.shutdown(Shutdown::Both); false } } diff --git a/src/main.rs b/src/main.rs index 242ef2c..2031475 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,7 @@ fn main() { let mut server = Hteapot::new(config.host.as_str(), config.port); println!("Server started at http://{}:{}", config.host, config.port); server.listen( move |req| { - println!("Request: {:?}", req.path); + //println!("Request: {:?}", req.path); let path = if req.path.ends_with("/") { let mut path = req.path.clone(); path.push_str(&config.index); From 5b3524f2e73029bb7937747b519698e7be7eb5c3 Mon Sep 17 00:00:00 2001 From: Alberto Ruiz <17555470+Az107@users.noreply.github.com> Date: Thu, 27 Jun 2024 19:59:14 +0200 Subject: [PATCH 3/6] add thread configuration and clean --- src/config.rs | 3 +++ src/hteapot.rs | 22 ++++++++++++++++------ src/main.rs | 3 +-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/config.rs b/src/config.rs index e009a59..9895706 100644 --- a/src/config.rs +++ b/src/config.rs @@ -52,6 +52,7 @@ pub struct Config { pub host: String, // Host name or IP pub root: String, // Root directory to serve files pub cache: bool, + pub threads: u16, pub index: String, // Index file to serve by default pub error: String, // Error file to serve when a file is not found pub proxy_rules: HashMap @@ -76,6 +77,7 @@ impl Config { root: "./".to_string(), index: "index.html".to_string(), error: "error.html".to_string(), + threads: 1, cache: false, proxy_rules: HashMap::new() } @@ -95,6 +97,7 @@ impl Config { port: map.get("port").unwrap_or(&"8080".to_string()).parse::().unwrap(), host: map.get("host").unwrap_or(&"".to_string()).to_string(), root: map.get("root").unwrap_or(&"./".to_string()).to_string(), + threads: map.get("threads").unwrap_or(&"1".to_string()).parse::().unwrap(), cache: match map.get("cache") { Some(r) => { if r == "true" { diff --git a/src/hteapot.rs b/src/hteapot.rs index 2bf6fc3..087c5cc 100644 --- a/src/hteapot.rs +++ b/src/hteapot.rs @@ -152,6 +152,7 @@ pub struct HttpRequest { pub struct Hteapot { port: u16, address: String, + threads: u16, //cache: HashMap, //pool: Arc<(Mutex>, Condvar)>, @@ -164,11 +165,24 @@ impl Hteapot { Hteapot { port: port, address: address.to_string(), + threads: 1, //cache: HashMap::new(), } } + pub fn new_threaded(address: &str, port: u16, thread: u16) -> Self { + Hteapot { + port: port, + address: address.to_string(), + threads: thread, + //cache: HashMap::new(), + + } + } + + + // Start the server pub fn listen(&self, action: impl Fn(HttpRequest) -> Vec + Send + Sync + 'static ){ let addr = format!("{}:{}", self.address, self.port); @@ -184,7 +198,7 @@ impl Hteapot { let arc_action = Arc::new(action); - for tn in 0..1 { + for tn in 0..self.threads { let pool_clone = pool.clone(); let action_clone = arc_action.clone(); thread::spawn(move || { @@ -194,9 +208,7 @@ impl Hteapot { let (lock, cvar) = &*pool_clone; let mut pool = lock.lock().expect("Error locking pool"); if streams_to_handle.is_empty() { - println!("no streams"); pool = cvar.wait_while(pool, |pool| pool.is_empty()).expect("Error waiting on cvar"); - println!("New streams!!!"); } @@ -206,7 +218,7 @@ impl Hteapot { } - streams_to_handle.retain(|mut stream| { + streams_to_handle.retain(|stream| { //println!("Handling request by {}", tn); let action_clone = action_clone.clone(); Hteapot::handle_client(stream, move |request| { @@ -219,9 +231,7 @@ impl Hteapot { let pool_clone = pool.clone(); loop { - println!("Waiting for connection"); let stream = listener.accept(); - println!("New connection"); if stream.is_err() { continue; } diff --git a/src/main.rs b/src/main.rs index 2031475..dc641c3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,6 @@ mod brew; use std::collections::HashMap; use std::fs; use std::io; -use std::sync::Arc; use std::sync::Mutex; use hteapot::Hteapot; @@ -22,7 +21,7 @@ fn main() { config::Config::new_default() }; let cache: Mutex>> = Mutex::new(HashMap::new()); - let mut server = Hteapot::new(config.host.as_str(), config.port); + let server = Hteapot::new_threaded(config.host.as_str(), config.port,config.threads); println!("Server started at http://{}:{}", config.host, config.port); server.listen( move |req| { //println!("Request: {:?}", req.path); From c374e363d81341ebd36852b8f92decb8770898da Mon Sep 17 00:00:00 2001 From: Alberto Ruiz <17555470+Az107@users.noreply.github.com> Date: Thu, 27 Jun 2024 20:02:39 +0200 Subject: [PATCH 4/6] updated demo config --- hteapot.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hteapot.toml b/hteapot.toml index 8641916..bec18e8 100644 --- a/hteapot.toml +++ b/hteapot.toml @@ -2,6 +2,8 @@ port = 8081 host = "0.0.0.0" #test de comentario root = "public" +threads = 5 +cache = true [proxy] "/test" = "https://example.com" "/google" = "http://google.com" \ No newline at end of file From e36ce9a8b043a7bb824b6a41a0011023360fbb64 Mon Sep 17 00:00:00 2001 From: Alberto Ruiz <17555470+Az107@users.noreply.github.com> Date: Wed, 3 Jul 2024 09:54:41 +0200 Subject: [PATCH 5/6] logs and cache config --- src/config.rs | 100 +++++++++++++++++++++++++++++++++++++------------ src/hteapot.rs | 12 ++---- src/logger.rs | 95 ++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 29 +++++++++++--- 4 files changed, 198 insertions(+), 38 deletions(-) create mode 100644 src/logger.rs diff --git a/src/config.rs b/src/config.rs index 9895706..4c6a019 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,10 +2,40 @@ // This is the config module, it will load the configuration // file and provide the settings -use std::{collections::HashMap, fs}; +use std::{any::{Any, TypeId}, clone, collections::HashMap, fs}; + +#[derive(Clone)] +#[derive(Debug)] +pub enum TOMLtype { + Text(String), + Number(u16), + Float(f64), + Boolean(bool), +} + +type TOMLSchema = HashMap; +trait Schema { + fn get2(&self, key: &str) -> Option ; +} -pub fn toml_parser(content: &str) -> HashMap> { +impl Schema for TOMLSchema { + fn get2(&self, key: &str) -> Option { + let value = self.get(key)?; + let value = value.clone(); + let any_value: Box = match value { + TOMLtype::Text(d) => Box::new(d), + TOMLtype::Number(d) => Box::new(d), + TOMLtype::Float(d) => Box::new(d), + TOMLtype::Boolean(d) => Box::new(d) + }; + let r = any_value.downcast_ref::().cloned(); + if r.is_none() {println!("{} is none", key);} + r + } +} + +pub fn toml_parser(content: &str) -> HashMap { let mut map = HashMap::new(); let mut submap = HashMap::new(); let mut title = "".to_string(); @@ -39,8 +69,27 @@ pub fn toml_parser(content: &str) -> HashMap> { continue; } let value = parts[1].trim(); - let value = value.trim_matches('"').trim(); - submap.insert(key.to_string(), value.to_string()); + let value = if value.contains('\'') || value.contains('"') { + let value = value.trim_matches('"').trim(); + TOMLtype::Text(value.to_string()) + } else if value.to_lowercase() == "true" || value.to_lowercase() == "false" { + let value = value.to_lowercase() == "true"; + TOMLtype::Boolean(value) + } else if value.contains('.') { + let value = value.parse::(); + if value.is_err() { + panic!("Error parsing toml"); + } + TOMLtype::Float(value.unwrap()) + } else { + let value = value.parse::(); + if value.is_err() { + panic!("Error parsing toml"); + } + TOMLtype::Number(value.unwrap()) + + }; + submap.insert(key.to_string(), value); } map.insert(title, submap.clone()); map @@ -52,6 +101,7 @@ pub struct Config { pub host: String, // Host name or IP pub root: String, // Root directory to serve files pub cache: bool, + pub cache_ttl: i64, pub threads: u16, pub index: String, // Index file to serve by default pub error: String, // Error file to serve when a file is not found @@ -79,6 +129,7 @@ impl Config { error: "error.html".to_string(), threads: 1, cache: false, + cache_ttl: 0, proxy_rules: HashMap::new() } } @@ -90,28 +141,29 @@ impl Config { } let content = content.unwrap(); let map = toml_parser(&content); - println!("{:?}", map); - let proxy_rules = map.get("proxy").unwrap_or(&HashMap::new()).clone(); + let mut proxy_rules: HashMap = HashMap::new(); + let proxy_map = map.get("proxy"); + if proxy_map.is_some() { + let proxy_map = proxy_map.unwrap(); + for k in map.keys() { + let url = proxy_map.get2(k); + if url.is_none() {continue;} + let url = url.unwrap(); + println!("Added proxy {} => {}",k, url); + proxy_rules.insert(k.clone(), url); + } + } + let map = map.get("HTEAPOT").unwrap(); Config { - port: map.get("port").unwrap_or(&"8080".to_string()).parse::().unwrap(), - host: map.get("host").unwrap_or(&"".to_string()).to_string(), - root: map.get("root").unwrap_or(&"./".to_string()).to_string(), - threads: map.get("threads").unwrap_or(&"1".to_string()).parse::().unwrap(), - cache: match map.get("cache") { - Some(r) => { - if r == "true" { - true - } else { - false - } - }, - None => { - false - } - }, - index: map.get("index").unwrap_or(&"index.html".to_string()).to_string(), - error: map.get("error").unwrap_or(&"error.html".to_string()).to_string(), + port: map.get2("port").unwrap_or(8080), + host: map.get2("host").unwrap_or("".to_string()), + root: map.get2("root").unwrap_or("./".to_string()), + threads: map.get2("threads").unwrap_or(1), + cache: map.get2("cache").unwrap_or(false), + cache_ttl: map.get2("cache_ttl").unwrap_or(0), + index: map.get2("index").unwrap_or("index.html".to_string()), + error: map.get2("error").unwrap_or("error.html".to_string()), proxy_rules } } diff --git a/src/hteapot.rs b/src/hteapot.rs index 087c5cc..6892c3d 100644 --- a/src/hteapot.rs +++ b/src/hteapot.rs @@ -2,13 +2,12 @@ // This is the HTTP server module, it will handle the requests and responses // Also provide utilities to parse the requests and build the responses + + use std::collections::{HashMap, VecDeque}; use std::hash::Hash; use std::io::{self, BufReader, BufWriter, Read, Write}; use std::net::{Shutdown, TcpListener, TcpStream}; -use std::os::fd::AsFd; -use std::thread::sleep; -use std::time::Duration; use std::{str, thread}; use std::sync::{Arc, Mutex, Condvar}; @@ -92,7 +91,6 @@ pub enum HttpStatus { ServiceUnavailable = 503, } - impl HttpStatus { pub fn from_u16(status: u16) -> HttpStatus { match status { @@ -148,7 +146,6 @@ pub struct HttpRequest { pub body: String, } - pub struct Hteapot { port: u16, address: String, @@ -182,7 +179,6 @@ impl Hteapot { } - // Start the server pub fn listen(&self, action: impl Fn(HttpRequest) -> Vec + Send + Sync + 'static ){ let addr = format!("{}:{}", self.address, self.port); @@ -198,7 +194,7 @@ impl Hteapot { let arc_action = Arc::new(action); - for tn in 0..self.threads { + for _tn in 0..self.threads { let pool_clone = pool.clone(); let action_clone = arc_action.clone(); thread::spawn(move || { @@ -416,7 +412,7 @@ fn test_http_parser() { fn test_http_response_maker() { let response = Hteapot::response_maker(HttpStatus::IAmATeapot, "Hello, World!", None); let response = String::from_utf8(response).unwrap(); - let expected_response = "HTTP/1.1 418 I'm a teapot\r\nContent-Length: 13\r\n\r\nHello, World!"; + let expected_response = "HTTP/1.1 418 I'm a teapot\r\nContent-Length: 13\r\n\r\nHello, World!\r\n"; assert_eq!(response, expected_response); } diff --git a/src/logger.rs b/src/logger.rs new file mode 100644 index 0000000..46792c2 --- /dev/null +++ b/src/logger.rs @@ -0,0 +1,95 @@ + +use std::time::{self, SystemTime, UNIX_EPOCH}; +use std::io::{BufWriter, Write}; + +struct SimpleTime; +impl SimpleTime { + fn epoch_to_ymdhms(seconds: u64) -> (i32, u32, u32, u32, u32, u32) { + // Constants for time calculations + const SECONDS_IN_MINUTE: u64 = 60; + const SECONDS_IN_HOUR: u64 = 3600; + const SECONDS_IN_DAY: u64 = 86400; + + // Leap year and normal year days + const DAYS_IN_YEAR: [u32; 2] = [365, 366]; + const DAYS_IN_MONTH: [[u32; 12]; 2] = [ + [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], // Normal years + [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] // Leap years + ]; + + // Calculate the number of days since the epoch + let mut remaining_days = seconds / SECONDS_IN_DAY; + + // Determine the current year + let mut year = 1970; + loop { + let leap_year = if (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) { 1 } else { 0 }; + if remaining_days < DAYS_IN_YEAR[leap_year] as u64 { + break; + } + remaining_days -= DAYS_IN_YEAR[leap_year] as u64; + year += 1; + } + + // Determine the current month and day + let leap_year = if (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) { 1 } else { 0 }; + let mut month = 0; + while remaining_days >= DAYS_IN_MONTH[leap_year][month] as u64 { + remaining_days -= DAYS_IN_MONTH[leap_year][month] as u64; + month += 1; + } + let day = remaining_days + 1; // Days are 1-based + + // Calculate the current hour, minute, and second + let remaining_seconds = seconds % SECONDS_IN_DAY; + let hour = (remaining_seconds / SECONDS_IN_HOUR) as u32; + let minute = ((remaining_seconds % SECONDS_IN_HOUR) / SECONDS_IN_MINUTE) as u32; + let second = (remaining_seconds % SECONDS_IN_MINUTE) as u32; + + (year, month as u32 + 1, day as u32, hour, minute, second) +} + pub fn get_current_timestamp() -> String { + let now = SystemTime::now(); + let since_epoch = now.duration_since(UNIX_EPOCH).expect("Time went backwards"); + let secs = since_epoch.as_secs(); + let (year, month, day, hour, minute, second) = Self::epoch_to_ymdhms(secs); + + + format!("{:04}/{:02}/{:02} - {:02}:{:02}:{:02}", + year, month, day, hour, minute, second) + } +} + + + +pub struct Logger { + buffer: BufWriter, +} + +impl Logger { + pub fn new(writer: W) -> Logger { + Logger { + buffer: BufWriter::new(writer) + } + } + + fn log(&mut self, content: String) { + let _ = self.buffer.write(content.as_bytes()); + let _ = self.buffer.flush(); + } + + pub fn msg(&mut self, content: String) { + self.log(format!("[{}] - {}\n",SimpleTime::get_current_timestamp() ,content)); + } + +} + +#[cfg(test)] +use std::io::stdout; + +#[test] +fn test_basic() { + + let mut logs = Logger::new(stdout()); + logs.msg("test".to_string()); +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index dc641c3..9704d21 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +mod logger; pub mod hteapot; mod config; mod brew; @@ -7,10 +8,13 @@ use std::collections::HashMap; use std::fs; use std::io; use std::sync::Mutex; +use std::time; +use std::time::SystemTime; use hteapot::Hteapot; use hteapot::HttpStatus; use brew::fetch; +use logger::Logger; fn main() { @@ -20,11 +24,13 @@ fn main() { } else { config::Config::new_default() }; - let cache: Mutex>> = Mutex::new(HashMap::new()); + let mut logger = Logger::new(io::stdout()); + let cache: Mutex, u64)>> = Mutex::new(HashMap::new()); let server = Hteapot::new_threaded(config.host.as_str(), config.port,config.threads); - println!("Server started at http://{}:{}", config.host, config.port); + logger.msg(format!("Server started at http://{}:{}", config.host, config.port)); server.listen( move |req| { - //println!("Request: {:?}", req.path); + let mut logger = Logger::new(io::stdout()); + logger.msg(format!("Request {} {}",req.method.to_str(), req.path)); let path = if req.path.ends_with("/") { let mut path = req.path.clone(); path.push_str(&config.index); @@ -33,7 +39,7 @@ fn main() { req.path.clone() }; if config.proxy_rules.contains_key(&req.path) { - println!("Proxying to: {}", config.proxy_rules.get(&req.path).unwrap()); + logger.msg(format!("Proxying to: {}", config.proxy_rules.get(&req.path).unwrap())); let url = config.proxy_rules.get(&req.path).unwrap(); return match fetch(url) { Ok(response) => { @@ -61,7 +67,15 @@ fn main() { }; let content: Result, _> = if cache_result.is_some() { - Ok(cache_result.unwrap()) + let (content,ttl) = cache_result.unwrap(); + let now = SystemTime::now(); + let since_epoch = now.duration_since(time::UNIX_EPOCH).expect("Time went backwards"); + let secs = since_epoch.as_secs(); + if secs > ttl { + fs::read(&path) + } else { + Ok(content) + } } else { fs::read(&path) }; @@ -72,7 +86,10 @@ fn main() { let cache = cache.lock(); if cache.is_ok() { let mut cache = cache.unwrap(); - cache.insert(path,content.clone()); + let now = SystemTime::now(); + let since_epoch = now.duration_since(time::UNIX_EPOCH).expect("Time went backwards"); + let secs = since_epoch.as_secs(); + cache.insert(path,(content.clone(),secs)); } } From 9344a0281e3f75df2b0405882e6a283ee299c460 Mon Sep 17 00:00:00 2001 From: Alberto Ruiz <17555470+Az107@users.noreply.github.com> Date: Thu, 4 Jul 2024 12:17:58 +0200 Subject: [PATCH 6/6] Log and Cache improvements --- src/config.rs | 13 +++++++------ src/logger.rs | 19 ++++++++++++++----- src/main.rs | 48 ++++++++++++++++++++++++++++++------------------ 3 files changed, 51 insertions(+), 29 deletions(-) diff --git a/src/config.rs b/src/config.rs index 4c6a019..d63c5d2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,7 +2,7 @@ // This is the config module, it will load the configuration // file and provide the settings -use std::{any::{Any, TypeId}, clone, collections::HashMap, fs}; +use std::{any::Any, collections::HashMap, fs}; #[derive(Clone)] #[derive(Debug)] @@ -64,7 +64,6 @@ pub fn toml_parser(content: &str) -> HashMap { continue; } let key = parts[0].trim().trim_end_matches('"').trim_start_matches('"'); - println!("{}",key); if key.is_empty(){ continue; } @@ -101,7 +100,7 @@ pub struct Config { pub host: String, // Host name or IP pub root: String, // Root directory to serve files pub cache: bool, - pub cache_ttl: i64, + pub cache_ttl: u64, pub threads: u16, pub index: String, // Index file to serve by default pub error: String, // Error file to serve when a file is not found @@ -145,11 +144,13 @@ impl Config { let proxy_map = map.get("proxy"); if proxy_map.is_some() { let proxy_map = proxy_map.unwrap(); - for k in map.keys() { + for k in proxy_map.keys() { let url = proxy_map.get2(k); - if url.is_none() {continue;} + if url.is_none() { + println!(); + continue; + } let url = url.unwrap(); - println!("Added proxy {} => {}",k, url); proxy_rules.insert(k.clone(), url); } } diff --git a/src/logger.rs b/src/logger.rs index 46792c2..e5654b8 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -62,21 +62,30 @@ impl SimpleTime { -pub struct Logger { - buffer: BufWriter, + +pub struct Logger { + buffers: Vec>, } impl Logger { pub fn new(writer: W) -> Logger { + let mut buffers = Vec::new(); + buffers.push(BufWriter::new(writer)); Logger { - buffer: BufWriter::new(writer) + buffers: buffers, } } fn log(&mut self, content: String) { - let _ = self.buffer.write(content.as_bytes()); - let _ = self.buffer.flush(); + for b in self.buffers.iter_mut() { + let _ = b.write(content.as_bytes()); + let _ = b.flush(); + }; + } + pub fn add(&mut self, writer: W) { + self.buffers.push(BufWriter::new(writer)); + } pub fn msg(&mut self, content: String) { self.log(format!("[{}] - {}\n",SimpleTime::get_current_timestamp() ,content)); diff --git a/src/main.rs b/src/main.rs index 9704d21..c5b4605 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,9 @@ mod brew; use std::collections::HashMap; use std::fs; use std::io; +use std::io::BufWriter; +use std::net::TcpStream; +use std::sync::Arc; use std::sync::Mutex; use std::time; use std::time::SystemTime; @@ -24,13 +27,17 @@ fn main() { } else { config::Config::new_default() }; - let mut logger = Logger::new(io::stdout()); + let logger = Mutex::new(Logger::new(io::stdout())); let cache: Mutex, u64)>> = Mutex::new(HashMap::new()); let server = Hteapot::new_threaded(config.host.as_str(), config.port,config.threads); - logger.msg(format!("Server started at http://{}:{}", config.host, config.port)); + logger.lock().expect("this doesnt work :C").msg(format!("Server started at http://{}:{}", config.host, config.port)); + if config.cache { + logger.lock().expect("this doesnt work :C").msg("Cache Enabled".to_string()); + } + server.listen( move |req| { - let mut logger = Logger::new(io::stdout()); - logger.msg(format!("Request {} {}",req.method.to_str(), req.path)); + //let mut logger = Logger::new(io::stdout()); + logger.lock().expect("this doesnt work :C").msg(format!("Request {} {}",req.method.to_str(), req.path)); let path = if req.path.ends_with("/") { let mut path = req.path.clone(); path.push_str(&config.index); @@ -39,7 +46,7 @@ fn main() { req.path.clone() }; if config.proxy_rules.contains_key(&req.path) { - logger.msg(format!("Proxying to: {}", config.proxy_rules.get(&req.path).unwrap())); + logger.lock().expect("this doesnt work :C").msg(format!("Proxying to: {}", config.proxy_rules.get(&req.path).unwrap())); let url = config.proxy_rules.get(&req.path).unwrap(); return match fetch(url) { Ok(response) => { @@ -53,19 +60,23 @@ fn main() { let path = format!("./{}/{}",config.root, path); let cache_result = { - let cache = cache.lock(); - if cache.is_err() { - None - }else { - let cache = cache.unwrap(); - let r = cache.get(&path); - match r { - Some(r) => Some(r.clone()), - None => None + if config.cache { + let cache = cache.lock(); + if cache.is_err() { + None + }else { + let cache = cache.unwrap(); + let r = cache.get(&path); + match r { + Some(r) => Some(r.clone()), + None => None + } } + } else { + None } }; - + let mut is_cache = false; let content: Result, _> = if cache_result.is_some() { let (content,ttl) = cache_result.unwrap(); let now = SystemTime::now(); @@ -74,6 +85,7 @@ fn main() { if secs > ttl { fs::read(&path) } else { + is_cache = true; Ok(content) } } else { @@ -82,13 +94,13 @@ fn main() { match content { Ok(content) => { - { + if config.cache { let cache = cache.lock(); - if cache.is_ok() { + if cache.is_ok() && is_cache { let mut cache = cache.unwrap(); let now = SystemTime::now(); let since_epoch = now.duration_since(time::UNIX_EPOCH).expect("Time went backwards"); - let secs = since_epoch.as_secs(); + let secs = since_epoch.as_secs() + config.cache_ttl; cache.insert(path,(content.clone(),secs)); }