@@ -4,9 +4,9 @@ use std::str::FromStr;
4
4
5
5
use async_std:: io:: { BufReader , Read , Write } ;
6
6
use async_std:: prelude:: * ;
7
- use http_types:: headers:: { CONTENT_LENGTH , EXPECT , HOST , TRANSFER_ENCODING } ;
7
+ use http_types:: headers:: { CONTENT_LENGTH , EXPECT , TRANSFER_ENCODING } ;
8
8
use http_types:: { ensure, ensure_eq, format_err} ;
9
- use http_types:: { Body , Method , Request } ;
9
+ use http_types:: { Body , Method , Request , Url } ;
10
10
11
11
use crate :: chunked:: ChunkedDecoder ;
12
12
use crate :: { MAX_HEADERS , MAX_HEAD_LENGTH } ;
56
56
let method = httparse_req. method ;
57
57
let method = method. ok_or_else ( || format_err ! ( "No method found" ) ) ?;
58
58
59
- let path = httparse_req. path ;
60
- let path = path. ok_or_else ( || format_err ! ( "No uri found" ) ) ?;
61
-
62
59
let version = httparse_req. version ;
63
60
let version = version. ok_or_else ( || format_err ! ( "No version found" ) ) ?;
64
61
@@ -69,16 +66,14 @@ where
69
66
version
70
67
) ;
71
68
72
- let mut req = Request :: new (
73
- Method :: from_str ( method) ?,
74
- url:: Url :: parse ( "http://_" ) . unwrap ( ) . join ( path) ?,
75
- ) ;
69
+ let url = url_from_httparse_req ( & httparse_req) ?;
70
+
71
+ let mut req = Request :: new ( Method :: from_str ( method) ?, url) ;
76
72
77
73
for header in httparse_req. headers . iter ( ) {
78
74
req. insert_header ( header. name , std:: str:: from_utf8 ( header. value ) ?) ;
79
75
}
80
76
81
- set_url_and_port_from_host_header ( & mut req) ?;
82
77
handle_100_continue ( & req, & mut io) . await ?;
83
78
84
79
let content_length = req. header ( CONTENT_LENGTH ) ;
@@ -109,23 +104,27 @@ where
109
104
Ok ( Some ( req) )
110
105
}
111
106
112
- fn set_url_and_port_from_host_header ( req : & mut Request ) -> http_types:: Result < ( ) > {
107
+ fn url_from_httparse_req ( req : & httparse:: Request < ' _ , ' _ > ) -> http_types:: Result < Url > {
108
+ let path = req. path . ok_or_else ( || format_err ! ( "No uri found" ) ) ?;
113
109
let host = req
114
- . header ( HOST )
115
- . map ( |header| header. last ( ) ) // There must only exactly one Host header, so this is permissive
116
- . ok_or_else ( || format_err ! ( "Mandatory Host header missing" ) ) ? // https://tools.ietf.org/html/rfc7230#section-5.4
117
- . to_string ( ) ;
118
-
119
- if let Some ( colon) = host. find ( ":" ) {
120
- req. url_mut ( ) . set_host ( Some ( & host[ 0 ..colon] ) ) ?;
121
- req. url_mut ( )
122
- . set_port ( host[ colon + 1 ..] . parse ( ) . ok ( ) )
123
- . unwrap ( ) ;
110
+ . headers
111
+ . iter ( )
112
+ . filter ( |x| x. name . eq_ignore_ascii_case ( "host" ) )
113
+ . next ( )
114
+ . ok_or_else ( || format_err ! ( "Mandatory Host header missing" ) ) ?
115
+ . value ;
116
+
117
+ let host = std:: str:: from_utf8 ( host) ?;
118
+
119
+ if path. starts_with ( "http://" ) || path. starts_with ( "https://" ) {
120
+ Ok ( Url :: parse ( path) ?)
121
+ } else if path. starts_with ( "/" ) {
122
+ Ok ( Url :: parse ( & format ! ( "http://{}/" , host) ) ?. join ( path) ?)
123
+ } else if req. method . unwrap ( ) . eq_ignore_ascii_case ( "connect" ) {
124
+ Ok ( Url :: parse ( & format ! ( "http://{}/" , path) ) ?)
124
125
} else {
125
- req . url_mut ( ) . set_host ( Some ( & host ) ) ? ;
126
+ Err ( format_err ! ( "unexpected uri format" ) )
126
127
}
127
-
128
- Ok ( ( ) )
129
128
}
130
129
131
130
const EXPECT_HEADER_VALUE : & str = "100-continue" ;
@@ -146,103 +145,96 @@ where
146
145
mod tests {
147
146
use super :: * ;
148
147
149
- #[ test]
150
- fn handle_100_continue_does_nothing_with_no_expect_header ( ) {
151
- let request = Request :: new ( Method :: Get , url:: Url :: parse ( "x:" ) . unwrap ( ) ) ;
152
- let mut io = async_std:: io:: Cursor :: new ( vec ! [ ] ) ;
153
- let result = async_std:: task:: block_on ( handle_100_continue ( & request, & mut io) ) ;
154
- assert_eq ! ( std:: str :: from_utf8( & io. into_inner( ) ) . unwrap( ) , "" ) ;
155
- assert ! ( result. is_ok( ) ) ;
148
+ fn httparse_req ( buf : & str , f : impl Fn ( httparse:: Request < ' _ , ' _ > ) ) {
149
+ let mut headers = [ httparse:: EMPTY_HEADER ; MAX_HEADERS ] ;
150
+ let mut res = httparse:: Request :: new ( & mut headers[ ..] ) ;
151
+ res. parse ( buf. as_bytes ( ) ) . unwrap ( ) ;
152
+ f ( res)
156
153
}
157
154
158
155
#[ test]
159
- fn handle_100_continue_sends_header_if_expects_is_exactly_right ( ) {
160
- let mut request = Request :: new ( Method :: Get , url:: Url :: parse ( "x:" ) . unwrap ( ) ) ;
161
- request. append_header ( "expect" , "100-continue" ) ;
162
- let mut io = async_std:: io:: Cursor :: new ( vec ! [ ] ) ;
163
- let result = async_std:: task:: block_on ( handle_100_continue ( & request, & mut io) ) ;
164
- assert_eq ! (
165
- std:: str :: from_utf8( & io. into_inner( ) ) . unwrap( ) ,
166
- "HTTP/1.1 100 Continue\r \n "
156
+ fn url_for_connect ( ) {
157
+ httparse_req (
158
+ "CONNECT server.example.com:443 HTTP/1.1\r \n Host: server.example.com:443\r \n " ,
159
+ |req| {
160
+ let url = url_from_httparse_req ( & req) . unwrap ( ) ;
161
+ assert_eq ! ( url. as_str( ) , "http://server.example.com:443/" ) ;
162
+ } ,
167
163
) ;
168
- assert ! ( result. is_ok( ) ) ;
169
164
}
170
165
171
166
#[ test]
172
- fn handle_100_continue_does_nothing_if_expects_header_is_wrong ( ) {
173
- let mut request = Request :: new ( Method :: Get , url:: Url :: parse ( "x:" ) . unwrap ( ) ) ;
174
- request. append_header ( "expect" , "110-extensions-not-allowed" ) ;
175
- let mut io = async_std:: io:: Cursor :: new ( vec ! [ ] ) ;
176
- let result = async_std:: task:: block_on ( handle_100_continue ( & request, & mut io) ) ;
177
- assert_eq ! ( std:: str :: from_utf8( & io. into_inner( ) ) . unwrap( ) , "" ) ;
178
- assert ! ( result. is_ok( ) ) ;
167
+ fn url_for_host_plus_path ( ) {
168
+ httparse_req (
169
+ "GET /some/resource HTTP/1.1\r \n Host: server.example.com:443\r \n " ,
170
+ |req| {
171
+ let url = url_from_httparse_req ( & req) . unwrap ( ) ;
172
+ assert_eq ! ( url. as_str( ) , "http://server.example.com:443/some/resource" ) ;
173
+ } ,
174
+ )
179
175
}
180
176
181
177
#[ test]
182
- fn test_setting_host_with_no_port ( ) {
183
- let mut request = request_with_host_header ( "subdomain.mydomain.tld" ) ;
184
- set_url_and_port_from_host_header ( & mut request) . unwrap ( ) ;
185
- assert_eq ! (
186
- request. url( ) ,
187
- & url:: Url :: parse( "http://subdomain.mydomain.tld/some/path" ) . unwrap( )
188
- ) ;
178
+ fn url_for_host_plus_absolute_url ( ) {
179
+ httparse_req (
180
+ "GET http://domain.com/some/resource HTTP/1.1\r \n Host: server.example.com\r \n " ,
181
+ |req| {
182
+ let url = url_from_httparse_req ( & req) . unwrap ( ) ;
183
+ assert_eq ! ( url. as_str( ) , "http://domain.com/some/resource" ) ; // host header MUST be ignored according to spec
184
+ } ,
185
+ )
189
186
}
190
187
191
188
#[ test]
192
- fn test_setting_host_with_a_port ( ) {
193
- let mut request = request_with_host_header ( "subdomain.mydomain.tld:8080" ) ;
194
- set_url_and_port_from_host_header ( & mut request) . unwrap ( ) ;
195
- assert_eq ! (
196
- request. url( ) ,
197
- & url:: Url :: parse( "http://subdomain.mydomain.tld:8080/some/path" ) . unwrap( )
198
- ) ;
189
+ fn url_for_conflicting_connect ( ) {
190
+ httparse_req (
191
+ "CONNECT server.example.com:443 HTTP/1.1\r \n Host: conflicting.host\r \n " ,
192
+ |req| {
193
+ let url = url_from_httparse_req ( & req) . unwrap ( ) ;
194
+ assert_eq ! ( url. as_str( ) , "http://server.example.com:443/" ) ;
195
+ } ,
196
+ )
199
197
}
200
198
201
199
#[ test]
202
- fn test_setting_host_with_an_ip_and_port ( ) {
203
- let mut request = request_with_host_header ( "12.34.56.78:90" ) ;
204
- set_url_and_port_from_host_header ( & mut request ) . unwrap ( ) ;
205
- assert_eq ! (
206
- request . url ( ) ,
207
- & url :: Url :: parse ( "http://12.34.56.78:90/some/path" ) . unwrap ( )
208
- ) ;
200
+ fn url_for_malformed_resource_path ( ) {
201
+ httparse_req (
202
+ "GET not-a-url HTTP/1.1 \r \n Host: server.example.com \r \n " ,
203
+ |req| {
204
+ assert ! ( url_from_httparse_req ( & req ) . is_err ( ) ) ;
205
+ } ,
206
+ )
209
207
}
210
208
211
209
#[ test]
212
- fn test_malformed_nonnumeric_port_is_ignored ( ) {
213
- let mut request = request_with_host_header ( "hello.world:uh-oh" ) ;
214
- set_url_and_port_from_host_header ( & mut request) . unwrap ( ) ;
215
- assert_eq ! (
216
- request. url( ) ,
217
- & url:: Url :: parse( "http://hello.world/some/path" ) . unwrap( )
218
- ) ;
210
+ fn handle_100_continue_does_nothing_with_no_expect_header ( ) {
211
+ let request = Request :: new ( Method :: Get , Url :: parse ( "x:" ) . unwrap ( ) ) ;
212
+ let mut io = async_std:: io:: Cursor :: new ( vec ! [ ] ) ;
213
+ let result = async_std:: task:: block_on ( handle_100_continue ( & request, & mut io) ) ;
214
+ assert_eq ! ( std:: str :: from_utf8( & io. into_inner( ) ) . unwrap( ) , "" ) ;
215
+ assert ! ( result. is_ok( ) ) ;
219
216
}
220
217
221
218
#[ test]
222
- fn test_malformed_trailing_colon_is_ignored ( ) {
223
- let mut request = request_with_host_header ( "edge.cases:" ) ;
224
- set_url_and_port_from_host_header ( & mut request) . unwrap ( ) ;
219
+ fn handle_100_continue_sends_header_if_expects_is_exactly_right ( ) {
220
+ let mut request = Request :: new ( Method :: Get , Url :: parse ( "x:" ) . unwrap ( ) ) ;
221
+ request. append_header ( "expect" , "100-continue" ) ;
222
+ let mut io = async_std:: io:: Cursor :: new ( vec ! [ ] ) ;
223
+ let result = async_std:: task:: block_on ( handle_100_continue ( & request, & mut io) ) ;
225
224
assert_eq ! (
226
- request . url ( ) ,
227
- & url :: Url :: parse ( "http://edge.cases/some/path" ) . unwrap ( )
225
+ std :: str :: from_utf8 ( & io . into_inner ( ) ) . unwrap ( ) ,
226
+ "HTTP/1.1 100 Continue \r \n "
228
227
) ;
228
+ assert ! ( result. is_ok( ) ) ;
229
229
}
230
230
231
231
#[ test]
232
- fn test_malformed_leading_colon_is_invalid_host_value ( ) {
233
- let mut request = request_with_host_header ( ":300" ) ;
234
- assert ! ( set_url_and_port_from_host_header( & mut request) . is_err( ) ) ;
235
- }
236
-
237
- #[ test]
238
- fn test_malformed_invalid_url_host_is_invalid_host_header_value ( ) {
239
- let mut request = request_with_host_header ( " " ) ;
240
- assert ! ( set_url_and_port_from_host_header( & mut request) . is_err( ) ) ;
241
- }
242
-
243
- fn request_with_host_header ( host : & str ) -> Request {
244
- let mut req = Request :: new ( Method :: Get , url:: Url :: parse ( "http://_/some/path" ) . unwrap ( ) ) ;
245
- req. insert_header ( HOST , host) ;
246
- req
232
+ fn handle_100_continue_does_nothing_if_expects_header_is_wrong ( ) {
233
+ let mut request = Request :: new ( Method :: Get , Url :: parse ( "x:" ) . unwrap ( ) ) ;
234
+ request. append_header ( "expect" , "110-extensions-not-allowed" ) ;
235
+ let mut io = async_std:: io:: Cursor :: new ( vec ! [ ] ) ;
236
+ let result = async_std:: task:: block_on ( handle_100_continue ( & request, & mut io) ) ;
237
+ assert_eq ! ( std:: str :: from_utf8( & io. into_inner( ) ) . unwrap( ) , "" ) ;
238
+ assert ! ( result. is_ok( ) ) ;
247
239
}
248
240
}
0 commit comments