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 pub trailers: Option<Trailers>,
73}
74
75#[derive(Clone, Default, Debug)]
79pub struct Trailers(std::sync::Arc<std::sync::Mutex<Vec<Header>>>);
80
81impl Trailers {
82 pub fn new() -> Self {
84 Trailers::default()
85 }
86
87 pub fn with(fields: Vec<Header>) -> Self {
89 let trailers = Trailers::new();
90 trailers.set(fields);
91 trailers
92 }
93
94 pub fn set(&self, fields: Vec<Header>) {
96 *self.0.lock().unwrap_or_else(|e| e.into_inner()) = fields;
97 }
98
99 pub fn get(&self) -> Vec<Header> {
101 self.0.lock().unwrap_or_else(|e| e.into_inner()).clone()
102 }
103}
104
105impl Clone for Response {
106 fn clone(&self) -> Self {
107 Response {
108 http_version: self.http_version.clone(),
109 status_code: self.status_code,
110 reason_phrase: self.reason_phrase.clone(),
111 headers: self.headers.clone(),
112 content_range_list: self.content_range_list.clone(),
113 stream_file: self.stream_file.clone(),
114 stream_pipe: None,
115 trailers: self.trailers.clone(),
116 }
117 }
118}
119
120impl PartialEq for Response {
121 fn eq(&self, other: &Self) -> bool {
122 self.http_version == other.http_version
123 && self.status_code == other.status_code
124 && self.reason_phrase == other.reason_phrase
125 && self.headers == other.headers
126 && self.content_range_list == other.content_range_list
127 && self.stream_file == other.stream_file
128 }
129}
130
131impl Eq for Response {}
132
133impl std::fmt::Debug for Response {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 f.debug_struct("Response")
136 .field("http_version", &self.http_version)
137 .field("status_code", &self.status_code)
138 .field("reason_phrase", &self.reason_phrase)
139 .field("headers", &self.headers)
140 .field("content_range_list", &self.content_range_list)
141 .field("stream_file", &self.stream_file)
142 .field("stream_pipe", &self.stream_pipe.as_ref().map(|_| "<stream>"))
143 .finish()
144 }
145}
146
147#[derive(PartialEq, Eq, Clone, Debug)]
148pub struct StatusCodeReasonPhrase {
149 pub status_code: &'static i16,
150 pub reason_phrase: &'static str,
151}
152
153#[derive(PartialEq, Eq, Clone, Debug)]
154pub struct ResponseStatusCodeReasonPhrase {
155 pub n100_continue: &'static StatusCodeReasonPhrase,
156 pub n101_switching_protocols: &'static StatusCodeReasonPhrase,
157 pub n102_processing: &'static StatusCodeReasonPhrase,
158 pub n103_early_hints: &'static StatusCodeReasonPhrase,
159 pub n200_ok: &'static StatusCodeReasonPhrase,
160 pub n201_created: &'static StatusCodeReasonPhrase,
161 pub n202_accepted: &'static StatusCodeReasonPhrase,
162 pub n203_non_authoritative_information: &'static StatusCodeReasonPhrase,
163 pub n204_no_content: &'static StatusCodeReasonPhrase,
164 pub n205_reset_content: &'static StatusCodeReasonPhrase,
165 pub n206_partial_content: &'static StatusCodeReasonPhrase,
166 pub n207_multi_status: &'static StatusCodeReasonPhrase,
167 pub n208_already_reported: &'static StatusCodeReasonPhrase,
168 pub n226_im_used: &'static StatusCodeReasonPhrase,
169 pub n300_multiple_choices: &'static StatusCodeReasonPhrase,
170 pub n301_moved_permanently: &'static StatusCodeReasonPhrase,
171 pub n302_found: &'static StatusCodeReasonPhrase,
172 pub n303_see_other: &'static StatusCodeReasonPhrase,
173 pub n304_not_modified: &'static StatusCodeReasonPhrase,
174 pub n307_temporary_redirect: &'static StatusCodeReasonPhrase,
175 pub n308_permanent_redirect: &'static StatusCodeReasonPhrase,
176 pub n400_bad_request: &'static StatusCodeReasonPhrase,
177 pub n401_unauthorized: &'static StatusCodeReasonPhrase,
178 pub n402_payment_required: &'static StatusCodeReasonPhrase,
179 pub n403_forbidden: &'static StatusCodeReasonPhrase,
180 pub n404_not_found: &'static StatusCodeReasonPhrase,
181 pub n405_method_not_allowed: &'static StatusCodeReasonPhrase,
182 pub n406_not_acceptable: &'static StatusCodeReasonPhrase,
183 pub n407_proxy_authentication_required: &'static StatusCodeReasonPhrase,
184 pub n408_request_timeout: &'static StatusCodeReasonPhrase,
185 pub n409_conflict: &'static StatusCodeReasonPhrase,
186 pub n410_gone: &'static StatusCodeReasonPhrase,
187 pub n411_length_required: &'static StatusCodeReasonPhrase,
188 pub n412_precondition_failed: &'static StatusCodeReasonPhrase,
189 pub n413_payload_too_large: &'static StatusCodeReasonPhrase,
190 pub n414_uri_too_long: &'static StatusCodeReasonPhrase,
191 pub n415_unsupported_media_type: &'static StatusCodeReasonPhrase,
192 pub n416_range_not_satisfiable: &'static StatusCodeReasonPhrase,
193 pub n417_expectation_failed: &'static StatusCodeReasonPhrase,
194 pub n418_im_a_teapot: &'static StatusCodeReasonPhrase,
195 pub n421_misdirected_request: &'static StatusCodeReasonPhrase,
196 pub n422_unprocessable_entity: &'static StatusCodeReasonPhrase,
197 pub n423_locked: &'static StatusCodeReasonPhrase,
198 pub n424_failed_dependency: &'static StatusCodeReasonPhrase,
199 pub n425_too_early: &'static StatusCodeReasonPhrase,
200 pub n426_upgrade_required: &'static StatusCodeReasonPhrase,
201 pub n428_precondition_required: &'static StatusCodeReasonPhrase,
202 pub n429_too_many_requests: &'static StatusCodeReasonPhrase,
203 pub n431_request_header_fields_too_large: &'static StatusCodeReasonPhrase,
204 pub n451_unavailable_for_legal_reasons: &'static StatusCodeReasonPhrase,
205 pub n500_internal_server_error: &'static StatusCodeReasonPhrase,
206 pub n501_not_implemented: &'static StatusCodeReasonPhrase,
207 pub n502_bad_gateway: &'static StatusCodeReasonPhrase,
208 pub n503_service_unavailable: &'static StatusCodeReasonPhrase,
209 pub n504_gateway_timeout: &'static StatusCodeReasonPhrase,
210 pub n505_http_version_not_supported: &'static StatusCodeReasonPhrase,
211 pub n506_variant_also_negotiates: &'static StatusCodeReasonPhrase,
212 pub n507_insufficient_storage: &'static StatusCodeReasonPhrase,
213 pub n508_loop_detected: &'static StatusCodeReasonPhrase,
214 pub n510_not_extended: &'static StatusCodeReasonPhrase,
215 pub n511_network_authentication_required: &'static StatusCodeReasonPhrase,
216}
217
218pub const STATUS_CODE_REASON_PHRASE: ResponseStatusCodeReasonPhrase = ResponseStatusCodeReasonPhrase {
219 n100_continue: &StatusCodeReasonPhrase { status_code: &100, reason_phrase: "Continue" },
220 n101_switching_protocols: &StatusCodeReasonPhrase { status_code: &101, reason_phrase: "Switching Protocols" },
221 n102_processing: &StatusCodeReasonPhrase { status_code: &102, reason_phrase: "Processing" },
222 n103_early_hints: &StatusCodeReasonPhrase { status_code: &103, reason_phrase: "Early Hints" },
223 n200_ok: &StatusCodeReasonPhrase {
224 status_code: &200,
225 reason_phrase: "OK"
226 },
227
228 n201_created: &StatusCodeReasonPhrase { status_code: &201, reason_phrase: "Created" },
229 n202_accepted: &StatusCodeReasonPhrase { status_code: &202, reason_phrase: "Accepted" },
230 n203_non_authoritative_information: &StatusCodeReasonPhrase { status_code: &203, reason_phrase: "Non Authoritative Information" },
231 n204_no_content: &StatusCodeReasonPhrase {
232 status_code: &204,
233 reason_phrase: "No Content"
234 },
235
236 n205_reset_content: &StatusCodeReasonPhrase { status_code: &205, reason_phrase: "Reset Content" },
237 n206_partial_content: &StatusCodeReasonPhrase {
238 status_code: &206,
239 reason_phrase: "Partial Content"
240 },
241
242 n207_multi_status: &StatusCodeReasonPhrase { status_code: &207, reason_phrase: "Multi-Status" },
243 n208_already_reported: &StatusCodeReasonPhrase { status_code: &208, reason_phrase: "Already Reported" },
244 n226_im_used: &StatusCodeReasonPhrase { status_code: &226, reason_phrase: "IM Used" },
245 n300_multiple_choices: &StatusCodeReasonPhrase { status_code: &300, reason_phrase: "Multiple Choices" },
246 n301_moved_permanently: &StatusCodeReasonPhrase { status_code: &301, reason_phrase: "Moved Permanently" },
247 n302_found: &StatusCodeReasonPhrase { status_code: &302, reason_phrase: "Found" },
248 n303_see_other: &StatusCodeReasonPhrase { status_code: &303, reason_phrase: "See Other" },
249 n304_not_modified: &StatusCodeReasonPhrase { status_code: &304, reason_phrase: "Not Modified" },
250 n307_temporary_redirect: &StatusCodeReasonPhrase { status_code: &307, reason_phrase: "Temporary Redirect" },
251 n308_permanent_redirect: &StatusCodeReasonPhrase { status_code: &308, reason_phrase: "Permanent Redirect" },
252 n400_bad_request: &StatusCodeReasonPhrase {
253 status_code: &400,
254 reason_phrase: "Bad Request"
255 },
256
257 n401_unauthorized: &StatusCodeReasonPhrase { status_code: &401, reason_phrase: "Unauthorized" },
258 n402_payment_required: &StatusCodeReasonPhrase { status_code: &402, reason_phrase: "Payment Required" },
259 n403_forbidden: &StatusCodeReasonPhrase { status_code: &403, reason_phrase: "Forbidden" },
260 n404_not_found: &StatusCodeReasonPhrase {
261 status_code: &404,
262 reason_phrase: "Not Found"
263 },
264
265 n405_method_not_allowed: &StatusCodeReasonPhrase { status_code: &405, reason_phrase: "Method Not Allowed" },
266 n406_not_acceptable: &StatusCodeReasonPhrase { status_code: &406, reason_phrase: "Not Acceptable" },
267 n407_proxy_authentication_required: &StatusCodeReasonPhrase { status_code: &407, reason_phrase: "Proxy Authentication Required" },
268 n408_request_timeout: &StatusCodeReasonPhrase { status_code: &408, reason_phrase: "Request Timeout" },
269 n409_conflict: &StatusCodeReasonPhrase { status_code: &409, reason_phrase: "Conflict" },
270 n410_gone: &StatusCodeReasonPhrase { status_code: &410, reason_phrase: "Gone" },
271 n411_length_required: &StatusCodeReasonPhrase { status_code: &411, reason_phrase: "Length Required" },
272 n412_precondition_failed: &StatusCodeReasonPhrase { status_code: &412, reason_phrase: "Precondition Failed" },
273 n413_payload_too_large: &StatusCodeReasonPhrase { status_code: &413, reason_phrase: "Payload Too Large" },
274 n414_uri_too_long: &StatusCodeReasonPhrase { status_code: &414, reason_phrase: "URI Too Long" },
275 n415_unsupported_media_type: &StatusCodeReasonPhrase { status_code: &415, reason_phrase: "Unsupported Media Type" },
276 n416_range_not_satisfiable: &StatusCodeReasonPhrase {
277 status_code: &416,
278 reason_phrase: "Range Not Satisfiable"
279 },
280
281 n417_expectation_failed: &StatusCodeReasonPhrase { status_code: &417, reason_phrase: "Expectation Failed" },
282 n418_im_a_teapot: &StatusCodeReasonPhrase { status_code: &418, reason_phrase: "I'm A Teapot" },
283 n421_misdirected_request: &StatusCodeReasonPhrase { status_code: &421, reason_phrase: "Misdirected Request" },
284 n422_unprocessable_entity: &StatusCodeReasonPhrase { status_code: &422, reason_phrase: "Unprocessable Entity" },
285 n423_locked: &StatusCodeReasonPhrase { status_code: &423, reason_phrase: "Locked" },
286 n424_failed_dependency: &StatusCodeReasonPhrase { status_code: &424, reason_phrase: "Failed Dependency" },
287 n425_too_early: &StatusCodeReasonPhrase { status_code: &425, reason_phrase: "Too Early" },
288 n426_upgrade_required: &StatusCodeReasonPhrase { status_code: &426, reason_phrase: "Upgrade Required" },
289 n428_precondition_required: &StatusCodeReasonPhrase { status_code: &428, reason_phrase: "Precondition Required" },
290 n429_too_many_requests: &StatusCodeReasonPhrase { status_code: &429, reason_phrase: "Too Many Requests" },
291 n431_request_header_fields_too_large: &StatusCodeReasonPhrase { status_code: &431, reason_phrase: "Request Header Fields Too Large" },
292 n451_unavailable_for_legal_reasons: &StatusCodeReasonPhrase { status_code: &451, reason_phrase: "Unavailable For Legal Reasons" },
293 n500_internal_server_error: &StatusCodeReasonPhrase {
294 status_code: &500,
295 reason_phrase: "Internal Server Error"
296 },
297 n501_not_implemented: &StatusCodeReasonPhrase { status_code: &501, reason_phrase: "Not Implemented" },
298 n502_bad_gateway: &StatusCodeReasonPhrase { status_code: &502, reason_phrase: "Bad Gateway" },
299 n503_service_unavailable: &StatusCodeReasonPhrase { status_code: &503, reason_phrase: "Service Unavailable" },
300 n504_gateway_timeout: &StatusCodeReasonPhrase { status_code: &504, reason_phrase: "Gateway Timeout" },
301 n505_http_version_not_supported: &StatusCodeReasonPhrase { status_code: &505, reason_phrase: "HTTP Version Not Supported" },
302 n506_variant_also_negotiates: &StatusCodeReasonPhrase { status_code: &506, reason_phrase: "Variant Also Negotiates" },
303 n507_insufficient_storage: &StatusCodeReasonPhrase { status_code: &507, reason_phrase: "Insufficient Storage" },
304 n508_loop_detected: &StatusCodeReasonPhrase { status_code: &508, reason_phrase: "Loop Detected" },
305 n510_not_extended: &StatusCodeReasonPhrase { status_code: &510, reason_phrase: "Not Extended" },
306 n511_network_authentication_required: &StatusCodeReasonPhrase { status_code: &511, reason_phrase: "Network Authentication Required" }
307};
308
309pub fn content_range_header_value(status_code: i16, content_range: &ContentRange) -> Option<String> {
315 if status_code != *STATUS_CODE_REASON_PHRASE.n206_partial_content.status_code {
316 return None;
317 }
318 Some(format!(
319 "{} {}-{}/{}",
320 Range::BYTES, content_range.range.start, content_range.range.end, content_range.size
321 ))
322}
323
324impl Response {
325
326 #[doc(hidden)]
329 pub fn empty() -> Response {
330 Response {
331 http_version: String::new(),
332 status_code: 0,
333 reason_phrase: String::new(),
334 headers: Vec::new(),
335 content_range_list: Vec::new(),
336 stream_file: None,
337 stream_pipe: None,
338 trailers: None,
339 }
340 }
341
342 pub fn with_status(status: &StatusCodeReasonPhrase) -> Response {
354 let mut response = <Response as New>::new();
355 response.set_status(status);
356 response
357 }
358
359 #[doc(hidden)]
361 pub fn set_status(&mut self, status: &StatusCodeReasonPhrase) {
362 self.status_code = *status.status_code;
363 self.reason_phrase.clear();
364 self.reason_phrase.push_str(status.reason_phrase);
365 }
366
367 pub fn build(status: StatusCodeReasonPhrase, header_list : Vec<Header>, body: Vec<ContentRange>) -> Response {
368 Response {
369 http_version: VERSION.http_1_1.to_string(),
370 status_code: *status.status_code,
371 reason_phrase: status.reason_phrase.to_string(),
372 headers: header_list,
373 content_range_list: body,
374 stream_file: None,
375 stream_pipe: None,
376 trailers: None,
377 }
378 }
379
380 pub fn status_code_reason_phrase_list() -> Vec<&'static StatusCodeReasonPhrase> {
381 let list = vec![
382 STATUS_CODE_REASON_PHRASE.n100_continue,
383 STATUS_CODE_REASON_PHRASE.n101_switching_protocols,
384 STATUS_CODE_REASON_PHRASE.n102_processing,
385 STATUS_CODE_REASON_PHRASE.n103_early_hints,
386 STATUS_CODE_REASON_PHRASE.n200_ok,
387 STATUS_CODE_REASON_PHRASE.n201_created,
388 STATUS_CODE_REASON_PHRASE.n202_accepted,
389 STATUS_CODE_REASON_PHRASE.n203_non_authoritative_information,
390 STATUS_CODE_REASON_PHRASE.n204_no_content,
391 STATUS_CODE_REASON_PHRASE.n205_reset_content,
392 STATUS_CODE_REASON_PHRASE.n206_partial_content,
393 STATUS_CODE_REASON_PHRASE.n207_multi_status,
394 STATUS_CODE_REASON_PHRASE.n208_already_reported,
395 STATUS_CODE_REASON_PHRASE.n226_im_used,
396 STATUS_CODE_REASON_PHRASE.n300_multiple_choices,
397 STATUS_CODE_REASON_PHRASE.n301_moved_permanently,
398 STATUS_CODE_REASON_PHRASE.n302_found,
399 STATUS_CODE_REASON_PHRASE.n303_see_other,
400 STATUS_CODE_REASON_PHRASE.n304_not_modified,
401 STATUS_CODE_REASON_PHRASE.n307_temporary_redirect,
402 STATUS_CODE_REASON_PHRASE.n308_permanent_redirect,
403 STATUS_CODE_REASON_PHRASE.n400_bad_request,
404 STATUS_CODE_REASON_PHRASE.n401_unauthorized,
405 STATUS_CODE_REASON_PHRASE.n402_payment_required,
406 STATUS_CODE_REASON_PHRASE.n403_forbidden,
407 STATUS_CODE_REASON_PHRASE.n404_not_found,
408 STATUS_CODE_REASON_PHRASE.n405_method_not_allowed,
409 STATUS_CODE_REASON_PHRASE.n406_not_acceptable,
410 STATUS_CODE_REASON_PHRASE.n407_proxy_authentication_required,
411 STATUS_CODE_REASON_PHRASE.n408_request_timeout,
412 STATUS_CODE_REASON_PHRASE.n409_conflict,
413 STATUS_CODE_REASON_PHRASE.n410_gone,
414 STATUS_CODE_REASON_PHRASE.n411_length_required,
415 STATUS_CODE_REASON_PHRASE.n412_precondition_failed,
416 STATUS_CODE_REASON_PHRASE.n413_payload_too_large,
417 STATUS_CODE_REASON_PHRASE.n414_uri_too_long,
418 STATUS_CODE_REASON_PHRASE.n415_unsupported_media_type,
419 STATUS_CODE_REASON_PHRASE.n416_range_not_satisfiable,
420 STATUS_CODE_REASON_PHRASE.n417_expectation_failed,
421 STATUS_CODE_REASON_PHRASE.n418_im_a_teapot,
422 STATUS_CODE_REASON_PHRASE.n421_misdirected_request,
423 STATUS_CODE_REASON_PHRASE.n422_unprocessable_entity,
424 STATUS_CODE_REASON_PHRASE.n423_locked,
425 STATUS_CODE_REASON_PHRASE.n424_failed_dependency,
426 STATUS_CODE_REASON_PHRASE.n425_too_early,
427 STATUS_CODE_REASON_PHRASE.n426_upgrade_required,
428 STATUS_CODE_REASON_PHRASE.n428_precondition_required,
429 STATUS_CODE_REASON_PHRASE.n429_too_many_requests,
430 STATUS_CODE_REASON_PHRASE.n431_request_header_fields_too_large,
431 STATUS_CODE_REASON_PHRASE.n451_unavailable_for_legal_reasons,
432 STATUS_CODE_REASON_PHRASE.n500_internal_server_error,
433 STATUS_CODE_REASON_PHRASE.n501_not_implemented,
434 STATUS_CODE_REASON_PHRASE.n502_bad_gateway,
435 STATUS_CODE_REASON_PHRASE.n503_service_unavailable,
436 STATUS_CODE_REASON_PHRASE.n504_gateway_timeout,
437 STATUS_CODE_REASON_PHRASE.n505_http_version_not_supported,
438 STATUS_CODE_REASON_PHRASE.n506_variant_also_negotiates,
439 STATUS_CODE_REASON_PHRASE.n507_insufficient_storage,
440 STATUS_CODE_REASON_PHRASE.n508_loop_detected,
441 STATUS_CODE_REASON_PHRASE.n510_not_extended,
442 STATUS_CODE_REASON_PHRASE.n511_network_authentication_required,
443 ];
444 list
445 }
446
447 pub const _ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE: &'static str = "Unable to parse status code";
448
449 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>.+)";
450
451 pub fn _get_header(&self, name: String) -> Option<&Header> {
452 let header = self.headers.iter().find(|x| x.name.eq_ignore_ascii_case(&name));
454 header
455 }
456
457 pub fn status_has_no_content(status_code: i16) -> bool {
460 (100..200).contains(&status_code) || status_code == 204 || status_code == 304
461 }
462
463 pub fn drop_content_for_status(&mut self) {
468 if !Response::status_has_no_content(self.status_code) {
469 return;
470 }
471 self.content_range_list.clear();
472 self.stream_file = None;
473 self.stream_pipe = None;
474 self.headers.retain(|h| {
475 !h.name.eq_ignore_ascii_case(Header::_CONTENT_LENGTH)
476 && !h.name.eq_ignore_ascii_case(Header::_TRANSFER_ENCODING)
477 });
478 }
479
480 pub fn generate_body(content_range_list: Vec<ContentRange>) -> Vec<u8> {
481 let mut body = vec![];
482 let one = 1;
483
484 if content_range_list.len() == one {
485 let mut content_range_list = content_range_list;
488 return content_range_list.swap_remove(0).body;
489 }
490
491 if content_range_list.len() > one {
492 let boundary = Range::multipart_boundary(&content_range_list);
495 for (i, content_range) in content_range_list.iter().enumerate() {
496 let mut body_str = SYMBOL.empty_string.to_string();
497 if i != 0 {
498 body_str.push_str(SYMBOL.new_line_carriage_return);
499 }
500 body_str.push_str(SYMBOL.hyphen);
501 body_str.push_str(SYMBOL.hyphen);
502 body_str.push_str(&boundary);
503 body_str.push_str(SYMBOL.new_line_carriage_return);
504 let content_type = [Header::_CONTENT_TYPE, Header::NAME_VALUE_SEPARATOR, SYMBOL.whitespace, &Header::single_line(&content_range.content_type)].join("");
505 body_str.push_str(content_type.as_str());
506 body_str.push_str(SYMBOL.new_line_carriage_return);
507 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("");
508 body_str.push_str(content_range_header.as_str());
509 body_str.push_str(SYMBOL.new_line_carriage_return);
510 body_str.push_str(SYMBOL.new_line_carriage_return);
511
512 body.extend_from_slice(body_str.as_bytes());
513 body.extend_from_slice(&content_range.body);
514 }
515 body.extend_from_slice(b"\r\n--");
516 body.extend_from_slice(boundary.as_bytes());
517 body.extend_from_slice(b"--\r\n");
518 }
519
520 body
521 }
522
523 pub fn generate_response(response: Response, request: Request) -> Vec<u8> {
525 Response::generate_response_coalesced(response, request, usize::MAX).0
526 }
527
528 pub fn generate_response_parts(response: Response, request: Request) -> (Vec<u8>, Vec<u8>) {
534 Response::generate_response_coalesced(response, request, 0)
535 }
536
537 #[doc(hidden)]
542 pub fn generate_response_coalesced(mut response: Response, request: Request, coalesce_limit: usize) -> (Vec<u8>, Vec<u8>) {
543 response.drop_content_for_status();
544 let streamed = response.stream_pipe.is_some() || response.stream_file.is_some();
545 let Response { http_version, status_code, reason_phrase, headers, mut content_range_list, .. } = response;
546
547 let mut content_type: Option<String> = None;
551 let mut content_range: Option<String> = None;
552 let mut content_length: Option<Decimal> = None;
553 if content_range_list.len() == 1 {
554 let range = &mut content_range_list[0];
555 content_range = content_range_header_value(status_code, range);
556 content_length = Some(Decimal::new(range.body.len() as u128));
557 content_type = Some(std::mem::take(&mut range.content_type));
560 }
561 let multipart = content_range_list.len() > 1;
562 if multipart {
563 content_type = Some(Range::multipart_content_type(&content_range_list));
564 }
565
566 let is_head = request.method == METHOD.head;
570 let body = if is_head && !multipart {
571 Vec::new()
572 } else {
573 Response::generate_body(content_range_list)
574 };
575 if multipart {
576 content_length = Some(Decimal::new(body.len() as u128));
579 }
580 if content_length.is_none()
585 && !streamed
586 && !Response::status_has_no_content(status_code)
587 && !headers.iter().any(|h| h.name.eq_ignore_ascii_case(Header::_CONTENT_LENGTH))
588 {
589 content_length = Some(Decimal::new(0));
590 }
591 let body = if is_head { Vec::new() } else { body };
592 let coalesce = body.len() <= coalesce_limit;
593
594 let status = u16::try_from(status_code).map(|code| Decimal::new(code as u128));
599 let status_text = match &status {
600 Ok(decimal) => std::borrow::Cow::Borrowed(decimal.as_str()),
601 Err(_) => std::borrow::Cow::Owned(status_code.to_string()),
602 };
603 let extra = [
604 (Header::_CONTENT_TYPE, content_type.as_deref()),
605 (Header::_CONTENT_RANGE, content_range.as_deref()),
606 (Header::_CONTENT_LENGTH, content_length.as_ref().map(Decimal::as_str)),
607 ];
608 let capacity = http_version.len() + status_text.len() + reason_phrase.len() + 4
609 + headers.iter().map(|h| h.name.len() + h.value.len() + 4).sum::<usize>()
610 + extra.iter().map(|(name, value)| value.map_or(0, |v| name.len() + v.len() + 4)).sum::<usize>()
611 + 2
612 + if coalesce { body.len() } else { 0 };
613 let mut head = Vec::with_capacity(capacity);
614 head.extend_from_slice(http_version.as_bytes());
615 head.push(b' ');
616 head.extend_from_slice(status_text.as_bytes());
617 head.push(b' ');
618 head.extend_from_slice(Header::single_line(&reason_phrase).as_bytes());
619 head.extend_from_slice(b"\r\n");
620 let header_lines = headers.iter().map(|h| (h.name.as_ref(), h.value.as_ref()))
621 .chain(extra.iter().filter_map(|(name, value)| value.map(|v| (*name, v))));
622 for (name, value) in header_lines {
623 head.extend_from_slice(Header::single_line(name).as_bytes());
624 head.extend_from_slice(Header::NAME_VALUE_SEPARATOR.as_bytes());
625 head.extend_from_slice(Header::single_line(value).as_bytes());
626 head.extend_from_slice(b"\r\n");
627 }
628 head.extend_from_slice(b"\r\n");
629
630 if coalesce {
631 head.extend_from_slice(&body);
632 (head, Vec::new())
633 } else {
634 (head, body)
635 }
636 }
637
638 pub fn _parse_response(response_vec_u8: &[u8]) -> Response {
639 let mut cursor = io::Cursor::new(response_vec_u8);
640
641 let mut response = Response {
642 http_version: "".to_string(),
643 status_code: 0,
644 reason_phrase: "".to_string(),
645 headers: vec![],
646 content_range_list: vec![],
647 stream_file: None,
648 stream_pipe: None,
649 trailers: None,
650 };
651
652 let content_length: usize = 0;
653 let iteration_number : usize = 0;
654
655 Response::_parse_raw_response_via_cursor(&mut cursor, iteration_number, &mut response, content_length);
656
657 return response;
658 }
659
660 pub fn _parse_http_version_status_code_reason_phrase_string(http_version_status_code_reason_phrase: &str) -> Result<(String, i16, String), String> {
661 let line = without_cr_lf(http_version_status_code_reason_phrase);
662 let (http_version, status_code, reason_phrase) = Response::status_line_parts(&line)?;
663 Ok((http_version.to_string(), status_code, reason_phrase.to_string()))
664 }
665
666 fn status_line_parts(line: &str) -> Result<(&str, i16, &str), String> {
669 let boxed_split = line.split_once(SYMBOL.whitespace);
670 if boxed_split.is_none() {
671 return Err(Response::_ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE.to_string())
672 }
673
674 let (http_version, status_code_reason_phrase) = boxed_split.unwrap();
675 if !HTTP::is_supported_version(http_version) {
676 return Err(Response::_ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE.to_string())
677 }
678
679 let (status_code, reason_phrase) = status_code_reason_phrase
684 .split_once(SYMBOL.whitespace)
685 .unwrap_or((status_code_reason_phrase, ""));
686
687 let is_three_digits = status_code.len() == 3 && status_code.bytes().all(|b| b.is_ascii_digit());
688 let status_code_i16 = match status_code.parse::<i16>() {
689 Ok(code) if is_three_digits && code >= 100 => code,
690 _ => return Err(Response::_ERROR_UNABLE_TO_PARSE_HTTP_VERSION_STATUS_CODE.to_string()),
691 };
692
693 Ok((http_version, status_code_i16, reason_phrase))
694 }
695
696 pub fn _parse_http_response_header_string(header_string: &str) -> Header {
697 let (header_name, raw_header_value) = header_string
701 .split_once(Header::NAME_VALUE_SEPARATOR)
702 .unwrap_or((header_string, ""));
703 let header_value = StringExt::truncate_new_line_carriage_return(raw_header_value);
704
705
706 Header {
707 name: header_name.to_string().into(),
708 value: header_value.to_string().into()
709 }
710 }
711
712 pub fn _parse_raw_response_via_cursor(
713 cursor: &mut Cursor<&[u8]>,
714 mut iteration_number: usize,
715 response: &mut Response,
716 mut content_length: usize) {
717
718 let mut buffer = vec![];
719 let boxed_read = cursor.read_until(b'\n', &mut buffer);
720 if boxed_read.is_err() {
721 eprintln!("unable to parse raw response via cursor {}", boxed_read.err().unwrap());
722 return;
723 }
724 let bytes_offset = boxed_read.unwrap();
725 let mut buffer_as_u8_array: &[u8] = &buffer;
726 let string = String::from_utf8(Vec::from(buffer_as_u8_array)).unwrap();
727
728 let is_first_iteration = iteration_number == 0;
729 let new_line_char_found = bytes_offset != 0;
730 let current_string_is_empty = string.trim().len() == 0;
731
732 if is_first_iteration {
733 let boxed_http_version_status_code_reason_phrase = Response::_parse_http_version_status_code_reason_phrase_string(&string);
734 if boxed_http_version_status_code_reason_phrase.is_err() {
735 let error = boxed_http_version_status_code_reason_phrase.err().unwrap();
736 eprintln!("{}", error);
737 return;
738 }
739
740 let (http_version, status_code, reason_phrase) = boxed_http_version_status_code_reason_phrase.unwrap();
741
742 response.http_version = http_version;
743 response.status_code = status_code;
744 response.reason_phrase = reason_phrase;
745 }
746
747 if current_string_is_empty {
748 let content_type = response._get_header(Header::_CONTENT_TYPE.to_string()).cloned()
750 .unwrap_or(Header { name: Header::_CONTENT_TYPE.into(), value: "".into() });
751 let is_multipart = Response::_is_multipart_byteranges_content_type(&content_type);
752
753 if is_multipart {
754 let content_range_list : Vec<ContentRange> = vec![];
755
756 let boundary = content_type.value.split(';')
757 .filter_map(|param| param.trim().strip_prefix("boundary="))
758 .next()
759 .map(|b| b.trim_matches('"').to_string())
760 .unwrap_or_else(|| Range::STRING_SEPARATOR.to_string());
761 let mut buf = vec![];
762 let _ = cursor.read_until(b'\n', &mut buf);
763 let boxed_value = Range::_parse_multipart_body_with_boundary(cursor, content_range_list, &boundary);
764 let mut range_list = vec![];
765 if boxed_value.is_ok() {
766 range_list = boxed_value.unwrap();
767 }
768 response.content_range_list = range_list;
769 } else {
770 buffer = vec![];
771 let boxed_read = cursor.read_to_end(&mut buffer);
772 if boxed_read.is_ok() {
773 buffer_as_u8_array = &buffer;
774
775 let content_range = ContentRange {
776 unit: Range::BYTES.to_string(),
777 range: Range {
778 start: 0,
779 end: buffer_as_u8_array.len() as u64
780 },
781 size: buffer_as_u8_array.len().to_string(),
782 body: Vec::from(buffer_as_u8_array),
783 content_type: content_type.value.to_string()
784 };
785 response.content_range_list = vec![content_range];
786 } else {
787 let reason = boxed_read.err().unwrap();
788 eprintln!("error reading file: {}", reason.to_string())
789 }
790
791 }
792
793 return;
794 }
795
796 if new_line_char_found && !current_string_is_empty {
797 let mut header = Header { name: "".into(), value: "".into() };
798 if !is_first_iteration {
799 header = Response::_parse_http_response_header_string(&string);
800 if header.name.eq_ignore_ascii_case(Header::_CONTENT_LENGTH) {
801 content_length = header.value.trim().parse().unwrap_or(0);
803 }
804 }
805
806 response.headers.push(header);
807 iteration_number += 1;
808 Response::_parse_raw_response_via_cursor(cursor, iteration_number, response, content_length);
809 }
810 }
811
812 pub fn _is_multipart_byteranges_content_type(content_type: &Header) -> bool {
813 content_type.value.strip_prefix(Range::MULTIPART)
815 .and_then(|rest| rest.strip_prefix(SYMBOL.slash))
816 .is_some_and(|rest| rest.starts_with(Range::BYTERANGES))
817 }
818
819
820 pub fn get_response(
821 status_code_reason_phrase: &StatusCodeReasonPhrase,
822 boxed_header_list: Option<Vec<Header>>,
823 boxed_content_range_list: Option<Vec<ContentRange>>) -> Response {
824
825 let mut header_list: Vec<Header> = vec![];
826 if boxed_header_list.is_some() {
827 header_list = boxed_header_list.unwrap();
828 }
829
830 let mut content_range_list: Vec<ContentRange> = vec![];
831 if boxed_content_range_list.is_some() {
832 content_range_list = boxed_content_range_list.unwrap();
833 }
834
835 let response = Response {
836 http_version: VERSION.http_1_1.to_string(),
837 status_code: *status_code_reason_phrase.status_code,
838 reason_phrase: status_code_reason_phrase.reason_phrase.to_string(),
839 headers: header_list,
840 content_range_list,
841 stream_file: None,
842 stream_pipe: None,
843 trailers: None,
844 };
845
846 response
847 }
848
849 pub fn json(status_code_reason_phrase: &StatusCodeReasonPhrase, body: Vec<u8>) -> Response {
863 let content_range = Range::get_content_range(body, MimeType::APPLICATION_JSON.to_string());
864 Response::get_response(status_code_reason_phrase, None, Some(vec![content_range]))
865 }
866
867 pub fn text(status_code_reason_phrase: &StatusCodeReasonPhrase, body: &str) -> Response {
876 let content_range = Range::get_content_range(body.as_bytes().to_vec(), MimeType::TEXT_PLAIN.to_string());
877 Response::get_response(status_code_reason_phrase, None, Some(vec![content_range]))
878 }
879
880 pub fn generate(&mut self) -> Vec<u8> {
881 let response = &mut self.clone();
882 response.drop_content_for_status();
883
884 if response.content_range_list.len() == 1 {
885 let content_range_index = 0;
886 let content_range = response.content_range_list.get(content_range_index).unwrap();
887 response.headers.push(Header {
888 name: Header::_CONTENT_TYPE.into(),
889 value: content_range.content_type.to_string().into()
890 });
891
892 if let Some(value) = content_range_header_value(response.status_code, content_range) {
893 response.headers.push(Header {
894 name: Header::_CONTENT_RANGE.into(),
895 value: value.into(),
896 });
897 }
898
899 response.headers.push(Header {
900 name: Header::_CONTENT_LENGTH.into(),
901 value: content_range.body.len().to_string().into()
902 });
903 }
904
905 if response.content_range_list.len() > 1 {
906 response.headers.push(Header {
907 name: Header::_CONTENT_TYPE.into(),
908 value: Range::multipart_content_type(&response.content_range_list).into(),
909 });
910 }
911
912 let response_clone = response.clone();
913 let body = Response::generate_body(response_clone.content_range_list);
914 if response.content_range_list.len() > 1 {
915 response.headers.push(Header {
916 name: Header::_CONTENT_LENGTH.into(),
917 value: body.len().to_string().into(),
918 });
919 }
920
921 let mut headers_str = SYMBOL.new_line_carriage_return.to_string();
922
923 let header_list = response.headers.clone();
924 for header in header_list {
925 let mut header_string = SYMBOL.empty_string.to_string();
926 header_string.push_str(&Header::single_line(&header.name));
927 header_string.push_str(Header::NAME_VALUE_SEPARATOR);
928 header_string.push_str(&Header::single_line(&header.value));
929 header_string.push_str(SYMBOL.new_line_carriage_return);
930 headers_str.push_str(&header_string);
931 }
932
933 let response_clone = response.clone();
934 let status = [response_clone.http_version, response.status_code.to_string(), Header::single_line(&response_clone.reason_phrase).into_owned()].join(SYMBOL.whitespace);
935 let response_without_body = format!(
936 "{}{}{}",
937 status,
938 headers_str,
939 SYMBOL.new_line_carriage_return,
940 );
941
942
943 [response_without_body.into_bytes(), body].concat()
944 }
945
946 pub fn get_header(&self, name: String) -> Option<&Header> {
947 self._get_header(name)
948 }
949
950 pub fn header(&self, name: &str) -> Option<&Header> {
952 self.headers.iter().find(|h| h.name.eq_ignore_ascii_case(name))
953 }
954
955 pub fn parse(response_vec_u8: &[u8]) -> Result<Response, String> {
956 let total_bytes : i32 = response_vec_u8.len() as i32;
957 let bytes_read = 0;
958 let mut cursor = io::Cursor::new(response_vec_u8);
959
960 let mut response = Response {
961 http_version: "".to_string(),
962 status_code: 0,
963 reason_phrase: "".to_string(),
964 headers: Vec::with_capacity(16),
967 content_range_list: vec![],
968 stream_file: None,
969 stream_pipe: None,
970 trailers: None,
971 };
972
973 let content_length: usize = 0;
974 let iteration_number : usize = 0;
975
976 let boxed_parse = Response::parse_raw_response_via_cursor(
977 &mut cursor, iteration_number,
978 &mut response,
979 content_length,
980 total_bytes,
981 bytes_read
982 );
983 if boxed_parse.is_err() {
984 let message = boxed_parse.err().unwrap();
985 return Err(message);
986 }
987
988 return Ok(response);
989 }
990
991 #[doc(hidden)]
1001 pub fn parse_upstream(input: &[u8], dropped: &[&str]) -> Result<Response, String> {
1002 let mut response = Response {
1003 http_version: crate::http::VERSION.http_1_1.to_string(),
1004 status_code: 0,
1005 reason_phrase: String::new(),
1006 headers: Vec::with_capacity(16),
1007 content_range_list: vec![],
1008 stream_file: None,
1009 stream_pipe: None,
1010 trailers: None,
1011 };
1012 let mut content_type: Option<&str> = None;
1013 let mut pos = 0usize;
1014 let mut first = true;
1015 loop {
1016 let rest = &input[pos.min(input.len())..];
1017 let line_len = memchr::memchr(b'\n', rest).map_or(rest.len(), |i| i + 1);
1018 pos += line_len;
1019 let line = std::str::from_utf8(&rest[..line_len]).map_err(|e| e.to_string())?;
1020 if first {
1021 let status_line = without_cr_lf(line);
1022 let (_, status_code, reason_phrase) = Response::status_line_parts(&status_line)?;
1023 response.status_code = status_code;
1024 response.reason_phrase = reason_phrase.to_string();
1025 }
1026 if is_blank(line) {
1027 if content_type.is_some_and(|ct| ct.starts_with("multipart/byteranges")) {
1028 return Response::parse(input).map(normalized_like_proxy(dropped));
1029 }
1030 let body = input[pos.min(input.len())..].to_vec();
1031 response.content_range_list = vec![ContentRange {
1032 unit: Range::BYTES.to_string(),
1033 range: Range { start: 0, end: body.len() as u64 },
1034 size: Decimal::new(body.len() as u128).as_str().to_string(),
1035 content_type: content_type.unwrap_or(MimeType::APPLICATION_OCTET_STREAM).to_string(),
1036 body,
1037 }];
1038 return Ok(response);
1039 }
1040 if line_len == 0 {
1041 return Err("unable to parse".to_string());
1042 }
1043 if !first {
1044 let (name, raw_value) = split_name_value(line).ok_or_else(|| format!("unable to parse header: {}", line))?;
1045 if name.eq_ignore_ascii_case(Header::_CONTENT_LENGTH) {
1046 let value = without_cr_lf(raw_value);
1047 let _: usize = value.trim().parse().map_err(|_| format!("invalid Content-Length: {:?}", value))?;
1048 }
1049 if name.eq_ignore_ascii_case(Header::_CONTENT_TYPE) {
1050 if content_type.is_none() {
1051 match without_cr_lf(raw_value) {
1054 std::borrow::Cow::Borrowed(v) => content_type = Some(v),
1055 std::borrow::Cow::Owned(_) => return Response::parse(input).map(normalized_like_proxy(dropped)),
1056 }
1057 }
1058 continue;
1059 }
1060 if !dropped.iter().any(|d| name.eq_ignore_ascii_case(d)) {
1061 response.headers.push(Header {
1062 name: Header::name_from(name),
1063 value: without_cr_lf(raw_value).into_owned().into(),
1064 });
1065 }
1066 }
1067 first = false;
1068 }
1069 }
1070
1071 pub fn parse_raw_response_via_cursor(
1072 cursor: &mut Cursor<&[u8]>,
1073 mut iteration_number: usize,
1074 response: &mut Response,
1075 _content_length: usize,
1076 total_bytes: i32,
1077 mut bytes_read: i32) -> Result<(), String> {
1078 let input: &[u8] = cursor.get_ref();
1084 loop {
1085 let start = (cursor.position() as usize).min(input.len());
1086 let rest = &input[start..];
1087 let line_len = memchr::memchr(b'\n', rest).map_or(rest.len(), |i| i + 1);
1088 cursor.set_position((start + line_len) as u64);
1089 bytes_read += line_len as i32;
1090 let line = std::str::from_utf8(&rest[..line_len]).map_err(|e| e.to_string())?;
1091
1092 let is_first_iteration = iteration_number == 0;
1093 let new_line_char_found = line_len != 0;
1094 let current_string_is_empty = is_blank(line);
1095
1096 if is_first_iteration {
1097 let status_line = without_cr_lf(line);
1098 let (http_version, status_code, reason_phrase) = Response::status_line_parts(&status_line)?;
1099 response.http_version = http_version.to_string();
1100 response.status_code = status_code;
1101 response.reason_phrase = reason_phrase.to_string();
1102 }
1103
1104 if current_string_is_empty {
1105 let content_type_header = response.header(Header::_CONTENT_TYPE);
1107 let is_multipart = content_type_header.map_or(false, Response::_is_multipart_byteranges_content_type);
1108
1109 if is_multipart {
1110 let content_type = response.header(Header::_CONTENT_TYPE).ok_or("Content-Type is missing")?;
1111 let boundary = FormMultipartData::extract_boundary(content_type.value.as_ref())
1112 .map_err(|_| "unable to extract boundary from Content-Type".to_string())?;
1113 let is_opening_boundary_read = false;
1114 response.content_range_list = Range::parse_multipart_body_with_boundary(
1115 cursor,
1116 vec![],
1117 boundary,
1118 total_bytes,
1119 bytes_read,
1120 is_opening_boundary_read)?;
1121 } else {
1122 let content_type = content_type_header.map_or(MimeType::APPLICATION_OCTET_STREAM, |h| h.value.as_ref());
1123 let start = (cursor.position() as usize).min(input.len());
1124 let body = input[start..].to_vec();
1125 cursor.set_position(input.len() as u64);
1126 let content_range = ContentRange {
1127 unit: Range::BYTES.to_string(),
1128 range: Range {
1129 start: 0,
1130 end: body.len() as u64
1131 },
1132 size: Decimal::new(body.len() as u128).as_str().to_string(),
1133 content_type: content_type.to_string(),
1134 body,
1135 };
1136 response.content_range_list = vec![content_range];
1137 }
1138
1139 return Ok(());
1140 }
1141
1142 if new_line_char_found {
1143 if !is_first_iteration {
1144 let header = Response::parse_http_response_header_string(line)?;
1145 if header.name.eq_ignore_ascii_case(Header::_CONTENT_LENGTH) {
1146 let _: usize = header.value.trim().parse()
1148 .map_err(|_| format!("invalid Content-Length: {:?}", header.value))?;
1149 }
1150 response.headers.push(header);
1151 }
1152
1153 iteration_number += 1;
1154 } else {
1155 return Err("unable to parse".to_string());
1156 }
1157 }
1158 }
1159
1160 pub fn parse_http_response_header_string(header_string: &str) -> Result<Header, String> {
1161 match split_name_value(header_string) {
1162 Some((header_name, raw_header_value)) => Ok(Header {
1163 name: Header::name_from(header_name),
1164 value: without_cr_lf(raw_header_value).into_owned().into(),
1165 }),
1166 None => Err(format!("unable to parse header: {}", header_string)),
1167 }
1168 }
1169}
1170
1171impl Default for Response {
1172 fn default() -> Self {
1175 <Response as New>::new()
1176 }
1177}
1178
1179impl New for Response {
1180 fn new() -> Self {
1181 Response {
1182 http_version: VERSION.http_1_1.to_string(),
1183 status_code: *STATUS_CODE_REASON_PHRASE.n501_not_implemented.status_code,
1184 reason_phrase: STATUS_CODE_REASON_PHRASE.n501_not_implemented.reason_phrase.to_string(),
1185 headers: vec![],
1186 content_range_list: vec![],
1187 stream_file: None,
1188 stream_pipe: None,
1189 trailers: None,
1190 }
1191 }
1192}
1193fn normalized_like_proxy<'a>(dropped: &'a [&'a str]) -> impl FnOnce(Response) -> Response + 'a {
1196 move |mut response: Response| {
1197 response.http_version.clear();
1198 response.http_version.push_str(crate::http::VERSION.http_1_1);
1199 let has_body = !response.content_range_list.is_empty();
1200 if let [range] = response.content_range_list.as_mut_slice() {
1201 if let Some(h) = response.headers.iter().find(|h| h.name.eq_ignore_ascii_case(Header::_CONTENT_TYPE)) {
1202 range.content_type.clear();
1203 range.content_type.push_str(&h.value);
1204 }
1205 }
1206 response.headers.retain(|h| {
1207 if h.name.eq_ignore_ascii_case(Header::_CONTENT_TYPE) {
1208 return !has_body;
1209 }
1210 !dropped.iter().any(|d| h.name.eq_ignore_ascii_case(d))
1211 });
1212 response
1213 }
1214}
1215
1216fn is_blank(line: &str) -> bool {
1220 match line.bytes().find(|b| !b.is_ascii_whitespace()) {
1221 None => true,
1222 Some(b) if b.is_ascii() => false,
1223 Some(_) => line.trim().is_empty(),
1224 }
1225}
1226
1227fn without_cr_lf(s: &str) -> std::borrow::Cow<'_, str> {
1228 let trimmed = s.strip_suffix('\n').unwrap_or(s);
1229 let trimmed = trimmed.strip_suffix('\r').unwrap_or(trimmed);
1230 if memchr::memchr2(b'\r', b'\n', trimmed.as_bytes()).is_some() {
1231 std::borrow::Cow::Owned(StringExt::truncate_new_line_carriage_return(s))
1232 } else {
1233 std::borrow::Cow::Borrowed(trimmed)
1234 }
1235}
1236
1237fn split_name_value(line: &str) -> Option<(&str, &str)> {
1240 let bytes = line.as_bytes();
1241 let mut from = 0;
1242 while let Some(offset) = memchr::memchr(b':', &bytes[from..]) {
1243 let colon = from + offset;
1244 if bytes.get(colon + 1) == Some(&b' ') {
1245 return Some((&line[..colon], &line[colon + 2..]));
1246 }
1247 from = colon + 1;
1248 }
1249 None
1250}
1251
1252#[doc(hidden)]
1255pub const REGENERATED_OR_HOP_BY_HOP: &[&str] = &[
1256 "content-length", "transfer-encoding", "connection", "keep-alive",
1257 "proxy-connection", "te", "trailer", "upgrade",
1258];