@@ -4,9 +4,9 @@ use std::str::FromStr;
44
55use async_std:: io:: { BufReader , Read , Write } ;
66use async_std:: prelude:: * ;
7- use http_types:: headers:: { CONTENT_LENGTH , EXPECT , HOST , TRANSFER_ENCODING } ;
7+ use http_types:: headers:: { CONTENT_LENGTH , EXPECT , TRANSFER_ENCODING } ;
88use http_types:: { ensure, ensure_eq, format_err} ;
9- use http_types:: { Body , Method , Request } ;
9+ use http_types:: { Body , Method , Request , Url } ;
1010
1111use crate :: chunked:: ChunkedDecoder ;
1212use crate :: { MAX_HEADERS , MAX_HEAD_LENGTH } ;
5656 let method = httparse_req. method ;
5757 let method = method. ok_or_else ( || format_err ! ( "No method found" ) ) ?;
5858
59- let path = httparse_req. path ;
60- let path = path. ok_or_else ( || format_err ! ( "No uri found" ) ) ?;
61-
6259 let version = httparse_req. version ;
6360 let version = version. ok_or_else ( || format_err ! ( "No version found" ) ) ?;
6461
@@ -69,16 +66,14 @@ where
6966 version
7067 ) ;
7168
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) ;
7672
7773 for header in httparse_req. headers . iter ( ) {
7874 req. insert_header ( header. name , std:: str:: from_utf8 ( header. value ) ?) ;
7975 }
8076
81- set_url_and_port_from_host_header ( & mut req) ?;
8277 handle_100_continue ( & req, & mut io) . await ?;
8378
8479 let content_length = req. header ( CONTENT_LENGTH ) ;
@@ -109,23 +104,27 @@ where
109104 Ok ( Some ( req) )
110105}
111106
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" ) ) ?;
113109 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) ) ?)
124125 } else {
125- req . url_mut ( ) . set_host ( Some ( & host ) ) ? ;
126+ Err ( format_err ! ( "unexpected uri format" ) )
126127 }
127-
128- Ok ( ( ) )
129128}
130129
131130const EXPECT_HEADER_VALUE : & str = "100-continue" ;
@@ -146,103 +145,96 @@ where
146145mod tests {
147146 use super :: * ;
148147
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)
156153 }
157154
158155 #[ 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+ } ,
167163 ) ;
168- assert ! ( result. is_ok( ) ) ;
169164 }
170165
171166 #[ 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+ )
179175 }
180176
181177 #[ 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+ )
189186 }
190187
191188 #[ 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+ )
199197 }
200198
201199 #[ 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+ )
209207 }
210208
211209 #[ 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( ) ) ;
219216 }
220217
221218 #[ 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) ) ;
225224 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 "
228227 ) ;
228+ assert ! ( result. is_ok( ) ) ;
229229 }
230230
231231 #[ 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( ) ) ;
247239 }
248240}
0 commit comments