1#[cfg(test)]
13mod tests;
14#[cfg(test)]
15mod example;
16#[cfg(test)]
17mod reference_parser;
18
19use crate::ext::decimal::Decimal;
20use std::io;
21use std::io::{BufRead, Cursor, Read};
22use crate::body::multipart_form_data::FormMultipartData;
23use crate::core::New;
24use crate::header::Header;
25use crate::ext::string_ext::StringExt;
26use crate::http::{HTTP, VERSION};
27use crate::mime_type::MimeType;
28use crate::range::{ContentRange, Range};
29use crate::request::{METHOD, Request};
30use crate::symbol::SYMBOL;
31
32#[derive(PartialEq, Eq, Clone, Debug)]
33pub struct Error {
34 pub status_code_reason_phrase: &'static StatusCodeReasonPhrase,
35 pub message: String,
36}
37
38pub struct Response {
52 pub http_version: String,
54 pub status_code: i16,
56 pub reason_phrase: String,
58 pub headers: Vec<Header>,
60 pub content_range_list: Vec<ContentRange>,
62 pub stream_file: Option<String>,
65 pub stream_pipe: Option<Box<dyn std::io::Read + Send>>,
69}
70
71impl Clone for Response {
72 fn clone(&self) -> Self {
73 Response {
74 http_version: self.http_version.clone(),
75 status_code: self.status_code,
76 reason_phrase: self.reason_phrase.clone(),
77 headers: self.headers.clone(),
78 content_range_list: self.content_range_list.clone(),
79 stream_file: self.stream_file.clone(),
80 stream_pipe: None,
81 }
82 }
83}
84
85impl PartialEq for Response {
86 fn eq(&self, other: &Self) -> bool {
87 self.http_version == other.http_version
88 && self.status_code == other.status_code
89 && self.reason_phrase == other.reason_phrase
90 && self.headers == other.headers
91 && self.content_range_list == other.content_range_list
92 && self.stream_file == other.stream_file
93 }
94}
95
96impl Eq for Response {}
97
98impl std::fmt::Debug for Response {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 f.debug_struct("Response")
101 .field("http_version", &self.http_version)
102 .field("status_code", &self.status_code)
103 .field("reason_phrase", &self.reason_phrase)
104 .field("headers", &self.headers)
105 .field("content_range_list", &self.content_range_list)
106 .field("stream_file", &self.stream_file)
107 .field("stream_pipe", &self.stream_pipe.as_ref().map(|_| "<stream>"))
108 .finish()
109 }
110}
111
112#[derive(PartialEq, Eq, Clone, Debug)]
113pub struct StatusCodeReasonPhrase {
114 pub status_code: &'static i16,
115 pub reason_phrase: &'static str,
116}
117
118#[derive(PartialEq, Eq, Clone, Debug)]
119pub struct ResponseStatusCodeReasonPhrase {
120 pub n100_continue: &'static StatusCodeReasonPhrase,
121 pub n101_switching_protocols: &'static StatusCodeReasonPhrase,
122 pub n102_processing: &'static StatusCodeReasonPhrase,
123 pub n103_early_hints: &'static StatusCodeReasonPhrase,
124 pub n200_ok: &'static StatusCodeReasonPhrase,
125 pub n201_created: &'static StatusCodeReasonPhrase,
126 pub n202_accepted: &'static StatusCodeReasonPhrase,
127 pub n203_non_authoritative_information: &'static StatusCodeReasonPhrase,
128 pub n204_no_content: &'static StatusCodeReasonPhrase,
129 pub n205_reset_content: &'static StatusCodeReasonPhrase,
130 pub n206_partial_content: &'static StatusCodeReasonPhrase,
131 pub n207_multi_status: &'static StatusCodeReasonPhrase,
132 pub n208_already_reported: &'static StatusCodeReasonPhrase,
133 pub n226_im_used: &'static StatusCodeReasonPhrase,
134 pub n300_multiple_choices: &'static StatusCodeReasonPhrase,
135 pub n301_moved_permanently: &'static StatusCodeReasonPhrase,
136 pub n302_found: &'static StatusCodeReasonPhrase,
137 pub n303_see_other: &'static StatusCodeReasonPhrase,
138 pub n304_not_modified: &'static StatusCodeReasonPhrase,
139 pub n307_temporary_redirect: &'static StatusCodeReasonPhrase,
140 pub n308_permanent_redirect: &'static StatusCodeReasonPhrase,
141 pub n400_bad_request: &'static StatusCodeReasonPhrase,
142 pub n401_unauthorized: &'static StatusCodeReasonPhrase,
143 pub n402_payment_required: &'static StatusCodeReasonPhrase,
144 pub n403_forbidden: &'static StatusCodeReasonPhrase,
145 pub n404_not_found: &'static StatusCodeReasonPhrase,
146 pub n405_method_not_allowed: &'static StatusCodeReasonPhrase,
147 pub n406_not_acceptable: &'static StatusCodeReasonPhrase,
148 pub n407_proxy_authentication_required: &'static StatusCodeReasonPhrase,
149 pub n408_request_timeout: &'static StatusCodeReasonPhrase,
150 pub n409_conflict: &'static StatusCodeReasonPhrase,
151 pub n410_gone: &'static StatusCodeReasonPhrase,
152 pub n411_length_required: &'static StatusCodeReasonPhrase,
153 pub n412_precondition_failed: &'static StatusCodeReasonPhrase,
154 pub n413_payload_too_large: &'static StatusCodeReasonPhrase,
155 pub n414_uri_too_long: &'static StatusCodeReasonPhrase,
156 pub n415_unsupported_media_type: &'static StatusCodeReasonPhrase,
157 pub n416_range_not_satisfiable: &'static StatusCodeReasonPhrase,
158 pub n417_expectation_failed: &'static StatusCodeReasonPhrase,
159 pub n418_im_a_teapot: &'static StatusCodeReasonPhrase,
160 pub n421_misdirected_request: &'static StatusCodeReasonPhrase,
161 pub n422_unprocessable_entity: &'static StatusCodeReasonPhrase,
162 pub n423_locked: &'static StatusCodeReasonPhrase,
163 pub n424_failed_dependency: &'static StatusCodeReasonPhrase,
164 pub n425_too_early: &'static StatusCodeReasonPhrase,
165 pub n426_upgrade_required: &'static StatusCodeReasonPhrase,
166 pub n428_precondition_required: &'static StatusCodeReasonPhrase,
167 pub n429_too_many_requests: &'static StatusCodeReasonPhrase,
168 pub n431_request_header_fields_too_large: &'static StatusCodeReasonPhrase,
169 pub n451_unavailable_for_legal_reasons: &'static StatusCodeReasonPhrase,
170 pub n500_internal_server_error: &'static StatusCodeReasonPhrase,
171 pub n501_not_implemented: &'static StatusCodeReasonPhrase,
172 pub n502_bad_gateway: &'static StatusCodeReasonPhrase,
173 pub n503_service_unavailable: &'static StatusCodeReasonPhrase,
174 pub n504_gateway_timeout: &'static StatusCodeReasonPhrase,
175 pub n505_http_version_not_supported: &'static StatusCodeReasonPhrase,
176 pub n506_variant_also_negotiates: &'static StatusCodeReasonPhrase,
177 pub n507_insufficient_storage: &'static StatusCodeReasonPhrase,
178 pub n508_loop_detected: &'static StatusCodeReasonPhrase,
179 pub n510_not_extended: &'static StatusCodeReasonPhrase,
180 pub n511_network_authentication_required: &'static StatusCodeReasonPhrase,
181}
182
183pub const STATUS_CODE_REASON_PHRASE: ResponseStatusCodeReasonPhrase = ResponseStatusCodeReasonPhrase {
184 n100_continue: &StatusCodeReasonPhrase { status_code: &100, reason_phrase: "Continue" },
185 n101_switching_protocols: &StatusCodeReasonPhrase { status_code: &101, reason_phrase: "Switching Protocols" },
186 n102_processing: &StatusCodeReasonPhrase { status_code: &102, reason_phrase: "Processing" },
187 n103_early_hints: &StatusCodeReasonPhrase { status_code: &103, reason_phrase: "Early Hints" },
188 n200_ok: &StatusCodeReasonPhrase {
189 status_code: &200,
190 reason_phrase: "OK"
191 },
192
193 n201_created: &StatusCodeReasonPhrase { status_code: &201, reason_phrase: "Created" },
194 n202_accepted: &StatusCodeReasonPhrase { status_code: &202, reason_phrase: "Accepted" },
195 n203_non_authoritative_information: &StatusCodeReasonPhrase { status_code: &203, reason_phrase: "Non Authoritative Information" },
196 n204_no_content: &StatusCodeReasonPhrase {
197 status_code: &204,
198 reason_phrase: "No Content"
199 },
200
201 n205_reset_content: &StatusCodeReasonPhrase { status_code: &205, reason_phrase: "Reset Content" },
202 n206_partial_content: &StatusCodeReasonPhrase {
203 status_code: &206,
204 reason_phrase: "Partial Content"
205 },
206
207 n207_multi_status: &StatusCodeReasonPhrase { status_code: &207, reason_phrase: "Multi-Status" },
208 n208_already_reported: &StatusCodeReasonPhrase { status_code: &208, reason_phrase: "Already Reported" },
209 n226_im_used: &StatusCodeReasonPhrase { status_code: &226, reason_phrase: "IM Used" },
210 n300_multiple_choices: &StatusCodeReasonPhrase { status_code: &300, reason_phrase: "Multiple Choices" },
211 n301_moved_permanently: &StatusCodeReasonPhrase { status_code: &301, reason_phrase: "Moved Permanently" },
212 n302_found: &StatusCodeReasonPhrase { status_code: &302, reason_phrase: "Found" },
213 n303_see_other: &StatusCodeReasonPhrase { status_code: &303, reason_phrase: "See Other" },
214 n304_not_modified: &StatusCodeReasonPhrase { status_code: &304, reason_phrase: "Not Modified" },
215 n307_temporary_redirect: &StatusCodeReasonPhrase { status_code: &307, reason_phrase: "Temporary Redirect" },
216 n308_permanent_redirect: &StatusCodeReasonPhrase { status_code: &308, reason_phrase: "Permanent Redirect" },
217 n400_bad_request: &StatusCodeReasonPhrase {
218 status_code: &400,
219 reason_phrase: "Bad Request"
220 },
221
222 n401_unauthorized: &StatusCodeReasonPhrase { status_code: &401, reason_phrase: "Unauthorized" },
223 n402_payment_required: &StatusCodeReasonPhrase { status_code: &402, reason_phrase: "Payment Required" },
224 n403_forbidden: &StatusCodeReasonPhrase { status_code: &403, reason_phrase: "Forbidden" },
225 n404_not_found: &StatusCodeReasonPhrase {
226 status_code: &404,
227 reason_phrase: "Not Found"
228 },
229
230 n405_method_not_allowed: &StatusCodeReasonPhrase { status_code: &405, reason_phrase: "Method Not Allowed" },
231 n406_not_acceptable: &StatusCodeReasonPhrase { status_code: &406, reason_phrase: "Not Acceptable" },
232 n407_proxy_authentication_required: &StatusCodeReasonPhrase { status_code: &407, reason_phrase: "Proxy Authentication Required" },
233 n408_request_timeout: &StatusCodeReasonPhrase { status_code: &408, reason_phrase: "Request Timeout" },
234 n409_conflict: &StatusCodeReasonPhrase { status_code: &409, reason_phrase: "Conflict" },
235 n410_gone: &StatusCodeReasonPhrase { status_code: &410, reason_phrase: "Gone" },
236 n411_length_required: &StatusCodeReasonPhrase { status_code: &411, reason_phrase: "Length Required" },
237 n412_precondition_failed: &StatusCodeReasonPhrase { status_code: &412, reason_phrase: "Precondition Failed" },
238 n413_payload_too_large: &StatusCodeReasonPhrase { status_code: &413, reason_phrase: "Payload Too Large" },
239 n414_uri_too_long: &StatusCodeReasonPhrase { status_code: &414, reason_phrase: "URI Too Long" },
240 n415_unsupported_media_type: &StatusCodeReasonPhrase { status_code: &415, reason_phrase: "Unsupported Media Type" },
241 n416_range_not_satisfiable: &StatusCodeReasonPhrase {
242 status_code: &416,
243 reason_phrase: "Range Not Satisfiable"
244 },
245
246 n417_expectation_failed: &StatusCodeReasonPhrase { status_code: &417, reason_phrase: "Expectation Failed" },
247 n418_im_a_teapot: &StatusCodeReasonPhrase { status_code: &418, reason_phrase: "I'm A Teapot" },
248 n421_misdirected_request: &StatusCodeReasonPhrase { status_code: &421, reason_phrase: "Misdirected Request" },
249 n422_unprocessable_entity: &StatusCodeReasonPhrase { status_code: &422, reason_phrase: "Unprocessable Entity" },
250 n423_locked: &StatusCodeReasonPhrase { status_code: &423, reason_phrase: "Locked" },
251 n424_failed_dependency: &StatusCodeReasonPhrase { status_code: &424, reason_phrase: "Failed Dependency" },
252 n425_too_early: &StatusCodeReasonPhrase { status_code: &425, reason_phrase: "Too Early" },
253 n426_upgrade_required: &StatusCodeReasonPhrase { status_code: &426, reason_phrase: "Upgrade Required" },
254 n428_precondition_required: &StatusCodeReasonPhrase { status_code: &428, reason_phrase: "Precondition Required" },
255 n429_too_many_requests: &StatusCodeReasonPhrase { status_code: &429, reason_phrase: "Too Many Requests" },
256 n431_request_header_fields_too_large: &StatusCodeReasonPhrase { status_code: &431, reason_phrase: "Request Header Fields Too Large" },
257 n451_unavailable_for_legal_reasons: &StatusCodeReasonPhrase { status_code: &451, reason_phrase: "Unavailable For Legal Reasons" },
258 n500_internal_server_error: &StatusCodeReasonPhrase {
259 status_code: &500,
260 reason_phrase: "Internal Server Error"
261 },
262 n501_not_implemented: &StatusCodeReasonPhrase { status_code: &501, reason_phrase: "Not Implemented" },
263 n502_bad_gateway: &StatusCodeReasonPhrase { status_code: &502, reason_phrase: "Bad Gateway" },
264 n503_service_unavailable: &StatusCodeReasonPhrase { status_code: &503, reason_phrase: "Service Unavailable" },
265 n504_gateway_timeout: &StatusCodeReasonPhrase { status_code: &504, reason_phrase: "Gateway Timeout" },
266 n505_http_version_not_supported: &StatusCodeReasonPhrase { status_code: &505, reason_phrase: "HTTP Version Not Supported" },
267 n506_variant_also_negotiates: &StatusCodeReasonPhrase { status_code: &506, reason_phrase: "Variant Also Negotiates" },
268 n507_insufficient_storage: &StatusCodeReasonPhrase { status_code: &507, reason_phrase: "Insufficient Storage" },
269 n508_loop_detected: &StatusCodeReasonPhrase { status_code: &508, reason_phrase: "Loop Detected" },
270 n510_not_extended: &StatusCodeReasonPhrase { status_code: &510, reason_phrase: "Not Extended" },
271 n511_network_authentication_required: &StatusCodeReasonPhrase { status_code: &511, reason_phrase: "Network Authentication Required" }
272};
273
274pub fn content_range_header_value(status_code: i16, content_range: &ContentRange) -> Option<String> {
280 if status_code != *STATUS_CODE_REASON_PHRASE.n206_partial_content.status_code {
281 return None;
282 }
283 Some(format!(
284 "{} {}-{}/{}",
285 Range::BYTES, content_range.range.start, content_range.range.end, content_range.size
286 ))
287}
288
289impl Response {
290
291 pub(crate) fn empty() -> Response {
294 Response {
295 http_version: String::new(),
296 status_code: 0,
297 reason_phrase: String::new(),
298 headers: Vec::new(),
299 content_range_list: Vec::new(),
300 stream_file: None,
301 stream_pipe: None,
302 }
303 }
304
305 pub(crate) fn set_status(&mut self, status: &StatusCodeReasonPhrase) {
307 self.status_code = *status.status_code;
308 self.reason_phrase.clear();
309 self.reason_phrase.push_str(status.reason_phrase);
310 }
311
312 pub fn build(status: StatusCodeReasonPhrase, header_list : Vec<Header>, body: Vec<ContentRange>) -> Response {
313 Response {
314 http_version: VERSION.http_1_1.to_string(),
315 status_code: *status.status_code,
316 reason_phrase: status.reason_phrase.to_string(),
317 headers: header_list,
318 content_range_list: body,
319 stream_file: None,
320 stream_pipe: None,
321 }
322 }
323
324 pub fn status_code_reason_phrase_list() -> Vec<&'static StatusCodeReasonPhrase> {
325 let list = vec![
326 STATUS_CODE_REASON_PHRASE.n100_continue,
327 STATUS_CODE_REASON_PHRASE.n101_switching_protocols,
328 STATUS_CODE_REASON_PHRASE.n102_processing,
329 STATUS_CODE_REASON_PHRASE.n103_early_hints,
330 STATUS_CODE_REASON_PHRASE.n200_ok,
331 STATUS_CODE_REASON_PHRASE.n201_created,
332 STATUS_CODE_REASON_PHRASE.n202_accepted,
333 STATUS_CODE_REASON_PHRASE.n203_non_authoritative_information,
334 STATUS_CODE_REASON_PHRASE.n204_no_content,
335 STATUS_CODE_REASON_PHRASE.n205_reset_content,
336 STATUS_CODE_REASON_PHRASE.n206_partial_content,
337 STATUS_CODE_REASON_PHRASE.n207_multi_status,
338 STATUS_CODE_REASON_PHRASE.n208_already_reported,
339 STATUS_CODE_REASON_PHRASE.n226_im_used,
340 STATUS_CODE_REASON_PHRASE.n300_multiple_choices,
341 STATUS_CODE_REASON_PHRASE.n301_moved_permanently,
342 STATUS_CODE_REASON_PHRASE.n302_found,
343 STATUS_CODE_REASON_PHRASE.n303_see_other,
344 STATUS_CODE_REASON_PHRASE.n304_not_modified,
345 STATUS_CODE_REASON_PHRASE.n307_temporary_redirect,
346 STATUS_CODE_REASON_PHRASE.n308_permanent_redirect,
347 STATUS_CODE_REASON_PHRASE.n400_bad_request,
348 STATUS_CODE_REASON_PHRASE.n401_unauthorized,
349 STATUS_CODE_REASON_PHRASE.n402_payment_required,
350 STATUS_CODE_REASON_PHRASE.n403_forbidden,
351 STATUS_CODE_REASON_PHRASE.n404_not_found,
352 STATUS_CODE_REASON_PHRASE.n405_method_not_allowed,
353 STATUS_CODE_REASON_PHRASE.n406_not_acceptable,
354 STATUS_CODE_REASON_PHRASE.n407_proxy_authentication_required,
355 STATUS_CODE_REASON_PHRASE.n408_request_timeout,
356 STATUS_CODE_REASON_PHRASE.n409_conflict,
357 STATUS_CODE_REASON_PHRASE.n410_gone,
358 STATUS_CODE_REASON_PHRASE.n411_length_required,
359 STATUS_CODE_REASON_PHRASE.n412_precondition_failed,
360 STATUS_CODE_REASON_PHRASE.n413_payload_too_large,
361 STATUS_CODE_REASON_PHRASE.n414_uri_too_long,
362 STATUS_CODE_REASON_PHRASE.n415_unsupported_media_type,
363 STATUS_CODE_REASON_PHRASE.n416_range_not_satisfiable,
364 STATUS_CODE_REASON_PHRASE.n417_expectation_failed,
365 STATUS_CODE_REASON_PHRASE.n418_im_a_teapot,
366 STATUS_CODE_REASON_PHRASE.n421_misdirected_request,
367 STATUS_CODE_REASON_PHRASE.n422_unprocessable_entity,
368 STATUS_CODE_REASON_PHRASE.n423_locked,
369 STATUS_CODE_REASON_PHRASE.n424_failed_dependency,
370 STATUS_CODE_REASON_PHRASE.n425_too_early,
371 STATUS_CODE_REASON_PHRASE.n426_upgrade_required,
372 STATUS_CODE_REASON_PHRASE.n428_precondition_required,
373 STATUS_CODE_REASON_PHRASE.n429_too_many_requests,
374 STATUS_CODE_REASON_PHRASE.n431_request_header_fields_too_large,
375 STATUS_CODE_REASON_PHRASE.n451_unavailable_for_legal_reasons,
376 STATUS_CODE_REASON_PHRASE.n500_internal_server_error,
377 STATUS_CODE_REASON_PHRASE.n501_not_implemented,
378 STATUS_CODE_REASON_PHRASE.n502_bad_gateway,
379 STATUS_CODE_REASON_PHRASE.n503_service_unavailable,
380 STATUS_CODE_REASON_PHRASE.n504_gateway_timeout,
381 STATUS_CODE_REASON_PHRASE.n505_http_version_not_supported,
382 STATUS_CODE_REASON_PHRASE.n506_variant_also_negotiates,
383 STATUS_CODE_REASON_PHRASE.n507_insufficient_storage,
384 STATUS_CODE_REASON_PHRASE.n508_loop_detected,
385 STATUS_CODE_REASON_PHRASE.n510_not_extended,
386 STATUS_CODE_REASON_PHRASE.n511_network_authentication_required,
387 ];
388 list
389 }
390
391 pub const _ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE: &'static str = "Unable to parse status code";
392
393 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>.+)";
394
395 pub fn _get_header(&self, name: String) -> Option<&Header> {
396 let header = self.headers.iter().find(|x| x.name.eq_ignore_ascii_case(&name));
398 header
399 }
400
401 pub fn status_has_no_content(status_code: i16) -> bool {
404 (100..200).contains(&status_code) || status_code == 204 || status_code == 304
405 }
406
407 pub fn drop_content_for_status(&mut self) {
412 if !Response::status_has_no_content(self.status_code) {
413 return;
414 }
415 self.content_range_list.clear();
416 self.stream_file = None;
417 self.stream_pipe = None;
418 self.headers.retain(|h| {
419 !h.name.eq_ignore_ascii_case(Header::_CONTENT_LENGTH)
420 && !h.name.eq_ignore_ascii_case(Header::_TRANSFER_ENCODING)
421 });
422 }
423
424 pub fn generate_body(content_range_list: Vec<ContentRange>) -> Vec<u8> {
425 let mut body = vec![];
426 let one = 1;
427
428 if content_range_list.len() == one {
429 let mut content_range_list = content_range_list;
432 return content_range_list.swap_remove(0).body;
433 }
434
435 if content_range_list.len() > one {
436 let boundary = Range::multipart_boundary(&content_range_list);
439 for (i, content_range) in content_range_list.iter().enumerate() {
440 let mut body_str = SYMBOL.empty_string.to_string();
441 if i != 0 {
442 body_str.push_str(SYMBOL.new_line_carriage_return);
443 }
444 body_str.push_str(SYMBOL.hyphen);
445 body_str.push_str(SYMBOL.hyphen);
446 body_str.push_str(&boundary);
447 body_str.push_str(SYMBOL.new_line_carriage_return);
448 let content_type = [Header::_CONTENT_TYPE, Header::NAME_VALUE_SEPARATOR, SYMBOL.whitespace, &Header::single_line(&content_range.content_type)].join("");
449 body_str.push_str(content_type.as_str());
450 body_str.push_str(SYMBOL.new_line_carriage_return);
451 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("");
452 body_str.push_str(content_range_header.as_str());
453 body_str.push_str(SYMBOL.new_line_carriage_return);
454 body_str.push_str(SYMBOL.new_line_carriage_return);
455
456 body.extend_from_slice(body_str.as_bytes());
457 body.extend_from_slice(&content_range.body);
458 }
459 body.extend_from_slice(b"\r\n--");
460 body.extend_from_slice(boundary.as_bytes());
461 body.extend_from_slice(b"--\r\n");
462 }
463
464 body
465 }
466
467 pub fn generate_response(response: Response, request: Request) -> Vec<u8> {
469 Response::generate_response_coalesced(response, request, usize::MAX).0
470 }
471
472 pub fn generate_response_parts(response: Response, request: Request) -> (Vec<u8>, Vec<u8>) {
478 Response::generate_response_coalesced(response, request, 0)
479 }
480
481 pub(crate) fn generate_response_coalesced(mut response: Response, request: Request, coalesce_limit: usize) -> (Vec<u8>, Vec<u8>) {
486 response.drop_content_for_status();
487 let Response { http_version, status_code, reason_phrase, headers, mut content_range_list, .. } = response;
488
489 let mut content_type: Option<String> = None;
493 let mut content_range: Option<String> = None;
494 let mut content_length: Option<Decimal> = None;
495 if content_range_list.len() == 1 {
496 let range = &mut content_range_list[0];
497 content_range = content_range_header_value(status_code, range);
498 content_length = Some(Decimal::new(range.body.len() as u128));
499 content_type = Some(std::mem::take(&mut range.content_type));
502 }
503 let multipart = content_range_list.len() > 1;
504 if multipart {
505 content_type = Some(Range::multipart_content_type(&content_range_list));
506 }
507
508 let is_head = request.method == METHOD.head;
512 let body = if is_head && !multipart {
513 Vec::new()
514 } else {
515 Response::generate_body(content_range_list)
516 };
517 if multipart {
518 content_length = Some(Decimal::new(body.len() as u128));
521 }
522 let body = if is_head { Vec::new() } else { body };
523 let coalesce = body.len() <= coalesce_limit;
524
525 let status = u16::try_from(status_code).map(|code| Decimal::new(code as u128));
530 let status_text = match &status {
531 Ok(decimal) => std::borrow::Cow::Borrowed(decimal.as_str()),
532 Err(_) => std::borrow::Cow::Owned(status_code.to_string()),
533 };
534 let extra = [
535 (Header::_CONTENT_TYPE, content_type.as_deref()),
536 (Header::_CONTENT_RANGE, content_range.as_deref()),
537 (Header::_CONTENT_LENGTH, content_length.as_ref().map(Decimal::as_str)),
538 ];
539 let capacity = http_version.len() + status_text.len() + reason_phrase.len() + 4
540 + headers.iter().map(|h| h.name.len() + h.value.len() + 4).sum::<usize>()
541 + extra.iter().map(|(name, value)| value.map_or(0, |v| name.len() + v.len() + 4)).sum::<usize>()
542 + 2
543 + if coalesce { body.len() } else { 0 };
544 let mut head = Vec::with_capacity(capacity);
545 head.extend_from_slice(http_version.as_bytes());
546 head.push(b' ');
547 head.extend_from_slice(status_text.as_bytes());
548 head.push(b' ');
549 head.extend_from_slice(Header::single_line(&reason_phrase).as_bytes());
550 head.extend_from_slice(b"\r\n");
551 let header_lines = headers.iter().map(|h| (h.name.as_ref(), h.value.as_ref()))
552 .chain(extra.iter().filter_map(|(name, value)| value.map(|v| (*name, v))));
553 for (name, value) in header_lines {
554 head.extend_from_slice(Header::single_line(name).as_bytes());
555 head.extend_from_slice(Header::NAME_VALUE_SEPARATOR.as_bytes());
556 head.extend_from_slice(Header::single_line(value).as_bytes());
557 head.extend_from_slice(b"\r\n");
558 }
559 head.extend_from_slice(b"\r\n");
560
561 if coalesce {
562 head.extend_from_slice(&body);
563 (head, Vec::new())
564 } else {
565 (head, body)
566 }
567 }
568
569 pub fn _parse_response(response_vec_u8: &[u8]) -> Response {
570 let mut cursor = io::Cursor::new(response_vec_u8);
571
572 let mut response = Response {
573 http_version: "".to_string(),
574 status_code: 0,
575 reason_phrase: "".to_string(),
576 headers: vec![],
577 content_range_list: vec![],
578 stream_file: None,
579 stream_pipe: None,
580 };
581
582 let content_length: usize = 0;
583 let iteration_number : usize = 0;
584
585 Response::_parse_raw_response_via_cursor(&mut cursor, iteration_number, &mut response, content_length);
586
587 return response;
588 }
589
590 pub fn _parse_http_version_status_code_reason_phrase_string(http_version_status_code_reason_phrase: &str) -> Result<(String, i16, String), String> {
591 let line = without_cr_lf(http_version_status_code_reason_phrase);
592 let (http_version, status_code, reason_phrase) = Response::status_line_parts(&line)?;
593 Ok((http_version.to_string(), status_code, reason_phrase.to_string()))
594 }
595
596 fn status_line_parts(line: &str) -> Result<(&str, i16, &str), String> {
599 let boxed_split = line.split_once(SYMBOL.whitespace);
600 if boxed_split.is_none() {
601 return Err(Response::_ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE.to_string())
602 }
603
604 let (http_version, status_code_reason_phrase) = boxed_split.unwrap();
605 if !HTTP::is_supported_version(http_version) {
606 return Err(Response::_ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE.to_string())
607 }
608
609 let (status_code, reason_phrase) = status_code_reason_phrase
614 .split_once(SYMBOL.whitespace)
615 .unwrap_or((status_code_reason_phrase, ""));
616
617 let is_three_digits = status_code.len() == 3 && status_code.bytes().all(|b| b.is_ascii_digit());
618 let status_code_i16 = match status_code.parse::<i16>() {
619 Ok(code) if is_three_digits && code >= 100 => code,
620 _ => return Err(Response::_ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE.to_string()),
621 };
622
623 Ok((http_version, status_code_i16, reason_phrase))
624 }
625
626 pub fn _parse_http_response_header_string(header_string: &str) -> Header {
627 let (header_name, raw_header_value) = header_string
631 .split_once(Header::NAME_VALUE_SEPARATOR)
632 .unwrap_or((header_string, ""));
633 let header_value = StringExt::truncate_new_line_carriage_return(raw_header_value);
634
635
636 Header {
637 name: header_name.to_string().into(),
638 value: header_value.to_string().into()
639 }
640 }
641
642 pub fn _parse_raw_response_via_cursor(
643 cursor: &mut Cursor<&[u8]>,
644 mut iteration_number: usize,
645 response: &mut Response,
646 mut content_length: usize) {
647
648 let mut buffer = vec![];
649 let boxed_read = cursor.read_until(b'\n', &mut buffer);
650 if boxed_read.is_err() {
651 eprintln!("unable to parse raw response via cursor {}", boxed_read.err().unwrap());
652 return;
653 }
654 let bytes_offset = boxed_read.unwrap();
655 let mut buffer_as_u8_array: &[u8] = &buffer;
656 let string = String::from_utf8(Vec::from(buffer_as_u8_array)).unwrap();
657
658 let is_first_iteration = iteration_number == 0;
659 let new_line_char_found = bytes_offset != 0;
660 let current_string_is_empty = string.trim().len() == 0;
661
662 if is_first_iteration {
663 let boxed_http_version_status_code_reason_phrase = Response::_parse_http_version_status_code_reason_phrase_string(&string);
664 if boxed_http_version_status_code_reason_phrase.is_err() {
665 let error = boxed_http_version_status_code_reason_phrase.err().unwrap();
666 eprintln!("{}", error);
667 return;
668 }
669
670 let (http_version, status_code, reason_phrase) = boxed_http_version_status_code_reason_phrase.unwrap();
671
672 response.http_version = http_version;
673 response.status_code = status_code;
674 response.reason_phrase = reason_phrase;
675 }
676
677 if current_string_is_empty {
678 let content_type = response._get_header(Header::_CONTENT_TYPE.to_string()).cloned()
680 .unwrap_or(Header { name: Header::_CONTENT_TYPE.into(), value: "".into() });
681 let is_multipart = Response::_is_multipart_byteranges_content_type(&content_type);
682
683 if is_multipart {
684 let content_range_list : Vec<ContentRange> = vec![];
685
686 let boundary = content_type.value.split(';')
687 .filter_map(|param| param.trim().strip_prefix("boundary="))
688 .next()
689 .map(|b| b.trim_matches('"').to_string())
690 .unwrap_or_else(|| Range::STRING_SEPARATOR.to_string());
691 let mut buf = vec![];
692 let _ = cursor.read_until(b'\n', &mut buf);
693 let boxed_value = Range::_parse_multipart_body_with_boundary(cursor, content_range_list, &boundary);
694 let mut range_list = vec![];
695 if boxed_value.is_ok() {
696 range_list = boxed_value.unwrap();
697 }
698 response.content_range_list = range_list;
699 } else {
700 buffer = vec![];
701 let boxed_read = cursor.read_to_end(&mut buffer);
702 if boxed_read.is_ok() {
703 buffer_as_u8_array = &buffer;
704
705 let content_range = ContentRange {
706 unit: Range::BYTES.to_string(),
707 range: Range {
708 start: 0,
709 end: buffer_as_u8_array.len() as u64
710 },
711 size: buffer_as_u8_array.len().to_string(),
712 body: Vec::from(buffer_as_u8_array),
713 content_type: content_type.value.to_string()
714 };
715 response.content_range_list = vec![content_range];
716 } else {
717 let reason = boxed_read.err().unwrap();
718 eprintln!("error reading file: {}", reason.to_string())
719 }
720
721 }
722
723 return;
724 }
725
726 if new_line_char_found && !current_string_is_empty {
727 let mut header = Header { name: "".into(), value: "".into() };
728 if !is_first_iteration {
729 header = Response::_parse_http_response_header_string(&string);
730 if header.name.eq_ignore_ascii_case(Header::_CONTENT_LENGTH) {
731 content_length = header.value.trim().parse().unwrap_or(0);
733 }
734 }
735
736 response.headers.push(header);
737 iteration_number += 1;
738 Response::_parse_raw_response_via_cursor(cursor, iteration_number, response, content_length);
739 }
740 }
741
742 pub fn _is_multipart_byteranges_content_type(content_type: &Header) -> bool {
743 content_type.value.strip_prefix(Range::MULTIPART)
745 .and_then(|rest| rest.strip_prefix(SYMBOL.slash))
746 .is_some_and(|rest| rest.starts_with(Range::BYTERANGES))
747 }
748
749
750 pub fn get_response(
751 status_code_reason_phrase: &StatusCodeReasonPhrase,
752 boxed_header_list: Option<Vec<Header>>,
753 boxed_content_range_list: Option<Vec<ContentRange>>) -> Response {
754
755 let mut header_list: Vec<Header> = vec![];
756 if boxed_header_list.is_some() {
757 header_list = boxed_header_list.unwrap();
758 }
759
760 let mut content_range_list: Vec<ContentRange> = vec![];
761 if boxed_content_range_list.is_some() {
762 content_range_list = boxed_content_range_list.unwrap();
763 }
764
765 let response = Response {
766 http_version: VERSION.http_1_1.to_string(),
767 status_code: *status_code_reason_phrase.status_code,
768 reason_phrase: status_code_reason_phrase.reason_phrase.to_string(),
769 headers: header_list,
770 content_range_list,
771 stream_file: None,
772 stream_pipe: None,
773 };
774
775 response
776 }
777
778 pub fn json(status_code_reason_phrase: &StatusCodeReasonPhrase, body: Vec<u8>) -> Response {
791 let content_range = Range::get_content_range(body, MimeType::APPLICATION_JSON.to_string());
792 Response::get_response(status_code_reason_phrase, None, Some(vec![content_range]))
793 }
794
795 pub fn text(status_code_reason_phrase: &StatusCodeReasonPhrase, body: &str) -> Response {
803 let content_range = Range::get_content_range(body.as_bytes().to_vec(), MimeType::TEXT_PLAIN.to_string());
804 Response::get_response(status_code_reason_phrase, None, Some(vec![content_range]))
805 }
806
807 pub fn generate(&mut self) -> Vec<u8> {
808 let response = &mut self.clone();
809 response.drop_content_for_status();
810
811 if response.content_range_list.len() == 1 {
812 let content_range_index = 0;
813 let content_range = response.content_range_list.get(content_range_index).unwrap();
814 response.headers.push(Header {
815 name: Header::_CONTENT_TYPE.into(),
816 value: content_range.content_type.to_string().into()
817 });
818
819 if let Some(value) = content_range_header_value(response.status_code, content_range) {
820 response.headers.push(Header {
821 name: Header::_CONTENT_RANGE.into(),
822 value: value.into(),
823 });
824 }
825
826 response.headers.push(Header {
827 name: Header::_CONTENT_LENGTH.into(),
828 value: content_range.body.len().to_string().into()
829 });
830 }
831
832 if response.content_range_list.len() > 1 {
833 response.headers.push(Header {
834 name: Header::_CONTENT_TYPE.into(),
835 value: Range::multipart_content_type(&response.content_range_list).into(),
836 });
837 }
838
839 let response_clone = response.clone();
840 let body = Response::generate_body(response_clone.content_range_list);
841 if response.content_range_list.len() > 1 {
842 response.headers.push(Header {
843 name: Header::_CONTENT_LENGTH.into(),
844 value: body.len().to_string().into(),
845 });
846 }
847
848 let mut headers_str = SYMBOL.new_line_carriage_return.to_string();
849
850 let header_list = response.headers.clone();
851 for header in header_list {
852 let mut header_string = SYMBOL.empty_string.to_string();
853 header_string.push_str(&Header::single_line(&header.name));
854 header_string.push_str(Header::NAME_VALUE_SEPARATOR);
855 header_string.push_str(&Header::single_line(&header.value));
856 header_string.push_str(SYMBOL.new_line_carriage_return);
857 headers_str.push_str(&header_string);
858 }
859
860 let response_clone = response.clone();
861 let status = [response_clone.http_version, response.status_code.to_string(), Header::single_line(&response_clone.reason_phrase).into_owned()].join(SYMBOL.whitespace);
862 let response_without_body = format!(
863 "{}{}{}",
864 status,
865 headers_str,
866 SYMBOL.new_line_carriage_return,
867 );
868
869
870 [response_without_body.into_bytes(), body].concat()
871 }
872
873 pub fn get_header(&self, name: String) -> Option<&Header> {
874 self._get_header(name)
875 }
876
877 pub fn header(&self, name: &str) -> Option<&Header> {
879 self.headers.iter().find(|h| h.name.eq_ignore_ascii_case(name))
880 }
881
882 pub fn parse(response_vec_u8: &[u8]) -> Result<Response, String> {
883 let total_bytes : i32 = response_vec_u8.len() as i32;
884 let bytes_read = 0;
885 let mut cursor = io::Cursor::new(response_vec_u8);
886
887 let mut response = Response {
888 http_version: "".to_string(),
889 status_code: 0,
890 reason_phrase: "".to_string(),
891 headers: Vec::with_capacity(16),
894 content_range_list: vec![],
895 stream_file: None,
896 stream_pipe: None,
897 };
898
899 let content_length: usize = 0;
900 let iteration_number : usize = 0;
901
902 let boxed_parse = Response::parse_raw_response_via_cursor(
903 &mut cursor, iteration_number,
904 &mut response,
905 content_length,
906 total_bytes,
907 bytes_read
908 );
909 if boxed_parse.is_err() {
910 let message = boxed_parse.err().unwrap();
911 return Err(message);
912 }
913
914 return Ok(response);
915 }
916
917 pub(crate) fn parse_upstream(input: &[u8], dropped: &[&str]) -> Result<Response, String> {
927 let mut response = Response {
928 http_version: crate::http::VERSION.http_1_1.to_string(),
929 status_code: 0,
930 reason_phrase: String::new(),
931 headers: Vec::with_capacity(16),
932 content_range_list: vec![],
933 stream_file: None,
934 stream_pipe: None,
935 };
936 let mut content_type: Option<&str> = None;
937 let mut pos = 0usize;
938 let mut first = true;
939 loop {
940 let rest = &input[pos.min(input.len())..];
941 let line_len = rest.iter().position(|&b| b == b'\n').map_or(rest.len(), |i| i + 1);
942 pos += line_len;
943 let line = std::str::from_utf8(&rest[..line_len]).map_err(|e| e.to_string())?;
944 if first {
945 let status_line = without_cr_lf(line);
946 let (_, status_code, reason_phrase) = Response::status_line_parts(&status_line)?;
947 response.status_code = status_code;
948 response.reason_phrase = reason_phrase.to_string();
949 }
950 if line.trim().is_empty() {
951 if content_type.is_some_and(|ct| ct.starts_with("multipart/byteranges")) {
952 return Response::parse(input).map(normalized_like_proxy(dropped));
953 }
954 let body = input[pos.min(input.len())..].to_vec();
955 response.content_range_list = vec![ContentRange {
956 unit: Range::BYTES.to_string(),
957 range: Range { start: 0, end: body.len() as u64 },
958 size: Decimal::new(body.len() as u128).as_str().to_string(),
959 content_type: content_type.unwrap_or(MimeType::APPLICATION_OCTET_STREAM).to_string(),
960 body,
961 }];
962 return Ok(response);
963 }
964 if line_len == 0 {
965 return Err("unable to parse".to_string());
966 }
967 if !first {
968 let (name, raw_value) = split_name_value(line).ok_or_else(|| format!("unable to parse header: {}", line))?;
969 if name.eq_ignore_ascii_case(Header::_CONTENT_LENGTH) {
970 let value = without_cr_lf(raw_value);
971 let _: usize = value.trim().parse().map_err(|_| format!("invalid Content-Length: {:?}", value))?;
972 }
973 if name.eq_ignore_ascii_case(Header::_CONTENT_TYPE) {
974 if content_type.is_none() {
975 match without_cr_lf(raw_value) {
978 std::borrow::Cow::Borrowed(v) => content_type = Some(v),
979 std::borrow::Cow::Owned(_) => return Response::parse(input).map(normalized_like_proxy(dropped)),
980 }
981 }
982 continue;
983 }
984 if !dropped.iter().any(|d| name.eq_ignore_ascii_case(d)) {
985 response.headers.push(Header {
986 name: Header::name_from(name),
987 value: without_cr_lf(raw_value).into_owned().into(),
988 });
989 }
990 }
991 first = false;
992 }
993 }
994
995 pub fn parse_raw_response_via_cursor(
996 cursor: &mut Cursor<&[u8]>,
997 mut iteration_number: usize,
998 response: &mut Response,
999 _content_length: usize,
1000 total_bytes: i32,
1001 mut bytes_read: i32) -> Result<(), String> {
1002 let input: &[u8] = cursor.get_ref();
1008 loop {
1009 let start = (cursor.position() as usize).min(input.len());
1010 let rest = &input[start..];
1011 let line_len = rest.iter().position(|&b| b == b'\n').map_or(rest.len(), |i| i + 1);
1012 cursor.set_position((start + line_len) as u64);
1013 bytes_read += line_len as i32;
1014 let line = std::str::from_utf8(&rest[..line_len]).map_err(|e| e.to_string())?;
1015
1016 let is_first_iteration = iteration_number == 0;
1017 let new_line_char_found = line_len != 0;
1018 let current_string_is_empty = line.trim().is_empty();
1019
1020 if is_first_iteration {
1021 let status_line = without_cr_lf(line);
1022 let (http_version, status_code, reason_phrase) = Response::status_line_parts(&status_line)?;
1023 response.http_version = http_version.to_string();
1024 response.status_code = status_code;
1025 response.reason_phrase = reason_phrase.to_string();
1026 }
1027
1028 if current_string_is_empty {
1029 let content_type_header = response.header(Header::_CONTENT_TYPE);
1031 let is_multipart = content_type_header.map_or(false, Response::_is_multipart_byteranges_content_type);
1032
1033 if is_multipart {
1034 let content_type = response.header(Header::_CONTENT_TYPE).ok_or("Content-Type is missing")?;
1035 let boundary = FormMultipartData::extract_boundary(content_type.value.as_ref())
1036 .map_err(|_| "unable to extract boundary from Content-Type".to_string())?;
1037 let is_opening_boundary_read = false;
1038 response.content_range_list = Range::parse_multipart_body_with_boundary(
1039 cursor,
1040 vec![],
1041 boundary,
1042 total_bytes,
1043 bytes_read,
1044 is_opening_boundary_read)?;
1045 } else {
1046 let content_type = content_type_header.map_or(MimeType::APPLICATION_OCTET_STREAM, |h| h.value.as_ref());
1047 let start = (cursor.position() as usize).min(input.len());
1048 let body = input[start..].to_vec();
1049 cursor.set_position(input.len() as u64);
1050 let content_range = ContentRange {
1051 unit: Range::BYTES.to_string(),
1052 range: Range {
1053 start: 0,
1054 end: body.len() as u64
1055 },
1056 size: Decimal::new(body.len() as u128).as_str().to_string(),
1057 content_type: content_type.to_string(),
1058 body,
1059 };
1060 response.content_range_list = vec![content_range];
1061 }
1062
1063 return Ok(());
1064 }
1065
1066 if new_line_char_found {
1067 if !is_first_iteration {
1068 let header = Response::parse_http_response_header_string(line)?;
1069 if header.name.eq_ignore_ascii_case(Header::_CONTENT_LENGTH) {
1070 let _: usize = header.value.trim().parse()
1072 .map_err(|_| format!("invalid Content-Length: {:?}", header.value))?;
1073 }
1074 response.headers.push(header);
1075 }
1076
1077 iteration_number += 1;
1078 } else {
1079 return Err("unable to parse".to_string());
1080 }
1081 }
1082 }
1083
1084 pub fn parse_http_response_header_string(header_string: &str) -> Result<Header, String> {
1085 match split_name_value(header_string) {
1086 Some((header_name, raw_header_value)) => Ok(Header {
1087 name: Header::name_from(header_name),
1088 value: without_cr_lf(raw_header_value).into_owned().into(),
1089 }),
1090 None => Err(format!("unable to parse header: {}", header_string)),
1091 }
1092 }
1093}
1094
1095impl New for Response {
1096 fn new() -> Self {
1097 Response {
1098 http_version: VERSION.http_1_1.to_string(),
1099 status_code: *STATUS_CODE_REASON_PHRASE.n501_not_implemented.status_code,
1100 reason_phrase: STATUS_CODE_REASON_PHRASE.n501_not_implemented.reason_phrase.to_string(),
1101 headers: vec![],
1102 content_range_list: vec![],
1103 stream_file: None,
1104 stream_pipe: None,
1105 }
1106 }
1107}
1108fn normalized_like_proxy<'a>(dropped: &'a [&'a str]) -> impl FnOnce(Response) -> Response + 'a {
1111 move |mut response: Response| {
1112 response.http_version.clear();
1113 response.http_version.push_str(crate::http::VERSION.http_1_1);
1114 let has_body = !response.content_range_list.is_empty();
1115 if let [range] = response.content_range_list.as_mut_slice() {
1116 if let Some(h) = response.headers.iter().find(|h| h.name.eq_ignore_ascii_case(Header::_CONTENT_TYPE)) {
1117 range.content_type.clear();
1118 range.content_type.push_str(&h.value);
1119 }
1120 }
1121 response.headers.retain(|h| {
1122 if h.name.eq_ignore_ascii_case(Header::_CONTENT_TYPE) {
1123 return !has_body;
1124 }
1125 !dropped.iter().any(|d| h.name.eq_ignore_ascii_case(d))
1126 });
1127 response
1128 }
1129}
1130
1131fn without_cr_lf(s: &str) -> std::borrow::Cow<'_, str> {
1133 let trimmed = s.strip_suffix('\n').unwrap_or(s);
1134 let trimmed = trimmed.strip_suffix('\r').unwrap_or(trimmed);
1135 if trimmed.contains(['\r', '\n']) {
1136 std::borrow::Cow::Owned(StringExt::truncate_new_line_carriage_return(s))
1137 } else {
1138 std::borrow::Cow::Borrowed(trimmed)
1139 }
1140}
1141
1142fn split_name_value(line: &str) -> Option<(&str, &str)> {
1145 let bytes = line.as_bytes();
1146 let mut from = 0;
1147 while let Some(offset) = bytes[from..].iter().position(|&b| b == b':') {
1148 let colon = from + offset;
1149 if bytes.get(colon + 1) == Some(&b' ') {
1150 return Some((&line[..colon], &line[colon + 2..]));
1151 }
1152 from = colon + 1;
1153 }
1154 None
1155}