1use std::convert::TryFrom;
2use std::hash::{Hash, Hasher};
3use std::str::FromStr;
4use std::{cmp, fmt, str};
5
6use bytes::Bytes;
7
8use super::{ErrorKind, InvalidUri, Port, URI_CHARS};
9use crate::byte_str::ByteStr;
10
11/// Represents the authority component of a URI.
12#[derive(Clone)]
13pub struct Authority {
14 pub(super) data: ByteStr,
15}
16
17impl Authority {
18 pub(super) fn empty() -> Self {
19 Authority {
20 data: ByteStr::new(),
21 }
22 }
23
24 // Not public while `bytes` is unstable.
25 pub(super) fn from_shared(s: Bytes) -> Result<Self, InvalidUri> {
26 // Precondition on create_authority: trivially satisfied by the
27 // identity clousre
28 create_authority(s, |s| s)
29 }
30
31 /// Attempt to convert an `Authority` from a static string.
32 ///
33 /// This function will not perform any copying, and the string will be
34 /// checked if it is empty or contains an invalid character.
35 ///
36 /// # Panics
37 ///
38 /// This function panics if the argument contains invalid characters or
39 /// is empty.
40 ///
41 /// # Examples
42 ///
43 /// ```
44 /// # use http::uri::Authority;
45 /// let authority = Authority::from_static("example.com");
46 /// assert_eq!(authority.host(), "example.com");
47 /// ```
48 pub fn from_static(src: &'static str) -> Self {
49 Authority::from_shared(Bytes::from_static(src.as_bytes()))
50 .expect("static str is not valid authority")
51 }
52
53 /// Attempt to convert a `Bytes` buffer to a `Authority`.
54 ///
55 /// This will try to prevent a copy if the type passed is the type used
56 /// internally, and will copy the data if it is not.
57 pub fn from_maybe_shared<T>(src: T) -> Result<Self, InvalidUri>
58 where
59 T: AsRef<[u8]> + 'static,
60 {
61 if_downcast_into!(T, Bytes, src, {
62 return Authority::from_shared(src);
63 });
64
65 Authority::try_from(src.as_ref())
66 }
67
68 // Note: this may return an *empty* Authority. You might want `parse_non_empty`.
69 // Postcondition: for all Ok() returns, s[..ret.unwrap()] is valid UTF-8 where
70 // ret is the return value.
71 pub(super) fn parse(s: &[u8]) -> Result<usize, InvalidUri> {
72 let mut colon_cnt = 0u32;
73 let mut start_bracket = false;
74 let mut end_bracket = false;
75 let mut has_percent = false;
76 let mut end = s.len();
77 let mut at_sign_pos = None;
78 const MAX_COLONS: u32 = 8; // e.g., [FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80
79
80 // Among other things, this loop checks that every byte in s up to the
81 // first '/', '?', or '#' is a valid URI character (or in some contexts,
82 // a '%'). This means that each such byte is a valid single-byte UTF-8
83 // code point.
84 for (i, &b) in s.iter().enumerate() {
85 match URI_CHARS[b as usize] {
86 b'/' | b'?' | b'#' => {
87 end = i;
88 break;
89 }
90 b':' => {
91 if colon_cnt >= MAX_COLONS {
92 return Err(ErrorKind::InvalidAuthority.into());
93 }
94 colon_cnt += 1;
95 }
96 b'[' => {
97 if has_percent || start_bracket {
98 // Something other than the userinfo has a `%`, so reject it.
99 return Err(ErrorKind::InvalidAuthority.into());
100 }
101 start_bracket = true;
102 }
103 b']' => {
104 if (!start_bracket) || end_bracket {
105 return Err(ErrorKind::InvalidAuthority.into());
106 }
107 end_bracket = true;
108
109 // Those were part of an IPv6 hostname, so forget them...
110 colon_cnt = 0;
111 has_percent = false;
112 }
113 b'@' => {
114 at_sign_pos = Some(i);
115
116 // Those weren't a port colon, but part of the
117 // userinfo, so it needs to be forgotten.
118 colon_cnt = 0;
119 has_percent = false;
120 }
121 0 if b == b'%' => {
122 // Per https://tools.ietf.org/html/rfc3986#section-3.2.1 and
123 // https://url.spec.whatwg.org/#authority-state
124 // the userinfo can have a percent-encoded username and password,
125 // so record that a `%` was found. If this turns out to be
126 // part of the userinfo, this flag will be cleared.
127 // Also per https://tools.ietf.org/html/rfc6874, percent-encoding can
128 // be used to indicate a zone identifier.
129 // If the flag hasn't been cleared at the end, that means this
130 // was part of the hostname (and not part of an IPv6 address), and
131 // will fail with an error.
132 has_percent = true;
133 }
134 0 => {
135 return Err(ErrorKind::InvalidUriChar.into());
136 }
137 _ => {}
138 }
139 }
140
141 if start_bracket ^ end_bracket {
142 return Err(ErrorKind::InvalidAuthority.into());
143 }
144
145 if colon_cnt > 1 {
146 // Things like 'localhost:8080:3030' are rejected.
147 return Err(ErrorKind::InvalidAuthority.into());
148 }
149
150 if end > 0 && at_sign_pos == Some(end - 1) {
151 // If there's nothing after an `@`, this is bonkers.
152 return Err(ErrorKind::InvalidAuthority.into());
153 }
154
155 if has_percent {
156 // Something after the userinfo has a `%`, so reject it.
157 return Err(ErrorKind::InvalidAuthority.into());
158 }
159
160 Ok(end)
161 }
162
163 // Parse bytes as an Authority, not allowing an empty string.
164 //
165 // This should be used by functions that allow a user to parse
166 // an `Authority` by itself.
167 //
168 // Postcondition: for all Ok() returns, s[..ret.unwrap()] is valid UTF-8 where
169 // ret is the return value.
170 fn parse_non_empty(s: &[u8]) -> Result<usize, InvalidUri> {
171 if s.is_empty() {
172 return Err(ErrorKind::Empty.into());
173 }
174 Authority::parse(s)
175 }
176
177 /// Get the host of this `Authority`.
178 ///
179 /// The host subcomponent of authority is identified by an IP literal
180 /// encapsulated within square brackets, an IPv4 address in dotted- decimal
181 /// form, or a registered name. The host subcomponent is **case-insensitive**.
182 ///
183 /// ```notrust
184 /// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
185 /// |---------|
186 /// |
187 /// host
188 /// ```
189 ///
190 /// # Examples
191 ///
192 /// ```
193 /// # use http::uri::*;
194 /// let authority: Authority = "example.org:80".parse().unwrap();
195 ///
196 /// assert_eq!(authority.host(), "example.org");
197 /// ```
198 #[inline]
199 pub fn host(&self) -> &str {
200 host(self.as_str())
201 }
202
203 /// Get the port part of this `Authority`.
204 ///
205 /// The port subcomponent of authority is designated by an optional port
206 /// number following the host and delimited from it by a single colon (":")
207 /// character. It can be turned into a decimal port number with the `as_u16`
208 /// method or as a `str` with the `as_str` method.
209 ///
210 /// ```notrust
211 /// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
212 /// |-|
213 /// |
214 /// port
215 /// ```
216 ///
217 /// # Examples
218 ///
219 /// Authority with port
220 ///
221 /// ```
222 /// # use http::uri::Authority;
223 /// let authority: Authority = "example.org:80".parse().unwrap();
224 ///
225 /// let port = authority.port().unwrap();
226 /// assert_eq!(port.as_u16(), 80);
227 /// assert_eq!(port.as_str(), "80");
228 /// ```
229 ///
230 /// Authority without port
231 ///
232 /// ```
233 /// # use http::uri::Authority;
234 /// let authority: Authority = "example.org".parse().unwrap();
235 ///
236 /// assert!(authority.port().is_none());
237 /// ```
238 pub fn port(&self) -> Option<Port<&str>> {
239 let bytes = self.as_str();
240 bytes
241 .rfind(":")
242 .and_then(|i| Port::from_str(&bytes[i + 1..]).ok())
243 }
244
245 /// Get the port of this `Authority` as a `u16`.
246 ///
247 /// # Example
248 ///
249 /// ```
250 /// # use http::uri::Authority;
251 /// let authority: Authority = "example.org:80".parse().unwrap();
252 ///
253 /// assert_eq!(authority.port_u16(), Some(80));
254 /// ```
255 pub fn port_u16(&self) -> Option<u16> {
256 self.port().and_then(|p| Some(p.as_u16()))
257 }
258
259 /// Return a str representation of the authority
260 #[inline]
261 pub fn as_str(&self) -> &str {
262 &self.data[..]
263 }
264}
265
266// Purposefully not public while `bytes` is unstable.
267// impl TryFrom<Bytes> for Authority
268
269impl AsRef<str> for Authority {
270 fn as_ref(&self) -> &str {
271 self.as_str()
272 }
273}
274
275impl PartialEq for Authority {
276 fn eq(&self, other: &Authority) -> bool {
277 self.data.eq_ignore_ascii_case(&other.data)
278 }
279}
280
281impl Eq for Authority {}
282
283/// Case-insensitive equality
284///
285/// # Examples
286///
287/// ```
288/// # use http::uri::Authority;
289/// let authority: Authority = "HELLO.com".parse().unwrap();
290/// assert_eq!(authority, "hello.coM");
291/// assert_eq!("hello.com", authority);
292/// ```
293impl PartialEq<str> for Authority {
294 fn eq(&self, other: &str) -> bool {
295 self.data.eq_ignore_ascii_case(other)
296 }
297}
298
299impl PartialEq<Authority> for str {
300 fn eq(&self, other: &Authority) -> bool {
301 self.eq_ignore_ascii_case(other.as_str())
302 }
303}
304
305impl<'a> PartialEq<Authority> for &'a str {
306 fn eq(&self, other: &Authority) -> bool {
307 self.eq_ignore_ascii_case(other.as_str())
308 }
309}
310
311impl<'a> PartialEq<&'a str> for Authority {
312 fn eq(&self, other: &&'a str) -> bool {
313 self.data.eq_ignore_ascii_case(other)
314 }
315}
316
317impl PartialEq<String> for Authority {
318 fn eq(&self, other: &String) -> bool {
319 self.data.eq_ignore_ascii_case(other.as_str())
320 }
321}
322
323impl PartialEq<Authority> for String {
324 fn eq(&self, other: &Authority) -> bool {
325 self.as_str().eq_ignore_ascii_case(other.as_str())
326 }
327}
328
329/// Case-insensitive ordering
330///
331/// # Examples
332///
333/// ```
334/// # use http::uri::Authority;
335/// let authority: Authority = "DEF.com".parse().unwrap();
336/// assert!(authority < "ghi.com");
337/// assert!(authority > "abc.com");
338/// ```
339impl PartialOrd for Authority {
340 fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
341 let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
342 let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
343 left.partial_cmp(right)
344 }
345}
346
347impl PartialOrd<str> for Authority {
348 fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
349 let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
350 let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
351 left.partial_cmp(right)
352 }
353}
354
355impl PartialOrd<Authority> for str {
356 fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
357 let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
358 let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
359 left.partial_cmp(right)
360 }
361}
362
363impl<'a> PartialOrd<Authority> for &'a str {
364 fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
365 let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
366 let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
367 left.partial_cmp(right)
368 }
369}
370
371impl<'a> PartialOrd<&'a str> for Authority {
372 fn partial_cmp(&self, other: &&'a str) -> Option<cmp::Ordering> {
373 let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
374 let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
375 left.partial_cmp(right)
376 }
377}
378
379impl PartialOrd<String> for Authority {
380 fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
381 let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
382 let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
383 left.partial_cmp(right)
384 }
385}
386
387impl PartialOrd<Authority> for String {
388 fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
389 let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
390 let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
391 left.partial_cmp(right)
392 }
393}
394
395/// Case-insensitive hashing
396///
397/// # Examples
398///
399/// ```
400/// # use http::uri::Authority;
401/// # use std::hash::{Hash, Hasher};
402/// # use std::collections::hash_map::DefaultHasher;
403///
404/// let a: Authority = "HELLO.com".parse().unwrap();
405/// let b: Authority = "hello.coM".parse().unwrap();
406///
407/// let mut s = DefaultHasher::new();
408/// a.hash(&mut s);
409/// let a = s.finish();
410///
411/// let mut s = DefaultHasher::new();
412/// b.hash(&mut s);
413/// let b = s.finish();
414///
415/// assert_eq!(a, b);
416/// ```
417impl Hash for Authority {
418 fn hash<H>(&self, state: &mut H)
419 where
420 H: Hasher,
421 {
422 self.data.len().hash(state);
423 for &b in self.data.as_bytes() {
424 state.write_u8(b.to_ascii_lowercase());
425 }
426 }
427}
428
429impl<'a> TryFrom<&'a [u8]> for Authority {
430 type Error = InvalidUri;
431 #[inline]
432 fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
433 // parse first, and only turn into Bytes if valid
434
435 // Preconditon on create_authority: copy_from_slice() copies all of
436 // bytes from the [u8] parameter into a new Bytes
437 create_authority(s, |s| Bytes::copy_from_slice(s))
438 }
439}
440
441impl<'a> TryFrom<&'a str> for Authority {
442 type Error = InvalidUri;
443 #[inline]
444 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
445 TryFrom::try_from(s.as_bytes())
446 }
447}
448
449impl TryFrom<Vec<u8>> for Authority {
450 type Error = InvalidUri;
451
452 #[inline]
453 fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
454 Authority::from_shared(vec.into())
455 }
456}
457
458impl TryFrom<String> for Authority {
459 type Error = InvalidUri;
460
461 #[inline]
462 fn try_from(t: String) -> Result<Self, Self::Error> {
463 Authority::from_shared(t.into())
464 }
465}
466
467impl FromStr for Authority {
468 type Err = InvalidUri;
469
470 fn from_str(s: &str) -> Result<Self, InvalidUri> {
471 TryFrom::try_from(s)
472 }
473}
474
475impl fmt::Debug for Authority {
476 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477 f.write_str(self.as_str())
478 }
479}
480
481impl fmt::Display for Authority {
482 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
483 f.write_str(self.as_str())
484 }
485}
486
487fn host(auth: &str) -> &str {
488 let host_port = auth
489 .rsplitn(2, '@')
490 .next()
491 .expect("split always has at least 1 item");
492
493 if host_port.as_bytes()[0] == b'[' {
494 let i = host_port
495 .find(']')
496 .expect("parsing should validate brackets");
497 // ..= ranges aren't available in 1.20, our minimum Rust version...
498 &host_port[0..i + 1]
499 } else {
500 host_port
501 .split(':')
502 .next()
503 .expect("split always has at least 1 item")
504 }
505}
506
507// Precondition: f converts all of the bytes in the passed in B into the
508// returned Bytes.
509fn create_authority<B, F>(b: B, f: F) -> Result<Authority, InvalidUri>
510where
511 B: AsRef<[u8]>,
512 F: FnOnce(B) -> Bytes,
513{
514 let s = b.as_ref();
515 let authority_end = Authority::parse_non_empty(s)?;
516
517 if authority_end != s.len() {
518 return Err(ErrorKind::InvalidUriChar.into());
519 }
520
521 let bytes = f(b);
522
523 Ok(Authority {
524 // Safety: the postcondition on parse_non_empty() and the check against
525 // s.len() ensure that b is valid UTF-8. The precondition on f ensures
526 // that this is carried through to bytes.
527 data: unsafe { ByteStr::from_utf8_unchecked(bytes) },
528 })
529}
530
531#[cfg(test)]
532mod tests {
533 use super::*;
534
535 #[test]
536 fn parse_empty_string_is_error() {
537 let err = Authority::parse_non_empty(b"").unwrap_err();
538 assert_eq!(err.0, ErrorKind::Empty);
539 }
540
541 #[test]
542 fn equal_to_self_of_same_authority() {
543 let authority1: Authority = "example.com".parse().unwrap();
544 let authority2: Authority = "EXAMPLE.COM".parse().unwrap();
545 assert_eq!(authority1, authority2);
546 assert_eq!(authority2, authority1);
547 }
548
549 #[test]
550 fn not_equal_to_self_of_different_authority() {
551 let authority1: Authority = "example.com".parse().unwrap();
552 let authority2: Authority = "test.com".parse().unwrap();
553 assert_ne!(authority1, authority2);
554 assert_ne!(authority2, authority1);
555 }
556
557 #[test]
558 fn equates_with_a_str() {
559 let authority: Authority = "example.com".parse().unwrap();
560 assert_eq!(&authority, "EXAMPLE.com");
561 assert_eq!("EXAMPLE.com", &authority);
562 assert_eq!(authority, "EXAMPLE.com");
563 assert_eq!("EXAMPLE.com", authority);
564 }
565
566 #[test]
567 fn from_static_equates_with_a_str() {
568 let authority = Authority::from_static("example.com");
569 assert_eq!(authority, "example.com");
570 }
571
572 #[test]
573 fn not_equal_with_a_str_of_a_different_authority() {
574 let authority: Authority = "example.com".parse().unwrap();
575 assert_ne!(&authority, "test.com");
576 assert_ne!("test.com", &authority);
577 assert_ne!(authority, "test.com");
578 assert_ne!("test.com", authority);
579 }
580
581 #[test]
582 fn equates_with_a_string() {
583 let authority: Authority = "example.com".parse().unwrap();
584 assert_eq!(authority, "EXAMPLE.com".to_string());
585 assert_eq!("EXAMPLE.com".to_string(), authority);
586 }
587
588 #[test]
589 fn equates_with_a_string_of_a_different_authority() {
590 let authority: Authority = "example.com".parse().unwrap();
591 assert_ne!(authority, "test.com".to_string());
592 assert_ne!("test.com".to_string(), authority);
593 }
594
595 #[test]
596 fn compares_to_self() {
597 let authority1: Authority = "abc.com".parse().unwrap();
598 let authority2: Authority = "def.com".parse().unwrap();
599 assert!(authority1 < authority2);
600 assert!(authority2 > authority1);
601 }
602
603 #[test]
604 fn compares_with_a_str() {
605 let authority: Authority = "def.com".parse().unwrap();
606 // with ref
607 assert!(&authority < "ghi.com");
608 assert!("ghi.com" > &authority);
609 assert!(&authority > "abc.com");
610 assert!("abc.com" < &authority);
611
612 // no ref
613 assert!(authority < "ghi.com");
614 assert!("ghi.com" > authority);
615 assert!(authority > "abc.com");
616 assert!("abc.com" < authority);
617 }
618
619 #[test]
620 fn compares_with_a_string() {
621 let authority: Authority = "def.com".parse().unwrap();
622 assert!(authority < "ghi.com".to_string());
623 assert!("ghi.com".to_string() > authority);
624 assert!(authority > "abc.com".to_string());
625 assert!("abc.com".to_string() < authority);
626 }
627
628 #[test]
629 fn allows_percent_in_userinfo() {
630 let authority_str = "a%2f:b%2f@example.com";
631 let authority: Authority = authority_str.parse().unwrap();
632 assert_eq!(authority, authority_str);
633 }
634
635 #[test]
636 fn rejects_percent_in_hostname() {
637 let err = Authority::parse_non_empty(b"example%2f.com").unwrap_err();
638 assert_eq!(err.0, ErrorKind::InvalidAuthority);
639
640 let err = Authority::parse_non_empty(b"a%2f:b%2f@example%2f.com").unwrap_err();
641 assert_eq!(err.0, ErrorKind::InvalidAuthority);
642 }
643
644 #[test]
645 fn allows_percent_in_ipv6_address() {
646 let authority_str = "[fe80::1:2:3:4%25eth0]";
647 let result: Authority = authority_str.parse().unwrap();
648 assert_eq!(result, authority_str);
649 }
650
651 #[test]
652 fn reject_obviously_invalid_ipv6_address() {
653 let err = Authority::parse_non_empty(b"[0:1:2:3:4:5:6:7:8:9:10:11:12:13:14]").unwrap_err();
654 assert_eq!(err.0, ErrorKind::InvalidAuthority);
655 }
656
657 #[test]
658 fn rejects_percent_outside_ipv6_address() {
659 let err = Authority::parse_non_empty(b"1234%20[fe80::1:2:3:4]").unwrap_err();
660 assert_eq!(err.0, ErrorKind::InvalidAuthority);
661
662 let err = Authority::parse_non_empty(b"[fe80::1:2:3:4]%20").unwrap_err();
663 assert_eq!(err.0, ErrorKind::InvalidAuthority);
664 }
665
666 #[test]
667 fn rejects_invalid_utf8() {
668 let err = Authority::try_from([0xc0u8].as_ref()).unwrap_err();
669 assert_eq!(err.0, ErrorKind::InvalidUriChar);
670
671 let err = Authority::from_shared(Bytes::from_static([0xc0u8].as_ref())).unwrap_err();
672 assert_eq!(err.0, ErrorKind::InvalidUriChar);
673 }
674
675 #[test]
676 fn rejects_invalid_use_of_brackets() {
677 let err = Authority::parse_non_empty(b"[]@[").unwrap_err();
678 assert_eq!(err.0, ErrorKind::InvalidAuthority);
679
680 // reject tie-fighter
681 let err = Authority::parse_non_empty(b"]o[").unwrap_err();
682 assert_eq!(err.0, ErrorKind::InvalidAuthority);
683 }
684}
685