use dhttp::name::DhttpName as Name;
use http::{Method, Uri};
use hyper::body::Incoming;
#[derive(Debug)]
pub enum Route {
GenmetaPlainHttp {
authority: http::uri::Authority,
uri: Uri,
},
GenmetaConnect { authority: http::uri::Authority },
TunnelConnect { authority: http::uri::Authority },
StandardForward { uri: Uri },
}
pub struct Router {
_blacklist: Vec<String>,
}
impl Default for Router {
fn default() -> Self {
Self::new()
}
}
impl Router {
pub fn new() -> Self {
Self {
_blacklist: Vec::new(),
}
}
pub fn is_genmeta(&self, host: &str) -> bool {
let host = host.split(':').next().unwrap_or(host);
host.ends_with('~')
|| (host.len() >= Name::SUFFIX.len()
&& host[host.len() - Name::SUFFIX.len()..].eq_ignore_ascii_case(Name::SUFFIX))
}
pub fn classify(&self, req: &hyper::Request<Incoming>) -> Route {
let method = req.method();
let uri = req.uri();
if method == Method::CONNECT {
if let Some(authority) = uri.authority() {
if self.is_genmeta(authority.host()) {
return Route::GenmetaConnect {
authority: authority.clone(),
};
} else {
return Route::TunnelConnect {
authority: authority.clone(),
};
}
}
}
if let Some(authority) = uri.authority()
&& self.is_genmeta(authority.host())
{
return Route::GenmetaPlainHttp {
authority: authority.clone(),
uri: uri.clone(),
};
}
Route::StandardForward { uri: uri.clone() }
}
}
#[cfg(test)]
mod tests {
use super::*;
fn router() -> Router {
Router::new()
}
#[test]
fn test_is_genmeta_exact_suffix() {
let r = router();
assert!(r.is_genmeta("api.dhttp.net"));
assert!(r.is_genmeta("API.Dhttp.Net"));
assert!(r.is_genmeta("test.dhttp.net"));
assert!(r.is_genmeta("a.b.dhttp.net"));
}
#[test]
fn test_is_genmeta_non_match() {
let r = router();
assert!(!r.is_genmeta("example.com"));
assert!(!r.is_genmeta("dhttp.net.evil.com"));
assert!(!r.is_genmeta("notdhttp.net"));
}
#[test]
fn test_is_genmeta_with_port() {
let r = router();
assert!(r.is_genmeta("api.dhttp.net:443"));
}
#[test]
fn test_is_genmeta_bare_domain() {
let r = router();
assert!(!r.is_genmeta("dhttp.net"));
}
}