1pub mod hop_by_hop;
9pub mod mitm;
10pub mod rate_limit;
11pub mod scope;
12pub mod tui;
13pub mod upstream;
14pub mod upstream_policy;
15
16#[allow(clippy::collapsible_if)]
22pub fn extract_host_from_header(s: &str) -> String {
23 let s = s.trim();
24 if s.is_empty() {
25 return String::new();
26 }
27 if s.starts_with('[') {
28 if let Some(end_idx) = s.find(']') {
29 if end_idx > 1 {
30 return s[1..end_idx].to_string();
31 }
32 }
33 return String::new();
35 }
36 if s.contains(':') {
38 if let Ok(std::net::IpAddr::V6(ip)) = s.parse() {
39 return ip.to_string();
40 }
41 }
42 s.split(':').next().unwrap_or(s).to_string()
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48
49 #[test]
50 fn extract_bare_ipv6() {
51 assert_eq!(extract_host_from_header("2001:db8::1"), "2001:db8::1");
52 }
53
54 #[test]
55 fn extract_bracketed_v6_with_port() {
56 assert_eq!(extract_host_from_header("[::1]:443"), "::1");
57 }
58
59 #[test]
60 fn extract_hostname_with_port() {
61 assert_eq!(extract_host_from_header("example.com:443"), "example.com");
62 }
63
64 #[test]
65 fn extract_bare_ipv4() {
66 assert_eq!(extract_host_from_header("192.168.1.1"), "192.168.1.1");
67 }
68
69 #[test]
70 fn extract_ipv4_with_port() {
71 assert_eq!(extract_host_from_header("192.168.1.1:8080"), "192.168.1.1");
72 }
73
74 #[test]
75 fn extract_bracketed_v6_no_port() {
76 assert_eq!(extract_host_from_header("[::1]"), "::1");
77 assert_eq!(extract_host_from_header("[2001:db8::1]"), "2001:db8::1");
78 }
79
80 #[test]
81 fn extract_malformed_bracket_returns_empty() {
82 assert_eq!(extract_host_from_header("[::1"), "");
84 assert_eq!(extract_host_from_header("["), "");
85 }
86
87 #[test]
88 fn extract_empty_returns_empty() {
89 assert_eq!(extract_host_from_header(""), "");
90 assert_eq!(extract_host_from_header(" "), "");
91 }
92}