mirror of
https://github.com/ivabus/urouter
synced 2024-11-22 00:15:11 +03:00
0.7.0: Ununsafe urouter
Signed-off-by: Ivan Bushchik <ivabus@ivabus.dev>
This commit is contained in:
parent
653b41a67f
commit
0451d494c5
2 changed files with 62 additions and 67 deletions
14
Cargo.toml
14
Cargo.toml
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "urouter"
|
name = "urouter"
|
||||||
version = "0.6.1"
|
version = "0.7.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/ivabus/urouter"
|
repository = "https://github.com/ivabus/urouter"
|
||||||
|
@ -9,11 +9,11 @@ description = "Small HTTP router"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rocket = "0.5.0"
|
rocket = "0.5.0"
|
||||||
serde = { version = "1.0.193", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
serde_json = "1.0.108"
|
serde_json = "1.0.114"
|
||||||
url-escape = "0.1.1"
|
url-escape = "0.1.1"
|
||||||
smurf = "0.3.0"
|
once_cell = "1.19"
|
||||||
clap = { version = "4.4.11", features = ["derive"] }
|
clap = { version = "4.5.3", features = ["derive"] }
|
||||||
regex = "1.10.2"
|
regex = "1.10.3"
|
||||||
ureq = { version = "2.9.1", features = ["brotli", "native-certs"] }
|
ureq = { version = "2.9.6", features = ["brotli", "native-certs"] }
|
||||||
users = "0.11.0"
|
users = "0.11.0"
|
113
src/main.rs
113
src/main.rs
|
@ -6,29 +6,62 @@ Global comments:
|
||||||
500 Internal Server Error
|
500 Internal Server Error
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
mod structs;
|
mod structs;
|
||||||
use structs::*;
|
use structs::*;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
use rocket::http::{ContentType, Status};
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::{
|
use std::sync::Arc;
|
||||||
cell::OnceCell, collections::HashMap, hint::unreachable_unchecked, path::PathBuf, time::Instant,
|
use std::{collections::HashMap, path::PathBuf, time::Instant};
|
||||||
};
|
|
||||||
|
|
||||||
|
use rocket::http::{ContentType, Status};
|
||||||
use rocket::{
|
use rocket::{
|
||||||
figment::Figment,
|
figment::Figment,
|
||||||
response::{content::RawText, Redirect},
|
response::{content::RawHtml, content::RawText, Redirect},
|
||||||
};
|
};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rocket::response::content::RawHtml;
|
|
||||||
|
|
||||||
static mut ALIAS: OnceCell<Vec<Alias>> = OnceCell::new();
|
static ALIAS: Lazy<Arc<Vec<Alias>>> = Lazy::new(|| {
|
||||||
static mut COMPILED_REGEXES: OnceCell<HashMap<String, Regex>> = OnceCell::new();
|
let mut args = Args::parse();
|
||||||
|
if args.alias_file.is_none() {
|
||||||
|
args.alias_file = Some(get_config_file_location());
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = std::fs::File::open(args.alias_file.unwrap()).unwrap();
|
||||||
|
let alias: Vec<Alias> = if args.alias_file_is_set_not_a_list {
|
||||||
|
serde_json::from_reader::<std::fs::File, NixJson>(file).unwrap().alias
|
||||||
|
} else {
|
||||||
|
serde_json::from_reader::<std::fs::File, Vec<Alias>>(file).unwrap()
|
||||||
|
};
|
||||||
|
Arc::new(alias)
|
||||||
|
});
|
||||||
|
static COMPILED_REGEXES: Lazy<Arc<HashMap<String, Regex>>> = Lazy::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 {
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Arc::new(compiled_regexes)
|
||||||
|
});
|
||||||
|
|
||||||
fn get_return(alias: &Alias) -> Response {
|
fn get_return(alias: &Alias) -> Response {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
@ -37,7 +70,7 @@ fn get_return(alias: &Alias) -> Response {
|
||||||
AliasType::Url(url) => Response::Redirect(Box::from(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(Box::new(RawText(smurf::io::read_file_str(&dir).unwrap())))
|
Response::Text(Box::new(RawText(std::fs::read_to_string(&dir).unwrap())))
|
||||||
}
|
}
|
||||||
AliasType::Text(text) => Response::Text(Box::new(RawText(text.clone()))),
|
AliasType::Text(text) => Response::Text(Box::new(RawText(text.clone()))),
|
||||||
AliasType::Html(html) => Response::Html(Box::new(RawHtml(html.clone()))),
|
AliasType::Html(html) => Response::Html(Box::new(RawHtml(html.clone()))),
|
||||||
|
@ -60,29 +93,22 @@ fn get_return(alias: &Alias) -> Response {
|
||||||
fn get_page(page: &str, user_agent: UserAgent) -> Response {
|
fn get_page(page: &str, user_agent: UserAgent) -> Response {
|
||||||
let mut decoded_page = String::new();
|
let mut decoded_page = String::new();
|
||||||
url_escape::decode_to_string(page, &mut decoded_page);
|
url_escape::decode_to_string(page, &mut decoded_page);
|
||||||
let alias = unsafe { ALIAS.get().unwrap() };
|
|
||||||
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 {
|
||||||
if let Some(agent) = &i.agent {
|
if let Some(agent) = &i.agent {
|
||||||
unsafe {
|
let regexes = &COMPILED_REGEXES;
|
||||||
let regexes = COMPILED_REGEXES.get_mut();
|
let re = regexes.get(&agent.regex).unwrap();
|
||||||
let re = if let Some(r) = regexes {
|
|
||||||
// Unwrapping safely, guaranteed to be generated during initialization
|
|
||||||
r.get(&agent.regex).unwrap()
|
|
||||||
} else {
|
|
||||||
unreachable_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,40 +146,9 @@ fn get_config_file_location() -> PathBuf {
|
||||||
|
|
||||||
#[rocket::main]
|
#[rocket::main]
|
||||||
async fn main() -> Result<(), rocket::Error> {
|
async fn main() -> Result<(), rocket::Error> {
|
||||||
let mut args = Args::parse();
|
let args = Args::parse();
|
||||||
|
let _alias = &**ALIAS;
|
||||||
if args.alias_file.is_none() {
|
let _regex = &**COMPILED_REGEXES;
|
||||||
args.alias_file = Some(get_config_file_location());
|
|
||||||
}
|
|
||||||
|
|
||||||
let file = std::fs::File::open(args.alias_file.unwrap()).unwrap();
|
|
||||||
let alias: Vec<Alias> = if args.alias_file_is_set_not_a_list {
|
|
||||||
serde_json::from_reader::<std::fs::File, NixJson>(file).unwrap().alias
|
|
||||||
} else {
|
|
||||||
serde_json::from_reader::<std::fs::File, Vec<Alias>>(file).unwrap()
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
ALIAS.set(alias).unwrap();
|
|
||||||
|
|
||||||
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.set(compiled_regexes).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let figment = Figment::from(rocket::Config::default())
|
let figment = Figment::from(rocket::Config::default())
|
||||||
.merge(("ident", format!("urouter/{}", env!("CARGO_PKG_VERSION"))))
|
.merge(("ident", format!("urouter/{}", env!("CARGO_PKG_VERSION"))))
|
||||||
|
|
Loading…
Reference in a new issue