@@ -9,7 +9,10 @@ use crate::common::{Body, Method, Version};
9
9
10
10
pub use crate :: common:: RequestError ;
11
11
12
- /// Finds the first occurrence of `sequence` in the `bytes` slice.
12
+ // This type represents the RequestLine raw parts: method, uri and version.
13
+ type RequestLineParts < ' a > = ( & ' a [ u8 ] , & ' a [ u8 ] , & ' a [ u8 ] ) ;
14
+
15
+ /// Finds the first occurence of `sequence` in the `bytes` slice.
13
16
///
14
17
/// Returns the starting position of the `sequence` in `bytes` or `None` if the
15
18
/// `sequence` is not found.
@@ -58,13 +61,15 @@ impl Uri {
58
61
const HTTP_SCHEME_PREFIX : & str = "http://" ;
59
62
60
63
if self . string . starts_with ( HTTP_SCHEME_PREFIX ) {
64
+ // Slice access is safe because we checked above that `self.string` size <= `HTTP_SCHEME_PREFIX.len()`.
61
65
let without_scheme = & self . string [ HTTP_SCHEME_PREFIX . len ( ) ..] ;
62
66
if without_scheme. is_empty ( ) {
63
67
return "" ;
64
68
}
65
69
// The host in this case includes the port and contains the bytes after http:// up to
66
70
// the next '/'.
67
71
match without_scheme. bytes ( ) . position ( |byte| byte == b'/' ) {
72
+ // Slice access is safe because `position` validates that `len` is a valid index.
68
73
Some ( len) => & without_scheme[ len..] ,
69
74
None => "" ,
70
75
}
@@ -87,24 +92,36 @@ pub struct RequestLine {
87
92
}
88
93
89
94
impl RequestLine {
90
- fn parse_request_line ( request_line : & [ u8 ] ) -> ( & [ u8 ] , & [ u8 ] , & [ u8 ] ) {
95
+ fn parse_request_line (
96
+ request_line : & [ u8 ] ,
97
+ ) -> std:: result:: Result < RequestLineParts , RequestError > {
91
98
if let Some ( method_end) = find ( request_line, & [ SP ] ) {
99
+ // The slice access is safe because `find` validates that `method_end` < `request_line` size.
92
100
let method = & request_line[ ..method_end] ;
93
101
94
- let uri_and_version = & request_line[ ( method_end + 1 ) ..] ;
102
+ // `uri_start` <= `request_line` size.
103
+ let uri_start = method_end. checked_add ( 1 ) . ok_or ( RequestError :: Overflow ) ?;
104
+
105
+ // Slice access is safe because `uri_start` <= `request_line` size.
106
+ // If `uri_start` == `request_line` size, then `uri_and_version` will be an empty slice.
107
+ let uri_and_version = & request_line[ uri_start..] ;
95
108
96
109
if let Some ( uri_end) = find ( uri_and_version, & [ SP ] ) {
110
+ // Slice access is safe because `find` validates that `uri_end` < `uri_and_version` size.
97
111
let uri = & uri_and_version[ ..uri_end] ;
98
112
99
- let version = & uri_and_version[ ( uri_end + 1 ) ..] ;
113
+ // `version_start` <= `uri_and_version` size.
114
+ let version_start = uri_end. checked_add ( 1 ) . ok_or ( RequestError :: Overflow ) ?;
100
115
101
- return ( method , uri , version ) ;
102
- }
116
+ // Slice access is safe because `version_start` <= `uri_and_version` size.
117
+ let version = & uri_and_version [ version_start.. ] ;
103
118
104
- return ( method, uri_and_version, b"" ) ;
119
+ return Ok ( ( method, uri, version) ) ;
120
+ }
105
121
}
106
122
107
- ( b"" , b"" , b"" )
123
+ // Request Line can be valid only if it contains the method, uri and version separated with SP.
124
+ Err ( RequestError :: InvalidRequest )
108
125
}
109
126
110
127
/// Tries to parse a byte stream in a request line. Fails if the request line is malformed.
@@ -114,7 +131,7 @@ impl RequestLine {
114
131
/// `InvalidHttpVersion` is returned if the specified HTTP version is unsupported.
115
132
/// `InvalidUri` is returned if the specified Uri is not valid.
116
133
pub fn try_from ( request_line : & [ u8 ] ) -> Result < Self , RequestError > {
117
- let ( method, uri, version) = Self :: parse_request_line ( request_line) ;
134
+ let ( method, uri, version) = Self :: parse_request_line ( request_line) ? ;
118
135
119
136
Ok ( Self {
120
137
method : Method :: try_from ( method) ?,
@@ -124,9 +141,10 @@ impl RequestLine {
124
141
}
125
142
126
143
// Returns the minimum length of a valid request. The request must contain
127
- // the method (GET), the URI (minmum 1 character), the HTTP version(HTTP/DIGIT.DIGIT) and
144
+ // the method (GET), the URI (minimum 1 character), the HTTP version(HTTP/DIGIT.DIGIT) and
128
145
// 2 separators (SP).
129
146
fn min_len ( ) -> usize {
147
+ // Addition is safe because these are small constants.
130
148
Method :: Get . raw ( ) . len ( ) + 1 + Version :: Http10 . raw ( ) . len ( ) + 2
131
149
}
132
150
}
@@ -148,7 +166,10 @@ impl Request {
148
166
/// The byte slice is expected to have the following format: </br>
149
167
/// * Request Line: "GET SP Request-uri SP HTTP/1.0 CRLF" - Mandatory </br>
150
168
/// * Request Headers "<headers> CRLF"- Optional </br>
169
+ /// * Empty Line "CRLF" </br>
151
170
/// * Entity Body - Optional </br>
171
+ /// The request headers and the entity body are not parsed and None is returned because
172
+ /// these are not used by the MMDS server.
152
173
/// The only supported method is GET and the HTTP protocol is expected to be HTTP/1.0
153
174
/// or HTTP/1.1.
154
175
///
@@ -171,6 +192,7 @@ impl Request {
171
192
None => return Err ( RequestError :: InvalidRequest ) ,
172
193
} ;
173
194
195
+ // Slice access is safe because `find` validates that `request_line_end` < `byte_stream` size.
174
196
let request_line_bytes = & byte_stream[ ..request_line_end] ;
175
197
if request_line_bytes. len ( ) < RequestLine :: min_len ( ) {
176
198
return Err ( RequestError :: InvalidRequest ) ;
@@ -191,8 +213,20 @@ impl Request {
191
213
Some ( headers_end) => {
192
214
// Parse the request headers.
193
215
// Start by removing the leading CR LF from them.
194
- 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).
195
227
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).
196
230
let headers = Headers :: try_from ( & headers_and_body[ ..headers_end] ) ?;
197
231
198
232
// Parse the body of the request.
@@ -203,16 +237,22 @@ impl Request {
203
237
None
204
238
}
205
239
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;
206
246
// Headers suggest we have a body, but the buffer is shorter than the specified
207
247
// content length.
208
- if headers_and_body. len ( ) - ( headers_end + 2 * CRLF_LEN )
209
- < content_length as usize
210
- {
248
+ if body_len < content_length as usize {
211
249
return Err ( RequestError :: InvalidRequest ) ;
212
250
}
213
- 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..] ;
214
254
// If the actual length of the body is different than the `Content-Length` value
215
- // in the headers then this request is invalid.
255
+ // in the headers, then this request is invalid.
216
256
if body_as_bytes. len ( ) == content_length as usize {
217
257
Some ( Body :: new ( body_as_bytes) )
218
258
} else {
@@ -341,6 +381,13 @@ mod tests {
341
381
expected_request_line
342
382
) ;
343
383
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
+
344
391
// Test for invalid method.
345
392
let request_line = b"POST http://localhost/home HTTP/1.0" ;
346
393
assert_eq ! (
@@ -366,14 +413,14 @@ mod tests {
366
413
let request_line = b"nothing" ;
367
414
assert_eq ! (
368
415
RequestLine :: try_from( request_line) . unwrap_err( ) ,
369
- RequestError :: InvalidHttpMethod ( "Unsupported HTTP method." )
416
+ RequestError :: InvalidRequest
370
417
) ;
371
418
372
419
// Test for invalid format with no version.
373
420
let request_line = b"GET /" ;
374
421
assert_eq ! (
375
422
RequestLine :: try_from( request_line) . unwrap_err( ) ,
376
- RequestError :: InvalidHttpVersion ( "Unsupported HTTP version." )
423
+ RequestError :: InvalidRequest
377
424
) ;
378
425
}
379
426
@@ -396,6 +443,13 @@ mod tests {
396
443
assert_eq ! ( request. http_version( ) , Version :: Http10 ) ;
397
444
assert ! ( request. body. is_none( ) ) ;
398
445
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
+
399
453
// Test for invalid Request (length is less than minimum).
400
454
let request_bytes = b"GET" ;
401
455
assert_eq ! (
@@ -440,7 +494,6 @@ mod tests {
440
494
// Test for an invalid content length.
441
495
let request = Request :: try_from (
442
496
b"PATCH http://localhost/home HTTP/1.1\r \n \
443
- Expect: 100-continue\r \n \
444
497
Content-Length: 5000\r \n \r \n this is a short body",
445
498
)
446
499
. unwrap_err ( ) ;
0 commit comments