http_request/response/
impl.rs1use super::*;
2
3impl Default for HttpResponse {
4 fn default() -> Self {
9 Self {
10 version: HttpVersion::default(),
11 status_code: HttpStatus::Unknown.code(),
12 reason_phrase: HttpStatus::Unknown.to_string(),
13 headers: new_response_headers(),
14 body: ResponseBody::new(),
15 }
16 }
17}
18
19impl HttpResponse {
20 pub fn from_bytes(response: &[u8]) -> Self {
27 let split_lines: Vec<&[u8]> = split_multi_byte(response, HTTP_BR_BYTES);
28 let mut lines: IntoIter<&[u8]> = split_lines.into_iter();
29 let status_line: &[u8] = lines.next().unwrap_or(&[]);
30 let status_parts: Vec<&[u8]> = split_whitespace(status_line);
31 let version: HttpVersion = status_parts
32 .first()
33 .and_then(|part: &&[u8]| from_utf8(part).ok())
34 .and_then(|version_str: &str| version_str.parse::<HttpVersion>().ok())
35 .unwrap_or_default();
36 let status_code: ResponseStatusCode = status_parts
37 .get(1)
38 .and_then(|part: &&[u8]| from_utf8(part).ok())
39 .and_then(|code_str: &str| code_str.parse().ok())
40 .unwrap_or(HttpStatus::Unknown.code());
41 let reason_phrase: String = status_parts.get(2..).map_or_else(
42 || HttpStatus::Unknown.to_string(),
43 |parts: &[&[u8]]| {
44 if parts.is_empty() {
45 HttpStatus::Unknown.to_string()
46 } else if parts.len() == 1 {
47 String::from_utf8_lossy(parts[0]).into_owned()
48 } else {
49 let total_len: usize =
50 parts.iter().map(|p: &&[u8]| p.len()).sum::<usize>() + parts.len() - 1;
51 let mut result: String = String::with_capacity(total_len);
52 for (i, part) in parts.iter().enumerate() {
53 if i > 0 {
54 result.push(' ');
55 }
56 result.push_str(&String::from_utf8_lossy(part));
57 }
58 result
59 }
60 },
61 );
62 let mut headers: HttpResponseHeaders = new_response_headers();
63 for line in lines.by_ref() {
64 if line.is_empty() {
65 break;
66 }
67 let mut colon_pos: Option<usize> = None;
68 for (i, &byte) in line.iter().enumerate() {
69 if byte == COLON_U8 {
70 colon_pos = Some(i);
71 break;
72 }
73 }
74 if let Some(pos) = colon_pos
75 && pos > 0
76 && pos + 1 < line.len()
77 {
78 let key_bytes: &[u8] = &line[..pos];
79 let value_start: usize = if line.get(pos + 1) == Some(&SPACE_U8) {
80 pos + 2
81 } else {
82 pos + 1
83 };
84 let value_bytes: &[u8] = &line[value_start..];
85 if let (Ok(key_str), Ok(value_str)) = (from_utf8(key_bytes), from_utf8(value_bytes))
86 {
87 headers.insert(
88 key_str.trim().to_ascii_lowercase(),
89 value_str.trim().to_owned(),
90 );
91 }
92 }
93 }
94 let body: ResponseBody = match lines.len() {
95 0 => ResponseBody::new(),
96 1 => {
97 let line: &[u8] = lines.next().unwrap_or(&[]);
98 line.to_vec()
99 }
100 _ => {
101 let lines_slice: &[&[u8]] = lines.as_slice();
102 let total_size: usize = lines_slice
103 .iter()
104 .map(|line: &&[u8]| line.len())
105 .sum::<usize>()
106 + lines_slice.len().saturating_sub(1) * BR_BYTES.len();
107 let mut body: ResponseBody = ResponseBody::with_capacity(total_size);
108 let mut first: bool = true;
109 for line in lines {
110 if !first {
111 body.extend_from_slice(BR_BYTES);
112 }
113 body.extend_from_slice(line);
114 first = false;
115 }
116 body
117 }
118 };
119 HttpResponse {
120 version,
121 status_code,
122 reason_phrase,
123 headers,
124 body,
125 }
126 }
127
128 pub fn is_success(&self) -> bool {
134 (200..300).contains(&self.get_status_code())
135 }
136
137 pub fn is_redirect(&self) -> bool {
143 (300..400).contains(&self.get_status_code())
144 }
145
146 pub fn get_header<K: AsRef<str>>(&self, key: K) -> Option<&str> {
156 let normalized = key.as_ref().to_ascii_lowercase();
157 self.headers.get(&normalized).map(String::as_str)
158 }
159
160 pub fn text(&self) -> String {
166 String::from_utf8_lossy(self.get_body()).into_owned()
167 }
168
169 pub fn bytes(&self) -> &[u8] {
175 self.get_body()
176 }
177
178 pub fn decode(&self, buffer_size: usize) -> HttpResponse {
191 let flat_headers: HttpResponseHeaders = self.get_headers().clone();
192 let decoded: ResponseBody = Compress::from(&flat_headers)
193 .decode(self.get_body(), buffer_size)
194 .into_owned();
195 HttpResponse {
196 version: self.get_version().clone(),
197 status_code: self.get_status_code(),
198 reason_phrase: self.get_reason_phrase().clone(),
199 headers: self.get_headers().clone(),
200 body: decoded,
201 }
202 }
203}