Coverage Report

Created: 2025-10-10 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/http/src/uri/authority.rs
Line
Count
Source
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 {
18
6.35k
    pub(super) fn empty() -> Self {
19
6.35k
        Authority {
20
6.35k
            data: ByteStr::new(),
21
6.35k
        }
22
6.35k
    }
23
24
    // Not public while `bytes` is unstable.
25
1.09k
    pub(super) fn from_shared(s: Bytes) -> Result<Self, InvalidUri> {
26
        // Precondition on create_authority: trivially satisfied by the
27
        // identity closure
28
1.09k
        create_authority(s, |s| s)
29
1.09k
    }
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
0
    pub fn from_static(src: &'static str) -> Self {
49
0
        Authority::from_shared(Bytes::from_static(src.as_bytes()))
50
0
            .expect("static str is not valid authority")
51
0
    }
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
1.56k
    pub(super) fn parse(s: &[u8]) -> Result<usize, InvalidUri> {
72
1.56k
        let mut colon_cnt = 0u32;
73
1.56k
        let mut start_bracket = false;
74
1.56k
        let mut end_bracket = false;
75
1.56k
        let mut has_percent = false;
76
1.56k
        let mut end = s.len();
77
1.56k
        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
501k
        for (i, &b) in s.iter().enumerate() {
85
501k
            match URI_CHARS[b as usize] {
86
                b'/' | b'?' | b'#' => {
87
124
                    end = i;
88
124
                    break;
89
                }
90
                b':' => {
91
385
                    if colon_cnt >= MAX_COLONS {
92
2
                        return Err(ErrorKind::InvalidAuthority.into());
93
383
                    }
94
383
                    colon_cnt += 1;
95
                }
96
                b'[' => {
97
19
                    if has_percent || start_bracket {
98
                        // Something other than the userinfo has a `%`, so reject it.
99
4
                        return Err(ErrorKind::InvalidAuthority.into());
100
15
                    }
101
15
                    start_bracket = true;
102
                }
103
                b']' => {
104
6
                    if (!start_bracket) || end_bracket {
105
3
                        return Err(ErrorKind::InvalidAuthority.into());
106
3
                    }
107
3
                    end_bracket = true;
108
109
                    // Those were part of an IPv6 hostname, so forget them...
110
3
                    colon_cnt = 0;
111
3
                    has_percent = false;
112
                }
113
377
                b'@' => {
114
377
                    at_sign_pos = Some(i);
115
377
116
377
                    // Those weren't a port colon, but part of the
117
377
                    // userinfo, so it needs to be forgotten.
118
377
                    colon_cnt = 0;
119
377
                    has_percent = false;
120
377
                }
121
487
                0 if b == b'%' => {
122
210
                    // Per https://tools.ietf.org/html/rfc3986#section-3.2.1 and
123
210
                    // https://url.spec.whatwg.org/#authority-state
124
210
                    // the userinfo can have a percent-encoded username and password,
125
210
                    // so record that a `%` was found. If this turns out to be
126
210
                    // part of the userinfo, this flag will be cleared.
127
210
                    // Also per https://tools.ietf.org/html/rfc6874, percent-encoding can
128
210
                    // be used to indicate a zone identifier.
129
210
                    // If the flag hasn't been cleared at the end, that means this
130
210
                    // was part of the hostname (and not part of an IPv6 address), and
131
210
                    // will fail with an error.
132
210
                    has_percent = true;
133
210
                }
134
                0 => {
135
277
                    return Err(ErrorKind::InvalidUriChar.into());
136
                }
137
500k
                _ => {}
138
            }
139
        }
140
141
1.27k
        if start_bracket ^ end_bracket {
142
2
            return Err(ErrorKind::InvalidAuthority.into());
143
1.27k
        }
144
145
1.27k
        if colon_cnt > 1 {
146
            // Things like 'localhost:8080:3030' are rejected.
147
10
            return Err(ErrorKind::InvalidAuthority.into());
148
1.26k
        }
149
150
1.26k
        if end > 0 && at_sign_pos == Some(end - 1) {
151
            // If there's nothing after an `@`, this is bonkers.
152
9
            return Err(ErrorKind::InvalidAuthority.into());
153
1.25k
        }
154
155
1.25k
        if has_percent {
156
            // Something after the userinfo has a `%`, so reject it.
157
9
            return Err(ErrorKind::InvalidAuthority.into());
158
1.24k
        }
159
160
1.24k
        Ok(end)
161
1.56k
    }
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
1.09k
    fn parse_non_empty(s: &[u8]) -> Result<usize, InvalidUri> {
171
1.09k
        if s.is_empty() {
172
0
            return Err(ErrorKind::Empty.into());
173
1.09k
        }
174
1.09k
        Authority::parse(s)
175
1.09k
    }
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
0
    pub fn port(&self) -> Option<Port<&str>> {
239
0
        let bytes = self.as_str();
240
0
        bytes
241
0
            .rfind(':')
242
0
            .and_then(|i| Port::from_str(&bytes[i + 1..]).ok())
243
0
    }
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
0
    pub fn port_u16(&self) -> Option<u16> {
256
0
        self.port().map(|p| p.as_u16())
257
0
    }
258
259
    /// Return a str representation of the authority
260
    #[inline]
261
0
    pub fn as_str(&self) -> &str {
262
0
        &self.data[..]
263
0
    }
264
}
265
266
// Purposefully not public while `bytes` is unstable.
267
// impl TryFrom<Bytes> for Authority
268
269
impl AsRef<str> for Authority {
270
0
    fn as_ref(&self) -> &str {
271
0
        self.as_str()
272
0
    }
273
}
274
275
impl PartialEq for Authority {
276
0
    fn eq(&self, other: &Authority) -> bool {
277
0
        self.data.eq_ignore_ascii_case(&other.data)
278
0
    }
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 {
294
0
    fn eq(&self, other: &str) -> bool {
295
0
        self.data.eq_ignore_ascii_case(other)
296
0
    }
297
}
298
299
impl PartialEq<Authority> for str {
300
0
    fn eq(&self, other: &Authority) -> bool {
301
0
        self.eq_ignore_ascii_case(other.as_str())
302
0
    }
303
}
304
305
impl<'a> PartialEq<Authority> for &'a str {
306
0
    fn eq(&self, other: &Authority) -> bool {
307
0
        self.eq_ignore_ascii_case(other.as_str())
308
0
    }
309
}
310
311
impl<'a> PartialEq<&'a str> for Authority {
312
0
    fn eq(&self, other: &&'a str) -> bool {
313
0
        self.data.eq_ignore_ascii_case(other)
314
0
    }
315
}
316
317
impl PartialEq<String> for Authority {
318
0
    fn eq(&self, other: &String) -> bool {
319
0
        self.data.eq_ignore_ascii_case(other.as_str())
320
0
    }
321
}
322
323
impl PartialEq<Authority> for String {
324
0
    fn eq(&self, other: &Authority) -> bool {
325
0
        self.as_str().eq_ignore_ascii_case(other.as_str())
326
0
    }
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 {
340
0
    fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
341
0
        let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
342
0
        let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
343
0
        left.partial_cmp(right)
344
0
    }
345
}
346
347
impl PartialOrd<str> for Authority {
348
0
    fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
349
0
        let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
350
0
        let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
351
0
        left.partial_cmp(right)
352
0
    }
353
}
354
355
impl PartialOrd<Authority> for str {
356
0
    fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
357
0
        let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
358
0
        let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
359
0
        left.partial_cmp(right)
360
0
    }
