web_url/parse/pre_path/
host.rs1use std::str::FromStr;
2
3use address::{Domain, IPAddress, IPv4Address, IPv6Address};
4
5use crate::parse::Error;
6use crate::parse::Error::InvalidHost;
7
8pub fn parse_host(s: &str) -> (&str, &str) {
14 let host_and_port: &str = if let Some(slash) = s.as_bytes().iter().position(|c| *c == b'/') {
15 &s[..slash]
16 } else {
17 s
18 };
19 if host_and_port.is_empty() {
20 ("", s)
21 } else {
22 let bracketed: bool = host_and_port.as_bytes()[0] == b'['
23 && host_and_port.as_bytes()[host_and_port.len() - 1] == b']';
24 if bracketed {
25 (host_and_port, &s[host_and_port.len()..])
26 } else if let Some(last_colon) = host_and_port.as_bytes().iter().rposition(|c| *c == b':') {
27 s.split_at(last_colon)
28 } else {
29 (host_and_port, &s[host_and_port.len()..])
30 }
31 }
32}
33
34pub fn parse_ip_and_validate_domain(host: &str) -> Result<Option<IPAddress>, Error> {
41 if host.is_empty() {
42 Err(InvalidHost)
43 } else if host.as_bytes()[0] == b'[' {
44 if host.as_bytes()[host.len() - 1] != b']' {
45 Err(InvalidHost)
46 } else {
47 let host: &str = &host[1..(host.len() - 1)];
48 let ip: IPv6Address = IPv6Address::from_str(host).map_err(|_| InvalidHost)?;
49 Ok(Some(ip.to_ip()))
50 }
51 } else if let Ok(ip) = IPv4Address::from_str(host) {
52 Ok(Some(ip.to_ip()))
53 } else if Domain::is_valid_name_str(host, true) {
54 Ok(None)
55 } else {
56 Err(InvalidHost)
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use address::{IPAddress, IPv4Address, IPv6Address};
63
64 use crate::parse::pre_path::{parse_host, parse_ip_and_validate_domain};
65 use crate::parse::Error;
66 use crate::parse::Error::InvalidHost;
67
68 #[test]
69 fn fn_extract_host() {
70 let test_cases: &[(&str, (&str, &str))] = &[
71 ("", ("", "")),
72 ("host", ("host", "")),
73 ("host/", ("host", "/")),
74 ("host/rest", ("host", "/rest")),
75 ("host:port/rest", ("host", ":port/rest")),
76 ("[host:port/rest", ("[host", ":port/rest")),
77 ("[host:port]/rest", ("[host:port]", "/rest")),
78 ("[host:port]", ("[host:port]", "")),
79 ("[host:port]80", ("[host", ":port]80")),
80 ("host:", ("host", ":")),
81 ];
82 for (s, expected) in test_cases {
83 let result: (&str, &str) = parse_host(s);
84 assert_eq!(result, *expected, "s={}", s);
85 }
86 }
87
88 #[test]
89 fn fn_parse_ip() {
90 let test_cases: &[(&str, Result<Option<IPAddress>, Error>)] = &[
91 ("", Err(InvalidHost)),
92 ("[::1", Err(InvalidHost)),
93 ("[127.0.0.1]", Err(InvalidHost)),
94 ("[::1]", Ok(Some(IPv6Address::LOCALHOST.to_ip()))),
95 ("!", Err(InvalidHost)),
96 ("127.0.0.1", Ok(Some(IPv4Address::LOCALHOST.to_ip()))),
97 ("localhost", Ok(None)),
98 ("LocalHost", Ok(None)),
99 ("Local!Host", Err(InvalidHost)),
100 ];
101 for (host, expected) in test_cases {
102 let result: Result<Option<IPAddress>, Error> = parse_ip_and_validate_domain(host);
103 assert_eq!(result, *expected, "host={}", *host);
104 }
105 }
106}