/rust/git/checkouts/micro-http-22be4cdcbef12607/ef43cef/src/response.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::io::{Error as WriteError, Write}; |
5 | | |
6 | | use crate::ascii::{COLON, CR, LF, SP}; |
7 | | use crate::common::{Body, Version}; |
8 | | use crate::headers::{Header, MediaType}; |
9 | | use crate::Method; |
10 | | |
11 | | /// Wrapper over a response status code. |
12 | | /// |
13 | | /// The status code is defined as specified in the |
14 | | /// [RFC](https://tools.ietf.org/html/rfc7231#section-6). |
15 | 0 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
16 | | pub enum StatusCode { |
17 | | /// 100, Continue |
18 | | Continue, |
19 | | /// 200, OK |
20 | | OK, |
21 | | /// 204, No Content |
22 | | NoContent, |
23 | | /// 400, Bad Request |
24 | | BadRequest, |
25 | | /// 401, Unauthorized |
26 | | Unauthorized, |
27 | | /// 404, Not Found |
28 | | NotFound, |
29 | | /// 405, Method Not Allowed |
30 | | MethodNotAllowed, |
31 | | /// 413, Payload Too Large |
32 | | PayloadTooLarge, |
33 | | /// 500, Internal Server Error |
34 | | InternalServerError, |
35 | | /// 501, Not Implemented |
36 | | NotImplemented, |
37 | | /// 503, Service Unavailable |
38 | | ServiceUnavailable, |
39 | | } |
40 | | |
41 | | impl StatusCode { |
42 | | /// Returns the status code as bytes. |
43 | 0 | pub fn raw(self) -> &'static [u8; 3] { |
44 | 0 | match self { |
45 | 0 | Self::Continue => b"100", |
46 | 0 | Self::OK => b"200", |
47 | 0 | Self::NoContent => b"204", |
48 | 0 | Self::BadRequest => b"400", |
49 | 0 | Self::Unauthorized => b"401", |
50 | 0 | Self::NotFound => b"404", |
51 | 0 | Self::MethodNotAllowed => b"405", |
52 | 0 | Self::PayloadTooLarge => b"413", |
53 | 0 | Self::InternalServerError => b"500", |
54 | 0 | Self::NotImplemented => b"501", |
55 | 0 | Self::ServiceUnavailable => b"503", |
56 | | } |
57 | 0 | } |
58 | | } |
59 | | |
60 | 0 | #[derive(Debug, PartialEq)] |
61 | | struct StatusLine { |
62 | | http_version: Version, |
63 | | status_code: StatusCode, |
64 | | } |
65 | | |
66 | | impl StatusLine { |
67 | 15.9k | fn new(http_version: Version, status_code: StatusCode) -> Self { |
68 | 15.9k | Self { |
69 | 15.9k | http_version, |
70 | 15.9k | status_code, |
71 | 15.9k | } |
72 | 15.9k | } |
73 | | |
74 | 0 | fn write_all<T: Write>(&self, mut buf: T) -> Result<(), WriteError> { |
75 | 0 | buf.write_all(self.http_version.raw())?; |
76 | 0 | buf.write_all(&[SP])?; |
77 | 0 | buf.write_all(self.status_code.raw())?; |
78 | 0 | buf.write_all(&[SP, CR, LF])?; |
79 | | |
80 | 0 | Ok(()) |
81 | 0 | } |
82 | | } |
83 | | |
84 | | /// Wrapper over the list of headers associated with a HTTP Response. |
85 | | /// When creating a ResponseHeaders object, the content type is initialized to `text/plain`. |
86 | | /// The content type can be updated with a call to `set_content_type`. |
87 | 0 | #[derive(Debug, PartialEq, Eq)] |
88 | | pub struct ResponseHeaders { |
89 | | content_length: Option<i32>, |
90 | | content_type: MediaType, |
91 | | deprecation: bool, |
92 | | server: String, |
93 | | allow: Vec<Method>, |
94 | | accept_encoding: bool, |
95 | | } |
96 | | |
97 | | impl Default for ResponseHeaders { |
98 | 15.9k | fn default() -> Self { |
99 | 15.9k | Self { |
100 | 15.9k | content_length: Default::default(), |
101 | 15.9k | content_type: Default::default(), |
102 | 15.9k | deprecation: false, |
103 | 15.9k | server: String::from("Firecracker API"), |
104 | 15.9k | allow: Vec::new(), |
105 | 15.9k | accept_encoding: false, |
106 | 15.9k | } |
107 | 15.9k | } |
108 | | } |
109 | | |
110 | | impl ResponseHeaders { |
111 | | // The logic pertaining to `Allow` header writing. |
112 | 0 | fn write_allow_header<T: Write>(&self, buf: &mut T) -> Result<(), WriteError> { |
113 | 0 | if self.allow.is_empty() { |
114 | 0 | return Ok(()); |
115 | 0 | } |
116 | 0 |
|
117 | 0 | buf.write_all(b"Allow: ")?; |
118 | | |
119 | 0 | let delimitator = b", "; |
120 | 0 | for (idx, method) in self.allow.iter().enumerate() { |
121 | 0 | buf.write_all(method.raw())?; |
122 | | // We check above that `self.allow` is not empty. |
123 | 0 | if idx < self.allow.len() - 1 { |
124 | 0 | buf.write_all(delimitator)?; |
125 | 0 | } |
126 | | } |
127 | | |
128 | 0 | buf.write_all(&[CR, LF]) |
129 | 0 | } |
130 | | |
131 | | // The logic pertaining to `Deprecation` header writing. |
132 | 0 | fn write_deprecation_header<T: Write>(&self, buf: &mut T) -> Result<(), WriteError> { |
133 | 0 | if !self.deprecation { |
134 | 0 | return Ok(()); |
135 | 0 | } |
136 | 0 |
|
137 | 0 | buf.write_all(b"Deprecation: true")?; |
138 | 0 | buf.write_all(&[CR, LF]) |
139 | 0 | } |
140 | | |
141 | | /// Writes the headers to `buf` using the HTTP specification. |
142 | 0 | pub fn write_all<T: Write>(&self, buf: &mut T) -> Result<(), WriteError> { |
143 | 0 | buf.write_all(Header::Server.raw())?; |
144 | 0 | buf.write_all(&[COLON, SP])?; |
145 | 0 | buf.write_all(self.server.as_bytes())?; |
146 | 0 | buf.write_all(&[CR, LF])?; |
147 | | |
148 | 0 | buf.write_all(b"Connection: keep-alive")?; |
149 | 0 | buf.write_all(&[CR, LF])?; |
150 | | |
151 | 0 | self.write_allow_header(buf)?; |
152 | 0 | self.write_deprecation_header(buf)?; |
153 | | |
154 | 0 | if let Some(content_length) = self.content_length { |
155 | 0 | buf.write_all(Header::ContentType.raw())?; |
156 | 0 | buf.write_all(&[COLON, SP])?; |
157 | 0 | buf.write_all(self.content_type.as_str().as_bytes())?; |
158 | 0 | buf.write_all(&[CR, LF])?; |
159 | | |
160 | 0 | buf.write_all(Header::ContentLength.raw())?; |
161 | 0 | buf.write_all(&[COLON, SP])?; |
162 | 0 | buf.write_all(content_length.to_string().as_bytes())?; |
163 | 0 | buf.write_all(&[CR, LF])?; |
164 | | |
165 | 0 | if self.accept_encoding { |
166 | 0 | buf.write_all(Header::AcceptEncoding.raw())?; |
167 | 0 | buf.write_all(&[COLON, SP])?; |
168 | 0 | buf.write_all(b"identity")?; |
169 | 0 | buf.write_all(&[CR, LF])?; |
170 | 0 | } |
171 | 0 | } |
172 | | |
173 | 0 | buf.write_all(&[CR, LF]) |
174 | 0 | } |
175 | | |
176 | | /// Sets the content length to be written in the HTTP response. |
177 | 15.5k | pub fn set_content_length(&mut self, content_length: Option<i32>) { |
178 | 15.5k | self.content_length = content_length; |
179 | 15.5k | } |
180 | | |
181 | | /// Sets the HTTP response header server. |
182 | 0 | pub fn set_server(&mut self, server: &str) { |
183 | 0 | self.server = String::from(server); |
184 | 0 | } |
185 | | |
186 | | /// Sets the content type to be written in the HTTP response. |
187 | 0 | pub fn set_content_type(&mut self, content_type: MediaType) { |
188 | 0 | self.content_type = content_type; |
189 | 0 | } |
190 | | |
191 | | /// Sets the `Deprecation` header to be written in the HTTP response. |
192 | | /// https://tools.ietf.org/id/draft-dalal-deprecation-header-03.html |
193 | | #[allow(unused)] |
194 | 0 | pub fn set_deprecation(&mut self) { |
195 | 0 | self.deprecation = true; |
196 | 0 | } |
197 | | |
198 | | /// Sets the encoding type to be written in the HTTP response. |
199 | | #[allow(unused)] |
200 | 0 | pub fn set_encoding(&mut self) { |
201 | 0 | self.accept_encoding = true; |
202 | 0 | } |
203 | | } |
204 | | |
205 | | /// Wrapper over an HTTP Response. |
206 | | /// |
207 | | /// The Response is created using a `Version` and a `StatusCode`. When creating a Response object, |
208 | | /// the body is initialized to `None` and the header is initialized with the `default` value. The body |
209 | | /// can be updated with a call to `set_body`. The header can be updated with `set_content_type` and |
210 | | /// `set_server`. |
211 | 0 | #[derive(Debug, PartialEq)] |
212 | | pub struct Response { |
213 | | status_line: StatusLine, |
214 | | headers: ResponseHeaders, |
215 | | body: Option<Body>, |
216 | | } |
217 | | |
218 | | impl Response { |
219 | | /// Creates a new HTTP `Response` with an empty body. |
220 | | /// |
221 | | /// Although there are several cases where Content-Length field must not |
222 | | /// be sent, micro-http omits Content-Length field when the response |
223 | | /// status code is 1XX or 204. If needed, users can remove it by calling |
224 | | /// `set_content_length(None)`. |
225 | | /// |
226 | | /// https://datatracker.ietf.org/doc/html/rfc9110#name-content-length |
227 | | /// > A server MAY send a Content-Length header field in a response to a |
228 | | /// > HEAD request (Section 9.3.2); a server MUST NOT send Content-Length |
229 | | /// > in such a response unless its field value equals the decimal number |
230 | | /// > of octets that would have been sent in the content of a response if |
231 | | /// > the same request had used the GET method. |
232 | | /// > |
233 | | /// > A server MAY send a Content-Length header field in a 304 (Not |
234 | | /// > Modified) response to a conditional GET request (Section 15.4.5); a |
235 | | /// > server MUST NOT send Content-Length in such a response unless its |
236 | | /// > field value equals the decimal number of octets that would have been |
237 | | /// > sent in the content of a 200 (OK) response to the same request. |
238 | | /// > |
239 | | /// > A server MUST NOT send a Content-Length header field in any response |
240 | | /// > with a status code of 1xx (Informational) or 204 (No Content). A |
241 | | /// > server MUST NOT send a Content-Length header field in any 2xx |
242 | | /// > (Successful) response to a CONNECT request (Section 9.3.6). |
243 | 15.9k | pub fn new(http_version: Version, status_code: StatusCode) -> Self { |
244 | 15.9k | Self { |
245 | 15.9k | status_line: StatusLine::new(http_version, status_code), |
246 | 15.9k | headers: ResponseHeaders { |
247 | 15.9k | content_length: match status_code { |
248 | 377 | StatusCode::Continue | StatusCode::NoContent => None, |
249 | 15.5k | _ => Some(0), |
250 | | }, |
251 | 15.9k | ..Default::default() |
252 | 15.9k | }, |
253 | 15.9k | body: Default::default(), |
254 | 15.9k | } |
255 | 15.9k | } |
256 | | |
257 | | /// Updates the body of the `Response`. |
258 | | /// |
259 | | /// This function has side effects because it also updates the headers: |
260 | | /// - `ContentLength`: this is set to the length of the specified body. |
261 | 15.5k | pub fn set_body(&mut self, body: Body) { |
262 | 15.5k | self.headers.set_content_length(Some(body.len() as i32)); |
263 | 15.5k | self.body = Some(body); |
264 | 15.5k | } |
265 | | |
266 | | /// Updates the content length of the `Response`. |
267 | | /// |
268 | | /// It is recommended to use this method only when removing Content-Length |
269 | | /// field if the response status is not 1XX or 204. |
270 | 0 | pub fn set_content_length(&mut self, content_length: Option<i32>) { |
271 | 0 | self.headers.set_content_length(content_length); |
272 | 0 | } |
273 | | |
274 | | /// Updates the content type of the `Response`. |
275 | 0 | pub fn set_content_type(&mut self, content_type: MediaType) { |
276 | 0 | self.headers.set_content_type(content_type); |
277 | 0 | } |
278 | | |
279 | | /// Marks the `Response` as deprecated. |
280 | 0 | pub fn set_deprecation(&mut self) { |
281 | 0 | self.headers.set_deprecation(); |
282 | 0 | } |
283 | | |
284 | | /// Updates the encoding type of `Response`. |
285 | 0 | pub fn set_encoding(&mut self) { |
286 | 0 | self.headers.set_encoding(); |
287 | 0 | } |
288 | | |
289 | | /// Sets the HTTP response server. |
290 | 0 | pub fn set_server(&mut self, server: &str) { |
291 | 0 | self.headers.set_server(server); |
292 | 0 | } |
293 | | |
294 | | /// Sets the HTTP allowed methods. |
295 | 0 | pub fn set_allow(&mut self, methods: Vec<Method>) { |
296 | 0 | self.headers.allow = methods; |
297 | 0 | } |
298 | | |
299 | | /// Allows a specific HTTP method. |
300 | 0 | pub fn allow_method(&mut self, method: Method) { |
301 | 0 | self.headers.allow.push(method); |
302 | 0 | } |
303 | | |
304 | 0 | fn write_body<T: Write>(&self, mut buf: T) -> Result<(), WriteError> { |
305 | 0 | if let Some(ref body) = self.body { |
306 | 0 | buf.write_all(body.raw())?; |
307 | 0 | } |
308 | 0 | Ok(()) |
309 | 0 | } |
310 | | |
311 | | /// Writes the content of the `Response` to the specified `buf`. |
312 | | /// |
313 | | /// # Errors |
314 | | /// Returns an error when the buffer is not large enough. |
315 | 0 | pub fn write_all<T: Write>(&self, mut buf: &mut T) -> Result<(), WriteError> { |
316 | 0 | self.status_line.write_all(&mut buf)?; |
317 | 0 | self.headers.write_all(&mut buf)?; |
318 | 0 | self.write_body(&mut buf)?; |
319 | | |
320 | 0 | Ok(()) |
321 | 0 | } |
322 | | |
323 | | /// Returns the Status Code of the Response. |
324 | 0 | pub fn status(&self) -> StatusCode { |
325 | 0 | self.status_line.status_code |
326 | 0 | } |
327 | | |
328 | | /// Returns the Body of the response. If the response does not have a body, |
329 | | /// it returns None. |
330 | 0 | pub fn body(&self) -> Option<Body> { |
331 | 0 | self.body.clone() |
332 | 0 | } |
333 | | |
334 | | /// Returns the Content Length of the response. |
335 | 0 | pub fn content_length(&self) -> i32 { |
336 | 0 | self.headers.content_length.unwrap_or(0) |
337 | 0 | } |
338 | | |
339 | | /// Returns the Content Type of the response. |
340 | 0 | pub fn content_type(&self) -> MediaType { |
341 | 0 | self.headers.content_type |
342 | 0 | } |
343 | | |
344 | | /// Returns the deprecation status of the response. |
345 | 0 | pub fn deprecation(&self) -> bool { |
346 | 0 | self.headers.deprecation |
347 | 0 | } |
348 | | |
349 | | /// Returns the HTTP Version of the response. |
350 | 0 | pub fn http_version(&self) -> Version { |
351 | 0 | self.status_line.http_version |
352 | 0 | } |
353 | | |
354 | | /// Returns the allowed HTTP methods. |
355 | 0 | pub fn allow(&self) -> Vec<Method> { |
356 | 0 | self.headers.allow.clone() |
357 | 0 | } |
358 | | } |
359 | | |
360 | | #[cfg(test)] |
361 | | mod tests { |
362 | | use super::*; |
363 | | |
364 | | #[test] |
365 | | fn test_write_response() { |
366 | | let mut response = Response::new(Version::Http10, StatusCode::OK); |
367 | | let body = "This is a test"; |
368 | | response.set_body(Body::new(body)); |
369 | | response.set_content_type(MediaType::PlainText); |
370 | | response.set_encoding(); |
371 | | |
372 | | assert_eq!(response.status(), StatusCode::OK); |
373 | | assert_eq!(response.body().unwrap(), Body::new(body)); |
374 | | assert_eq!(response.http_version(), Version::Http10); |
375 | | assert_eq!(response.content_length(), 14); |
376 | | assert_eq!(response.content_type(), MediaType::PlainText); |
377 | | |
378 | | let expected_response: &'static [u8] = b"HTTP/1.0 200 \r\n\ |
379 | | Server: Firecracker API\r\n\ |
380 | | Connection: keep-alive\r\n\ |
381 | | Content-Type: text/plain\r\n\ |
382 | | Content-Length: 14\r\n\ |
383 | | Accept-Encoding: identity\r\n\r\n\ |
384 | | This is a test"; |
385 | | |
386 | | let mut response_buf: [u8; 153] = [0; 153]; |
387 | | assert!(response.write_all(&mut response_buf.as_mut()).is_ok()); |
388 | | assert_eq!(response_buf.as_ref(), expected_response); |
389 | | |
390 | | // Test response `Allow` header. |
391 | | let mut response = Response::new(Version::Http10, StatusCode::OK); |
392 | | let allowed_methods = vec![Method::Get, Method::Patch, Method::Put]; |
393 | | response.set_allow(allowed_methods.clone()); |
394 | | assert_eq!(response.allow(), allowed_methods); |
395 | | |
396 | | let expected_response: &'static [u8] = b"HTTP/1.0 200 \r\n\ |
397 | | Server: Firecracker API\r\n\ |
398 | | Connection: keep-alive\r\n\ |
399 | | Allow: GET, PATCH, PUT\r\n\ |
400 | | Content-Type: application/json\r\n\ |
401 | | Content-Length: 0\r\n\r\n"; |
402 | | let mut response_buf: [u8; 141] = [0; 141]; |
403 | | assert!(response.write_all(&mut response_buf.as_mut()).is_ok()); |
404 | | assert_eq!(response_buf.as_ref(), expected_response); |
405 | | |
406 | | // Test write failed. |
407 | | let mut response_buf: [u8; 1] = [0; 1]; |
408 | | assert!(response.write_all(&mut response_buf.as_mut()).is_err()); |
409 | | } |
410 | | |
411 | | #[test] |
412 | | fn test_set_server() { |
413 | | let mut response = Response::new(Version::Http10, StatusCode::OK); |
414 | | let body = "This is a test"; |
415 | | let server = "rust-vmm API"; |
416 | | response.set_body(Body::new(body)); |
417 | | response.set_content_type(MediaType::PlainText); |
418 | | response.set_server(server); |
419 | | |
420 | | assert_eq!(response.status(), StatusCode::OK); |
421 | | assert_eq!(response.body().unwrap(), Body::new(body)); |
422 | | assert_eq!(response.http_version(), Version::Http10); |
423 | | assert_eq!(response.content_length(), 14); |
424 | | assert_eq!(response.content_type(), MediaType::PlainText); |
425 | | |
426 | | let expected_response = format!( |
427 | | "HTTP/1.0 200 \r\n\ |
428 | | Server: {}\r\n\ |
429 | | Connection: keep-alive\r\n\ |
430 | | Content-Type: text/plain\r\n\ |
431 | | Content-Length: 14\r\n\r\n\ |
432 | | This is a test", |
433 | | server |
434 | | ); |
435 | | |
436 | | let mut response_buf: [u8; 123] = [0; 123]; |
437 | | assert!(response.write_all(&mut response_buf.as_mut()).is_ok()); |
438 | | assert!(response_buf.as_ref() == expected_response.as_bytes()); |
439 | | } |
440 | | |
441 | | #[test] |
442 | | fn test_status_code() { |
443 | | assert_eq!(StatusCode::Continue.raw(), b"100"); |
444 | | assert_eq!(StatusCode::OK.raw(), b"200"); |
445 | | assert_eq!(StatusCode::NoContent.raw(), b"204"); |
446 | | assert_eq!(StatusCode::BadRequest.raw(), b"400"); |
447 | | assert_eq!(StatusCode::Unauthorized.raw(), b"401"); |
448 | | assert_eq!(StatusCode::NotFound.raw(), b"404"); |
449 | | assert_eq!(StatusCode::MethodNotAllowed.raw(), b"405"); |
450 | | assert_eq!(StatusCode::PayloadTooLarge.raw(), b"413"); |
451 | | assert_eq!(StatusCode::InternalServerError.raw(), b"500"); |
452 | | assert_eq!(StatusCode::NotImplemented.raw(), b"501"); |
453 | | assert_eq!(StatusCode::ServiceUnavailable.raw(), b"503"); |
454 | | } |
455 | | |
456 | | #[test] |
457 | | fn test_allow_method() { |
458 | | let mut response = Response::new(Version::Http10, StatusCode::MethodNotAllowed); |
459 | | response.allow_method(Method::Get); |
460 | | response.allow_method(Method::Put); |
461 | | assert_eq!(response.allow(), vec![Method::Get, Method::Put]); |
462 | | } |
463 | | |
464 | | #[test] |
465 | | fn test_deprecation() { |
466 | | // Test a deprecated response with body. |
467 | | let mut response = Response::new(Version::Http10, StatusCode::OK); |
468 | | let body = "This is a test"; |
469 | | response.set_body(Body::new(body)); |
470 | | response.set_content_type(MediaType::PlainText); |
471 | | response.set_encoding(); |
472 | | response.set_deprecation(); |
473 | | |
474 | | assert_eq!(response.status(), StatusCode::OK); |
475 | | assert_eq!(response.body().unwrap(), Body::new(body)); |
476 | | assert_eq!(response.http_version(), Version::Http10); |
477 | | assert_eq!(response.content_length(), 14); |
478 | | assert_eq!(response.content_type(), MediaType::PlainText); |
479 | | assert!(response.deprecation()); |
480 | | |
481 | | let expected_response: &'static [u8] = b"HTTP/1.0 200 \r\n\ |
482 | | Server: Firecracker API\r\n\ |
483 | | Connection: keep-alive\r\n\ |
484 | | Deprecation: true\r\n\ |
485 | | Content-Type: text/plain\r\n\ |
486 | | Content-Length: 14\r\n\ |
487 | | Accept-Encoding: identity\r\n\r\n\ |
488 | | This is a test"; |
489 | | |
490 | | let mut response_buf: [u8; 172] = [0; 172]; |
491 | | assert!(response.write_all(&mut response_buf.as_mut()).is_ok()); |
492 | | assert_eq!(response_buf.as_ref(), expected_response); |
493 | | |
494 | | // Test a deprecated response without a body. |
495 | | let mut response = Response::new(Version::Http10, StatusCode::NoContent); |
496 | | response.set_deprecation(); |
497 | | |
498 | | assert_eq!(response.status(), StatusCode::NoContent); |
499 | | assert_eq!(response.http_version(), Version::Http10); |
500 | | assert!(response.deprecation()); |
501 | | |
502 | | let expected_response: &'static [u8] = b"HTTP/1.0 204 \r\n\ |
503 | | Server: Firecracker API\r\n\ |
504 | | Connection: keep-alive\r\n\ |
505 | | Deprecation: true\r\n\r\n"; |
506 | | |
507 | | let mut response_buf: [u8; 85] = [0; 85]; |
508 | | assert!(response.write_all(&mut response_buf.as_mut()).is_ok()); |
509 | | assert_eq!(response_buf.as_ref(), expected_response); |
510 | | } |
511 | | |
512 | | #[test] |
513 | | fn test_equal() { |
514 | | let response = Response::new(Version::Http10, StatusCode::MethodNotAllowed); |
515 | | let another_response = Response::new(Version::Http10, StatusCode::MethodNotAllowed); |
516 | | assert_eq!(response, another_response); |
517 | | |
518 | | let response = Response::new(Version::Http10, StatusCode::OK); |
519 | | let another_response = Response::new(Version::Http10, StatusCode::BadRequest); |
520 | | assert_ne!(response, another_response); |
521 | | } |
522 | | |
523 | | #[test] |
524 | | fn test_content_length() { |
525 | | // If the status code is 1XX or 204, Content-Length field must not exist. |
526 | | let response = Response::new(Version::Http10, StatusCode::Continue); |
527 | | let expected_response: &'static [u8] = b"HTTP/1.0 100 \r\n\ |
528 | | Server: Firecracker API\r\n\ |
529 | | Connection: keep-alive\r\n\r\n"; |
530 | | let mut response_buf: [u8; 66] = [0; 66]; |
531 | | assert!(response.write_all(&mut response_buf.as_mut()).is_ok()); |
532 | | assert_eq!(response_buf.as_ref(), expected_response); |
533 | | |
534 | | let response = Response::new(Version::Http10, StatusCode::NoContent); |
535 | | let expected_response: &'static [u8] = b"HTTP/1.0 204 \r\n\ |
536 | | Server: Firecracker API\r\n\ |
537 | | Connection: keep-alive\r\n\r\n"; |
538 | | let mut response_buf: [u8; 66] = [0; 66]; |
539 | | assert!(response.write_all(&mut response_buf.as_mut()).is_ok()); |
540 | | assert_eq!(response_buf.as_ref(), expected_response); |
541 | | |
542 | | // If not 1XX or 204, Content-Length field must exist even if the body isn't set. |
543 | | let response = Response::new(Version::Http10, StatusCode::OK); |
544 | | let expected_response: &'static [u8] = b"HTTP/1.0 200 \r\n\ |
545 | | Server: Firecracker API\r\n\ |
546 | | Connection: keep-alive\r\n\ |
547 | | Content-Type: application/json\r\n\ |
548 | | Content-Length: 0\r\n\r\n"; |
549 | | let mut response_buf: [u8; 117] = [0; 117]; |
550 | | assert!(response.write_all(&mut response_buf.as_mut()).is_ok()); |
551 | | assert_eq!(response_buf.as_ref(), expected_response); |
552 | | } |
553 | | } |