1use core::net::{IpAddr, SocketAddr};
2
3use crate::address::{Authority, Host, HostWithOptPort, HostWithPort, ProxyAddress, SocketAddress};
4
5pub trait IntoCanonicalIpAddr {
24 #[must_use]
25 fn into_canonical_ip_addr(self) -> Self;
26}
27
28impl IntoCanonicalIpAddr for IpAddr {
29 #[inline(always)]
30 fn into_canonical_ip_addr(self) -> Self {
31 self.to_canonical()
32 }
33}
34
35impl IntoCanonicalIpAddr for SocketAddress {
36 #[inline(always)]
37 fn into_canonical_ip_addr(mut self) -> Self {
38 self.ip_addr = self.ip_addr.into_canonical_ip_addr();
39 self
40 }
41}
42
43impl IntoCanonicalIpAddr for SocketAddr {
44 #[inline(always)]
45 fn into_canonical_ip_addr(self) -> Self {
46 let ip_addr = self.ip().into_canonical_ip_addr();
47 Self::new(ip_addr, self.port())
48 }
49}
50
51impl IntoCanonicalIpAddr for Host {
52 fn into_canonical_ip_addr(self) -> Self {
53 match self.try_as_ip() {
59 Ok(ip) => Self::Address(ip.into_canonical_ip_addr()),
60 Err(_) => self,
61 }
62 }
63}
64
65impl IntoCanonicalIpAddr for HostWithPort {
66 #[inline(always)]
67 fn into_canonical_ip_addr(self) -> Self {
68 Self {
69 host: self.host.into_canonical_ip_addr(),
70 port: self.port,
71 }
72 }
73}
74
75impl IntoCanonicalIpAddr for HostWithOptPort {
76 #[inline(always)]
77 fn into_canonical_ip_addr(self) -> Self {
78 Self {
79 host: self.host.into_canonical_ip_addr(),
80 port: self.port,
81 }
82 }
83}
84
85impl IntoCanonicalIpAddr for ProxyAddress {
86 #[inline(always)]
87 fn into_canonical_ip_addr(self) -> Self {
88 Self {
89 protocol: self.protocol,
90 address: self.address.into_canonical_ip_addr(),
91 credential: self.credential,
92 }
93 }
94}
95
96impl IntoCanonicalIpAddr for Authority {
97 fn into_canonical_ip_addr(self) -> Self {
98 Self {
99 user_info: self.user_info,
100 address: self.address.into_canonical_ip_addr(),
101 }
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn ipv4_loopback_is_unchanged() {
111 let socket_addr = SocketAddress::local_ipv4(8080);
112 assert_eq!(socket_addr.into_canonical_ip_addr(), socket_addr);
113 }
114
115 #[test]
116 fn ipv6_loopback_is_unchanged() {
117 let socket_addr = SocketAddress::local_ipv6(8080);
118 assert_eq!(socket_addr.into_canonical_ip_addr(), socket_addr);
119 }
120
121 #[test]
122 fn ipv4_is_unchanged() {
123 let socket_addr = SocketAddress::from(([192, 168, 1, 1], 8080));
124 assert_eq!(socket_addr.into_canonical_ip_addr(), socket_addr);
125 }
126
127 #[test]
128 fn ipv6_is_unchanged() {
129 let socket_addr = SocketAddress::from(([0x2001, 0x0db8, 0, 0, 0, 0, 0xdead, 0xbeef], 8080));
130 assert_eq!(socket_addr.into_canonical_ip_addr(), socket_addr);
131 }
132
133 #[test]
134 fn ipv4_mapped_ipv6_is_converted_to_ipv4() {
135 let socket_addr = SocketAddress::from(([0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x02ff], 8080));
137 assert_eq!(
138 socket_addr.into_canonical_ip_addr(),
139 SocketAddress::from(([192, 10, 2, 255], 8080))
140 );
141 }
142
143 #[test]
144 fn ipv4_mapped_loopback_ipv6_is_converted_to_ipv4() {
145 let socket_addr = SocketAddress::from(([0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x0001], 8080));
147 assert_eq!(
148 socket_addr.into_canonical_ip_addr(),
149 SocketAddress::from(([127, 0, 0, 1], 8080))
150 );
151 }
152
153 #[test]
154 fn ipv4_compatible_ipv6_is_not_converted_to_ipv4() {
155 let socket_addr = SocketAddress::from(([0, 0, 0, 0, 0, 0, 0xc000, 0x0221], 8080));
157 assert_eq!(socket_addr.into_canonical_ip_addr(), socket_addr);
158 }
159
160 #[test]
161 fn ipv4_mapped_zero_zero_zero_one_is_converted_to_ipv4() {
162 let socket_addr = SocketAddress::from(([0, 0, 0, 0, 0, 0xffff, 0x0000, 0x0001], 8080));
164 assert_eq!(
165 socket_addr.into_canonical_ip_addr(),
166 SocketAddress::from(([0, 0, 0, 1], 8080))
167 );
168 }
169
170 #[test]
178 fn host_uninterpreted_pct_encoded_v4_mapped_v6_canonicalizes_to_v4() {
179 let host = crate::uri::Uri::parse("http://%3A%3Affff%3A192.0.2.1/")
182 .unwrap()
183 .host()
184 .unwrap()
185 .into_owned();
186 assert!(matches!(host, Host::Uninterpreted(_)), "fixture sanity");
187 let canonical = host.into_canonical_ip_addr();
188 assert_eq!(
189 canonical,
190 Host::Address("192.0.2.1".parse::<IpAddr>().unwrap())
191 );
192 }
193
194 #[test]
196 fn host_uninterpreted_pct_encoded_v4_canonicalizes() {
197 let host = crate::uri::Uri::parse("http://%31%32%37.0.0.1/")
198 .unwrap()
199 .host()
200 .unwrap()
201 .into_owned();
202 assert!(matches!(host, Host::Uninterpreted(_)));
203 let canonical = host.into_canonical_ip_addr();
204 assert_eq!(
205 canonical,
206 Host::Address("127.0.0.1".parse::<IpAddr>().unwrap())
207 );
208 }
209
210 #[test]
212 fn host_uninterpreted_subdelim_passes_through() {
213 let host = crate::uri::Uri::parse("http://tag,with,commas/")
214 .unwrap()
215 .host()
216 .unwrap()
217 .into_owned();
218 assert!(matches!(host, Host::Uninterpreted(_)));
219 let canonical = host.clone().into_canonical_ip_addr();
220 assert_eq!(canonical, host);
221 }
222
223 #[test]
226 fn host_uninterpreted_ipvfuture_passes_through() {
227 let host = crate::uri::Uri::parse("http://[v1.fe80::a]/")
228 .unwrap()
229 .host()
230 .unwrap()
231 .into_owned();
232 assert!(matches!(host, Host::Uninterpreted(_)));
233 let canonical = host.clone().into_canonical_ip_addr();
234 assert_eq!(canonical, host);
235 }
236
237 #[test]
240 fn host_name_passes_through() {
241 let host = Host::Name(crate::address::Domain::from_static("example.com"));
242 let canonical = host.clone().into_canonical_ip_addr();
243 assert_eq!(canonical, host);
244 }
245}