diff --git a/Cargo.lock b/Cargo.lock index 888c00a..b4e4710 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -146,6 +146,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -235,6 +246,45 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "3.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "clap_lex", + "indexmap", + "once_cell", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_derive" +version = "3.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "coarsetime" version = "0.1.22" @@ -256,6 +306,15 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "configparser" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5458d9d1a587efaf5091602c59d299696a3877a439c8f6d461a2d3cce11df87a" +dependencies = [ + "indexmap", +] + [[package]] name = "const-oid" version = "0.7.1" @@ -279,12 +338,11 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -299,9 +357,9 @@ dependencies = [ [[package]] name = "crypto-bigint" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f2b443d17d49dad5ef0ede301c3179cc923b8822f3393b4d2c28c269dd4a122" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ "generic-array", "rand_core", @@ -397,7 +455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ "base16ct", - "crypto-bigint 0.4.8", + "crypto-bigint 0.4.9", "der 0.6.0", "digest", "ff", @@ -517,6 +575,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -568,6 +638,16 @@ dependencies = [ "digest", ] +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "instant" version = "0.1.12" @@ -585,9 +665,9 @@ checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" [[package]] name = "js-sys" -version = "0.3.59" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] @@ -656,9 +736,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.132" +version = "0.2.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" +checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" [[package]] name = "libm" @@ -668,9 +748,9 @@ checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565" [[package]] name = "lock_api" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f80bf5aacaf25cbfc8210d1cfb718f2bf3b11c4c54e5afe36c236853a8ec390" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg", "scopeguard", @@ -764,9 +844,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" + +[[package]] +name = "os_str_bytes" +version = "6.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] name = "p256" @@ -902,10 +988,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] -name = "proc-macro2" -version = "1.0.43" +name = "proc-macro-error" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" dependencies = [ "unicode-ident", ] @@ -981,7 +1091,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88c86280f057430a52f4861551b092a01b419b8eacefc7c995eacb9dc132fe32" dependencies = [ - "crypto-bigint 0.4.8", + "crypto-bigint 0.4.9", "hmac", "zeroize", ] @@ -1099,6 +1209,8 @@ version = "0.1.0" dependencies = [ "async-std", "async-trait", + "clap", + "configparser", "json", "jwt-simple", "lazy_static", @@ -1117,9 +1229,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" @@ -1157,6 +1269,12 @@ dependencies = [ "der 0.6.0", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "subtle" version = "2.4.1" @@ -1165,15 +1283,30 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.99" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" + [[package]] name = "thiserror" version = "1.0.37" @@ -1196,9 +1329,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.21.1" +version = "1.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0020c875007ad96677dcc890298f4b942882c5d4eb7cc8f439fc3bf813dc9c95" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" dependencies = [ "autocfg", "bytes", @@ -1206,7 +1339,6 @@ dependencies = [ "memchr", "mio", "num_cpus", - "once_cell", "parking_lot", "pin-project-lite", "signal-hook-registry", @@ -1234,9 +1366,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "unicode-ident" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "value-bag" @@ -1268,9 +1400,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1278,9 +1410,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", "log", @@ -1293,9 +1425,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.32" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" dependencies = [ "cfg-if", "js-sys", @@ -1305,9 +1437,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1315,9 +1447,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", @@ -1328,15 +1460,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "web-sys" -version = "0.3.59" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", @@ -1367,6 +1499,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index ef180d1..95e5ae6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,14 @@ jwt-simple = "0.11.1" # useful for tests (embedded files should be delete in release ?) #rust-embed="6.4.1" +[dependencies.configparser] +version = "3.0.2" +features = ["indexmap"] + +[dependencies.clap] +version = "3.2" +features = ["derive"] + [dependencies.async-std] version = "1.6" features = ["attributes"] diff --git a/src/http/mod.rs b/src/http/mod.rs index f9dbf7c..38d58b0 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -6,4 +6,4 @@ pub mod router; pub use request::HTTPRequest; pub use response::{HTTPResponse, HTTPStatusCode}; -pub use router::ROUTER; +pub use router::{Config, ROUTER}; diff --git a/src/http/request.rs b/src/http/request.rs index 113459f..d80471e 100644 --- a/src/http/request.rs +++ b/src/http/request.rs @@ -206,6 +206,7 @@ impl HTTPRequest { } } + #[allow(dead_code)] pub fn is_valid(&self) -> bool { return self.start_line.is_valid(); } diff --git a/src/http/response.rs b/src/http/response.rs index 030acc4..fcf7fc0 100644 --- a/src/http/response.rs +++ b/src/http/response.rs @@ -53,6 +53,7 @@ impl HTTPStatusLine { self.status_code = code; } + #[allow(dead_code)] pub fn get_status_code(&self) -> HTTPStatusCode { self.status_code.clone() } diff --git a/src/http/router.rs b/src/http/router.rs index 4eed572..c242fc7 100644 --- a/src/http/router.rs +++ b/src/http/router.rs @@ -1,25 +1,109 @@ //! router aims to handle correctly the request corresponding to the target //! it implements all the logic to build an `HTTPResponse` -use super::{HTTPRequest, HTTPResponse, HTTPStatusCode}; +use super::{HTTPRequest, HTTPResponse}; use crate::stores::FileStore; use crate::stores::Store; +use configparser::ini::Ini; use jwt_simple::prelude::*; use lazy_static::lazy_static; use std::collections::HashMap; use std::future::Future; use std::pin::Pin; - -const JWT_EXPIRATION_TIME: u64 = 2; -const ISSUER: &'static str = "thegux.fr"; +use std::str::FromStr; type FuturePinned = Pin>>; -type Handler = fn(HTTPRequest) -> FuturePinned; +type Handler = fn(HTTPRequest, Config) -> FuturePinned; -fn handle_get(request: HTTPRequest) -> FuturePinned { +#[derive(Clone)] +pub struct Config { + jwt_exp_time: u64, + jwt_issuer: String, + jwt_priv_key: String, + jwt_pub_key: String, + filestore_path: String, +} + +impl Default for Config { + fn default() -> Self { + Config { + jwt_exp_time: 0, + jwt_issuer: "".to_string(), + jwt_priv_key: "".to_string(), + jwt_pub_key: "".to_string(), + filestore_path: "".to_string(), + } + } +} + +impl TryFrom for Config { + type Error = String; + fn try_from(config: Ini) -> Result { + let exp_time = config + .get("jwt", "expiration_time") + .unwrap_or("".to_string()); + let jwt_exp_time = { + match u64::from_str(&exp_time) { + Ok(v) => v, + Err(e) => { + eprintln!("unable to convert JWT expiration time into u64 err={}", e); + 0 + } + } + }; + let config = Config { + jwt_exp_time, + jwt_issuer: config.get("jwt", "issuer").unwrap_or("".to_string()), + jwt_pub_key: config.get("jwt", "public_key").unwrap_or("".to_string()), + jwt_priv_key: config.get("jwt", "private_key").unwrap_or("".to_string()), + filestore_path: config.get("store", "path").unwrap_or("".to_string()), + }; + + if !config.validate() { + return Err("ini file configuration validation failed".to_string()); + } + + Ok(config) + } +} + +impl Config { + /// validates config ini file + fn validate(&self) -> bool { + if self.jwt_exp_time <= 0 { + eprintln!("invalid config parameter: JWT expiration time is negative or equals to 0"); + return false; + } + + if self.jwt_issuer == "" { + eprintln!("invalid config parameter: JWT issuer is empty"); + return false; + } + + // TODO: check if the file exists and rights are ok + if self.jwt_pub_key == "" { + eprintln!("invalid config parameter: JWT public key file path is empty"); + return false; + } + + // TODO: check if the file exists and rights are ok + if self.jwt_priv_key == "" { + eprintln!("invalid config parameter: JWT private key file path is empty"); + return false; + } + + if self.filestore_path == "" { + eprintln!("invalid config parameter: filestore path is empty"); + return false; + } + + true + } +} + +fn handle_get(request: HTTPRequest, config: Config) -> FuturePinned { Box::pin(async move { - // TODO: path to `store.txt` must not be hardcoded, should be in a config file and load at runtime - let mut store = FileStore::new("tests/data/store.txt".to_string()); + let mut store = FileStore::new(config.filestore_path); match &request.body { Some(ref b) => { let is_auth = store.is_auth(&b.get_data()).await; @@ -27,13 +111,35 @@ fn handle_get(request: HTTPRequest) -> FuturePinned { return HTTPResponse::as_403(); } - let mut claims = Claims::create(Duration::from_hours(JWT_EXPIRATION_TIME)); - claims.issuer = Some(ISSUER.to_string()); + let priv_key_content = { + match std::fs::read_to_string(config.jwt_priv_key) { + Ok(c) => c, + Err(e) => { + eprintln!("error while reading JWT priv key content err={}", e); + "".to_string() + } + } + }; + let jwt_key = { + match RS384KeyPair::from_pem(priv_key_content.as_str()) { + Ok(k) => k, + // TODO: set error in the message body + Err(e) => { + eprintln!("error occurred while getting private key err={}", e); + return HTTPResponse::as_500(); + } + } + }; + let mut claims = Claims::create(Duration::from_hours(config.jwt_exp_time)); + claims.issuer = Some(config.jwt_issuer); - match JWT_KEY.authenticate(claims) { + match jwt_key.sign(claims) { Ok(token) => HTTPResponse::as_200(token), // TODO: set the error in the message body - Err(_e) => HTTPResponse::as_500(), + Err(e) => { + eprintln!("error occurred while signing the token err={}", e); + return HTTPResponse::as_500(); + } } } None => HTTPResponse::as_400(), @@ -43,7 +149,7 @@ fn handle_get(request: HTTPRequest) -> FuturePinned { /// validates the token by checking: /// * expiration time -fn handle_validate(request: HTTPRequest) -> FuturePinned { +fn handle_validate(request: HTTPRequest, _config: Config) -> FuturePinned { Box::pin(async move { match &request.body { Some(ref _b) => { @@ -66,19 +172,17 @@ lazy_static! { ("/validate/", handle_validate as Handler) ] ); - - static ref JWT_KEY: jwt_simple::algorithms::HS256Key = HS256Key::generate(); } pub struct Router; impl Router { - pub async fn route(&self, request_str: &str) -> HTTPResponse { + pub async fn route(&self, request_str: &str, config: Config) -> HTTPResponse { let request = HTTPRequest::from(request_str); let target = request.start_line.get_target(); match HTTP_METHODS.get(target.as_str()) { - Some(f) => f(request).await, + Some(f) => f(request, config).await, None => HTTPResponse::as_404(), } } @@ -89,12 +193,59 @@ pub const ROUTER: Router = Router {}; #[tokio::test] async fn test_route() { + use super::HTTPStatusCode; + let router: &Router = &ROUTER; + let config: Config = Config::default(); let request_str = "POST /get/ HTTP/1.1\r\n\r\n"; - let response: HTTPResponse = router.route(request_str).await; + let response: HTTPResponse = router.route(request_str, config).await; assert_eq!( HTTPStatusCode::Http400, response.status_line.get_status_code() ); } + +#[test] +fn test_config() { + use std::env; + + let root_path = env::var("CARGO_MANIFEST_DIR").unwrap(); + + // TODO: path::Path should be better + let config_path = format!("{}/{}/{}/{}", root_path, "tests", "data", "config.ini"); + let mut config = Ini::new(); + let _r = config.load(config_path); + + let router_config = Config::try_from(config); + assert!(router_config.is_ok()); +} + +#[test] +fn test_bad_config() { + use std::env; + + let root_path = env::var("CARGO_MANIFEST_DIR").unwrap(); + + // TODO: path::Path should be better + let config_path = format!("{}/{}/{}/{}", root_path, "tests", "data", "bad_config.ini"); + let mut config = Ini::new(); + let _r = config.load(config_path); + + let router_config = Config::try_from(config); + assert!(router_config.is_err()); +} + +#[test] +fn test_bad_config_path() { + use std::env; + + let root_path = env::var("CARGO_MANIFEST_DIR").unwrap(); + + // TODO: path::Path should be better + let config_path = format!("{}/{}/{}/{}", root_path, "tests", "data", "con.ini"); + let mut config = Ini::new(); + + let result = config.load(config_path); + assert!(result.is_err()); +} diff --git a/src/main.rs b/src/main.rs index 5b58bda..cb91c62 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,33 +1,68 @@ mod http; mod stores; +use clap::Parser; +use configparser::ini::Ini; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, net::{TcpListener, TcpStream}, }; -use http::ROUTER; +use http::{Config, ROUTER}; -const SERVER_URL: &str = "127.0.0.1:9000"; +#[derive(Parser)] +#[clap(author, version, about, long_about = None)] +struct Cli { + /// config filepath (.ini) + config: String, +} #[tokio::main] async fn main() { - let listener = TcpListener::bind(SERVER_URL).await.unwrap(); - println!("server is listening on '{}'", SERVER_URL); + let args = Cli::parse(); + + let mut config = Ini::new(); + match config.load(args.config) { + Ok(c) => c, + Err(e) => { + eprintln!("error while loading the config file, err={}", e); + std::process::exit(1); + } + }; + + let server_url = config.get("server", "url").unwrap_or("".to_string()); + let listener = { + match TcpListener::bind(&server_url).await { + Ok(t) => { + println!("server is listening on '{}'", server_url); + t + } + Err(e) => { + eprintln!("error occurred while initializing tcp listener err={}", e); + std::process::exit(1); + } + } + }; + + let router_config: Config = if let Ok(c) = Config::try_from(config) { + c + } else { + std::process::exit(1); + }; loop { let (stream, _) = listener.accept().await.unwrap(); - handle_connection(stream).await; + handle_connection(stream, router_config.clone()).await; } } /// parses the incoming request (partial spec implementation) and build an HTTP response -async fn handle_connection(mut stream: TcpStream) { +async fn handle_connection(mut stream: TcpStream, config: Config) { let mut buffer: [u8; 1024] = [0; 1024]; let n = stream.read(&mut buffer).await.unwrap(); let request_string = std::str::from_utf8(&buffer[0..n]).unwrap(); - let response = ROUTER.route(request_string).await; + let response = ROUTER.route(request_string, config).await; let response_str: String = response.into(); stream.write(response_str.as_bytes()).await.unwrap();