361
}
362
363
impl<'a> PartialOrd<Authority> for &'a str {
364
0
    fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
365
0
        let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
366
0
        let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
367
0
        left.partial_cmp(right)
368
0
    }
369
}
370
371
impl<'a> PartialOrd<&'a str> for Authority {
372
0
    fn partial_cmp(&self, other: &&'a str) -> Option<cmp::Ordering> {
373
0
        let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
374
0
        let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
375
0
        left.partial_cmp(right)
376
0
    }
377
}
378
379
impl PartialOrd<String> for Authority {
380
0
    fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
381
0
        let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
382
0
        let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
383
0
        left.partial_cmp(right)
384
0
    }
385
}
386
387
impl PartialOrd<Authority> for String {
388
0
    fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
389
0
        let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
390
0
        let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
391
0
        left.partial_cmp(right)
392
0
    }
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 {
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]
432
0
    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
0
        create_authority(s, Bytes::copy_from_slice)
438
0
    }
439
}
440
441
impl<'a> TryFrom<&'a str> for Authority {
442
    type Error = InvalidUri;
443
    #[inline]
444
0
    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
445
0
        TryFrom::try_from(s.as_bytes())
446
0
    }
447
}
448
449
impl 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
458
impl 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
467
impl FromStr for Authority {
468
    type Err = InvalidUri;
469
470
0
    fn from_str(s: &str) -> Result<Self, InvalidUri> {
471
0
        TryFrom::try_from(s)
472
0
    }
473
}
474
475
impl fmt::Debug for Authority {
476
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477
0
        f.write_str(self.as_str())
478
0
    }
