0.5.2: beautify everything

- Use serde_json::from_reader in alias list initialization
- Compile all regexes in initialization, not while recieving first
  triggering request
- Count regex compilation time
- Specify version in Server responce header

Signed-off-by: Ivan Bushchik <ivabus@ivabus.dev>
This commit is contained in:
Ivan Bushchik 2023-12-22 13:46:07 +03:00
parent 0815b295b2
commit 886c4cf2fa
No known key found for this signature in database
GPG key ID: 2F16FBF3262E090C
4 changed files with 43 additions and 31 deletions

View file

@ -1,10 +1,10 @@
[package] [package]
name = "urouter" name = "urouter"
version = "0.5.1" version = "0.5.2"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/ivabus/urouter" repository = "https://github.com/ivabus/urouter"
description = "Small router for (kinda) short domains (fork of ivabus/aliurl without REST API)" description = "Small HTTP router"
[dependencies] [dependencies]

View file

@ -20,8 +20,8 @@ Each set contains 2 necessary elements and 1 optional.
- `uri` (string) - of url after host (e.g., `/`, `some/cool/path`, should not start with `/` (only for root)) - `uri` (string) - of url after host (e.g., `/`, `some/cool/path`, should not start with `/` (only for root))
- `alias` (set) - set of one field - `alias` (set) - set of one field
- `url` (string) - redirect to url with HTTP 303 See Other - `url` (string) - redirect to url with HTTP 303 See Other
- `file` (string) - read file from path `--dir/file` where `--dir` is option (default: `.`, see `--help`) and respond with HTTP 200 OK `content-type: text/plain; charset=utf-8` - `file` (string) - read file from path `--dir/file` where `--dir` is option (default: `.`, see `--help`) and respond with HTTP 200 OK with `content-type: text/plain`
- `text` (string) - plain text - `text` (string) - plain text HTTP 200 OK with `content-type: text/plain`
- Optional - Optional
- `agent` (set) - set of one necessary field and one optional - `agent` (set) - set of one necessary field and one optional
- `regex` (string) - regular expression to match user-agent HTTP header - `regex` (string) - regular expression to match user-agent HTTP header

View file

