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
260impl Response {
261
262    pub fn build(status: StatusCodeReasonPhrase, header_list : Vec<Header>, body: Vec<ContentRange>) -> Response {
263        Response {
264            http_version: VERSION.http_1_1.to_string(),
265            status_code: *status.status_code,
266            reason_phrase: status.reason_phrase.to_string(),
267            headers: header_list,
268            content_range_list: body,
269            stream_file: None,
270            stream_pipe: None,
271        }
272    }
273
274    pub fn status_code_reason_phrase_list() -> Vec<&'static StatusCodeReasonPhrase> {
275        let list = vec![
276            STATUS_CODE_REASON_PHRASE.n100_continue,
277            STATUS_CODE_REASON_PHRASE.n101_switching_protocols,
278            STATUS_CODE_REASON_PHRASE.n102_processing,
279            STATUS_CODE_REASON_PHRASE.n103_early_hints,
280            STATUS_CODE_REASON_PHRASE.n200_ok,
281            STATUS_CODE_REASON_PHRASE.n201_created,
282            STATUS_CODE_REASON_PHRASE.n202_accepted,
283            STATUS_CODE_REASON_PHRASE.n203_non_authoritative_information,
284            STATUS_CODE_REASON_PHRASE.n204_no_content,
285            STATUS_CODE_REASON_PHRASE.n205_reset_content,
286            STATUS_CODE_REASON_PHRASE.n206_partial_content,
287            STATUS_CODE_REASON_PHRASE.n207_multi_status,
288            STATUS_CODE_REASON_PHRASE.n208_already_reported,
289            STATUS_CODE_REASON_PHRASE.n226_im_used,
290            STATUS_CODE_REASON_PHRASE.n300_multiple_choices,
291            STATUS_CODE_REASON_PHRASE.n301_moved_permanently,
292            STATUS_CODE_REASON_PHRASE.n302_found,
293            STATUS_CODE_REASON_PHRASE.n303_see_other,
294            STATUS_CODE_REASON_PHRASE.n304_not_modified,
295            STATUS_CODE_REASON_PHRASE.n307_temporary_redirect,
296            STATUS_CODE_REASON_PHRASE.n308_permanent_redirect,
297            STATUS_CODE_REASON_PHRASE.n400_bad_request,
298            STATUS_CODE_REASON_PHRASE.n401_unauthorized,
299            STATUS_CODE_REASON_PHRASE.n402_payment_required,
300            STATUS_CODE_REASON_PHRASE.n403_forbidden,
301            STATUS_CODE_REASON_PHRASE.n404_not_found,
302            STATUS_CODE_REASON_PHRASE.n405_method_not_allowed,
303            STATUS_CODE_REASON_PHRASE.n406_not_acceptable,
304            STATUS_CODE_REASON_PHRASE.n407_proxy_authentication_required,
305            STATUS_CODE_REASON_PHRASE.n408_request_timeout,
306            STATUS_CODE_REASON_PHRASE.n409_conflict,
307            STATUS_CODE_REASON_PHRASE.n410_gone,
308            STATUS_CODE_REASON_PHRASE.n411_length_required,
309            STATUS_CODE_REASON_PHRASE.n412_precondition_failed,
310            STATUS_CODE_REASON_PHRASE.n413_payload_too_large,
311            STATUS_CODE_REASON_PHRASE.n414_uri_too_long,
312            STATUS_CODE_REASON_PHRASE.n415_unsupported_media_type,
313            STATUS_CODE_REASON_PHRASE.n416_range_not_satisfiable,
314            STATUS_CODE_REASON_PHRASE.n417_expectation_failed,
315            STATUS_CODE_REASON_PHRASE.n418_im_a_teapot,
316            STATUS_CODE_REASON_PHRASE.n421_misdirected_request,
317            STATUS_CODE_REASON_PHRASE.n422_unprocessable_entity,
318            STATUS_CODE_REASON_PHRASE.n423_locked,
319            STATUS_CODE_REASON_PHRASE.n424_failed_dependency,
320            STATUS_CODE_REASON_PHRASE.n425_too_early,
321            STATUS_CODE_REASON_PHRASE.n426_upgrade_required,
322            STATUS_CODE_REASON_PHRASE.n428_precondition_required,
323            STATUS_CODE_REASON_PHRASE.n429_too_many_requests,
324            STATUS_CODE_REASON_PHRASE.n431_request_header_fields_too_large,
325            STATUS_CODE_REASON_PHRASE.n451_unavailable_for_legal_reasons,
326            STATUS_CODE_REASON_PHRASE.n500_internal_server_error,
327            STATUS_CODE_REASON_PHRASE.n501_not_implemented,
328            STATUS_CODE_REASON_PHRASE.n502_bad_gateway,
329            STATUS_CODE_REASON_PHRASE.n503_service_unavailable,
330            STATUS_CODE_REASON_PHRASE.n504_gateway_timeout,
331            STATUS_CODE_REASON_PHRASE.n505_http_version_not_supported,
332            STATUS_CODE_REASON_PHRASE.n506_variant_also_negotiates,
333            STATUS_CODE_REASON_PHRASE.n507_insufficient_storage,
334            STATUS_CODE_REASON_PHRASE.n508_loop_detected,
335            STATUS_CODE_REASON_PHRASE.n510_not_extended,
336            STATUS_CODE_REASON_PHRASE.n511_network_authentication_required,
337        ];
338        list
339    }
340
341    pub const _ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE: &'static str = "Unable to parse status code";
342
343    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>.+)";
344
345    pub fn _get_header(&self, name: String) -> Option<&Header> {
346        let header =  self.headers.iter().find(|x| x.name == name);
347        header
348    }
349
350    pub fn generate_body(content_range_list: Vec<ContentRange>) -> Vec<u8> {
351        let mut body = vec![];
352        let one = 1;
353
354        if content_range_list.len() == one {
355            let index = 0;
356            let content_range = content_range_list.get(index).unwrap();
357            body = content_range.body.to_vec();
358        }
359
360        if content_range_list.len() > one {
361            for (i, content_range) in content_range_list.iter().enumerate() {
362                let mut body_str = SYMBOL.empty_string.to_string();
363                if i != 0 {
364                    body_str.push_str(SYMBOL.new_line_carriage_return);
365                }
366                body_str.push_str(SYMBOL.hyphen);
367                body_str.push_str(SYMBOL.hyphen);
368                body_str.push_str(Range::STRING_SEPARATOR);
369                body_str.push_str(SYMBOL.new_line_carriage_return);
370                let content_type = [Header::_CONTENT_TYPE, Header::NAME_VALUE_SEPARATOR, SYMBOL.whitespace, &content_range.content_type.to_string()].join("");
371                body_str.push_str(content_type.as_str());
372                body_str.push_str(SYMBOL.new_line_carriage_return);
373                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, &content_range.size].join("");
374                body_str.push_str(content_range_header.as_str());
375                body_str.push_str(SYMBOL.new_line_carriage_return);
376                body_str.push_str(SYMBOL.new_line_carriage_return);
377
378                let inner_body = [body_str.as_bytes(), &content_range.body].concat();
379                body = [body, inner_body].concat();
380            }
381            let mut trailing_separator = SYMBOL.empty_string.to_string();
382            trailing_separator.push_str(SYMBOL.new_line_carriage_return);
383            trailing_separator.push_str(SYMBOL.hyphen);
384            trailing_separator.push_str(SYMBOL.hyphen);
385            trailing_separator.push_str(Range::STRING_SEPARATOR);
386            body = [&body, trailing_separator.as_bytes()].concat();
387        }
388
389        body
390    }
391
392    pub fn generate_response(mut response: Response, request: Request) -> Vec<u8> {
393
394        if response.content_range_list.len() == 1 {
395            let content_range_index = 0;
396            let content_range = response.content_range_list.get(content_range_index).unwrap();
397            response.headers.push(Header {
398                name: Header::_CONTENT_TYPE.to_string(),
399                value: content_range.content_type.to_string()
400            });
401
402            let content_range_header_value = [
403                Range::BYTES,
404                SYMBOL.whitespace,
405                &content_range.range.start.to_string(),
406                SYMBOL.hyphen,
407                &content_range.range.end.to_string(),
408                SYMBOL.slash,
409                &content_range.size
410            ].join("");
411            response.headers.push(Header {
412                name: Header::_CONTENT_RANGE.to_string(),
413                value: content_range_header_value.to_string()
414            });
415
416            response.headers.push(Header {
417                name: Header::_CONTENT_LENGTH.to_string(),
418                value: content_range.body.len().to_string()
419            });
420        }
421
422        if response.content_range_list.len() > 1 {
423            let content_range_header_value = [
424                Range::MULTIPART,
425                SYMBOL.slash,
426                Range::BYTERANGES,
427                SYMBOL.semicolon,
428                SYMBOL.whitespace,
429                Range::BOUNDARY,
430                SYMBOL.equals,
431                Range::STRING_SEPARATOR
432            ].join("");
433            response.headers.push(Header {
434                name: Header::_CONTENT_TYPE.to_string(),
435                value: content_range_header_value,
436            });
437        }
438
439        let body = Response::generate_body(response.content_range_list);
440
441        let mut headers_str = SYMBOL.new_line_carriage_return.to_string();
442        for header in response.headers {
443            let mut header_string = SYMBOL.empty_string.to_string();
444            header_string.push_str(&header.name);
445            header_string.push_str(Header::NAME_VALUE_SEPARATOR);
446            header_string.push_str(&header.value);
447            header_string.push_str(SYMBOL.new_line_carriage_return);
448            headers_str.push_str(&header_string);
449        }
450        let status = [response.http_version, response.status_code.to_string(), response.reason_phrase].join(SYMBOL.whitespace);
451        let response_without_body = format!(
452            "{}{}{}",
453            status,
454            headers_str,
455            SYMBOL.new_line_carriage_return,
456        );
457
458        let is_head = request.method == METHOD.head;
459        let is_options = request.method == METHOD.options;
460
461        return if is_head || is_options {
462            response_without_body.into_bytes()
463        } else {
464            [response_without_body.into_bytes(), body].concat()
465        }
466
467    }
468
469    pub fn _parse_response(response_vec_u8: &[u8]) -> Response {
470        let mut cursor = io::Cursor::new(response_vec_u8);
471
472        let mut response = Response {
473            http_version: "".to_string(),
474            status_code: 0,
475            reason_phrase: "".to_string(),
476            headers: vec![],
477            content_range_list: vec![],
478            stream_file: None,
479            stream_pipe: None,
480        };
481
482        let content_length: usize = 0;
483        let iteration_number : usize = 0;
484
485        Response::_parse_raw_response_via_cursor(&mut cursor, iteration_number, &mut response, content_length);
486
487        return response;
488    }
489
490    pub fn _parse_http_version_status_code_reason_phrase_string(http_version_status_code_reason_phrase: &str) -> Result<(String, i16, String), String> {
491        let truncated = StringExt::truncate_new_line_carriage_return(http_version_status_code_reason_phrase);
492
493        let boxed_split = truncated.split_once(SYMBOL.whitespace);
494        if boxed_split.is_none() {
495            return Err(Response::_ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE.to_string())
496        }
497
498        let (http_version, status_code_reason_phrase) = boxed_split.unwrap();
499        let supported_http_versions = HTTP::version_list();
500        if !supported_http_versions.contains(&http_version.to_uppercase().to_string()) {
501            return Err(Response::_ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE.to_string())
502        }
503
504        let boxed_split = status_code_reason_phrase.split_once(SYMBOL.whitespace);
505        if boxed_split.is_none() {
506            return Err(Response::_ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE.to_string())
507        }
508        let (status_code, reason_phrase) = boxed_split.unwrap();
509
510        let boxed_status_code_i16 = status_code.parse::<i16>();
511        if boxed_status_code_i16.is_err() {
512            return Err(Response::_ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE.to_string())
513        }
514
515        let status_code_i16 = boxed_status_code_i16.unwrap();
516
517        let list = Response::status_code_reason_phrase_list();
518        let boxed_search =
519            list
520                .iter()
521                .find(|x| {
522                    return x.status_code == &status_code_i16
523                });
524
525        if boxed_search.is_none() {
526            return Err(Response::_ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE.to_string())
527        }
528
529        let found_status_code_reason_phrase = boxed_search.unwrap();
530        let uppercase_reason_phrase = reason_phrase.to_uppercase();
531        let is_equal =
532            &found_status_code_reason_phrase.reason_phrase
533            .to_uppercase()
534                .eq(uppercase_reason_phrase.as_str());
535
536        if !is_equal {
537            return Err(Response::_ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE.to_string())
538        }
539
540        return Ok((http_version.to_string(), status_code_i16, reason_phrase.to_string()))
541    }
542
543    pub fn _parse_http_response_header_string(header_string: &str) -> Header {
544        let header_parts: Vec<&str> = header_string.split(Header::NAME_VALUE_SEPARATOR).collect();
545        let header_name = header_parts[0].to_string();
546        let raw_header_value = header_parts[1].to_string();
547        let header_value = StringExt::truncate_new_line_carriage_return(&raw_header_value);
548
549
550        Header {
551            name: header_name.to_string(),
552            value: header_value.to_string()
553        }
554    }
555
556    pub fn _parse_raw_response_via_cursor(
557        cursor: &mut Cursor<&[u8]>,
558        mut iteration_number: usize,
559        response: &mut Response,
560        mut content_length: usize) {
561
562        let mut buffer = vec![];
563        let boxed_read = cursor.read_until(b'\n', &mut buffer);
564        if boxed_read.is_err() {
565            eprintln!("unable to parse raw response via cursor {}", boxed_read.err().unwrap());
566            return;
567        }
568        let bytes_offset = boxed_read.unwrap();
569        let mut buffer_as_u8_array: &[u8] = &buffer;
570        let string = String::from_utf8(Vec::from(buffer_as_u8_array)).unwrap();
571
572        let is_first_iteration = iteration_number == 0;
573        let new_line_char_found = bytes_offset != 0;
574        let current_string_is_empty = string.trim().len() == 0;
575
576        if is_first_iteration {
577            let boxed_http_version_status_code_reason_phrase = Response::_parse_http_version_status_code_reason_phrase_string(&string);
578            if boxed_http_version_status_code_reason_phrase.is_err() {
579                let error = boxed_http_version_status_code_reason_phrase.err().unwrap();
580                eprintln!("{}", error);
581                return;
582            }
583
584            let (http_version, status_code, reason_phrase) = boxed_http_version_status_code_reason_phrase.unwrap();
585
586            response.http_version = http_version;
587            response.status_code = status_code;
588            response.reason_phrase = reason_phrase;
589        }
590
591        if current_string_is_empty {
592            let content_type = response._get_header(Header::_CONTENT_TYPE.to_string()).unwrap();
593            let is_multipart = Response::_is_multipart_byteranges_content_type(&content_type);
594
595            if is_multipart {
596                let content_range_list : Vec<ContentRange> = vec![];
597
598                let mut buf = vec![];
599                cursor.read_until(b'\n', &mut buf).unwrap();
600                let boxed_value = Range::_parse_multipart_body(cursor, content_range_list);
601                let mut range_list = vec![];
602                if boxed_value.is_ok() {
603                    range_list = boxed_value.unwrap();
604                }
605                response.content_range_list = range_list;
606            } else {
607                buffer = vec![];
608                let boxed_read = cursor.read_to_end(&mut buffer);
609                if boxed_read.is_ok() {
610                    buffer_as_u8_array = &buffer;
611
612                    let content_range = ContentRange {
613                        unit: Range::BYTES.to_string(),
614                        range: Range {
615                            start: 0,
616                            end: buffer_as_u8_array.len() as u64
617                        },
618                        size: buffer_as_u8_array.len().to_string(),
619                        body: Vec::from(buffer_as_u8_array),
620                        content_type: content_type.value.to_string()
621                    };
622                    response.content_range_list = vec![content_range];
623                } else {
624                    let reason = boxed_read.err().unwrap();
625                    eprintln!("error reading file: {}", reason.to_string())
626                }
627
628            }
629
630            return;
631        }
632
633        if new_line_char_found && !current_string_is_empty {
634            let mut header = Header { name: "".to_string(), value: "".to_string() };
635            if !is_first_iteration {
636                header = Response::_parse_http_response_header_string(&string);
637                if header.name == Header::_CONTENT_LENGTH {
638                    content_length = header.value.parse().unwrap();
639                }
640            }
641
642            response.headers.push(header);
643            iteration_number += 1;
644            Response::_parse_raw_response_via_cursor(cursor, iteration_number, response, content_length);
645        }
646    }
647
648    pub fn _is_multipart_byteranges_content_type(content_type: &Header) -> bool {
649        let multipart_byteranges =
650            [
651                Range::MULTIPART,
652                SYMBOL.slash,
653                Range::BYTERANGES
654            ].join("");
655        let is_multipart_byteranges = content_type.value.starts_with(&multipart_byteranges);
656        is_multipart_byteranges
657    }
658
659
660    pub fn get_response(
661        status_code_reason_phrase: &StatusCodeReasonPhrase,
662        boxed_header_list: Option<Vec<Header>>,
663        boxed_content_range_list: Option<Vec<ContentRange>>) -> Response {
664
665        let mut header_list: Vec<Header> = vec![];
666        if boxed_header_list.is_some() {
667            header_list = boxed_header_list.unwrap();
668        }
669
670        let mut content_range_list: Vec<ContentRange> = vec![];
671        if boxed_content_range_list.is_some() {
672            content_range_list = boxed_content_range_list.unwrap();
673        }
674
675        let response = Response {
676            http_version: VERSION.http_1_1.to_string(),
677            status_code: *status_code_reason_phrase.status_code,
678            reason_phrase: status_code_reason_phrase.reason_phrase.to_string(),
679            headers: header_list,
680            content_range_list,
681            stream_file: None,
682            stream_pipe: None,
683        };
684
685        response
686    }
687
688    pub fn generate(&mut self) -> Vec<u8> {
689        let response = &mut self.clone();
690
691        if response.content_range_list.len() == 1 {
692            let content_range_index = 0;
693            let content_range = response.content_range_list.get(content_range_index).unwrap();
694            self.headers.push(Header {
695                name: Header::_CONTENT_TYPE.to_string(),
696                value: content_range.content_type.to_string()
697            });
698
699            let content_range_header_value = [
700                Range::BYTES,
701                SYMBOL.whitespace,
702                &content_range.range.start.to_string(),
703                SYMBOL.hyphen,
704                &content_range.range.end.to_string(),
705                SYMBOL.slash,
706                &content_range.size
707            ].join("");
708            response.headers.push(Header {
709                name: Header::_CONTENT_RANGE.to_string(),
710                value: content_range_header_value.to_string()
711            });
712
713            response.headers.push(Header {
714                name: Header::_CONTENT_LENGTH.to_string(),
715                value: content_range.body.len().to_string()
716            });
717        }
718
719        if response.content_range_list.len() > 1 {
720            let content_range_header_value = [
721                Range::MULTIPART,
722                SYMBOL.slash,
723                Range::BYTERANGES,
724                SYMBOL.semicolon,
725                SYMBOL.whitespace,
726                Range::BOUNDARY,
727                SYMBOL.equals,
728                Range::STRING_SEPARATOR
729            ].join("");
730            response.headers.push(Header {
731                name: Header::_CONTENT_TYPE.to_string(),
732                value: content_range_header_value,
733            });
734        }
735
736        let response_clone = response.clone();
737        let body = Response::generate_body(response_clone.content_range_list);
738
739        let mut headers_str = SYMBOL.new_line_carriage_return.to_string();
740
741        let header_list = response.headers.clone();
742        for header in header_list {
743            let mut header_string = SYMBOL.empty_string.to_string();
744            header_string.push_str(&header.name);
745            header_string.push_str(Header::NAME_VALUE_SEPARATOR);
746            header_string.push_str(&header.value);
747            header_string.push_str(SYMBOL.new_line_carriage_return);
748            headers_str.push_str(&header_string);
749        }
750
751        let response_clone = response.clone();
752        let status = [response_clone.http_version, response.status_code.to_string(), response_clone.reason_phrase].join(SYMBOL.whitespace);
753        let response_without_body = format!(
754            "{}{}{}",
755            status,
756            headers_str,
757            SYMBOL.new_line_carriage_return,
758        );
759
760
761        [response_without_body.into_bytes(), body].concat()
762    }
763
764    pub fn get_header(&self, name: String) -> Option<&Header> {
765        self._get_header(name)
766    }
767
768    pub fn parse(response_vec_u8: &[u8]) -> Result<Response, String> {
769        let total_bytes : i32 = response_vec_u8.len() as i32;
770        let bytes_read = 0;
771        let mut cursor = io::Cursor::new(response_vec_u8);
772
773        let mut response = Response {
774            http_version: "".to_string(),
775            status_code: 0,
776            reason_phrase: "".to_string(),
777            headers: vec![],
778            content_range_list: vec![],
779            stream_file: None,
780            stream_pipe: None,
781        };
782
783        let content_length: usize = 0;
784        let iteration_number : usize = 0;
785
786        let boxed_parse = Response::parse_raw_response_via_cursor(
787            &mut cursor, iteration_number,
788            &mut response,
789            content_length,
790            total_bytes,
791            bytes_read
792        );
793        if boxed_parse.is_err() {
794            let message = boxed_parse.err().unwrap();
795            return Err(message);
796        }
797
798        return Ok(response);
799    }
800
801    pub fn parse_raw_response_via_cursor(
802        cursor: &mut Cursor<&[u8]>,
803        mut iteration_number: usize,
804        response: &mut Response,
805        mut content_length: usize,
806        total_bytes: i32,
807        mut bytes_read: i32) -> Result<(), String> {
808
809        let mut buffer = vec![];
810        let boxed_read = cursor.read_until(b'\n', &mut buffer);
811        if boxed_read.is_err() {
812            let message = format!("unable to parse raw response via cursor {}", boxed_read.err().unwrap());
813            return Err(message);
814        }
815        let bytes_offset = boxed_read.unwrap();
816        bytes_read = bytes_read + bytes_offset as i32;
817        if bytes_read == total_bytes {
818            // end of stream
819        }
820        let mut buffer_as_u8_array: &[u8] = &buffer;
821        let boxed_string = String::from_utf8(Vec::from(buffer_as_u8_array));
822        if boxed_string.is_err() {
823            let message = boxed_string.err().unwrap().to_string();
824            return Err(message);
825        }
826        let string = boxed_string.unwrap();
827
828        let is_first_iteration = iteration_number == 0;
829        let new_line_char_found = bytes_offset != 0;
830        let current_string_is_empty = string.trim().len() == 0;
831
832        if is_first_iteration {
833            let boxed_http_version_status_code_reason_phrase = Response::_parse_http_version_status_code_reason_phrase_string(&string);
834            if boxed_http_version_status_code_reason_phrase.is_err() {
835                let message = boxed_http_version_status_code_reason_phrase.err().unwrap();
836                return Err(message);
837            }
838
839            let (http_version, status_code, reason_phrase) = boxed_http_version_status_code_reason_phrase.unwrap();
840
841            response.http_version = http_version;
842            response.status_code = status_code;
843            response.reason_phrase = reason_phrase;
844        }
845
846        if current_string_is_empty {
847            let mut is_multipart = false;
848            // if response does not contain Content-Type, it will be defaulted to APPLICATION_OCTET_STREAM
849            let mut content_type = MimeType::APPLICATION_OCTET_STREAM;
850
851            let boxed_content_type = response.get_header(Header::_CONTENT_TYPE.to_string());
852            if boxed_content_type.is_some() {
853                let content_type_header = response.get_header(Header::_CONTENT_TYPE.to_string()).unwrap();
854                content_type = content_type_header.value.as_str();
855                is_multipart = Response::_is_multipart_byteranges_content_type(&content_type_header);
856            }
857
858
859            if is_multipart {
860                let content_range_list : Vec<ContentRange> = vec![];
861                let boxed_content_type = response.get_header(Header::_CONTENT_TYPE.to_string());
862                if boxed_content_type.is_none() {
863                    return Err("Content-Type is missing".to_string());
864                }
865                let content_type = boxed_content_type.unwrap();
866                let boxed_boundary = FormMultipartData::extract_boundary(content_type.value.as_str());
867                if boxed_boundary.is_err() {
868                    return Err("unable to extract boundary from Content-Type".to_string());
869                }
870                let boundary = boxed_boundary.unwrap();
871
872                let is_opening_boundary_read = false;
873                let boxed_content_range_list =
874                    Range::parse_multipart_body_with_boundary(
875                        cursor,
876                        content_range_list,
877                        boundary,
878                        total_bytes,
879                        bytes_read,
880                        is_opening_boundary_read);
881                if boxed_content_range_list.is_err() {
882                    let message = boxed_content_range_list.err().unwrap();
883                    return Err(message);
884                }
885
886                response.content_range_list = boxed_content_range_list.unwrap();
887            } else {
888                buffer = vec![];
889                let boxed_read = cursor.read_to_end(&mut buffer);
890                if boxed_read.is_err() {
891                    let message = boxed_read.err().unwrap().to_string();
892                    return Err(message);
893                }
894                let bytes_offset = boxed_read.unwrap();
895                bytes_read = bytes_read + bytes_offset as i32;
896                if bytes_read == total_bytes {
897                    // end of stream
898                }
899
900                buffer_as_u8_array = &buffer;
901
902                let content_range = ContentRange {
903                    unit: Range::BYTES.to_string(),
904                    range: Range {
905                        start: 0,
906                        end: buffer_as_u8_array.len() as u64
907                    },
908                    size: buffer_as_u8_array.len().to_string(),
909                    body: Vec::from(buffer_as_u8_array),
910                    content_type: content_type.to_string()
911                };
912                response.content_range_list = vec![content_range];
913
914
915            }
916
917            return Ok(());
918        }
919
920        if new_line_char_found && !current_string_is_empty {
921            if !is_first_iteration {
922                let boxed_header = Response::parse_http_response_header_string(&string);
923                if boxed_header.is_err() {
924                    let message = boxed_header.err().unwrap();
925                    return Err(message);
926                }
927                let header = boxed_header.unwrap();
928                if header.name == Header::_CONTENT_LENGTH {
929                    content_length = header.value.parse().unwrap();
930                }
931                response.headers.push(header);
932            }
933
934            iteration_number += 1;
935            return Response::parse_raw_response_via_cursor(cursor, iteration_number, response, content_length, total_bytes, bytes_read );
936        } else {
937            return Err("unable to parse".to_string());
938        }
939    }
940
941    pub fn parse_http_response_header_string(header_string: &str) -> Result<Header, String> {
942        let header_parts: Option<(&str, &str)> = header_string.split_once(Header::NAME_VALUE_SEPARATOR);
943        if header_parts.is_none() {
944            let message = format!("unable to parse header: {}", header_string);
945            return Err(message);
946        }
947        let (header_name, raw_header_value) = header_parts.unwrap();
948        let header_value = StringExt::truncate_new_line_carriage_return(&raw_header_value);
949
950        Ok(Header {
951            name: header_name.to_string(),
952            value: header_value.to_string()
953        })
954    }
955}
956
957impl New for Response {
958    fn new() -> Self {
959        Response {
960            http_version: VERSION.http_1_1.to_string(),
961            status_code: *STATUS_CODE_REASON_PHRASE.n501_not_implemented.status_code,
962            reason_phrase: STATUS_CODE_REASON_PHRASE.n501_not_implemented.reason_phrase.to_string(),
963            headers: vec![],
964            content_range_list: vec![],
965            stream_file: None,
966            stream_pipe: None,
967        }
968    }
969}