nano_get/
response.rs

1use std::collections::HashMap;
2use std::fmt::{Display, Error, Formatter};
3
4use super::url::Tuple;
5
6/// This is the HTTP Reponse Object.
7///
8/// Represents the Response from executing a HTTP `Request`.
9///
10/// This allows inspection of the HTTP Status Code & Reason and HTTP Response Body.
11///
12/// ## Example
13/// ```rust
14/// use nano_get::Response;
15/// let mut request = nano_get::Request::default_get_request("http://example.com/").unwrap();
16/// request.add_header("test", "value testing");
17/// let response: Response = request.execute().unwrap();
18/// println!("Status: {}", response.status);
19/// println!("Body: {}", response.body);
20/// ```
21pub struct Response {
22    /// The status of the Response.
23    pub status: ResponseStatus,
24    /// The body of the Response.
25    pub body: String,
26    headers: Option<HashMap<String, String>>,
27}
28
29impl Response {
30    /// Get an iterator of the Headers in the Response.
31    ///
32    /// ## Example
33    ///
34    /// ```rust
35    /// use nano_get::Response;
36    ///
37    /// let mut request = nano_get::Request::default_get_request("http://example.com/").unwrap();
38    /// request.add_header("test", "value testing");
39    /// let response = request.execute().unwrap();
40    /// for (k, v) in response.get_response_headers().unwrap() {
41    ///     println!("{}, {}", k, v);
42    /// }
43    /// ```
44    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    /// Returns the status code of the Response as an unsigned 16-bit Integer (u16).
52    ///
53    /// Provided as a convenience. This can be got through the embedded `ResponseStatus` also.
54    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)]
99/// Represents the status of the Response. This includes HTTP Status Code & Reason Phrase as per [RFC-2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1).
100pub 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)]
113/// Represents the HTTP Status Codes.
114///
115/// Based on the general categories of the Response [RFC-2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1).
116/// [Wikipedia](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) article for the same.
117///
118/// The `Ignore` and `Failure` are for Internal purposes.
119pub enum StatusCode {
120    /// Represents status codes in the 1xx range.
121    Informational(u16),
122    /// Represents status codes in the 2xx range. Generally the successful responses.
123    Success(u16),
124    /// Represents status codes in the 3xx range.
125    Redirection(u16),
126    /// Represents status codes in the 4xx range.
127    ClientError(u16),
128    /// Represents status codes in the 5xx range.
129    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    /// Extracts the actual numeric status code (like 200, 404, etc.).
146    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}