1use std::collections::HashMap;
2use std::fmt::{Display, Error, Formatter};
3
4use super::url::Tuple;
5
6pub struct Response {
22 pub status: ResponseStatus,
24 pub body: String,
26 headers: Option<HashMap<String, String>>,
27}
28
29impl Response {
30 pub fn get_response_headers(&self) -> Option<impl Iterator<Item=(&str, &str)>> {
45 self.headers.as_ref()?;
46 Some(self.headers.as_ref().unwrap().iter().map(|(k, v)| {
47 (k.as_str(), v.as_str())
48 }))
49 }
50
51 pub fn get_status_code(&self) -> Option<u16> {
55 self.status.0.get_code()
56 }
57}
58
59pub fn new_response_from_complete(response: String) -> Response {
60 let lines: Vec<&str> = response.splitn(2, "\r\n\r\n").collect();
61 let heads = (*lines.first().unwrap()).to_string();
62 let head_lines: Vec<&str> = heads.split("\r\n").collect();
63 let (resp_state, headers) = process_head_lines(head_lines);
64 let body = (*lines.last().unwrap()).to_string();
65 Response {
66 status: resp_state,
67 body,
68 headers,
69 }
70}
71
72fn process_head_lines(lines: Vec<&str>) -> (ResponseStatus, Option<HashMap<String, String>>) {
73 let head = *lines.get(0).unwrap();
74 let parts: Vec<&str> = head.split(' ').collect();
75 let status_code = StatusCode::from_code(parts.get(1).unwrap());
76 let reason = parts.get(2).map(|v| (*v).to_string());
77 let response_headers = process_response_headers(&lines[1..]);
78 (ResponseStatus(status_code, reason), response_headers)
79}
80
81fn process_response_headers(lines: &[&str]) -> Option<HashMap<String, String>> {
82 if lines.is_empty() {
83 None
84 } else {
85 let mut headers = HashMap::new();
86 for &line in lines {
87 if line.contains(':') {
88 let line_comp: Tuple<&str> = line.splitn(2, ':').collect();
89 headers.insert((*line_comp.left).to_string(), (*line_comp.right).trim().to_string());
90 } else {
91 continue;
92 }
93 }
94 Some(headers)
95 }
96}
97
98#[derive(Debug, Clone)]
99pub struct ResponseStatus(pub StatusCode, pub Option<String>);
101
102impl Display for ResponseStatus {
103 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
104 if let Some(reason) = self.1.as_ref() {
105 write!(f, "{} - {}", &self.0, reason.as_str())
106 } else {
107 write!(f, "{}", self.0)
108 }
109 }
110}
111
112#[derive(Debug, Copy, Clone)]
113pub enum StatusCode {
120 Informational(u16),
122 Success(u16),
124 Redirection(u16),
126 ClientError(u16),
128 ServerError(u16),
130 Ignore,
131 Failure,
132}
133
134impl Display for StatusCode {
135 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
136 if let Some(code) = self.get_code().as_ref() {
137 write!(f, "HTTP Response Code: {}", *code)
138 } else {
139 write!(f, "HTTP Response Code: ERROR!")
140 }
141 }
142}
143
144impl StatusCode {
145 pub fn get_code(self) -> Option<u16> {
147 match self {
148 StatusCode::Informational(val) => Some(val),
149 StatusCode::ClientError(val) => Some(val),
150 StatusCode::ServerError(val) => Some(val),
151 StatusCode::Success(val) => Some(val),
152 StatusCode::Redirection(val) => Some(val),
153 _ => None,
154 }
155 }
156
157 fn from_code(code: &str) -> Self {
158 let code = code.trim();
159 if code.len() != 3 {
160 return StatusCode::Failure;
161 }
162 let code_num: u16 = code.parse().unwrap();
163 match code_num {
164 100..=199 => StatusCode::Informational(code_num),
165 200..=299 => StatusCode::Success(code_num),
166 300..=399 => StatusCode::Redirection(code_num),
167 400..=499 => StatusCode::ClientError(code_num),
168 500..=599 => StatusCode::ServerError(code_num),
169 _ => StatusCode::Failure,
170 }
171 }
172}