Coverage Report

Created: 2026-08-05 07:20

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