@@ -23,15 +23,9 @@ impl Authority {
23
23
24
24
// Not public while `bytes` is unstable.
25
25
pub ( super ) fn from_shared ( s : Bytes ) -> Result < Self , InvalidUri > {
26
- let authority_end = Authority :: parse_non_empty ( & s[ ..] ) ?;
27
-
28
- if authority_end != s. len ( ) {
29
- return Err ( ErrorKind :: InvalidUriChar . into ( ) ) ;
30
- }
31
-
32
- Ok ( Authority {
33
- data : unsafe { ByteStr :: from_utf8_unchecked ( s) } ,
34
- } )
26
+ // Precondition on create_authority: trivially satisfied by the
27
+ // identity clousre
28
+ create_authority ( s, |s| s)
35
29
}
36
30
37
31
/// Attempt to convert an `Authority` from a static string.
@@ -52,18 +46,8 @@ impl Authority {
52
46
/// assert_eq!(authority.host(), "example.com");
53
47
/// ```
54
48
pub fn from_static ( src : & ' static str ) -> Self {
55
- let s = src. as_bytes ( ) ;
56
- let b = Bytes :: from_static ( s) ;
57
- let authority_end =
58
- Authority :: parse_non_empty ( & b[ ..] ) . expect ( "static str is not valid authority" ) ;
59
-
60
- if authority_end != b. len ( ) {
61
- panic ! ( "static str is not valid authority" ) ;
62
- }
63
-
64
- Authority {
65
- data : unsafe { ByteStr :: from_utf8_unchecked ( b) } ,
66
- }
49
+ Authority :: from_shared ( Bytes :: from_static ( src. as_bytes ( ) ) )
50
+ . expect ( "static str is not valid authority" )
67
51
}
68
52
69
53
@@ -83,6 +67,8 @@ impl Authority {
83
67
}
84
68
85
69
// Note: this may return an *empty* Authority. You might want `parse_non_empty`.
70
+ // Postcondition: for all Ok() returns, s[..ret.unwrap()] is valid UTF-8 where
71
+ // ret is the return value.
86
72
pub ( super ) fn parse ( s : & [ u8 ] ) -> Result < usize , InvalidUri > {
87
73
let mut colon_cnt = 0 ;
88
74
let mut start_bracket = false ;
@@ -91,6 +77,10 @@ impl Authority {
91
77
let mut end = s. len ( ) ;
92
78
let mut at_sign_pos = None ;
93
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.
94
84
for ( i, & b) in s. iter ( ) . enumerate ( ) {
95
85
match URI_CHARS [ b as usize ] {
96
86
b'/' | b'?' | b'#' => {
@@ -168,6 +158,9 @@ impl Authority {
168
158
//
169
159
// This should be used by functions that allow a user to parse
170
160
// an `Authority` by itself.
161
+ //
162
+ // Postcondition: for all Ok() returns, s[..ret.unwrap()] is valid UTF-8 where
163
+ // ret is the return value.
171
164
fn parse_non_empty ( s : & [ u8 ] ) -> Result < usize , InvalidUri > {
172
165
if s. is_empty ( ) {
173
166
return Err ( ErrorKind :: Empty . into ( ) ) ;
@@ -432,17 +425,10 @@ impl<'a> TryFrom<&'a [u8]> for Authority {
432
425
#[ inline]
433
426
fn try_from ( s : & ' a [ u8 ] ) -> Result < Self , Self :: Error > {
434
427
// parse first, and only turn into Bytes if valid
435
- let end = Authority :: parse_non_empty ( s) ?;
436
-
437
- if end != s. len ( ) {
438
- return Err ( ErrorKind :: InvalidAuthority . into ( ) ) ;
439
- }
440
428
441
- Ok ( Authority {
442
- data : unsafe {
443
- ByteStr :: from_utf8_unchecked ( Bytes :: copy_from_slice ( s) )
444
- } ,
445
- } )
429
+ // Preconditon on create_authority: copy_from_slice() copies all of
430
+ // bytes from the [u8] parameter into a new Bytes
431
+ create_authority ( s, |s| Bytes :: copy_from_slice ( s) )
446
432
}
447
433
}
448
434
@@ -494,6 +480,30 @@ fn host(auth: &str) -> &str {
494
480
}
495
481
}
496
482
483
+ // Precondition: f converts all of the bytes in the passed in B into the
484
+ // returned Bytes.
485
+ fn create_authority < B , F > ( b : B , f : F ) -> Result < Authority , InvalidUri >
486
+ where
487
+ B : AsRef < [ u8 ] > ,
488
+ F : FnOnce ( B ) -> Bytes ,
489
+ {
490
+ let s = b. as_ref ( ) ;
491
+ let authority_end = Authority :: parse_non_empty ( s) ?;
492
+
493
+ if authority_end != s. len ( ) {
494
+ return Err ( ErrorKind :: InvalidUriChar . into ( ) ) ;
495
+ }
496
+
497
+ let bytes = f ( b) ;
498
+
499
+ Ok ( Authority {
500
+ // Safety: the postcondition on parse_non_empty() and the check against
501
+ // s.len() ensure that b is valid UTF-8. The precondition on f ensures
502
+ // that this is carried through to bytes.
503
+ data : unsafe { ByteStr :: from_utf8_unchecked ( bytes) } ,
504
+ } )
505
+ }
506
+
497
507
#[ cfg( test) ]
498
508
mod tests {
499
509
use super :: * ;
@@ -529,6 +539,12 @@ mod tests {
529
539
assert_eq ! ( "EXAMPLE.com" , authority) ;
530
540
}
531
541
542
+ #[ test]
543
+ fn from_static_equates_with_a_str ( ) {
544
+ let authority = Authority :: from_static ( "example.com" ) ;
545
+ assert_eq ! ( authority, "example.com" ) ;
546
+ }
547
+
532
548
#[ test]
533
549
fn not_equal_with_a_str_of_a_different_authority ( ) {
534
550
let authority: Authority = "example.com" . parse ( ) . unwrap ( ) ;
@@ -616,4 +632,14 @@ mod tests {
616
632
let err = Authority :: parse_non_empty ( b"[fe80::1:2:3:4]%20" ) . unwrap_err ( ) ;
617
633
assert_eq ! ( err. 0 , ErrorKind :: InvalidAuthority ) ;
618
634
}
635
+
636
+ #[ test]
637
+ fn rejects_invalid_utf8 ( ) {
638
+ let err = Authority :: try_from ( [ 0xc0u8 ] . as_ref ( ) ) . unwrap_err ( ) ;
639
+ assert_eq ! ( err. 0 , ErrorKind :: InvalidUriChar ) ;
640
+
641
+ let err = Authority :: from_shared ( Bytes :: from_static ( [ 0xc0u8 ] . as_ref ( ) ) )
642
+ . unwrap_err ( ) ;
643
+ assert_eq ! ( err. 0 , ErrorKind :: InvalidUriChar ) ;
644
+ }
619
645
}
0 commit comments