/rust/git/checkouts/micro-http-22be4cdcbef12607/ef43cef/src/request.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
2 | | // SPDX-License-Identifier: Apache-2.0 |
3 | | |
4 | | use std::fs::File; |
5 | | use std::str::from_utf8; |
6 | | |
7 | | use crate::common::ascii::{CR, CRLF_LEN, LF, SP}; |
8 | | |
9 | | pub use crate::common::RequestError; |
10 | | use crate::common::{Body, Method, Version}; |
11 | | use crate::headers::Headers; |
12 | | |
13 | | // This type represents the RequestLine raw parts: method, uri and version. |
14 | | type RequestLineParts<'a> = (&'a [u8], &'a [u8], &'a [u8]); |
15 | | |
16 | | /// Finds the first occurrence of `sequence` in the `bytes` slice. |
17 | | /// |
18 | | /// Returns the starting position of the `sequence` in `bytes` or `None` if the |
19 | | /// `sequence` is not found. |
20 | 69.4k | pub(crate) fn find(bytes: &[u8], sequence: &[u8]) -> Option<usize> { |
21 | 69.4k | bytes |
22 | 69.4k | .windows(sequence.len()) |
23 | 11.1M | .position(|window| window == sequence) |
24 | 69.4k | } |
25 | | |
26 | | /// Wrapper over HTTP URIs. |
27 | | /// |
28 | | /// The `Uri` can not be used directly and it is only accessible from an HTTP Request. |
29 | 0 | #[derive(Clone, Debug, PartialEq, Eq)] |
30 | | pub struct Uri { |
31 | | string: String, |
32 | | } |
33 | | |
34 | | impl Uri { |
35 | 17.3k | fn new(slice: &str) -> Self { |
36 | 17.3k | Self { |
37 | 17.3k | string: String::from(slice), |
38 | 17.3k | } |
39 | 17.3k | } |
40 | | |
41 | 17.3k | fn try_from(bytes: &[u8]) -> Result<Self, RequestError> { |
42 | 17.3k | if bytes.is_empty() { |
43 | 0 | return Err(RequestError::InvalidUri("Empty URI not allowed.")); |
44 | 17.3k | } |
45 | 17.3k | let utf8_slice = |
46 | 17.3k | from_utf8(bytes).map_err(|_| RequestError::InvalidUri("Cannot parse URI as UTF-8."))?; |
47 | 17.3k | Ok(Self::new(utf8_slice)) |
48 | 17.3k | } |
49 | | |
50 | | /// Returns the absolute path of the `Uri`. |
51 | | /// |
52 | | /// URIs can be represented in absolute form or relative form. The absolute form includes |
53 | | /// the HTTP scheme, followed by the absolute path as follows: |
54 | | /// "http:" "//" host [ ":" port ] [ abs_path ] |
55 | | /// The relative URIs can be one of net_path | abs_path | rel_path. |
56 | | /// This method only handles absolute URIs and relative URIs specified by abs_path. |
57 | | /// The abs_path is expected to start with '/'. |
58 | | /// |
59 | | /// # Errors |
60 | | /// Returns an empty byte array when the host or the path are empty/invalid. |
61 | 0 | pub fn get_abs_path(&self) -> &str { |
62 | 0 | const HTTP_SCHEME_PREFIX: &str = "http://"; |
63 | 0 |
|
64 | 0 | if self.string.starts_with(HTTP_SCHEME_PREFIX) { |
65 | | // Slice access is safe because we checked above that `self.string` size <= `HTTP_SCHEME_PREFIX.len()`. |
66 | 0 | let without_scheme = &self.string[HTTP_SCHEME_PREFIX.len()..]; |
67 | 0 | if without_scheme.is_empty() { |
68 | 0 | return ""; |
69 | 0 | } |
70 | 0 | // The host in this case includes the port and contains the bytes after http:// up to |
71 | 0 | // the next '/'. |
72 | 0 | match without_scheme.bytes().position(|byte| byte == b'/') { |
73 | | // Slice access is safe because `position` validates that `len` is a valid index. |
74 | 0 | Some(len) => &without_scheme[len..], |
75 | 0 | None => "", |
76 | | } |
77 | | } else { |
78 | 0 | if self.string.starts_with('/') { |
79 | 0 | return self.string.as_str(); |
80 | 0 | } |
81 | 0 |
|
82 | 0 | "" |
83 | | } |
84 | 0 | } |
85 | | } |
86 | | |
87 | | /// Wrapper over an HTTP Request Line. |
88 | 0 | #[derive(Debug, PartialEq, Eq)] |
89 | | pub struct RequestLine { |
90 | | method: Method, |
91 | | uri: Uri, |
92 | | http_version: Version, |
93 | | } |
94 | | |
95 | | impl RequestLine { |
96 | 17.3k | fn parse_request_line( |
97 | 17.3k | request_line: &[u8], |
98 | 17.3k | ) -> std::result::Result<RequestLineParts, RequestError> { |
99 | 17.3k | if let Some(method_end) = find(request_line, &[SP]) { |
100 | | // The slice access is safe because `find` validates that `method_end` < `request_line` size. |
101 | 17.3k | let method = &request_line[..method_end]; |
102 | | |
103 | | // `uri_start` <= `request_line` size. |
104 | 17.3k | let uri_start = method_end.checked_add(1).ok_or(RequestError::Overflow)?; |
105 | | |
106 | | // Slice access is safe because `uri_start` <= `request_line` size. |
107 | | // If `uri_start` == `request_line` size, then `uri_and_version` will be an empty slice. |
108 | 17.3k | let uri_and_version = &request_line[uri_start..]; |
109 | | |
110 | 17.3k | if let Some(uri_end) = find(uri_and_version, &[SP]) { |
111 | | // Slice access is safe because `find` validates that `uri_end` < `uri_and_version` size. |
112 | 17.3k | let uri = &uri_and_version[..uri_end]; |
113 | | |
114 | | // `version_start` <= `uri_and_version` size. |
115 | 17.3k | let version_start = uri_end.checked_add(1).ok_or(RequestError::Overflow)?; |
116 | | |
117 | | // Slice access is safe because `version_start` <= `uri_and_version` size. |
118 | 17.3k | let version = &uri_and_version[version_start..]; |
119 | 17.3k | |
120 | 17.3k | return Ok((method, uri, version)); |
121 | 0 | } |
122 | 0 | } |
123 | | |
124 | | // Request Line can be valid only if it contains the method, uri and version separated with SP. |
125 | 0 | Err(RequestError::InvalidRequest) |
126 | 17.3k | } |
127 | | |
128 | | /// Tries to parse a byte stream in a request line. Fails if the request line is malformed. |
129 | | /// |
130 | | /// # Errors |
131 | | /// `InvalidHttpMethod` is returned if the specified HTTP method is unsupported. |
132 | | /// `InvalidHttpVersion` is returned if the specified HTTP version is unsupported. |
133 | | /// `InvalidUri` is returned if the specified Uri is not valid. |
134 | 17.3k | pub fn try_from(request_line: &[u8]) -> Result<Self, RequestError> { |
135 | 17.3k | let (method, uri, version) = Self::parse_request_line(request_line)?; |
136 | | |
137 | | Ok(Self { |
138 | 17.3k | method: Method::try_from(method)?, |
139 | 17.3k | uri: Uri::try_from(uri)?, |
140 | 17.3k | http_version: Version::try_from(version)?, |
141 | | }) |
142 | 17.3k | } |
143 | | |
144 | | // Returns the minimum length of a valid request. The request must contain |
145 | | // the method (GET), the URI (minimum 1 character), the HTTP version(HTTP/DIGIT.DIGIT) and |
146 | | // 2 separators (SP). |
147 | 17.3k | fn min_len() -> usize { |
148 | 17.3k | // Addition is safe because these are small constants. |
149 | 17.3k | Method::Get.raw().len() + 1 + Version::Http10.raw().len() + 2 |
150 | 17.3k | } |
151 | | } |
152 | | |
153 | | /// Wrapper over an HTTP Request. |
154 | 0 | #[derive(Debug)] |
155 | | pub struct Request { |
156 | | /// The request line of the request. |
157 | | pub request_line: RequestLine, |
158 | | /// The headers of the request. |
159 | | pub headers: Headers, |
160 | | /// The body of the request. |
161 | | pub body: Option<Body>, |
162 | | /// The optional files associated with the request. |
163 | | pub files: Vec<File>, |
164 | | } |
165 | | |
166 | | impl Request { |
167 | | /// Parses a byte slice into a HTTP Request. |
168 | | /// |
169 | | /// The byte slice is expected to have the following format: </br> |
170 | | /// * Request Line: "GET SP Request-uri SP HTTP/1.0 CRLF" - Mandatory </br> |
171 | | /// * Request Headers "<headers> CRLF"- Optional </br> |
172 | | /// * Empty Line "CRLF" </br> |
173 | | /// * Entity Body - Optional </br> |
174 | | /// The request headers and the entity body are not parsed and None is returned because |
175 | | /// these are not used by the MMDS server. |
176 | | /// The only supported method is GET and the HTTP protocol is expected to be HTTP/1.0 |
177 | | /// or HTTP/1.1. |
178 | | /// |
179 | | /// # Errors |
180 | | /// The function returns InvalidRequest when parsing the byte stream fails. |
181 | | /// |
182 | | /// # Examples |
183 | | /// |
184 | | /// ``` |
185 | | /// use micro_http::Request; |
186 | | /// |
187 | | /// let max_request_len = 2000; |
188 | | /// let request_bytes = b"GET http://localhost/home HTTP/1.0\r\n\r\n"; |
189 | | /// let http_request = Request::try_from(request_bytes, Some(max_request_len)).unwrap(); |
190 | | /// ``` |
191 | 17.3k | pub fn try_from(byte_stream: &[u8], max_len: Option<usize>) -> Result<Self, RequestError> { |
192 | | // If a size limit is provided, verify the request length does not exceed it. |
193 | 17.3k | if let Some(limit) = max_len { |
194 | 0 | if byte_stream.len() >= limit { |
195 | 0 | return Err(RequestError::InvalidRequest); |
196 | 0 | } |
197 | 17.3k | } |
198 | | |
199 | | // The first line of the request is the Request Line. The line ending is CR LF. |
200 | 17.3k | let request_line_end = match find(byte_stream, &[CR, LF]) { |
201 | 17.3k | Some(len) => len, |
202 | | // If no CR LF is found in the stream, the request format is invalid. |
203 | 0 | None => return Err(RequestError::InvalidRequest), |
204 | | }; |
205 | | |
206 | | // Slice access is safe because `find` validates that `request_line_end` < `byte_stream` size. |
207 | 17.3k | let request_line_bytes = &byte_stream[..request_line_end]; |
208 | 17.3k | if request_line_bytes.len() < RequestLine::min_len() { |
209 | 0 | return Err(RequestError::InvalidRequest); |
210 | 17.3k | } |
211 | | |
212 | 17.3k | let request_line = RequestLine::try_from(request_line_bytes)?; |
213 | | |
214 | | // Find the next CR LF CR LF sequence in our buffer starting at the end on the Request |
215 | | // Line, including the trailing CR LF previously found. |
216 | 17.3k | match find(&byte_stream[request_line_end..], &[CR, LF, CR, LF]) { |
217 | | // If we have found a CR LF CR LF at the end of the Request Line, the request |
218 | | // is complete. |
219 | 210 | Some(0) => Ok(Self { |
220 | 210 | request_line, |
221 | 210 | headers: Headers::default(), |
222 | 210 | body: None, |
223 | 210 | files: Vec::new(), |
224 | 210 | }), |
225 | 17.1k | Some(headers_end) => { |
226 | 17.1k | // Parse the request headers. |
227 | 17.1k | // Start by removing the leading CR LF from them. |
228 | 17.1k | // The addition is safe because `find()` guarantees that `request_line_end` |
229 | 17.1k | // precedes 2 `CRLF` sequences. |
230 | 17.1k | let headers_start = request_line_end + CRLF_LEN; |
231 | 17.1k | // Slice access is safe because starting from `request_line_end` there are at least two CRLF |
232 | 17.1k | // (enforced by `find` at the start of this method). |
233 | 17.1k | let headers_and_body = &byte_stream[headers_start..]; |
234 | 17.1k | // Because we advanced the start with CRLF_LEN, we now have to subtract CRLF_LEN |
235 | 17.1k | // from the end in order to keep the same window. |
236 | 17.1k | // Underflow is not possible here because `byte_stream[request_line_end..]` starts with CR LF, |
237 | 17.1k | // so `headers_end` can be either zero (this case is treated separately in the first match arm) |
238 | 17.1k | // or >= 3 (current case). |
239 | 17.1k | let headers_end = headers_end - CRLF_LEN; |
240 | | // Slice access is safe because `headers_end` is checked above |
241 | | // (`find` gives a valid position, and subtracting 2 can't underflow). |
242 | 17.1k | let headers = Headers::try_from(&headers_and_body[..headers_end])?; |
243 | | |
244 | | // Parse the body of the request. |
245 | | // Firstly check if we have a body. |
246 | 16.9k | let body = match headers.content_length() { |
247 | | 0 => { |
248 | | // No request body. |
249 | 76 | None |
250 | | } |
251 | 16.8k | content_length => { |
252 | 16.8k | if request_line.method == Method::Get { |
253 | 409 | return Err(RequestError::InvalidRequest); |
254 | 16.4k | } |
255 | 16.4k | // Multiplication is safe because `CRLF_LEN` is a small constant. |
256 | 16.4k | // Addition is also safe because `headers_end` started out as the result |
257 | 16.4k | // of `find(<something>, CRLFCRLF)`, then `CRLF_LEN` was subtracted from it. |
258 | 16.4k | let crlf_end = headers_end + 2 * CRLF_LEN; |
259 | 16.4k | // This can't underflow because `headers_and_body.len()` >= `crlf_end`. |
260 | 16.4k | let body_len = headers_and_body.len() - crlf_end; |
261 | 16.4k | // Headers suggest we have a body, but the buffer is shorter than the specified |
262 | 16.4k | // content length. |
263 | 16.4k | if body_len < content_length as usize { |
264 | 722 | return Err(RequestError::InvalidRequest); |
265 | 15.7k | } |
266 | 15.7k | // Slice access is safe because `crlf_end` is the index after two CRLF |
267 | 15.7k | // (it is <= `headers_and_body` size). |
268 | 15.7k | let body_as_bytes = &headers_and_body[crlf_end..]; |
269 | 15.7k | // If the actual length of the body is different than the `Content-Length` value |
270 | 15.7k | // in the headers, then this request is invalid. |
271 | 15.7k | if body_as_bytes.len() == content_length as usize { |
272 | 15.6k | Some(Body::new(body_as_bytes)) |
273 | | } else { |
274 | 39 | return Err(RequestError::InvalidRequest); |
275 | | } |
276 | | } |
277 | | }; |
278 | | |
279 | 15.7k | Ok(Self { |
280 | 15.7k | request_line, |
281 | 15.7k | headers, |
282 | 15.7k | body, |
283 | 15.7k | files: Vec::new(), |
284 | 15.7k | }) |
285 | | } |
286 | | // If we can't find a CR LF CR LF even though the request should have headers |
287 | | // the request format is invalid. |
288 | 31 | None => Err(RequestError::InvalidRequest), |
289 | | } |
290 | 17.3k | } |
291 | | |
292 | | /// Returns the `Uri` from the parsed `Request`. |
293 | | /// |
294 | | /// The return value can be used to get the absolute path of the URI. |
295 | 0 | pub fn uri(&self) -> &Uri { |
296 | 0 | &self.request_line.uri |
297 | 0 | } |
298 | | |
299 | | /// Returns the HTTP `Version` of the `Request`. |
300 | 0 | pub fn http_version(&self) -> Version { |
301 | 0 | self.request_line.http_version |
302 | 0 | } |
303 | | |
304 | | /// Returns the HTTP `Method` of the `Request`. |
305 | 15.9k | pub fn method(&self) -> Method { |
306 | 15.9k | self.request_line.method |
307 | 15.9k | } |
308 | | } |
309 | | |
310 | | #[cfg(test)] |
311 | | mod tests { |
312 | | use super::*; |
313 | | |
314 | | impl PartialEq for Request { |
315 | | fn eq(&self, other: &Self) -> bool { |
316 | | // Ignore the other fields of Request for now because they are not used. |
317 | | self.request_line == other.request_line |
318 | | && self.headers.content_length() == other.headers.content_length() |
319 | | && self.headers.expect() == other.headers.expect() |
320 | | && self.headers.chunked() == other.headers.chunked() |
321 | | } |
322 | | } |
323 | | |
324 | | impl RequestLine { |
325 | | pub fn new(method: Method, uri: &str, http_version: Version) -> Self { |
326 | | Self { |
327 | | method, |
328 | | uri: Uri::new(uri), |
329 | | http_version, |
330 | | } |
331 | | } |
332 | | } |
333 | | |
334 | | #[test] |
335 | | fn test_uri() { |
336 | | for tc in &vec![ |
337 | | ("http://localhost/home", "/home"), |
338 | | ("http://localhost:8080/home", "/home"), |
339 | | ("http://localhost/home/sub", "/home/sub"), |
340 | | ("/home", "/home"), |
341 | | ("home", ""), |
342 | | ("http://", ""), |
343 | | ("http://192.168.0.0", ""), |
344 | | ] { |
345 | | assert_eq!(Uri::new(tc.0).get_abs_path(), tc.1); |
346 | | } |
347 | | } |
348 | | |
349 | | #[test] |
350 | | fn test_find() { |
351 | | let bytes: &[u8; 13] = b"abcacrgbabsjl"; |
352 | | |
353 | | for tc in &vec![ |
354 | | ("ac", Some(3)), |
355 | | ("rgb", Some(5)), |
356 | | ("ab", Some(0)), |
357 | | ("l", Some(12)), |
358 | | ("abcacrgbabsjl", Some(0)), |
359 | | ("jle", None), |
360 | | ("asdkjhasjhdjhgsadg", None), |
361 | | ] { |
362 | | assert_eq!(find(&bytes[..], tc.0.as_bytes()), tc.1); |
363 | | } |
364 | | } |
365 | | |
366 | | #[test] |
367 | | fn test_into_request_line() { |
368 | | let expected_request_line = RequestLine { |
369 | | http_version: Version::Http10, |
370 | | method: Method::Get, |
371 | | uri: Uri::new("http://localhost/home"), |
372 | | }; |
373 | | |
374 | | let request_line = b"GET http://localhost/home HTTP/1.0"; |
375 | | assert_eq!( |
376 | | RequestLine::try_from(request_line).unwrap(), |
377 | | expected_request_line |
378 | | ); |
379 | | |
380 | | let expected_request_line = RequestLine { |
381 | | http_version: Version::Http11, |
382 | | method: Method::Get, |
383 | | uri: Uri::new("http://localhost/home"), |
384 | | }; |
385 | | |
386 | | // Happy case with request line ending in CRLF. |
387 | | let request_line = b"GET http://localhost/home HTTP/1.1"; |
388 | | assert_eq!( |
389 | | RequestLine::try_from(request_line).unwrap(), |
390 | | expected_request_line |
391 | | ); |
392 | | |
393 | | // Happy case with request line ending in LF instead of CRLF. |
394 | | let request_line = b"GET http://localhost/home HTTP/1.1"; |
395 | | assert_eq!( |
396 | | RequestLine::try_from(request_line).unwrap(), |
397 | | expected_request_line |
398 | | ); |
399 | | |
400 | | // Test for invalid request missing the separator. |
401 | | let request_line = b"GET"; |
402 | | assert_eq!( |
403 | | RequestLine::try_from(request_line).unwrap_err(), |
404 | | RequestError::InvalidRequest |
405 | | ); |
406 | | |
407 | | // Test for invalid method. |
408 | | let request_line = b"POST http://localhost/home HTTP/1.0"; |
409 | | assert_eq!( |
410 | | RequestLine::try_from(request_line).unwrap_err(), |
411 | | RequestError::InvalidHttpMethod("Unsupported HTTP method.") |
412 | | ); |
413 | | |
414 | | // Test for invalid uri. |
415 | | let request_line = b"GET HTTP/1.0"; |
416 | | assert_eq!( |
417 | | RequestLine::try_from(request_line).unwrap_err(), |
418 | | RequestError::InvalidUri("Empty URI not allowed.") |
419 | | ); |
420 | | |
421 | | // Test for invalid HTTP version. |
422 | | let request_line = b"GET http://localhost/home HTTP/2.0"; |
423 | | assert_eq!( |
424 | | RequestLine::try_from(request_line).unwrap_err(), |
425 | | RequestError::InvalidHttpVersion("Unsupported HTTP version.") |
426 | | ); |
427 | | |
428 | | // Test for invalid format with no method, uri or version. |
429 | | let request_line = b"nothing"; |
430 | | assert_eq!( |
431 | | RequestLine::try_from(request_line).unwrap_err(), |
432 | | RequestError::InvalidRequest |
433 | | ); |
434 | | |
435 | | // Test for invalid format with no version. |
436 | | let request_line = b"GET /"; |
437 | | assert_eq!( |
438 | | RequestLine::try_from(request_line).unwrap_err(), |
439 | | RequestError::InvalidRequest |
440 | | ); |
441 | | } |
442 | | |
443 | | #[test] |
444 | | fn test_into_request() { |
445 | | let expected_request = Request { |
446 | | request_line: RequestLine { |
447 | | http_version: Version::Http10, |
448 | | method: Method::Get, |
449 | | uri: Uri::new("http://localhost/home"), |
450 | | }, |
451 | | body: None, |
452 | | files: Vec::new(), |
453 | | headers: Headers::default(), |
454 | | }; |
455 | | let request_bytes = b"GET http://localhost/home HTTP/1.0\r\n\ |
456 | | Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT\r\n\r\n"; |
457 | | let request = Request::try_from(request_bytes, None).unwrap(); |
458 | | assert_eq!(request, expected_request); |
459 | | assert_eq!(request.uri(), &Uri::new("http://localhost/home")); |
460 | | assert_eq!(request.http_version(), Version::Http10); |
461 | | assert!(request.body.is_none()); |
462 | | |
463 | | // Test for invalid Request (missing CR LF). |
464 | | let request_bytes = b"GET / HTTP/1.1"; |
465 | | assert_eq!( |
466 | | Request::try_from(request_bytes, None).unwrap_err(), |
467 | | RequestError::InvalidRequest |
468 | | ); |
469 | | |
470 | | // Test for invalid Request (length is less than minimum). |
471 | | let request_bytes = b"GET"; |
472 | | assert_eq!( |
473 | | Request::try_from(request_bytes, None).unwrap_err(), |
474 | | RequestError::InvalidRequest |
475 | | ); |
476 | | |
477 | | // Test for invalid Request (`GET` requests should have no body). |
478 | | let request_bytes = b"GET /machine-config HTTP/1.1\r\n\ |
479 | | Content-Length: 13\r\n\ |
480 | | Content-Type: application/json\r\n\r\nwhatever body"; |
481 | | assert_eq!( |
482 | | Request::try_from(request_bytes, None).unwrap_err(), |
483 | | RequestError::InvalidRequest |
484 | | ); |
485 | | |
486 | | // Test for request larger than maximum len provided. |
487 | | let request_bytes = b"GET http://localhost/home HTTP/1.0\r\n\ |
488 | | Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT\r\n\r\n"; |
489 | | assert_eq!( |
490 | | Request::try_from(request_bytes, Some(20)).unwrap_err(), |
491 | | RequestError::InvalidRequest |
492 | | ); |
493 | | |
494 | | // Test request smaller than maximum len provided is ok. |
495 | | let request_bytes = b"GET http://localhost/home HTTP/1.0\r\n\ |
496 | | Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT\r\n\r\n"; |
497 | | assert!(Request::try_from(request_bytes, Some(500)).is_ok()); |
498 | | |
499 | | // Test for a request with the headers we are looking for. |
500 | | let request_bytes = b"PATCH http://localhost/home HTTP/1.1\r\n\ |
501 | | Expect: 100-continue\r\n\ |
502 | | Transfer-Encoding: chunked\r\n\ |
503 | | Content-Length: 26\r\n\r\nthis is not\n\r\na json \nbody"; |
504 | | let request = Request::try_from(request_bytes, None).unwrap(); |
505 | | assert_eq!(request.uri(), &Uri::new("http://localhost/home")); |
506 | | assert_eq!(request.http_version(), Version::Http11); |
507 | | assert_eq!(request.method(), Method::Patch); |
508 | | assert!(request.headers.chunked()); |
509 | | assert!(request.headers.expect()); |
510 | | assert_eq!(request.headers.content_length(), 26); |
511 | | assert_eq!( |
512 | | request.body.unwrap().body, |
513 | | String::from("this is not\n\r\na json \nbody") |
514 | | .as_bytes() |
515 | | .to_vec() |
516 | | ); |
517 | | |
518 | | // Test for an invalid request format. |
519 | | Request::try_from(b"PATCH http://localhost/home HTTP/1.1\r\n", None).unwrap_err(); |
520 | | |
521 | | // Test for an invalid encoding. |
522 | | let request_bytes = b"PATCH http://localhost/home HTTP/1.1\r\n\ |
523 | | Expect: 100-continue\r\n\ |
524 | | Transfer-Encoding: identity; q=0\r\n\ |
525 | | Content-Length: 26\r\n\r\nthis is not\n\r\na json \nbody"; |
526 | | |
527 | | assert!(Request::try_from(request_bytes, None).is_ok()); |
528 | | |
529 | | // Test for an invalid content length. |
530 | | let request_bytes = b"PATCH http://localhost/home HTTP/1.1\r\n\ |
531 | | Content-Length: 5000\r\n\r\nthis is a short body"; |
532 | | let request = Request::try_from(request_bytes, None).unwrap_err(); |
533 | | assert_eq!(request, RequestError::InvalidRequest); |
534 | | |
535 | | // Test for a request without a body and an optional header. |
536 | | let request_bytes = b"GET http://localhost/ HTTP/1.0\r\n\ |
537 | | Accept-Encoding: gzip\r\n\r\n"; |
538 | | let request = Request::try_from(request_bytes, None).unwrap(); |
539 | | assert_eq!(request.uri(), &Uri::new("http://localhost/")); |
540 | | assert_eq!(request.http_version(), Version::Http10); |
541 | | assert_eq!(request.method(), Method::Get); |
542 | | assert!(!request.headers.chunked()); |
543 | | assert!(!request.headers.expect()); |
544 | | assert_eq!(request.headers.content_length(), 0); |
545 | | assert!(request.body.is_none()); |
546 | | |
547 | | let request_bytes = b"GET http://localhost/ HTTP/1.0\r\n\ |
548 | | Accept-Encoding: identity;q=0\r\n\r\n"; |
549 | | let request = Request::try_from(request_bytes, None); |
550 | | assert_eq!( |
551 | | request.unwrap_err(), |
552 | | RequestError::HeaderError(crate::HttpHeaderError::InvalidValue( |
553 | | "Accept-Encoding".to_string(), |
554 | | "identity;q=0".to_string() |
555 | | )) |
556 | | ); |
557 | | } |
558 | | } |