• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::convert::TryFrom;
2 use std::hash::{Hash, Hasher};
3 use std::str::FromStr;
4 use std::{cmp, fmt, str};
5 
6 use bytes::Bytes;
7 
8 use super::{ErrorKind, InvalidUri, Port, URI_CHARS};
9 use crate::byte_str::ByteStr;
10 
11 /// Represents the authority component of a URI.
12 #[derive(Clone)]
13 pub struct Authority {
14     pub(super) data: ByteStr,
15 }
16 
17 impl Authority {
empty() -> Self18     pub(super) fn empty() -> Self {
19         Authority {
20             data: ByteStr::new(),
21         }
22     }
23 
24     // Not public while `bytes` is unstable.
from_shared(s: Bytes) -> Result<Self, InvalidUri>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     /// ```
from_static(src: &'static str) -> Self48     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.
from_maybe_shared<T>(src: T) -> Result<Self, InvalidUri> where T: AsRef<[u8]> + 'static,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.
parse(s: &[u8]) -> Result<usize, InvalidUri>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.
parse_non_empty(s: &[u8]) -> Result<usize, InvalidUri>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]
host(&self) -> &str199     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     /// ```
port(&self) -> Option<Port<&str>>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     /// ```
port_u16(&self) -> Option<u16>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]
as_str(&self) -> &str261     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 
269 impl AsRef<str> for Authority {
as_ref(&self) -> &str270     fn as_ref(&self) -> &str {
271         self.as_str()
272     }
273 }
274 
275 impl PartialEq for Authority {
eq(&self, other: &Authority) -> bool276     fn eq(&self, other: &Authority) -> bool {
277         self.data.eq_ignore_ascii_case(&other.data)
278     }
279 }
280 
281 impl 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 /// ```
293 impl PartialEq<str> for Authority {
eq(&self, other: &str) -> bool294     fn eq(&self, other: &str) -> bool {
295         self.data.eq_ignore_ascii_case(other)
296     }
297 }
298 
299 impl PartialEq<Authority> for str {
eq(&self, other: &Authority) -> bool300     fn eq(&self, other: &Authority) -> bool {
301         self.eq_ignore_ascii_case(other.as_str())
302     }
303 }
304 
305 impl<'a> PartialEq<Authority> for &'a str {
eq(&self, other: &Authority) -> bool306     fn eq(&self, other: &Authority) -> bool {
307         self.eq_ignore_ascii_case(other.as_str())
308     }
309 }
310 
311 impl<'a> PartialEq<&'a str> for Authority {
eq(&self, other: &&'a str) -> bool312     fn eq(&self, other: &&'a str) -> bool {
313         self.data.eq_ignore_ascii_case(other)
314     }
315 }
316 
317 impl PartialEq<String> for Authority {
eq(&self, other: &String) -> bool318     fn eq(&self, other: &String) -> bool {
319         self.data.eq_ignore_ascii_case(other.as_str())
320     }
321 }
322 
323 impl PartialEq<Authority> for String {
eq(&self, other: &Authority) -> bool324     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 /// ```
339 impl PartialOrd for Authority {
partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering>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 
347 impl PartialOrd<str> for Authority {
partial_cmp(&self, other: &str) -> Option<cmp::Ordering>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 
355 impl PartialOrd<Authority> for str {
partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering>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 
363 impl<'a> PartialOrd<Authority> for &'a str {
partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering>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 
371 impl<'a> PartialOrd<&'a str> for Authority {
partial_cmp(&self, other: &&'a str) -> Option<cmp::Ordering>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 
379 impl PartialOrd<String> for Authority {
partial_cmp(&self, other: &String) -> Option<cmp::Ordering>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 
387 impl PartialOrd<Authority> for String {
partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering>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 /// ```
417 impl Hash for Authority {
hash<H>(&self, state: &mut H) where H: Hasher,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 
429 impl<'a> TryFrom<&'a [u8]> for Authority {
430     type Error = InvalidUri;
431     #[inline]
try_from(s: &'a [u8]) -> Result<Self, Self::Error>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 
441 impl<'a> TryFrom<&'a str> for Authority {
442     type Error = InvalidUri;
443     #[inline]
try_from(s: &'a str) -> Result<Self, Self::Error>444     fn try_from(s: &'a str) -> Result<Self, Self::Error> {
445         TryFrom::try_from(s.as_bytes())
446     }
447 }
448 
449 impl TryFrom<Vec<u8>> for Authority {
450     type Error = InvalidUri;
451 
452     #[inline]
try_from(vec: Vec<u8>) -> Result<Self, Self::Error>453     fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
454         Authority::from_shared(vec.into())
455     }
456 }
457 
458 impl TryFrom<String> for Authority {
459     type Error = InvalidUri;
460 
461     #[inline]
try_from(t: String) -> Result<Self, Self::Error>462     fn try_from(t: String) -> Result<Self, Self::Error> {
463         Authority::from_shared(t.into())
464     }
465 }
466 
467 impl FromStr for Authority {
468     type Err = InvalidUri;
469 
from_str(s: &str) -> Result<Self, InvalidUri>470     fn from_str(s: &str) -> Result<Self, InvalidUri> {
471         TryFrom::try_from(s)
472     }
473 }
474 
475 impl fmt::Debug for Authority {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result476     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477         f.write_str(self.as_str())
478     }
479 }
480 
481 impl fmt::Display for Authority {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result482     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
483         f.write_str(self.as_str())
484     }
485 }
486 
host(auth: &str) -> &str487 fn 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.
create_authority<B, F>(b: B, f: F) -> Result<Authority, InvalidUri> where B: AsRef<[u8]>, F: FnOnce(B) -> Bytes,509 fn create_authority<B, F>(b: B, f: F) -> Result<Authority, InvalidUri>
510 where
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)]
532 mod tests {
533     use super::*;
534 
535     #[test]
parse_empty_string_is_error()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]
equal_to_self_of_same_authority()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]
not_equal_to_self_of_different_authority()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]
equates_with_a_str()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]
from_static_equates_with_a_str()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]
not_equal_with_a_str_of_a_different_authority()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]
equates_with_a_string()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]
equates_with_a_string_of_a_different_authority()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]
compares_to_self()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]
compares_with_a_str()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]
compares_with_a_string()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]
allows_percent_in_userinfo()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]
rejects_percent_in_hostname()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]
allows_percent_in_ipv6_address()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]
reject_obviously_invalid_ipv6_address()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]
rejects_percent_outside_ipv6_address()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]
rejects_invalid_utf8()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]
rejects_invalid_use_of_brackets()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