Coverage Report

Created: 2025-11-16 06:56

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