1use dhttp::name::DhttpName as Name;
2use http::{Method, Uri};
3use hyper::body::Incoming;
4
5#[derive(Debug)]
7pub enum Route {
8 GenmetaPlainHttp {
10 authority: http::uri::Authority,
11 uri: Uri,
12 },
13 GenmetaConnect { authority: http::uri::Authority },
15 TunnelConnect { authority: http::uri::Authority },
17 StandardForward { uri: Uri },
19}
20
21pub struct Router {
23 _blacklist: Vec<String>,
25}
26
27impl Default for Router {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl Router {
34 pub fn new() -> Self {
35 Self {
36 _blacklist: Vec::new(),
37 }
38 }
39
40 pub fn is_genmeta(&self, host: &str) -> bool {
42 let host = host.split(':').next().unwrap_or(host);
44 host.ends_with('~')
45 || (host.len() >= Name::SUFFIX.len()
46 && host[host.len() - Name::SUFFIX.len()..].eq_ignore_ascii_case(Name::SUFFIX))
47 }
48
49 pub fn classify(&self, req: &hyper::Request<Incoming>) -> Route {
51 let method = req.method();
52 let uri = req.uri();
53
54 if method == Method::CONNECT {
55 if let Some(authority) = uri.authority() {
57 if self.is_genmeta(authority.host()) {
58 return Route::GenmetaConnect {
59 authority: authority.clone(),
60 };
61 } else {
62 return Route::TunnelConnect {
63 authority: authority.clone(),
64 };
65 }
66 }
67 }
68
69 if let Some(authority) = uri.authority()
71 && self.is_genmeta(authority.host())
72 {
73 return Route::GenmetaPlainHttp {
74 authority: authority.clone(),
75 uri: uri.clone(),
76 };
77 }
78
79 Route::StandardForward { uri: uri.clone() }
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 fn router() -> Router {
88 Router::new()
89 }
90
91 #[test]
92 fn test_is_genmeta_exact_suffix() {
93 let r = router();
94 assert!(r.is_genmeta("api.dhttp.net"));
95 assert!(r.is_genmeta("API.Dhttp.Net"));
96 assert!(r.is_genmeta("test.dhttp.net"));
97 assert!(r.is_genmeta("a.b.dhttp.net"));
98 }
99
100 #[test]
101 fn test_is_genmeta_non_match() {
102 let r = router();
103 assert!(!r.is_genmeta("example.com"));
104 assert!(!r.is_genmeta("dhttp.net.evil.com"));
105 assert!(!r.is_genmeta("notdhttp.net"));
106 }
107
108 #[test]
109 fn test_is_genmeta_with_port() {
110 let r = router();
111 assert!(r.is_genmeta("api.dhttp.net:443"));
113 }
114
115 #[test]
116 fn test_is_genmeta_bare_domain() {
117 let r = router();
121 assert!(!r.is_genmeta("dhttp.net"));
122 }
123}