@@ -8,6 +8,9 @@ pub use common::RequestError;
88use common:: { Body , Method , Version } ;
99use headers:: Headers ;
1010
11+ // This type represents the RequestLine raw parts: method, uri and version.
12+ type RequestLineParts < ' a > = ( & ' a [ u8 ] , & ' a [ u8 ] , & ' a [ u8 ] ) ;
13+
1114/// Finds the first occurence of `sequence` in the `bytes` slice.
1215///
1316/// Returns the starting position of the `sequence` in `bytes` or `None` if the
@@ -57,13 +60,15 @@ impl Uri {
5760 const HTTP_SCHEME_PREFIX : & str = "http://" ;
5861
5962 if self . string . starts_with ( HTTP_SCHEME_PREFIX ) {
63+ // Slice access is safe because we checked above that `self.string` size <= `HTTP_SCHEME_PREFIX.len()`.
6064 let without_scheme = & self . string [ HTTP_SCHEME_PREFIX . len ( ) ..] ;
6165 if without_scheme. is_empty ( ) {
6266 return "" ;
6367 }
6468 // The host in this case includes the port and contains the bytes after http:// up to
6569 // the next '/'.
6670 match without_scheme. bytes ( ) . position ( |byte| byte == b'/' ) {
71+ // Slice access is safe because `position` validates that `len` is a valid index.
6772 Some ( len) => & without_scheme[ len..] ,
6873 None => "" ,
6974 }
@@ -86,24 +91,36 @@ pub struct RequestLine {
8691}
8792
8893impl RequestLine {
89- fn parse_request_line ( request_line : & [ u8 ] ) -> ( & [ u8 ] , & [ u8 ] , & [ u8 ] ) {
94+ fn parse_request_line (
95+ request_line : & [ u8 ] ,
96+ ) -> std:: result:: Result < RequestLineParts , RequestError > {
9097 if let Some ( method_end) = find ( request_line, & [ SP ] ) {
98+ // The slice access is safe because `find` validates that `method_end` < `request_line` size.
9199 let method = & request_line[ ..method_end] ;
92100
93- let uri_and_version = & request_line[ ( method_end + 1 ) ..] ;
101+ // `uri_start` <= `request_line` size.
102+ let uri_start = method_end. checked_add ( 1 ) . ok_or ( RequestError :: Overflow ) ?;
103+
104+ // Slice access is safe because `uri_start` <= `request_line` size.
105+ // If `uri_start` == `request_line` size, then `uri_and_version` will be an empty slice.
106+ let uri_and_version = & request_line[ uri_start..] ;
94107
95108 if let Some ( uri_end) = find ( uri_and_version, & [ SP ] ) {
109+ // Slice access is safe because `find` validates that `uri_end` < `uri_and_version` size.
96110 let uri = & uri_and_version[ ..uri_end] ;
97111
98- let version = & uri_and_version[ ( uri_end + 1 ) ..] ;
112+ // `version_start` <= `uri_and_version` size.
113+ let version_start = uri_end. checked_add ( 1 ) . ok_or ( RequestError :: Overflow ) ?;
99114
100- return ( method , uri , version ) ;
101- }
115+ // Slice access is safe because `version_start` <= `uri_and_version` size.
116+ let version = & uri_and_version [ version_start.. ] ;
102117
103- return ( method, uri_and_version, b"" ) ;
118+ return Ok ( ( method, uri, version) ) ;
119+ }
104120 }
105121
106- ( b"" , b"" , b"" )
122+ // Request Line can be valid only if it contains the method, uri and version separated with SP.
123+ Err ( RequestError :: InvalidRequest )
107124 }
108125
109126 /// Tries to parse a byte stream in a request line. Fails if the request line is malformed.
@@ -113,7 +130,7 @@ impl RequestLine {
113130 /// `InvalidHttpVersion` is returned if the specified HTTP version is unsupported.
114131 /// `InvalidUri` is returned if the specified Uri is not valid.
115132 pub fn try_from ( request_line : & [ u8 ] ) -> Result < Self , RequestError > {
116- let ( method, uri, version) = Self :: parse_request_line ( request_line) ;
133+ let ( method, uri, version) = Self :: parse_request_line ( request_line) ? ;
117134
118135 Ok ( Self {
119136 method : Method :: try_from ( method) ?,
@@ -123,9 +140,10 @@ impl RequestLine {
123140 }
124141
125142 // Returns the minimum length of a valid request. The request must contain
126- // the method (GET), the URI (minmum 1 character), the HTTP version(HTTP/DIGIT.DIGIT) and
143+ // the method (GET), the URI (minimum 1 character), the HTTP version(HTTP/DIGIT.DIGIT) and
127144 // 2 separators (SP).
128145 fn min_len ( ) -> usize {
146+ // Addition is safe because these are small constants.
129147 Method :: Get . raw ( ) . len ( ) + 1 + Version :: Http10 . raw ( ) . len ( ) + 2
130148 }
131149}
@@ -148,8 +166,9 @@ impl Request {
148166 /// The byte slice is expected to have the following format: </br>
149167 /// * Request Line: "GET SP Request-uri SP HTTP/1.0 CRLF" - Mandatory </br>
150168 /// * Request Headers "<headers> CRLF"- Optional </br>
169+ /// * Empty Line "CRLF" </br>
151170 /// * Entity Body - Optional </br>
152- /// The request headers and the entity body is not parsed and None is returned because
171+ /// The request headers and the entity body are not parsed and None is returned because
153172 /// these are not used by the MMDS server.
154173 /// The only supported method is GET and the HTTP protocol is expected to be HTTP/1.0
155174 /// or HTTP/1.1.
@@ -163,7 +182,7 @@ impl Request {
163182 /// extern crate micro_http;
164183 /// use micro_http::Request;
165184 ///
166- /// let http_request = Request::try_from(b"GET http://localhost/home HTTP/1.0\r\n" );
185+ /// let http_request = Request::try_from(b"GET http://localhost/home HTTP/1.0\r\n\r\n").unwrap( );
167186 /// ```
168187 pub fn try_from ( byte_stream : & [ u8 ] ) -> Result < Self , RequestError > {
169188 // The first line of the request is the Request Line. The line ending is CR LF.
@@ -173,6 +192,7 @@ impl Request {
173192 None => return Err ( RequestError :: InvalidRequest ) ,
174193 } ;
175194
195+ // Slice access is safe because `find` validates that `request_line_end` < `byte_stream` size.
176196 let request_line_bytes = & byte_stream[ ..request_line_end] ;
177197 if request_line_bytes. len ( ) < RequestLine :: min_len ( ) {
178198 return Err ( RequestError :: InvalidRequest ) ;
@@ -193,8 +213,20 @@ impl Request {
193213 Some ( headers_end) => {
194214 // Parse the request headers.
195215 // Start by removing the leading CR LF from them.
196- let headers_and_body = & byte_stream[ ( request_line_end + CRLF_LEN ) ..] ;
216+ let headers_start = request_line_end
217+ . checked_add ( CRLF_LEN )
218+ . ok_or ( RequestError :: Overflow ) ?;
219+ // Slice access is safe because starting from `request_line_end` there are at least two CRLF
220+ // (enforced by `find` at the start of this method).
221+ let headers_and_body = & byte_stream[ headers_start..] ;
222+ // Because we advanced the start with CRLF_LEN, we now have to subtract CRLF_LEN
223+ // from the end in order to keep the same window.
224+ // Underflow is not possible here because `byte_stream[request_line_end..]` starts with CR LF,
225+ // so `headers_end` can be either zero (this case is treated separately in the first match arm)
226+ // or >= 3 (current case).
197227 let headers_end = headers_end - CRLF_LEN ;
228+ // Slice access is safe because `headers_end` is checked above
229+ // (`find` gives a valid position, and subtracting 2 can't underflow).
198230 let headers = Headers :: try_from ( & headers_and_body[ ..headers_end] ) ?;
199231
200232 // Parse the body of the request.
@@ -205,16 +237,22 @@ impl Request {
205237 None
206238 }
207239 content_length => {
240+ // Multiplication is safe because `CRLF_LEN` is a small constant.
241+ let crlf_end = headers_end
242+ . checked_add ( 2 * CRLF_LEN )
243+ . ok_or ( RequestError :: Overflow ) ?;
244+ // This can't underflow because `headers_and_body.len()` >= `crlf_end`.
245+ let body_len = headers_and_body. len ( ) - crlf_end;
208246 // Headers suggest we have a body, but the buffer is shorter than the specified
209247 // content length.
210- if headers_and_body. len ( ) - ( headers_end + 2 * CRLF_LEN )
211- < content_length as usize
212- {
248+ if body_len < content_length as usize {
213249 return Err ( RequestError :: InvalidRequest ) ;
214250 }
215- let body_as_bytes = & headers_and_body[ ( headers_end + 2 * CRLF_LEN ) ..] ;
251+ // Slice access is safe because `crlf_end` is the index after two CRLF
252+ // (it is <= `headers_and_body` size).
253+ let body_as_bytes = & headers_and_body[ crlf_end..] ;
216254 // If the actual length of the body is different than the `Content-Length` value
217- // in the headers then this request is invalid.
255+ // in the headers, then this request is invalid.
218256 if body_as_bytes. len ( ) == content_length as usize {
219257 Some ( Body :: new ( body_as_bytes) )
220258 } else {
@@ -343,6 +381,13 @@ mod tests {
343381 expected_request_line
344382 ) ;
345383
384+ // Test for invalid request missing the separator.
385+ let request_line = b"GET" ;
386+ assert_eq ! (
387+ RequestLine :: try_from( request_line) . unwrap_err( ) ,
388+ RequestError :: InvalidRequest
389+ ) ;
390+
346391 // Test for invalid method.
347392 let request_line = b"POST http://localhost/home HTTP/1.0" ;
348393 assert_eq ! (
@@ -368,14 +413,14 @@ mod tests {
368413 let request_line = b"nothing" ;
369414 assert_eq ! (
370415 RequestLine :: try_from( request_line) . unwrap_err( ) ,
371- RequestError :: InvalidHttpMethod ( "Unsupported HTTP method." )
416+ RequestError :: InvalidRequest
372417 ) ;
373418
374419 // Test for invalid format with no version.
375420 let request_line = b"GET /" ;
376421 assert_eq ! (
377422 RequestLine :: try_from( request_line) . unwrap_err( ) ,
378- RequestError :: InvalidHttpVersion ( "Unsupported HTTP version." )
423+ RequestError :: InvalidRequest
379424 ) ;
380425 }
381426
@@ -398,6 +443,13 @@ mod tests {
398443 assert_eq ! ( request. http_version( ) , Version :: Http10 ) ;
399444 assert ! ( request. body. is_none( ) ) ;
400445
446+ // Test for invalid Request (missing CR LF).
447+ let request_bytes = b"GET / HTTP/1.1" ;
448+ assert_eq ! (
449+ Request :: try_from( request_bytes) . unwrap_err( ) ,
450+ RequestError :: InvalidRequest
451+ ) ;
452+
401453 // Test for invalid Request (length is less than minimum).
402454 let request_bytes = b"GET" ;
403455 assert_eq ! (
@@ -442,7 +494,6 @@ mod tests {
442494 // Test for an invalid content length.
443495 let request = Request :: try_from (
444496 b"PATCH http://localhost/home HTTP/1.1\r \n \
445- Expect: 100-continue\r \n \
446497 Content-Length: 5000\r \n \r \n this is a short body",
447498 )
448499 . unwrap_err ( ) ;
0 commit comments