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
24pub struct Response {
38 pub http_version: String,
40 pub status_code: i16,
42 pub reason_phrase: String,
44 pub headers: Vec<Header>,
46 pub content_range_list: Vec<ContentRange>,
48 pub stream_file: Option<String>,
51 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
260pub 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 let header = self.headers.iter().find(|x| x.name.eq_ignore_ascii_case(&name));
363 header
364 }
365
366 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 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 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 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 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 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 response.headers.push(Header {
481 name: Header::_CONTENT_LENGTH.to_string(),
482 value: body.len().to_string(),
483 });
484 }
485
486 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 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 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 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 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 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 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 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 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 }
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 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 }
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 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}