479
}
480
481
impl fmt::Display for Authority {
482
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
483
0
        f.write_str(self.as_str())
484
0
    }
485
}
486
487
0
fn host(auth: &str) -> &str {
488
0
    let host_port = auth
489
0
        .rsplit('@')
490
0
        .next()
491
0
        .expect("split always has at least 1 item");
492
493
0
    if host_port.as_bytes()[0] == b'[' {
494
0
        let i = host_port
495
0
            .find(']')
496
0
            .expect("parsing should validate brackets");
497
        // ..= ranges aren't available in 1.20, our minimum Rust version...
498
0
        &host_port[0..i + 1]
499
    } else {
500
0
        host_port
501
0
            .split(':')
502
0
            .next()
503
0
            .expect("split always has at least 1 item")
504
    }
505
0
}
506
507
// Precondition: f converts all of the bytes in the passed in B into the
508
// returned Bytes.
509
1.09k
fn create_authority<B, F>(b: B, f: F) -> Result<Authority, InvalidUri>
510
1.09k
where
511
1.09k
    B: AsRef<[u8]>,
512
1.09k
    F: FnOnce(B) -> Bytes,
513
{
514
1.09k
    let s = b.as_ref();
515
1.09k
    let authority_end = Authority::parse_non_empty(s)?;
516
517
1.06k
    if authority_end != s.len() {
518
2
        return Err(ErrorKind::InvalidUriChar.into());
519
1.06k
    }
520
521
1.06k
    let bytes = f(b);
522
523
1.06k
    Ok(Authority {
524
1.06k
        // Safety: the postcondition on parse_non_empty() and the check against
525
1.06k
        // s.len() ensure that b is valid UTF-8. The precondition on f ensures
526
1.06k
        // that this is carried through to bytes.
527
1.06k
        data: unsafe { ByteStr::from_utf8_unchecked(bytes) },
528
1.06k
    })
529
1.09k
}
http::uri::authority::create_authority::<bytes::bytes::Bytes, <http::uri::authority::Authority>::from_shared::{closure#0}>
Line
Count
Source
509
1.09k
fn create_authority<B, F>(b: B, f: F) -> Result<Authority, InvalidUri>
510
1.09k
where
511
1.09k
    B: AsRef<[u8]>,
512
1.09k
    F: FnOnce(B) -> Bytes,
513
{
514
1.09k
    let s = b.as_ref();
515
1.09k
    let authority_end = Authority::parse_non_empty(s)?;
516
517
1.06k
    if authority_end != s.len() {
518
2
        return Err(ErrorKind::InvalidUriChar.into());
519
1.06k
    }
520
521
1.06k
    let bytes = f(b);
522
523
1.06k
    Ok(Authority {
524
1.06k
        // Safety: the postcondition on parse_non_empty() and the check against
525
1.06k
        // s.len() ensure that b is valid UTF-8. The precondition on f ensures
526
1.06k
        // that this is carried through to bytes.
527
1.06k
        data: unsafe { ByteStr::from_utf8_unchecked(bytes) },
528
1.06k
    })
529
1.09k
}
Unexecuted instantiation: http::uri::authority::create_authority::<&[u8], <bytes::bytes::Bytes>::copy_from_slice>
530
531
#[cfg(test)]
532
mod 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
}