@@ -5,8 +5,9 @@ use std::borrow::Borrow;
5
5
use std:: error:: Error ;
6
6
use std:: convert:: { TryFrom } ;
7
7
use std:: hash:: { Hash , Hasher } ;
8
+ use std:: mem:: MaybeUninit ;
8
9
use std:: str:: FromStr ;
9
- use std:: { fmt, mem } ;
10
+ use std:: fmt;
10
11
11
12
/// Represents an HTTP header field name
12
13
///
@@ -50,6 +51,7 @@ enum Repr<T> {
50
51
struct Custom ( ByteStr ) ;
51
52
52
53
#[ derive( Debug , Clone ) ]
54
+ // Invariant: If lower then buf is valid UTF-8.
53
55
struct MaybeLower < ' a > {
54
56
buf : & ' a [ u8 ] ,
55
57
lower : bool ,
@@ -986,6 +988,8 @@ standard_headers! {
986
988
/// / DIGIT / ALPHA
987
989
/// ; any VCHAR, except delimiters
988
990
/// ```
991
+ // HEADER_CHARS maps every byte that is 128 or larger to 0 so everything that is
992
+ // mapped by HEADER_CHARS, maps to a valid single-byte UTF-8 codepoint.
989
993
const HEADER_CHARS : [ u8 ; 256 ] = [
990
994
// 0 1 2 3 4 5 6 7 8 9
991
995
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // x
@@ -1017,6 +1021,8 @@ const HEADER_CHARS: [u8; 256] = [
1017
1021
] ;
1018
1022
1019
1023
/// Valid header name characters for HTTP/2.0 and HTTP/3.0
1024
+ // HEADER_CHARS_H2 maps every byte that is 128 or larger to 0 so everything that is
1025
+ // mapped by HEADER_CHARS_H2, maps to a valid single-byte UTF-8 codepoint.
1020
1026
const HEADER_CHARS_H2 : [ u8 ; 256 ] = [
1021
1027
// 0 1 2 3 4 5 6 7 8 9
1022
1028
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // x
@@ -1049,15 +1055,18 @@ const HEADER_CHARS_H2: [u8; 256] = [
1049
1055
1050
1056
fn parse_hdr < ' a > (
1051
1057
data : & ' a [ u8 ] ,
1052
- b : & ' a mut [ u8 ; 64 ] ,
1058
+ b : & ' a mut [ MaybeUninit < u8 > ; SCRATCH_BUF_SIZE ] ,
1053
1059
table : & [ u8 ; 256 ] ,
1054
1060
) -> Result < HdrName < ' a > , InvalidHeaderName > {
1055
1061
match data. len ( ) {
1056
1062
0 => Err ( InvalidHeaderName :: new ( ) ) ,
1057
- len @ 1 ..=64 => {
1063
+ len @ 1 ..=SCRATCH_BUF_SIZE => {
1058
1064
// Read from data into the buffer - transforming using `table` as we go
1059
- data. iter ( ) . zip ( b. iter_mut ( ) ) . for_each ( |( index, out) | * out = table[ * index as usize ] ) ;
1060
- let name = & b[ 0 ..len] ;
1065
+ data. iter ( )
1066
+ . zip ( b. iter_mut ( ) )
1067
+ . for_each ( |( index, out) | * out = MaybeUninit :: new ( table[ * index as usize ] ) ) ;
1068
+ // Safety: len bytes of b were just initialized.
1069
+ let name: & ' a [ u8 ] = unsafe { slice_assume_init ( & b[ 0 ..len] ) } ;
1061
1070
match StandardHeader :: from_bytes ( name) {
1062
1071
Some ( sh) => Ok ( sh. into ( ) ) ,
1063
1072
None => {
@@ -1069,7 +1078,7 @@ fn parse_hdr<'a>(
1069
1078
}
1070
1079
}
1071
1080
}
1072
- 65 ..=super :: MAX_HEADER_NAME_LEN => Ok ( HdrName :: custom ( data, false ) ) ,
1081
+ 0 ..=super :: MAX_HEADER_NAME_LEN => Ok ( HdrName :: custom ( data, false ) ) ,
1073
1082
_ => Err ( InvalidHeaderName :: new ( ) ) ,
1074
1083
}
1075
1084
}
@@ -1086,14 +1095,14 @@ impl HeaderName {
1086
1095
/// Converts a slice of bytes to an HTTP header name.
1087
1096
///
1088
1097
/// This function normalizes the input.
1089
- #[ allow( deprecated) ]
1090
1098
pub fn from_bytes ( src : & [ u8 ] ) -> Result < HeaderName , InvalidHeaderName > {
1091
- # [ allow ( deprecated ) ]
1092
- let mut buf = unsafe { mem :: uninitialized ( ) } ;
1099
+ let mut buf = uninit_u8_array ( ) ;
1100
+ // Precondition: HEADER_CHARS is a valid table for parse_hdr().
1093
1101
match parse_hdr ( src, & mut buf, & HEADER_CHARS ) ?. inner {
1094
1102
Repr :: Standard ( std) => Ok ( std. into ( ) ) ,
1095
1103
Repr :: Custom ( MaybeLower { buf, lower : true } ) => {
1096
1104
let buf = Bytes :: copy_from_slice ( buf) ;
1105
+ // Safety: the invariant on MaybeLower ensures buf is valid UTF-8.
1097
1106
let val = unsafe { ByteStr :: from_utf8_unchecked ( buf) } ;
1098
1107
Ok ( Custom ( val) . into ( ) )
1099
1108
}
@@ -1102,6 +1111,7 @@ impl HeaderName {
1102
1111
let mut dst = BytesMut :: with_capacity ( buf. len ( ) ) ;
1103
1112
1104
1113
for b in buf. iter ( ) {
1114
+ // HEADER_CHARS maps all bytes to valid single-byte UTF-8
1105
1115
let b = HEADER_CHARS [ * b as usize ] ;
1106
1116
1107
1117
if b == 0 {
@@ -1111,6 +1121,9 @@ impl HeaderName {
1111
1121
dst. put_u8 ( b) ;
1112
1122
}
1113
1123
1124
+ // Safety: the loop above maps all bytes in buf to valid single byte
1125
+ // UTF-8 before copying them into dst. This means that dst (and hence
1126
+ // dst.freeze()) is valid UTF-8.
1114
1127
let val = unsafe { ByteStr :: from_utf8_unchecked ( dst. freeze ( ) ) } ;
1115
1128
1116
1129
Ok ( Custom ( val) . into ( ) )
@@ -1136,25 +1149,29 @@ impl HeaderName {
1136
1149
/// // Parsing a header that contains uppercase characters
1137
1150
/// assert!(HeaderName::from_lowercase(b"Content-Length").is_err());
1138
1151
/// ```
1139
- #[ allow( deprecated) ]
1140
1152
pub fn from_lowercase ( src : & [ u8 ] ) -> Result < HeaderName , InvalidHeaderName > {
1141
- # [ allow ( deprecated ) ]
1142
- let mut buf = unsafe { mem :: uninitialized ( ) } ;
1153
+ let mut buf = uninit_u8_array ( ) ;
1154
+ // Precondition: HEADER_CHARS_H2 is a valid table for parse_hdr()
1143
1155
match parse_hdr ( src, & mut buf, & HEADER_CHARS_H2 ) ?. inner {
1144
1156
Repr :: Standard ( std) => Ok ( std. into ( ) ) ,
1145
1157
Repr :: Custom ( MaybeLower { buf, lower : true } ) => {
1146
1158
let buf = Bytes :: copy_from_slice ( buf) ;
1159
+ // Safety: the invariant on MaybeLower ensures buf is valid UTF-8.
1147
1160
let val = unsafe { ByteStr :: from_utf8_unchecked ( buf) } ;
1148
1161
Ok ( Custom ( val) . into ( ) )
1149
1162
}
1150
1163
Repr :: Custom ( MaybeLower { buf, lower : false } ) => {
1151
1164
for & b in buf. iter ( ) {
1165
+ // HEADER_CHARS maps all bytes that are not valid single-byte
1166
+ // UTF-8 to 0 so this check returns an error for invalid UTF-8.
1152
1167
if b != HEADER_CHARS [ b as usize ] {
1153
1168
return Err ( InvalidHeaderName :: new ( ) ) ;
1154
1169
}
1155
1170
}
1156
1171
1157
1172
let buf = Bytes :: copy_from_slice ( buf) ;
1173
+ // Safety: the loop above checks that each byte of buf (either
1174
+ // version) is valid UTF-8.
1158
1175
let val = unsafe { ByteStr :: from_utf8_unchecked ( buf) } ;
1159
1176
Ok ( Custom ( val) . into ( ) )
1160
1177
}
@@ -1481,33 +1498,33 @@ impl Error for InvalidHeaderName {}
1481
1498
// ===== HdrName =====
1482
1499
1483
1500
impl < ' a > HdrName < ' a > {
1501
+ // Precondition: if lower then buf is valid UTF-8
1484
1502
fn custom ( buf : & ' a [ u8 ] , lower : bool ) -> HdrName < ' a > {
1485
1503
HdrName {
1504
+ // Invariant (on MaybeLower): follows from the precondition
1486
1505
inner : Repr :: Custom ( MaybeLower {
1487
1506
buf : buf,
1488
1507
lower : lower,
1489
1508
} ) ,
1490
1509
}
1491
1510
}
1492
1511
1493
- #[ allow( deprecated) ]
1494
1512
pub fn from_bytes < F , U > ( hdr : & [ u8 ] , f : F ) -> Result < U , InvalidHeaderName >
1495
1513
where F : FnOnce ( HdrName < ' _ > ) -> U ,
1496
1514
{
1497
- # [ allow ( deprecated ) ]
1498
- let mut buf = unsafe { mem :: uninitialized ( ) } ;
1515
+ let mut buf = uninit_u8_array ( ) ;
1516
+ // Precondition: HEADER_CHARS is a valid table for parse_hdr().
1499
1517
let hdr = parse_hdr ( hdr, & mut buf, & HEADER_CHARS ) ?;
1500
1518
Ok ( f ( hdr) )
1501
1519
}
1502
1520
1503
- #[ allow( deprecated) ]
1504
1521
pub fn from_static < F , U > ( hdr : & ' static str , f : F ) -> U
1505
1522
where
1506
1523
F : FnOnce ( HdrName < ' _ > ) -> U ,
1507
1524
{
1508
- #[ allow( deprecated) ]
1509
- let mut buf = unsafe { mem:: uninitialized ( ) } ;
1525
+ let mut buf = uninit_u8_array ( ) ;
1510
1526
let hdr =
1527
+ // Precondition: HEADER_CHARS is a valid table for parse_hdr().
1511
1528
parse_hdr ( hdr. as_bytes ( ) , & mut buf, & HEADER_CHARS ) . expect ( "static str is invalid name" ) ;
1512
1529
f ( hdr)
1513
1530
}
@@ -1523,6 +1540,7 @@ impl<'a> From<HdrName<'a>> for HeaderName {
1523
1540
Repr :: Custom ( maybe_lower) => {
1524
1541
if maybe_lower. lower {
1525
1542
let buf = Bytes :: copy_from_slice ( & maybe_lower. buf [ ..] ) ;
1543
+ // Safety: the invariant on MaybeLower ensures buf is valid UTF-8.
1526
1544
let byte_str = unsafe { ByteStr :: from_utf8_unchecked ( buf) } ;
1527
1545
1528
1546
HeaderName {
@@ -1533,9 +1551,14 @@ impl<'a> From<HdrName<'a>> for HeaderName {
1533
1551
let mut dst = BytesMut :: with_capacity ( maybe_lower. buf . len ( ) ) ;
1534
1552
1535
1553
for b in maybe_lower. buf . iter ( ) {
1554
+ // HEADER_CHARS maps each byte to a valid single-byte UTF-8
1555
+ // codepoint.
1536
1556
dst. put_u8 ( HEADER_CHARS [ * b as usize ] ) ;
1537
1557
}
1538
1558
1559
+ // Safety: the loop above maps each byte of maybe_lower.buf to a
1560
+ // valid single-byte UTF-8 codepoint before copying it into dst.
1561
+ // dst (and hence dst.freeze()) is thus valid UTF-8.
1539
1562
let buf = unsafe { ByteStr :: from_utf8_unchecked ( dst. freeze ( ) ) } ;
1540
1563
1541
1564
HeaderName {
@@ -1606,6 +1629,25 @@ fn eq_ignore_ascii_case(lower: &[u8], s: &[u8]) -> bool {
1606
1629
} )
1607
1630
}
1608
1631
1632
+ // Utility functions for MaybeUninit<>. These are drawn from unstable API's on
1633
+ // MaybeUninit<> itself.
1634
+ const SCRATCH_BUF_SIZE : usize = 64 ;
1635
+
1636
+ fn uninit_u8_array ( ) -> [ MaybeUninit < u8 > ; SCRATCH_BUF_SIZE ] {
1637
+ let arr = MaybeUninit :: < [ MaybeUninit < u8 > ; SCRATCH_BUF_SIZE ] > :: uninit ( ) ;
1638
+ // Safety: assume_init() is claiming that an array of MaybeUninit<>
1639
+ // has been initilized, but MaybeUninit<>'s do not require initilizaton.
1640
+ unsafe { arr. assume_init ( ) }
1641
+ }
1642
+
1643
+ // Assuming all the elements are initilized, get a slice of them.
1644
+ //
1645
+ // Safety: All elements of `slice` must be initilized to prevent
1646
+ // undefined behavior.
1647
+ unsafe fn slice_assume_init < T > ( slice : & [ MaybeUninit < T > ] ) -> & [ T ] {
1648
+ & * ( slice as * const [ MaybeUninit < T > ] as * const [ T ] )
1649
+ }
1650
+
1609
1651
#[ cfg( test) ]
1610
1652
mod tests {
1611
1653
use super :: * ;
@@ -1652,6 +1694,7 @@ mod tests {
1652
1694
#[ test]
1653
1695
#[ should_panic]
1654
1696
fn test_static_invalid_name_lengths ( ) {
1697
+ // Safety: ONE_TOO_LONG contains only the UTF-8 safe, single-byte codepoint b'a'.
1655
1698
let _ = HeaderName :: from_static ( unsafe { std:: str:: from_utf8_unchecked ( ONE_TOO_LONG ) } ) ;
1656
1699
}
1657
1700
0 commit comments