@ -31,7 +31,9 @@ extern crate rocket;
use rocket::http::Status; use rocket::http::Status;
use std::cell::{OnceCell, RefCell}; use std::cell::{OnceCell, RefCell};
use std::collections::HashMap; use std::collections::HashMap;
use std::hint::unreachable_unchecked;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::Instant;
use rocket::response::content::RawText; use rocket::response::content::RawText;
use rocket::response::Redirect; use rocket::response::Redirect;
@ -46,14 +48,14 @@ static mut COMPILED_REGEXES: RefCell<Option<HashMap<String, Regex>>> = RefCell::
fn get_return(alias: &Alias) -> Response { fn get_return(alias: &Alias) -> Response {
let args = Args::parse(); let args = Args::parse();
let mut dir = args.dir.clone(); let mut dir = args.dir.clone();
return match &alias.alias { match &alias.alias {
AliasType::Url(url) => Response::Redirect(Redirect::to(url.clone())), AliasType::Url(url) => Response::Redirect(Box::from(Redirect::to(url.clone()))),
AliasType::File(path) => { AliasType::File(path) => {
dir.push(&PathBuf::from(&path)); dir.push(&PathBuf::from(&path));
Response::Text(RawText(smurf::io::read_file_str(&dir).unwrap())) Response::Text(RawText(smurf::io::read_file_str(&dir).unwrap()))
} }
AliasType::Text(text) => Response::Text(RawText(text.clone())), AliasType::Text(text) => Response::Text(RawText(text.clone())),
}; }
} }
#[get("/<page>")] #[get("/<page>")]
@ -64,37 +66,30 @@ fn get_page(page: &str, user_agent: UserAgent) -> Response {
let mut pages = Vec::new(); let mut pages = Vec::new();
for i in alias { for i in alias {
if i.uri == decoded_page { if i.uri == decoded_page {
match &i.agent { if let Some(agent) = &i.agent {
Some(agent) => unsafe { unsafe {
let re = if let Some(regexes) = COMPILED_REGEXES.get_mut() { let regexes = COMPILED_REGEXES.get_mut();
match regexes.get(&agent.regex) { let re = if let Some(r) = regexes {
Some(re) => re.clone(), // Unwrapping safely, guaranteed to be generated during initialization
None => { r.get(&agent.regex).unwrap()
let re = Regex::new(&agent.regex).unwrap();
regexes.insert(agent.regex.clone(), re.clone());
re.clone()
}
}
} else { } else {
// guaranteed to be initialized at the beginning unreachable_unchecked()
unreachable!()
}; };
if re.is_match(&user_agent.0) { if re.is_match(&user_agent.0) {
return get_return(&i); return get_return(i);
} }
if let Some(true) = agent.only_matching { if let Some(true) = agent.only_matching {
continue; continue;
} }
}, }
_ => {}
} }
pages.push(i); pages.push(i);
} }
} }
// Returning normal page (if found) to curl users. // Returning normal page (if found) to curl users.
if pages.len() != 0 { if !pages.is_empty() {
return get_return(pages[0]); return get_return(pages[0]);
} }
Response::Status(Status::NotFound) Response::Status(Status::NotFound)
@ -108,20 +103,37 @@ async fn index(user_agent: UserAgent) -> Response {
#[rocket::main] #[rocket::main]
async fn main() -> Result<(), rocket::Error> { async fn main() -> Result<(), rocket::Error> {
let args = Args::parse(); let args = Args::parse();
let file = std::fs::File::open(args.alias_file).unwrap();
let alias: Vec<Alias> = if args.alias_file_is_set_not_a_list { let alias: Vec<Alias> = if args.alias_file_is_set_not_a_list {
let set: NixJson = serde_json::from_reader::<std::fs::File, NixJson>(file).unwrap().alias
serde_json::from_str(&smurf::io::read_file_str(&args.alias_file).unwrap()).unwrap();
set.alias
} else { } else {
serde_json::from_str(&smurf::io::read_file_str(&args.alias_file).unwrap()).unwrap() serde_json::from_reader::<std::fs::File, Vec<Alias>>(file).unwrap()
}; };
unsafe { unsafe {
ALIAS.set(alias).unwrap(); ALIAS.set(alias).unwrap();
*COMPILED_REGEXES.get_mut() = Some(HashMap::new());
let compilation_start = Instant::now();
let mut regexes_len = 0;
// Precompile all regexes
let mut compiled_regexes: HashMap<String, Regex> = HashMap::new();
for i in ALIAS.get().unwrap() {
if let Some(agent) = &i.agent {
compiled_regexes.insert(agent.regex.clone(), Regex::new(&agent.regex).unwrap());
regexes_len += 1;
}
}
if regexes_len != 0 {
println!(
"Compiled {} regexes in {} ms",
regexes_len,
(Instant::now() - compilation_start).as_secs_f64() * 1000.0
);
}
*COMPILED_REGEXES.get_mut() = Some(compiled_regexes);
} }
let figment = Figment::from(rocket::Config::default()) let figment = Figment::from(rocket::Config::default())
.merge(("ident", "urouter")) .merge(("ident", format!("urouter/{}", env!("CARGO_PKG_VERSION"))))
.merge(("port", args.port)) .merge(("port", args.port))
.merge(("address", args.address)); .merge(("address", args.address));

View file

@ -61,7 +61,7 @@ pub struct Agent {
#[derive(Responder)] #[derive(Responder)]
pub enum Response { pub enum Response {
Text(RawText<String>), Text(RawText<String>),
Redirect(Redirect), Redirect(Box<Redirect>),
Status(Status), Status(Status),
} }