Skip to main content

rust_web_server/response/
mod.rs

1#[cfg(test)]
2mod tests;
3#[cfg(test)]
4mod example;
5
6use std::io;
7use std::io::{BufRead, Cursor, Read};
8use crate::body::multipart_form_data::FormMultipartData;
9use crate::core::New;
10use crate::header::Header;
11use crate::ext::string_ext::StringExt;
12use crate::http::{HTTP, VERSION};
13use crate::mime_type::MimeType;
14use crate::range::{ContentRange, Range};
15use crate::request::{METHOD, Request};
16use crate::symbol::SYMBOL;
17
18#[derive(PartialEq, Eq, Clone, Debug)]
19pub struct Error {
20    pub status_code_reason_phrase: &'static StatusCodeReasonPhrase,
21    pub message: String,
22}
23
24/// An HTTP response. Build one inside [`Controller::process`] and return it.
25///
26/// Set `status_code` and `reason_phrase` from [`STATUS_CODE_REASON_PHRASE`],
27/// push body content via [`Range::get_content_range`] into `content_range_list`,
28/// and add any extra headers to `headers`.
29///
30/// For large files, set `stream_file` to the absolute file path instead of loading
31/// the body into `content_range_list`. The server will stream it with
32/// `Transfer-Encoding: chunked` so memory usage stays constant regardless of file size.
33///
34/// For proxy passthrough (SSE, AI token streams, large downloads), set `stream_pipe`
35/// to a boxed `Read` source instead. The server streams its output with
36/// `Transfer-Encoding: chunked` immediately after sending the response headers.
37pub struct Response {
38    /// HTTP version string, e.g. `"HTTP/1.1"`.
39    pub http_version: String,
40    /// Numeric status code, e.g. `200`. Set from [`STATUS_CODE_REASON_PHRASE`].
41    pub status_code: i16,
42    /// Reason phrase, e.g. `"OK"`. Set from [`STATUS_CODE_REASON_PHRASE`].
43    pub reason_phrase: String,
44    /// Response headers. Pre-populated with standard headers by [`Header::get_header_list`].
45    pub headers: Vec<Header>,
46    /// Response body as a list of content ranges (supports multipart and range responses).
47    pub content_range_list: Vec<ContentRange>,
48    /// If set, the server streams this file with `Transfer-Encoding: chunked`
49    /// instead of using `content_range_list`. Use for files larger than ~8 MB.
50    pub stream_file: Option<String>,
51    /// If set, the server pipes bytes from this reader with `Transfer-Encoding: chunked`
52    /// immediately after sending the response headers. Takes precedence over
53    /// `content_range_list` and `stream_file`. The reader is consumed and not cloned.
54    pub stream_pipe: Option<Box<dyn std::io::Read + Send>>,
55}
56
57impl Clone for Response {
58    fn clone(&self) -> Self {
59        Response {
60            http_version: self.http_version.clone(),
61            status_code: self.status_code,
62            reason_phrase: self.reason_phrase.clone(),
63            headers: self.headers.clone(),
64            content_range_list: self.content_range_list.clone(),
65            stream_file: self.stream_file.clone(),
66            stream_pipe: None,
67        }
68    }
69}
70
71impl PartialEq for Response {
72    fn eq(&self, other: &Self) -> bool {
73        self.http_version == other.http_version
74            && self.status_code == other.status_code
75            && self.reason_phrase == other.reason_phrase
76            && self.headers == other.headers
77            && self.content_range_list == other.content_range_list
78            && self.stream_file == other.stream_file
79    }
80}
81
82impl Eq for Response {}
83
84impl std::fmt::Debug for Response {
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        f.debug_struct("Response")
87            .field("http_version", &self.http_version)
88            .field("status_code", &self.status_code)
89            .field("reason_phrase", &self.reason_phrase)
90            .field("headers", &self.headers)
91            .field("content_range_list", &self.content_range_list)
92            .field("stream_file", &self.stream_file)
93            .field("stream_pipe", &self.stream_pipe.as_ref().map(|_| "<stream>"))
94            .finish()
95    }
96}
97
98#[derive(PartialEq, Eq, Clone, Debug)]
99pub struct StatusCodeReasonPhrase {
100    pub status_code: &'static i16,
101    pub reason_phrase: &'static str,
102}
103
104#[derive(PartialEq, Eq, Clone, Debug)]
105pub struct ResponseStatusCodeReasonPhrase {
106    pub n100_continue: &'static StatusCodeReasonPhrase,
107    pub n101_switching_protocols: &'static StatusCodeReasonPhrase,
108    pub n102_processing: &'static StatusCodeReasonPhrase,
109    pub n103_early_hints: &'static StatusCodeReasonPhrase,
110    pub n200_ok: &'static StatusCodeReasonPhrase,
111    pub n201_created: &'static StatusCodeReasonPhrase,
112    pub n202_accepted: &'static StatusCodeReasonPhrase,
113    pub n203_non_authoritative_information: &'static StatusCodeReasonPhrase,
114    pub n204_no_content: &'static StatusCodeReasonPhrase,
115    pub n205_reset_content: &'static StatusCodeReasonPhrase,
116    pub n206_partial_content: &'static StatusCodeReasonPhrase,
117    pub n207_multi_status: &'static StatusCodeReasonPhrase,
118    pub n208_already_reported: &'static StatusCodeReasonPhrase,
119    pub n226_im_used: &'static StatusCodeReasonPhrase,
120    pub n300_multiple_choices: &'static StatusCodeReasonPhrase,
121    pub n301_moved_permanently: &'static StatusCodeReasonPhrase,
122    pub n302_found: &'static StatusCodeReasonPhrase,
123    pub n303_see_other: &'static StatusCodeReasonPhrase,
124    pub n304_not_modified: &'static StatusCodeReasonPhrase,
125    pub n307_temporary_redirect: &'static StatusCodeReasonPhrase,
126    pub n308_permanent_redirect: &'static StatusCodeReasonPhrase,
127    pub n400_bad_request: &'static StatusCodeReasonPhrase,
128    pub n401_unauthorized: &'static StatusCodeReasonPhrase,
129    pub n402_payment_required: &'static StatusCodeReasonPhrase,
130    pub n403_forbidden: &'static StatusCodeReasonPhrase,
131    pub n404_not_found: &'static StatusCodeReasonPhrase,
132    pub n405_method_not_allowed: &'static StatusCodeReasonPhrase,
133    pub n406_not_acceptable: &'static StatusCodeReasonPhrase,
134    pub n407_proxy_authentication_required: &'static StatusCodeReasonPhrase,
135    pub n408_request_timeout: &'static StatusCodeReasonPhrase,
136    pub n409_conflict: &'static StatusCodeReasonPhrase,
137    pub n410_gone: &'static StatusCodeReasonPhrase,
138    pub n411_length_required: &'static StatusCodeReasonPhrase,
139    pub n412_precondition_failed: &'static StatusCodeReasonPhrase,
140    pub n413_payload_too_large: &'static StatusCodeReasonPhrase,
141    pub n414_uri_too_long: &'static StatusCodeReasonPhrase,
142    pub n415_unsupported_media_type: &'static StatusCodeReasonPhrase,
143    pub n416_range_not_satisfiable: &'static StatusCodeReasonPhrase,
144    pub n417_expectation_failed: &'static StatusCodeReasonPhrase,
145    pub n418_im_a_teapot: &'static StatusCodeReasonPhrase,
146    pub n421_misdirected_request: &'static StatusCodeReasonPhrase,
147    pub n422_unprocessable_entity: &'static StatusCodeReasonPhrase,
148    pub n423_locked: &'static StatusCodeReasonPhrase,
149    pub n424_failed_dependency: &'static StatusCodeReasonPhrase,
150    pub n425_too_early: &'static StatusCodeReasonPhrase,
151    pub n426_upgrade_required: &'static StatusCodeReasonPhrase,
152    pub n428_precondition_required: &'static StatusCodeReasonPhrase,
153    pub n429_too_many_requests: &'static StatusCodeReasonPhrase,
154    pub n431_request_header_fields_too_large: &'static StatusCodeReasonPhrase,
155    pub n451_unavailable_for_legal_reasons: &'static StatusCodeReasonPhrase,
156    pub n500_internal_server_error: &'static StatusCodeReasonPhrase,
157    pub n501_not_implemented: &'static StatusCodeReasonPhrase,
158    pub n502_bad_gateway: &'static StatusCodeReasonPhrase,
159    pub n503_service_unavailable: &'static StatusCodeReasonPhrase,
160    pub n504_gateway_timeout: &'static StatusCodeReasonPhrase,
161    pub n505_http_version_not_supported: &'static StatusCodeReasonPhrase,
162    pub n506_variant_also_negotiates: &'static StatusCodeReasonPhrase,
163    pub n507_insufficient_storage: &'static StatusCodeReasonPhrase,
164    pub n508_loop_detected: &'static StatusCodeReasonPhrase,
165    pub n510_not_extended: &'static StatusCodeReasonPhrase,
166    pub n511_network_authentication_required: &'static StatusCodeReasonPhrase,
167}
168
169pub const STATUS_CODE_REASON_PHRASE: ResponseStatusCodeReasonPhrase = ResponseStatusCodeReasonPhrase {
170    n100_continue: &StatusCodeReasonPhrase { status_code: &100, reason_phrase: "Continue" },
171    n101_switching_protocols: &StatusCodeReasonPhrase { status_code: &101, reason_phrase: "Switching Protocols" },
172    n102_processing: &StatusCodeReasonPhrase { status_code: &102, reason_phrase: "Processing" },
173    n103_early_hints: &StatusCodeReasonPhrase { status_code: &103, reason_phrase: "Early Hints" },
174    n200_ok: &StatusCodeReasonPhrase {
175        status_code: &200,
176        reason_phrase: "OK"
177    },
178
179    n201_created: &StatusCodeReasonPhrase { status_code: &201, reason_phrase: "Created" },
180    n202_accepted: &StatusCodeReasonPhrase { status_code: &202, reason_phrase: "Accepted" },
181    n203_non_authoritative_information: &StatusCodeReasonPhrase { status_code: &203, reason_phrase: "Non Authoritative Information" },
182    n204_no_content: &StatusCodeReasonPhrase {
183        status_code: &204,
184        reason_phrase: "No Content"
185    },
186
187    n205_reset_content: &StatusCodeReasonPhrase { status_code: &205, reason_phrase: "Reset Content" },
188    n206_partial_content: &StatusCodeReasonPhrase {
189        status_code: &206,
190        reason_phrase: "Partial Content"
191    },
192
193    n207_multi_status: &StatusCodeReasonPhrase { status_code: &207, reason_phrase: "Multi-Status" },
194    n208_already_reported: &StatusCodeReasonPhrase { status_code: &208, reason_phrase: "Already Reported" },
195    n226_im_used: &StatusCodeReasonPhrase { status_code: &226, reason_phrase: "IM Used" },
196    n300_multiple_choices: &StatusCodeReasonPhrase { status_code: &300, reason_phrase: "Multiple Choices" },
197    n301_moved_permanently: &StatusCodeReasonPhrase { status_code: &301, reason_phrase: "Moved Permanently" },
198    n302_found: &StatusCodeReasonPhrase { status_code: &302, reason_phrase: "Found" },
199    n303_see_other: &StatusCodeReasonPhrase { status_code: &303, reason_phrase: "See Other" },
200    n304_not_modified: &StatusCodeReasonPhrase { status_code: &304, reason_phrase: "Not Modified" },
201    n307_temporary_redirect: &StatusCodeReasonPhrase { status_code: &307, reason_phrase: "Temporary Redirect" },
202    n308_permanent_redirect: &StatusCodeReasonPhrase { status_code: &308, reason_phrase: "Permanent Redirect" },
203    n400_bad_request: &StatusCodeReasonPhrase {
204        status_code: &400,
205        reason_phrase: "Bad Request"
206    },
207
208    n401_unauthorized: &StatusCodeReasonPhrase { status_code: &401, reason_phrase: "Unauthorized" },
209    n402_payment_required: &StatusCodeReasonPhrase { status_code: &402, reason_phrase: "Payment Required" },
210    n403_forbidden: &StatusCodeReasonPhrase { status_code: &403, reason_phrase: "Forbidden" },
211    n404_not_found: &StatusCodeReasonPhrase {
212        status_code: &404,
213        reason_phrase: "Not Found"
214    },
215
216    n405_method_not_allowed: &StatusCodeReasonPhrase { status_code: &405, reason_phrase: "Method Not Allowed" },
217    n406_not_acceptable: &StatusCodeReasonPhrase { status_code: &406, reason_phrase: "Not Acceptable" },
218    n407_proxy_authentication_required: &StatusCodeReasonPhrase { status_code: &407, reason_phrase: "Proxy Authentication Required" },
219    n408_request_timeout: &StatusCodeReasonPhrase { status_code: &408, reason_phrase: "Request Timeout" },
220    n409_conflict: &StatusCodeReasonPhrase { status_code: &409, reason_phrase: "Conflict" },
221    n410_gone: &StatusCodeReasonPhrase { status_code: &410, reason_phrase: "Gone" },
222    n411_length_required: &StatusCodeReasonPhrase { status_code: &411, reason_phrase: "Length Required" },
223    n412_precondition_failed: &StatusCodeReasonPhrase { status_code: &412, reason_phrase: "Precondition Failed" },
224    n413_payload_too_large: &StatusCodeReasonPhrase { status_code: &413, reason_phrase: "Payload Too Large" },
225    n414_uri_too_long: &StatusCodeReasonPhrase { status_code: &414, reason_phrase: "URI Too Long" },
226    n415_unsupported_media_type: &StatusCodeReasonPhrase { status_code: &415, reason_phrase: "Unsupported Media Type" },
227    n416_range_not_satisfiable: &StatusCodeReasonPhrase {
228        status_code: &416,
229        reason_phrase: "Range Not Satisfiable"
230    },
231
232    n417_expectation_failed: &StatusCodeReasonPhrase { status_code: &417, reason_phrase: "Expectation Failed" },
233    n418_im_a_teapot: &StatusCodeReasonPhrase { status_code: &418, reason_phrase: "I'm A Teapot" },
234    n421_misdirected_request: &StatusCodeReasonPhrase { status_code: &421, reason_phrase: "Misdirected Request" },
235    n422_unprocessable_entity: &StatusCodeReasonPhrase { status_code: &422, reason_phrase: "Unprocessable Entity" },
236    n423_locked: &StatusCodeReasonPhrase { status_code: &423, reason_phrase: "Locked" },
237    n424_failed_dependency: &StatusCodeReasonPhrase { status_code: &424, reason_phrase: "Failed Dependency" },
238    n425_too_early: &StatusCodeReasonPhrase { status_code: &425, reason_phrase: "Too Early" },
239    n426_upgrade_required: &StatusCodeReasonPhrase { status_code: &426, reason_phrase: "Upgrade Required" },
240    n428_precondition_required: &StatusCodeReasonPhrase { status_code: &428, reason_phrase: "Precondition Required" },
241    n429_too_many_requests: &StatusCodeReasonPhrase { status_code: &429, reason_phrase: "Too Many Requests" },
242    n431_request_header_fields_too_large: &StatusCodeReasonPhrase { status_code: &431, reason_phrase: "Request Header Fields Too Large" },
243    n451_unavailable_for_legal_reasons: &StatusCodeReasonPhrase { status_code: &451, reason_phrase: "Unavailable For Legal Reasons" },
244    n500_internal_server_error: &StatusCodeReasonPhrase {
245        status_code: &500,
246        reason_phrase: "Internal Server Error"
247    },
248    n501_not_implemented: &StatusCodeReasonPhrase { status_code: &501, reason_phrase: "Not Implemented" },
249    n502_bad_gateway: &StatusCodeReasonPhrase { status_code: &502, reason_phrase: "Bad Gateway" },
250    n503_service_unavailable: &StatusCodeReasonPhrase { status_code: &503, reason_phrase: "Service Unavailable" },
251    n504_gateway_timeout: &StatusCodeReasonPhrase { status_code: &504, reason_phrase: "Gateway Timeout" },
252    n505_http_version_not_supported: &StatusCodeReasonPhrase { status_code: &505, reason_phrase: "HTTP Version Not Supported" },
253    n506_variant_also_negotiates: &StatusCodeReasonPhrase { status_code: &506, reason_phrase: "Variant Also Negotiates" },
254    n507_insufficient_storage: &StatusCodeReasonPhrase { status_code: &507, reason_phrase: "Insufficient Storage" },
255    n508_loop_detected: &StatusCodeReasonPhrase { status_code: &508, reason_phrase: "Loop Detected" },
256    n510_not_extended: &StatusCodeReasonPhrase { status_code: &510, reason_phrase: "Not Extended" },
257    n511_network_authentication_required: &StatusCodeReasonPhrase { status_code: &511, reason_phrase: "Network Authentication Required" }
258};
259
260/// The `Content-Range` value for a single-part body, or `None` when the
261/// response shouldn't carry one. RFC 9110 §14.4 gives `Content-Range` meaning
262/// only on `206 Partial Content` (and `416`, which rws answers with a plain
263/// error body), so full `200` responses get no such header. `range.end` is
264/// inclusive.
265pub fn content_range_header_value(status_code: i16, content_range: &ContentRange) -> Option<String> {
266    if status_code != *STATUS_CODE_REASON_PHRASE.n206_partial_content.status_code {
267        return None;
268    }
269    Some(format!(
270        "{} {}-{}/{}",
271        Range::BYTES, content_range.range.start, content_range.range.end, content_range.size
272    ))
273}
274
275impl Response {
276
277    pub fn build(status: StatusCodeReasonPhrase, header_list : Vec<Header>, body: Vec<ContentRange>) -> Response {
278        Response {
279            http_version: VERSION.http_1_1.to_string(),
280            status_code: *status.status_code,
281            reason_phrase: status.reason_phrase.to_string(),
282            headers: header_list,
283            content_range_list: body,
284            stream_file: None,
285            stream_pipe: None,
286        }
287    }
288
289    pub fn status_code_reason_phrase_list() -> Vec<&'static StatusCodeReasonPhrase> {
290        let list = vec![
291            STATUS_CODE_REASON_PHRASE.n100_continue,
292            STATUS_CODE_REASON_PHRASE.n101_switching_protocols,
293            STATUS_CODE_REASON_PHRASE.n102_processing,
294            STATUS_CODE_REASON_PHRASE.n103_early_hints,
295            STATUS_CODE_REASON_PHRASE.n200_ok,
296            STATUS_CODE_REASON_PHRASE.n201_created,
297            STATUS_CODE_REASON_PHRASE.n202_accepted,
298            STATUS_CODE_REASON_PHRASE.n203_non_authoritative_information,
299            STATUS_CODE_REASON_PHRASE.n204_no_content,
300            STATUS_CODE_REASON_PHRASE.n205_reset_content,
301            STATUS_CODE_REASON_PHRASE.n206_partial_content,
302            STATUS_CODE_REASON_PHRASE.n207_multi_status,
303            STATUS_CODE_REASON_PHRASE.n208_already_reported,
304            STATUS_CODE_REASON_PHRASE.n226_im_used,
305            STATUS_CODE_REASON_PHRASE.n300_multiple_choices,
306            STATUS_CODE_REASON_PHRASE.n301_moved_permanently,
307            STATUS_CODE_REASON_PHRASE.n302_found,
308            STATUS_CODE_REASON_PHRASE.n303_see_other,
309            STATUS_CODE_REASON_PHRASE.n304_not_modified,
310            STATUS_CODE_REASON_PHRASE.n307_temporary_redirect,
311            STATUS_CODE_REASON_PHRASE.n308_permanent_redirect,
312            STATUS_CODE_REASON_PHRASE.n400_bad_request,
313            STATUS_CODE_REASON_PHRASE.n401_unauthorized,
314            STATUS_CODE_REASON_PHRASE.n402_payment_required,
315            STATUS_CODE_REASON_PHRASE.n403_forbidden,
316            STATUS_CODE_REASON_PHRASE.n404_not_found,
317            STATUS_CODE_REASON_PHRASE.n405_method_not_allowed,
318            STATUS_CODE_REASON_PHRASE.n406_not_acceptable,
319            STATUS_CODE_REASON_PHRASE.n407_proxy_authentication_required,
320            STATUS_CODE_REASON_PHRASE.n408_request_timeout,
321            STATUS_CODE_REASON_PHRASE.n409_conflict,
322            STATUS_CODE_REASON_PHRASE.n410_gone,
323            STATUS_CODE_REASON_PHRASE.n411_length_required,
324            STATUS_CODE_REASON_PHRASE.n412_precondition_failed,
325            STATUS_CODE_REASON_PHRASE.n413_payload_too_large,
326            STATUS_CODE_REASON_PHRASE.n414_uri_too_long,
327            STATUS_CODE_REASON_PHRASE.n415_unsupported_media_type,
328            STATUS_CODE_REASON_PHRASE.n416_range_not_satisfiable,
329            STATUS_CODE_REASON_PHRASE.n417_expectation_failed,
330            STATUS_CODE_REASON_PHRASE.n418_im_a_teapot,
331            STATUS_CODE_REASON_PHRASE.n421_misdirected_request,
332            STATUS_CODE_REASON_PHRASE.n422_unprocessable_entity,
333            STATUS_CODE_REASON_PHRASE.n423_locked,
334            STATUS_CODE_REASON_PHRASE.n424_failed_dependency,
335            STATUS_CODE_REASON_PHRASE.n425_too_early,
336            STATUS_CODE_REASON_PHRASE.n426_upgrade_required,
337            STATUS_CODE_REASON_PHRASE.n428_precondition_required,
338            STATUS_CODE_REASON_PHRASE.n429_too_many_requests,
339            STATUS_CODE_REASON_PHRASE.n431_request_header_fields_too_large,
340            STATUS_CODE_REASON_PHRASE.n451_unavailable_for_legal_reasons,
341            STATUS_CODE_REASON_PHRASE.n500_internal_server_error,
342            STATUS_CODE_REASON_PHRASE.n501_not_implemented,
343            STATUS_CODE_REASON_PHRASE.n502_bad_gateway,
344            STATUS_CODE_REASON_PHRASE.n503_service_unavailable,
345            STATUS_CODE_REASON_PHRASE.n504_gateway_timeout,
346            STATUS_CODE_REASON_PHRASE.n505_http_version_not_supported,
347            STATUS_CODE_REASON_PHRASE.n506_variant_also_negotiates,
348            STATUS_CODE_REASON_PHRASE.n507_insufficient_storage,
349            STATUS_CODE_REASON_PHRASE.n508_loop_detected,
350            STATUS_CODE_REASON_PHRASE.n510_not_extended,
351            STATUS_CODE_REASON_PHRASE.n511_network_authentication_required,
352        ];
353        list
354    }
355
356    pub const _ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE: &'static str = "Unable to parse status code";
357
358    pub const _HTTP_VERSION_AND_STATUS_CODE_AND_REASON_PHRASE_REGEX: &'static str = "(?P<http_version>\\w+/\\w+.\\w)\\s(?P<status_code>\\w+)\\s(?P<reason_phrase>.+)";
359
360    pub fn _get_header(&self, name: String) -> Option<&Header> {
361        // Header names are case-insensitive (RFC 9110 §5.1), as in Request::get_header.
362        let header =  self.headers.iter().find(|x| x.name.eq_ignore_ascii_case(&name));
363        header
364    }
365
366    /// `true` for a status that never has content: `1xx`, `204`, `304`
367    /// (RFC 9110 §6.4.1, §15.3.5, §15.4.5).
368    pub fn status_has_no_content(status_code: i16) -> bool {
369        (100..200).contains(&status_code) || status_code == 204 || status_code == 304
370    }
371
372    /// For a status without content ([`Response::status_has_no_content`]),
373    /// drop the body — buffered, file or pipe — and any `Content-Length` or
374    /// `Transfer-Encoding`, which those responses must not carry (#54).
375    /// Every connection writer calls it before framing the response.
376    pub fn drop_content_for_status(&mut self) {
377        if !Response::status_has_no_content(self.status_code) {
378            return;
379        }
380        self.content_range_list.clear();
381        self.stream_file = None;
382        self.stream_pipe = None;
383        self.headers.retain(|h| {
384            !h.name.eq_ignore_ascii_case(Header::_CONTENT_LENGTH)
385                && !h.name.eq_ignore_ascii_case(Header::_TRANSFER_ENCODING)
386        });
387    }
388
389    pub fn generate_body(content_range_list: Vec<ContentRange>) -> Vec<u8> {
390        let mut body = vec![];
391        let one = 1;
392
393        if content_range_list.len() == one {
394            // Move the body out rather than copy it: for a large file that
395            // copy was a second full-size buffer per in-flight response.
396            let mut content_range_list = content_range_list;
397            return content_range_list.swap_remove(0).body;
398        }
399
400        if content_range_list.len() > one {
401            // RFC 2046 §5.1.1: each part opens with "--boundary", the body
402            // closes with "--boundary--" (#53).
403            let boundary = Range::multipart_boundary(&content_range_list);
404            for (i, content_range) in content_range_list.iter().enumerate() {
405                let mut body_str = SYMBOL.empty_string.to_string();
406                if i != 0 {
407                    body_str.push_str(SYMBOL.new_line_carriage_return);
408                }
409                body_str.push_str(SYMBOL.hyphen);
410                body_str.push_str(SYMBOL.hyphen);
411                body_str.push_str(&boundary);
412                body_str.push_str(SYMBOL.new_line_carriage_return);
413                let content_type = [Header::_CONTENT_TYPE, Header::NAME_VALUE_SEPARATOR, SYMBOL.whitespace, &Header::single_line(&content_range.content_type)].join("");
414                body_str.push_str(content_type.as_str());
415                body_str.push_str(SYMBOL.new_line_carriage_return);
416                let content_range_header = [Header::_CONTENT_RANGE, Header::NAME_VALUE_SEPARATOR, SYMBOL.whitespace, Range::BYTES, SYMBOL.whitespace, &content_range.range.start.to_string(), SYMBOL.hyphen, &content_range.range.end.to_string(), SYMBOL.slash, &Header::single_line(&content_range.size)].join("");
417                body_str.push_str(content_range_header.as_str());
418                body_str.push_str(SYMBOL.new_line_carriage_return);
419                body_str.push_str(SYMBOL.new_line_carriage_return);
420
421                body.extend_from_slice(body_str.as_bytes());
422                body.extend_from_slice(&content_range.body);
423            }
424            body.extend_from_slice(b"\r\n--");
425            body.extend_from_slice(boundary.as_bytes());
426            body.extend_from_slice(b"--\r\n");
427        }
428
429        body
430    }
431
432    /// The serialized response: status line + headers, then the body.
433    pub fn generate_response(response: Response, request: Request) -> Vec<u8> {
434        let (mut head, body) = Response::generate_response_parts(response, request);
435        head.extend_from_slice(&body);
436        head
437    }
438
439    /// Like [`generate_response`](Self::generate_response), but returns the
440    /// head (status line + headers + blank line) and the body separately, so
441    /// a connection writer can send them back to back without first copying
442    /// a large body into one combined buffer. The body is empty for HEAD and
443    /// OPTIONS.
444    pub fn generate_response_parts(mut response: Response, request: Request) -> (Vec<u8>, Vec<u8>) {
445        response.drop_content_for_status();
446
447        if response.content_range_list.len() == 1 {
448            let content_range_index = 0;
449            let content_range = response.content_range_list.get(content_range_index).unwrap();
450            response.headers.push(Header {
451                name: Header::_CONTENT_TYPE.to_string(),
452                value: content_range.content_type.to_string()
453            });
454
455            if let Some(value) = content_range_header_value(response.status_code, content_range) {
456                response.headers.push(Header {
457                    name: Header::_CONTENT_RANGE.to_string(),
458                    value,
459                });
460            }
461
462            response.headers.push(Header {
463                name: Header::_CONTENT_LENGTH.to_string(),
464                value: content_range.body.len().to_string()
465            });
466        }
467
468        if response.content_range_list.len() > 1 {
469            response.headers.push(Header {
470                name: Header::_CONTENT_TYPE.to_string(),
471                value: Range::multipart_content_type(&response.content_range_list),
472            });
473        }
474
475        let multipart = response.content_range_list.len() > 1;
476        let body = Response::generate_body(response.content_range_list);
477        if multipart {
478            // Without it a keep-alive client can't tell where the body ends
479            // (RFC 9112 §6.3; #53).
480            response.headers.push(Header {
481                name: Header::_CONTENT_LENGTH.to_string(),
482                value: body.len().to_string(),
483            });
484        }
485
486        // One buffer, sized up front, written once: this used to build a
487        // String per header, grow a second one, join the status line and
488        // copy it all again with format! — the biggest allocation cost of a
489        // small response.
490        let status_code = response.status_code.to_string();
491        let capacity = response.http_version.len() + status_code.len() + response.reason_phrase.len() + 4
492            + response.headers.iter().map(|h| h.name.len() + h.value.len() + 4).sum::<usize>()
493            + 2;
494        let mut head = Vec::with_capacity(capacity);
495        head.extend_from_slice(response.http_version.as_bytes());
496        head.push(b' ');
497        head.extend_from_slice(status_code.as_bytes());
498        head.push(b' ');
499        head.extend_from_slice(Header::single_line(&response.reason_phrase).as_bytes());
500        head.extend_from_slice(b"\r\n");
501        for header in &response.headers {
502            head.extend_from_slice(Header::single_line(&header.name).as_bytes());
503            head.extend_from_slice(Header::NAME_VALUE_SEPARATOR.as_bytes());
504            head.extend_from_slice(Header::single_line(&header.value).as_bytes());
505            head.extend_from_slice(b"\r\n");
506        }
507        head.extend_from_slice(b"\r\n");
508
509        // HEAD: the head of the GET response, no body. OPTIONS gets its body
510        // like any other method: dropping it under a Content-Length that
511        // announced it left the client waiting.
512        if request.method == METHOD.head {
513            (head, Vec::new())
514        } else {
515            (head, body)
516        }
517    }
518
519    pub fn _parse_response(response_vec_u8: &[u8]) -> Response {
520        let mut cursor = io::Cursor::new(response_vec_u8);
521
522        let mut response = Response {
523            http_version: "".to_string(),
524            status_code: 0,
525            reason_phrase: "".to_string(),
526            headers: vec![],
527            content_range_list: vec![],
528            stream_file: None,
529            stream_pipe: None,
530        };
531
532        let content_length: usize = 0;
533        let iteration_number : usize = 0;
534
535        Response::_parse_raw_response_via_cursor(&mut cursor, iteration_number, &mut response, content_length);
536
537        return response;
538    }
539
540    pub fn _parse_http_version_status_code_reason_phrase_string(http_version_status_code_reason_phrase: &str) -> Result<(String, i16, String), String> {
541        let truncated = StringExt::truncate_new_line_carriage_return(http_version_status_code_reason_phrase);
542
543        let boxed_split = truncated.split_once(SYMBOL.whitespace);
544        if boxed_split.is_none() {
545            return Err(Response::_ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE.to_string())
546        }
547
548        let (http_version, status_code_reason_phrase) = boxed_split.unwrap();
549        if !HTTP::is_supported_version(http_version) {
550            return Err(Response::_ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE.to_string())
551        }
552
553        // RFC 9112 §4: status-line = HTTP-version SP status-code SP [ reason-phrase ].
554        // The reason phrase is optional free text that clients should ignore
555        // (Python's http.server sends "404 File not found"), and any 3-digit
556        // code is valid — so neither is checked against the known list.
557        let (status_code, reason_phrase) = status_code_reason_phrase
558            .split_once(SYMBOL.whitespace)
559            .unwrap_or((status_code_reason_phrase, ""));
560
561        let is_three_digits = status_code.len() == 3 && status_code.bytes().all(|b| b.is_ascii_digit());
562        let status_code_i16 = match status_code.parse::<i16>() {
563            Ok(code) if is_three_digits && code >= 100 => code,
564            _ => return Err(Response::_ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE.to_string()),
565        };
566
567        return Ok((http_version.to_string(), status_code_i16, reason_phrase.to_string()))
568    }
569
570    pub fn _parse_http_response_header_string(header_string: &str) -> Header {
571        // A line without ": " (malformed upstream input) is a name with an
572        // empty value, as in Request::parse_http_request_header_string —
573        // not an index-out-of-bounds panic.
574        let (header_name, raw_header_value) = header_string
575            .split_once(Header::NAME_VALUE_SEPARATOR)
576            .unwrap_or((header_string, ""));
577        let header_value = StringExt::truncate_new_line_carriage_return(raw_header_value);
578
579
580        Header {
581            name: header_name.to_string(),
582            value: header_value.to_string()
583        }
584    }
585
586    pub fn _parse_raw_response_via_cursor(
587        cursor: &mut Cursor<&[u8]>,
588        mut iteration_number: usize,
589        response: &mut Response,
590        mut content_length: usize) {
591
592        let mut buffer = vec![];
593        let boxed_read = cursor.read_until(b'\n', &mut buffer);
594        if boxed_read.is_err() {
595            eprintln!("unable to parse raw response via cursor {}", boxed_read.err().unwrap());
596            return;
597        }
598        let bytes_offset = boxed_read.unwrap();
599        let mut buffer_as_u8_array: &[u8] = &buffer;
600        let string = String::from_utf8(Vec::from(buffer_as_u8_array)).unwrap();
601
602        let is_first_iteration = iteration_number == 0;
603        let new_line_char_found = bytes_offset != 0;
604        let current_string_is_empty = string.trim().len() == 0;
605
606        if is_first_iteration {
607            let boxed_http_version_status_code_reason_phrase = Response::_parse_http_version_status_code_reason_phrase_string(&string);
608            if boxed_http_version_status_code_reason_phrase.is_err() {
609                let error = boxed_http_version_status_code_reason_phrase.err().unwrap();
610                eprintln!("{}", error);
611                return;
612            }
613
614            let (http_version, status_code, reason_phrase) = boxed_http_version_status_code_reason_phrase.unwrap();
615
616            response.http_version = http_version;
617            response.status_code = status_code;
618            response.reason_phrase = reason_phrase;
619        }
620
621        if current_string_is_empty {
622            // No Content-Type (a 204, a 304): an empty, non-multipart body.
623            let content_type = response._get_header(Header::_CONTENT_TYPE.to_string()).cloned()
624                .unwrap_or(Header { name: Header::_CONTENT_TYPE.to_string(), value: String::new() });
625            let is_multipart = Response::_is_multipart_byteranges_content_type(&content_type);
626
627            if is_multipart {
628                let content_range_list : Vec<ContentRange> = vec![];
629
630                let boundary = content_type.value.split(';')
631                    .filter_map(|param| param.trim().strip_prefix("boundary="))
632                    .next()
633                    .map(|b| b.trim_matches('"').to_string())
634                    .unwrap_or_else(|| Range::STRING_SEPARATOR.to_string());
635                let mut buf = vec![];
636                let _ = cursor.read_until(b'\n', &mut buf);
637                let boxed_value = Range::_parse_multipart_body_with_boundary(cursor, content_range_list, &boundary);
638                let mut range_list = vec![];
639                if boxed_value.is_ok() {
640                    range_list = boxed_value.unwrap();
641                }
642                response.content_range_list = range_list;
643            } else {
644                buffer = vec![];
645                let boxed_read = cursor.read_to_end(&mut buffer);
646                if boxed_read.is_ok() {
647                    buffer_as_u8_array = &buffer;
648
649                    let content_range = ContentRange {
650                        unit: Range::BYTES.to_string(),
651                        range: Range {
652                            start: 0,
653                            end: buffer_as_u8_array.len() as u64
654                        },
655                        size: buffer_as_u8_array.len().to_string(),
656                        body: Vec::from(buffer_as_u8_array),
657                        content_type: content_type.value.to_string()
658                    };
659                    response.content_range_list = vec![content_range];
660                } else {
661                    let reason = boxed_read.err().unwrap();
662                    eprintln!("error reading file: {}", reason.to_string())
663                }
664
665            }
666
667            return;
668        }
669
670        if new_line_char_found && !current_string_is_empty {
671            let mut header = Header { name: "".to_string(), value: "".to_string() };
672            if !is_first_iteration {
673                header = Response::_parse_http_response_header_string(&string);
674                if header.name.eq_ignore_ascii_case(Header::_CONTENT_LENGTH) {
675                    // No error channel here: treat an invalid value as absent.
676                    content_length = header.value.trim().parse().unwrap_or(0);
677                }
678            }
679
680            response.headers.push(header);
681            iteration_number += 1;
682            Response::_parse_raw_response_via_cursor(cursor, iteration_number, response, content_length);
683        }
684    }
685
686    pub fn _is_multipart_byteranges_content_type(content_type: &Header) -> bool {
687        let multipart_byteranges =
688            [
689                Range::MULTIPART,
690                SYMBOL.slash,
691                Range::BYTERANGES
692            ].join("");
693        let is_multipart_byteranges = content_type.value.starts_with(&multipart_byteranges);
694        is_multipart_byteranges
695    }
696
697
698    pub fn get_response(
699        status_code_reason_phrase: &StatusCodeReasonPhrase,
700        boxed_header_list: Option<Vec<Header>>,
701        boxed_content_range_list: Option<Vec<ContentRange>>) -> Response {
702
703        let mut header_list: Vec<Header> = vec![];
704        if boxed_header_list.is_some() {
705            header_list = boxed_header_list.unwrap();
706        }
707
708        let mut content_range_list: Vec<ContentRange> = vec![];
709        if boxed_content_range_list.is_some() {
710            content_range_list = boxed_content_range_list.unwrap();
711        }
712
713        let response = Response {
714            http_version: VERSION.http_1_1.to_string(),
715            status_code: *status_code_reason_phrase.status_code,
716            reason_phrase: status_code_reason_phrase.reason_phrase.to_string(),
717            headers: header_list,
718            content_range_list,
719            stream_file: None,
720            stream_pipe: None,
721        };
722
723        response
724    }
725
726    /// Build a `Content-Type: application/json` response from already-serialized bytes.
727    ///
728    /// Takes raw bytes rather than a `Serialize` value so it carries no dependency on the
729    /// `serde` feature — pair it with `serde_json::to_vec` (or hand-rolled JSON) to build `body`.
730    /// For a one-line serialize-and-respond helper on a typed value, see [`crate::json::Json`]
731    /// (`serde` feature).
732    ///
733    /// ```rust,no_run
734    /// use rust_web_server::response::{Response, STATUS_CODE_REASON_PHRASE};
735    ///
736    /// let response = Response::json(STATUS_CODE_REASON_PHRASE.n200_ok, br#"{"ok":true}"#.to_vec());
737    /// ```
738    pub fn json(status_code_reason_phrase: &StatusCodeReasonPhrase, body: Vec<u8>) -> Response {
739        let content_range = Range::get_content_range(body, MimeType::APPLICATION_JSON.to_string());
740        Response::get_response(status_code_reason_phrase, None, Some(vec![content_range]))
741    }
742
743    /// Build a `Content-Type: text/plain` response from a body string.
744    ///
745    /// ```rust,no_run
746    /// use rust_web_server::response::{Response, STATUS_CODE_REASON_PHRASE};
747    ///
748    /// let response = Response::text(STATUS_CODE_REASON_PHRASE.n400_bad_request, "invalid input");
749    /// ```
750    pub fn text(status_code_reason_phrase: &StatusCodeReasonPhrase, body: &str) -> Response {
751        let content_range = Range::get_content_range(body.as_bytes().to_vec(), MimeType::TEXT_PLAIN.to_string());
752        Response::get_response(status_code_reason_phrase, None, Some(vec![content_range]))
753    }
754
755    pub fn generate(&mut self) -> Vec<u8> {
756        let response = &mut self.clone();
757        response.drop_content_for_status();
758
759        if response.content_range_list.len() == 1 {
760            let content_range_index = 0;
761            let content_range = response.content_range_list.get(content_range_index).unwrap();
762            response.headers.push(Header {
763                name: Header::_CONTENT_TYPE.to_string(),
764                value: content_range.content_type.to_string()
765            });
766
767            if let Some(value) = content_range_header_value(response.status_code, content_range) {
768                response.headers.push(Header {
769                    name: Header::_CONTENT_RANGE.to_string(),
770                    value,
771                });
772            }
773
774            response.headers.push(Header {
775                name: Header::_CONTENT_LENGTH.to_string(),
776                value: content_range.body.len().to_string()
777            });
778        }
779
780        if response.content_range_list.len() > 1 {
781            response.headers.push(Header {
782                name: Header::_CONTENT_TYPE.to_string(),
783                value: Range::multipart_content_type(&response.content_range_list),
784            });
785        }
786
787        let response_clone = response.clone();
788        let body = Response::generate_body(response_clone.content_range_list);
789        if response.content_range_list.len() > 1 {
790            response.headers.push(Header {
791                name: Header::_CONTENT_LENGTH.to_string(),
792                value: body.len().to_string(),
793            });
794        }
795
796        let mut headers_str = SYMBOL.new_line_carriage_return.to_string();
797
798        let header_list = response.headers.clone();
799        for header in header_list {
800            let mut header_string = SYMBOL.empty_string.to_string();
801            header_string.push_str(&Header::single_line(&header.name));
802            header_string.push_str(Header::NAME_VALUE_SEPARATOR);
803            header_string.push_str(&Header::single_line(&header.value));
804            header_string.push_str(SYMBOL.new_line_carriage_return);
805            headers_str.push_str(&header_string);
806        }
807
808        let response_clone = response.clone();
809        let status = [response_clone.http_version, response.status_code.to_string(), Header::single_line(&response_clone.reason_phrase).into_owned()].join(SYMBOL.whitespace);
810        let response_without_body = format!(
811            "{}{}{}",
812            status,
813            headers_str,
814            SYMBOL.new_line_carriage_return,
815        );
816
817
818        [response_without_body.into_bytes(), body].concat()
819    }
820
821    pub fn get_header(&self, name: String) -> Option<&Header> {
822        self._get_header(name)
823    }
824
825    pub fn parse(response_vec_u8: &[u8]) -> Result<Response, String> {
826        let total_bytes : i32 = response_vec_u8.len() as i32;
827        let bytes_read = 0;
828        let mut cursor = io::Cursor::new(response_vec_u8);
829
830        let mut response = Response {
831            http_version: "".to_string(),
832            status_code: 0,
833            reason_phrase: "".to_string(),
834            headers: vec![],
835            content_range_list: vec![],
836            stream_file: None,
837            stream_pipe: None,
838        };
839
840        let content_length: usize = 0;
841        let iteration_number : usize = 0;
842
843        let boxed_parse = Response::parse_raw_response_via_cursor(
844            &mut cursor, iteration_number,
845            &mut response,
846            content_length,
847            total_bytes,
848            bytes_read
849        );
850        if boxed_parse.is_err() {
851            let message = boxed_parse.err().unwrap();
852            return Err(message);
853        }
854
855        return Ok(response);
856    }
857
858    pub fn parse_raw_response_via_cursor(
859        cursor: &mut Cursor<&[u8]>,
860        mut iteration_number: usize,
861        response: &mut Response,
862        _content_length: usize,
863        total_bytes: i32,
864        mut bytes_read: i32) -> Result<(), String> {
865        // A loop, not tail recursion (which Rust doesn't guarantee to
866        // eliminate): one stack frame per header line let a backend sending
867        // thousands of header lines overflow the stack and abort the process.
868        loop {
869
870            let mut buffer = vec![];
871            let boxed_read = cursor.read_until(b'\n', &mut buffer);
872            if boxed_read.is_err() {
873                let message = format!("unable to parse raw response via cursor {}", boxed_read.err().unwrap());
874                return Err(message);
875            }
876            let bytes_offset = boxed_read.unwrap();
877            bytes_read = bytes_read + bytes_offset as i32;
878            if bytes_read == total_bytes {
879                // end of stream
880            }
881            let mut buffer_as_u8_array: &[u8] = &buffer;
882            let boxed_string = String::from_utf8(Vec::from(buffer_as_u8_array));
883            if boxed_string.is_err() {
884                let message = boxed_string.err().unwrap().to_string();
885                return Err(message);
886            }
887            let string = boxed_string.unwrap();
888
889            let is_first_iteration = iteration_number == 0;
890            let new_line_char_found = bytes_offset != 0;
891            let current_string_is_empty = string.trim().len() == 0;
892
893            if is_first_iteration {
894                let boxed_http_version_status_code_reason_phrase = Response::_parse_http_version_status_code_reason_phrase_string(&string);
895                if boxed_http_version_status_code_reason_phrase.is_err() {
896                    let message = boxed_http_version_status_code_reason_phrase.err().unwrap();
897                    return Err(message);
898                }
899
900                let (http_version, status_code, reason_phrase) = boxed_http_version_status_code_reason_phrase.unwrap();
901
902                response.http_version = http_version;
903                response.status_code = status_code;
904                response.reason_phrase = reason_phrase;
905            }
906
907            if current_string_is_empty {
908                let mut is_multipart = false;
909                // if response does not contain Content-Type, it will be defaulted to APPLICATION_OCTET_STREAM
910                let mut content_type = MimeType::APPLICATION_OCTET_STREAM;
911
912                let boxed_content_type = response.get_header(Header::_CONTENT_TYPE.to_string());
913                if boxed_content_type.is_some() {
914                    let content_type_header = response.get_header(Header::_CONTENT_TYPE.to_string()).unwrap();
915                    content_type = content_type_header.value.as_str();
916                    is_multipart = Response::_is_multipart_byteranges_content_type(&content_type_header);
917                }
918
919
920                if is_multipart {
921                    let content_range_list : Vec<ContentRange> = vec![];
922                    let boxed_content_type = response.get_header(Header::_CONTENT_TYPE.to_string());
923                    if boxed_content_type.is_none() {
924                        return Err("Content-Type is missing".to_string());
925                    }
926                    let content_type = boxed_content_type.unwrap();
927                    let boxed_boundary = FormMultipartData::extract_boundary(content_type.value.as_str());
928                    if boxed_boundary.is_err() {
929                        return Err("unable to extract boundary from Content-Type".to_string());
930                    }
931                    let boundary = boxed_boundary.unwrap();
932
933                    let is_opening_boundary_read = false;
934                    let boxed_content_range_list =
935                        Range::parse_multipart_body_with_boundary(
936                            cursor,
937                            content_range_list,
938                            boundary,
939                            total_bytes,
940                            bytes_read,
941                            is_opening_boundary_read);
942                    if boxed_content_range_list.is_err() {
943                        let message = boxed_content_range_list.err().unwrap();
944                        return Err(message);
945                    }
946
947                    response.content_range_list = boxed_content_range_list.unwrap();
948                } else {
949                    buffer = vec![];
950                    let boxed_read = cursor.read_to_end(&mut buffer);
951                    if boxed_read.is_err() {
952                        let message = boxed_read.err().unwrap().to_string();
953                        return Err(message);
954                    }
955                    let bytes_offset = boxed_read.unwrap();
956                    bytes_read = bytes_read + bytes_offset as i32;
957                    if bytes_read == total_bytes {
958                        // end of stream
959                    }
960
961                    buffer_as_u8_array = &buffer;
962
963                    let content_range = ContentRange {
964                        unit: Range::BYTES.to_string(),
965                        range: Range {
966                            start: 0,
967                            end: buffer_as_u8_array.len() as u64
968                        },
969                        size: buffer_as_u8_array.len().to_string(),
970                        body: Vec::from(buffer_as_u8_array),
971                        content_type: content_type.to_string()
972                    };
973                    response.content_range_list = vec![content_range];
974
975
976                }
977
978                return Ok(());
979            }
980
981            if new_line_char_found && !current_string_is_empty {
982                if !is_first_iteration {
983                    let boxed_header = Response::parse_http_response_header_string(&string);
984                    if boxed_header.is_err() {
985                        let message = boxed_header.err().unwrap();
986                        return Err(message);
987                    }
988                    let header = boxed_header.unwrap();
989                    if header.name.eq_ignore_ascii_case(Header::_CONTENT_LENGTH) {
990                        // An upstream-supplied value: reject garbage instead of panicking.
991                        let _: usize = header.value.trim().parse()
992                            .map_err(|_| format!("invalid Content-Length: {:?}", header.value))?;
993                    }
994                    response.headers.push(header);
995                }
996
997            iteration_number += 1;
998        } else {
999            return Err("unable to parse".to_string());
1000        }
1001        }
1002    }
1003
1004    pub fn parse_http_response_header_string(header_string: &str) -> Result<Header, String> {
1005        let header_parts: Option<(&str, &str)> = header_string.split_once(Header::NAME_VALUE_SEPARATOR);
1006        if header_parts.is_none() {
1007            let message = format!("unable to parse header: {}", header_string);
1008            return Err(message);
1009        }
1010        let (header_name, raw_header_value) = header_parts.unwrap();
1011        let header_value = StringExt::truncate_new_line_carriage_return(&raw_header_value);
1012
1013        Ok(Header {
1014            name: header_name.to_string(),
1015            value: header_value.to_string()
1016        })
1017    }
1018}
1019
1020impl New for Response {
1021    fn new() -> Self {
1022        Response {
1023            http_version: VERSION.http_1_1.to_string(),
1024            status_code: *STATUS_CODE_REASON_PHRASE.n501_not_implemented.status_code,
1025            reason_phrase: STATUS_CODE_REASON_PHRASE.n501_not_implemented.reason_phrase.to_string(),
1026            headers: vec![],
1027            content_range_list: vec![],
1028            stream_file: None,
1029            stream_pipe: None,
1030        }
1031    }
1032}