@@ -554,32 +554,38 @@ impl Pseudo {
554
554
pub fn request ( method : Method , uri : Uri , protocol : Option < Protocol > ) -> Self {
555
555
let parts = uri:: Parts :: from ( uri) ;
556
556
557
- let mut path = parts
558
- . path_and_query
559
- . map ( |v| BytesStr :: from ( v. as_str ( ) ) )
560
- . unwrap_or ( BytesStr :: from_static ( "" ) ) ;
561
-
562
- match method {
563
- Method :: OPTIONS | Method :: CONNECT => { }
564
- _ if path. is_empty ( ) => {
565
- path = BytesStr :: from_static ( "/" ) ;
566
- }
567
- _ => { }
568
- }
557
+ let ( scheme, path) = if method == Method :: CONNECT && protocol. is_none ( ) {
558
+ ( None , None )
559
+ } else {
560
+ let path = parts
561
+ . path_and_query
562
+ . map ( |v| BytesStr :: from ( v. as_str ( ) ) )
563
+ . unwrap_or ( BytesStr :: from_static ( "" ) ) ;
564
+
565
+ let path = if !path. is_empty ( ) {
566
+ path
567
+ } else {
568
+ if method == Method :: OPTIONS {
569
+ BytesStr :: from_static ( "*" )
570
+ } else {
571
+ BytesStr :: from_static ( "/" )
572
+ }
573
+ } ;
574
+
575
+ ( parts. scheme , Some ( path) )
576
+ } ;
569
577
570
578
let mut pseudo = Pseudo {
571
579
method : Some ( method) ,
572
580
scheme : None ,
573
581
authority : None ,
574
- path : Some ( path ) . filter ( |p| !p . is_empty ( ) ) ,
582
+ path,
575
583
protocol,
576
584
status : None ,
577
585
} ;
578
586
579
587
// If the URI includes a scheme component, add it to the pseudo headers
580
- //
581
- // TODO: Scheme must be set...
582
- if let Some ( scheme) = parts. scheme {
588
+ if let Some ( scheme) = scheme {
583
589
pseudo. set_scheme ( scheme) ;
584
590
}
585
591
@@ -1048,4 +1054,161 @@ mod test {
1048
1054
let mut buf = BytesMut :: new ( ) ;
1049
1055
huffman:: decode ( src, & mut buf) . unwrap ( )
1050
1056
}
1057
+
1058
+ #[ test]
1059
+ fn test_connect_request_pseudo_headers_omits_path_and_scheme ( ) {
1060
+ // a) CONNECT requests MUST NOT include :scheme & :path pseudo-header fields
1061
+ // See: https://datatracker.ietf.org/doc/html/rfc9113#section-8.5
1062
+
1063
+ assert_eq ! (
1064
+ Pseudo :: request(
1065
+ Method :: CONNECT ,
1066
+ Uri :: from_static( "https://example.com:8443" ) ,
1067
+ None
1068
+ ) ,
1069
+ Pseudo {
1070
+ method: Method :: CONNECT . into( ) ,
1071
+ authority: BytesStr :: from_static( "example.com:8443" ) . into( ) ,
1072
+ ..Default :: default ( )
1073
+ }
1074
+ ) ;
1075
+
1076
+ assert_eq ! (
1077
+ Pseudo :: request(
1078
+ Method :: CONNECT ,
1079
+ Uri :: from_static( "https://example.com/test" ) ,
1080
+ None
1081
+ ) ,
1082
+ Pseudo {
1083
+ method: Method :: CONNECT . into( ) ,
1084
+ authority: BytesStr :: from_static( "example.com" ) . into( ) ,
1085
+ ..Default :: default ( )
1086
+ }
1087
+ ) ;
1088
+
1089
+ assert_eq ! (
1090
+ Pseudo :: request( Method :: CONNECT , Uri :: from_static( "example.com:8443" ) , None ) ,
1091
+ Pseudo {
1092
+ method: Method :: CONNECT . into( ) ,
1093
+ authority: BytesStr :: from_static( "example.com:8443" ) . into( ) ,
1094
+ ..Default :: default ( )
1095
+ }
1096
+ ) ;
1097
+ }
1098
+
1099
+ #[ test]
1100
+ fn test_extended_connect_request_pseudo_headers_includes_path_and_scheme ( ) {
1101
+ // On requests that contain the :protocol pseudo-header field, the
1102
+ // :scheme and :path pseudo-header fields of the target URI (see
1103
+ // Section 5) MUST also be included.
1104
+ // See: https://datatracker.ietf.org/doc/html/rfc8441#section-4
1105
+
1106
+ assert_eq ! (
1107
+ Pseudo :: request(
1108
+ Method :: CONNECT ,
1109
+ Uri :: from_static( "https://example.com:8443" ) ,
1110
+ Protocol :: from_static( "the-bread-protocol" ) . into( )
1111
+ ) ,
1112
+ Pseudo {
1113
+ method: Method :: CONNECT . into( ) ,
1114
+ authority: BytesStr :: from_static( "example.com:8443" ) . into( ) ,
1115
+ scheme: BytesStr :: from_static( "https" ) . into( ) ,
1116
+ path: BytesStr :: from_static( "/" ) . into( ) ,
1117
+ protocol: Protocol :: from_static( "the-bread-protocol" ) . into( ) ,
1118
+ ..Default :: default ( )
1119
+ }
1120
+ ) ;
1121
+
1122
+ assert_eq ! (
1123
+ Pseudo :: request(
1124
+ Method :: CONNECT ,
1125
+ Uri :: from_static( "https://example.com:8443/test" ) ,
1126
+ Protocol :: from_static( "the-bread-protocol" ) . into( )
1127
+ ) ,
1128
+ Pseudo {
1129
+ method: Method :: CONNECT . into( ) ,
1130
+ authority: BytesStr :: from_static( "example.com:8443" ) . into( ) ,
1131
+ scheme: BytesStr :: from_static( "https" ) . into( ) ,
1132
+ path: BytesStr :: from_static( "/test" ) . into( ) ,
1133
+ protocol: Protocol :: from_static( "the-bread-protocol" ) . into( ) ,
1134
+ ..Default :: default ( )
1135
+ }
1136
+ ) ;
1137
+
1138
+ assert_eq ! (
1139
+ Pseudo :: request(
1140
+ Method :: CONNECT ,
1141
+ Uri :: from_static( "http://example.com/a/b/c" ) ,
1142
+ Protocol :: from_static( "the-bread-protocol" ) . into( )
1143
+ ) ,
1144
+ Pseudo {
1145
+ method: Method :: CONNECT . into( ) ,
1146
+ authority: BytesStr :: from_static( "example.com" ) . into( ) ,
1147
+ scheme: BytesStr :: from_static( "http" ) . into( ) ,
1148
+ path: BytesStr :: from_static( "/a/b/c" ) . into( ) ,
1149
+ protocol: Protocol :: from_static( "the-bread-protocol" ) . into( ) ,
1150
+ ..Default :: default ( )
1151
+ }
1152
+ ) ;
1153
+ }
1154
+
1155
+ #[ test]
1156
+ fn test_options_request_with_empty_path_has_asterisk_as_pseudo_path ( ) {
1157
+ // an OPTIONS request for an "http" or "https" URI that does not include a path component;
1158
+ // these MUST include a ":path" pseudo-header field with a value of '*' (see Section 7.1 of [HTTP]).
1159
+ // See: https://datatracker.ietf.org/doc/html/rfc9113#section-8.3.1
1160
+ assert_eq ! (
1161
+ Pseudo :: request( Method :: OPTIONS , Uri :: from_static( "example.com:8080" ) , None , ) ,
1162
+ Pseudo {
1163
+ method: Method :: OPTIONS . into( ) ,
1164
+ authority: BytesStr :: from_static( "example.com:8080" ) . into( ) ,
1165
+ path: BytesStr :: from_static( "*" ) . into( ) ,
1166
+ ..Default :: default ( )
1167
+ }
1168
+ ) ;
1169
+ }
1170
+
1171
+ #[ test]
1172
+ fn test_non_option_and_non_connect_requests_include_path_and_scheme ( ) {
1173
+ let methods = [
1174
+ Method :: GET ,
1175
+ Method :: POST ,
1176
+ Method :: PUT ,
1177
+ Method :: DELETE ,
1178
+ Method :: HEAD ,
1179
+ Method :: PATCH ,
1180
+ Method :: TRACE ,
1181
+ ] ;
1182
+
1183
+ for method in methods {
1184
+ assert_eq ! (
1185
+ Pseudo :: request(
1186
+ method. clone( ) ,
1187
+ Uri :: from_static( "http://example.com:8080" ) ,
1188
+ None ,
1189
+ ) ,
1190
+ Pseudo {
1191
+ method: method. clone( ) . into( ) ,
1192
+ authority: BytesStr :: from_static( "example.com:8080" ) . into( ) ,
1193
+ scheme: BytesStr :: from_static( "http" ) . into( ) ,
1194
+ path: BytesStr :: from_static( "/" ) . into( ) ,
1195
+ ..Default :: default ( )
1196
+ }
1197
+ ) ;
1198
+ assert_eq ! (
1199
+ Pseudo :: request(
1200
+ method. clone( ) ,
1201
+ Uri :: from_static( "https://example.com/a/b/c" ) ,
1202
+ None ,
1203
+ ) ,
1204
+ Pseudo {
1205
+ method: method. into( ) ,
1206
+ authority: BytesStr :: from_static( "example.com" ) . into( ) ,
1207
+ scheme: BytesStr :: from_static( "https" ) . into( ) ,
1208
+ path: BytesStr :: from_static( "/a/b/c" ) . into( ) ,
1209
+ ..Default :: default ( )
1210
+ }
1211
+ ) ;
1212
+ }
1213
+ }
1051
1214
}
0 commit comments