http_request/response/
struct.rs1use super::*;
2
3#[derive(Clone, Debug, Getter, Setter)]
14pub struct HttpResponse {
15 pub version: HttpVersion,
17 #[get(type(copy))]
19 pub status_code: ResponseStatusCode,
20 #[set(type(AsRef<str>))]
22 pub reason_phrase: String,
23 pub headers: HttpResponseHeaders,
25 #[set(type(AsRef<[u8]>))]
27 pub body: ResponseBody,
28}
29
30impl Default for HttpResponse {
31 fn default() -> Self {
32 Self {
33 version: HttpVersion::default(),
34 status_code: HttpStatus::Unknown.code(),
35 reason_phrase: HttpStatus::Unknown.to_string(),
36 headers: new_response_headers(),
37 body: ResponseBody::new(),
38 }
39 }
40}
41
42impl HttpResponse {
43 pub fn from_bytes(response: &[u8]) -> Self {
45 let split_lines: Vec<&[u8]> = split_multi_byte(response, HTTP_BR_BYTES);
46 let mut lines: IntoIter<&[u8]> = split_lines.into_iter();
47 let status_line: &[u8] = lines.next().unwrap_or(&[]);
48 let status_parts: Vec<&[u8]> = split_whitespace(status_line);
49 let version: HttpVersion = status_parts
50 .first()
51 .and_then(|part: &&[u8]| from_utf8(part).ok())
52 .and_then(|version_str: &str| version_str.parse::<HttpVersion>().ok())
53 .unwrap_or_default();
54 let status_code: ResponseStatusCode = status_parts
55 .get(1)
56 .and_then(|part: &&[u8]| from_utf8(part).ok())
57 .and_then(|code_str: &str| code_str.parse().ok())
58 .unwrap_or(HttpStatus::Unknown.code());
59 let reason_phrase: String = status_parts.get(2..).map_or_else(
60 || HttpStatus::Unknown.to_string(),
61 |parts: &[&[u8]]| {
62 if parts.is_empty() {
63 HttpStatus::Unknown.to_string()
64 } else if parts.len() == 1 {
65 String::from_utf8_lossy(parts[0]).into_owned()
66 } else {
67 let total_len: usize =
68 parts.iter().map(|p: &&[u8]| p.len()).sum::<usize>() + parts.len() - 1;
69 let mut result: String = String::with_capacity(total_len);
70 for (i, part) in parts.iter().enumerate() {
71 if i > 0 {
72 result.push(' ');
73 }
74 result.push_str(&String::from_utf8_lossy(part));
75 }
76 result
77 }
78 },
79 );
80 let mut headers: HttpResponseHeaders = new_response_headers();
81 for line in lines.by_ref() {
82 if line.is_empty() {
83 break;
84 }
85 let mut colon_pos: Option<usize> = None;
86 for (i, &byte) in line.iter().enumerate() {
87 if byte == COLON_U8 {
88 colon_pos = Some(i);
89 break;
90 }
91 }
92 if let Some(pos) = colon_pos
93 && pos > 0
94 && pos + 1 < line.len()
95 {
96 let key_bytes: &[u8] = &line[..pos];
97 let value_start: usize = if line.get(pos + 1) == Some(&SPACE_U8) {
98 pos + 2
99 } else {
100 pos + 1
101 };
102 let value_bytes: &[u8] = &line[value_start..];
103 if let (Ok(key_str), Ok(value_str)) = (from_utf8(key_bytes), from_utf8(value_bytes))
104 {
105 headers.insert(
106 key_str.trim().to_ascii_lowercase(),
107 value_str.trim().to_owned(),
108 );
109 }
110 }
111 }
112 let body: ResponseBody = match lines.len() {
113 0 => ResponseBody::new(),
114 1 => {
115 let line: &[u8] = lines.next().unwrap_or(&[]);
116 line.to_vec()
117 }
118 _ => {
119 let lines_slice: &[&[u8]] = lines.as_slice();
120 let total_size: usize = lines_slice
121 .iter()
122 .map(|line: &&[u8]| line.len())
123 .sum::<usize>()
124 + lines_slice.len().saturating_sub(1) * BR_BYTES.len();
125 let mut body: ResponseBody = ResponseBody::with_capacity(total_size);
126 let mut first: bool = true;
127 for line in lines {
128 if !first {
129 body.extend_from_slice(BR_BYTES);
130 }
131 body.extend_from_slice(line);
132 first = false;
133 }
134 body
135 }
136 };
137 HttpResponse {
138 version,
139 status_code,
140 reason_phrase,
141 headers,
142 body,
143 }
144 }
145
146 pub fn is_success(&self) -> bool {
148 (200..300).contains(&self.status_code)
149 }
150
151 pub fn is_redirect(&self) -> bool {
153 (300..400).contains(&self.status_code)
154 }
155
156 pub fn get_header<K: AsRef<str>>(&self, key: K) -> Option<&str> {
158 let normalized = key.as_ref().to_ascii_lowercase();
159 self.headers.get(&normalized).map(String::as_str)
160 }
161
162 pub fn text(&self) -> String {
164 String::from_utf8_lossy(&self.body).into_owned()
165 }
166
167 pub fn bytes(&self) -> &[u8] {
169 &self.body
170 }
171
172 pub fn decode(&self, buffer_size: usize) -> HttpResponse {
176 let flat_headers: HttpResponseHeaders = self.headers.clone();
177 let decoded: ResponseBody = Compress::from(&flat_headers)
178 .decode(&self.body, buffer_size)
179 .into_owned();
180 HttpResponse {
181 version: self.version.clone(),
182 status_code: self.status_code,
183 reason_phrase: self.reason_phrase.clone(),
184 headers: self.headers.clone(),
185 body: decoded,
186 }
187 }
188}