async_http_client/
response.rs

1//! Definition of response structure.
2
3use std::ascii::AsciiExt;
4use std::cmp;
5use std::fmt;
6use std::ops::Index;
7
8/// Representation of a header.
9///
10/// For convenience, the header value is trimmed at parsing time (optional spaces are
11/// removed from the beginning and the end of the value).
12#[derive(PartialEq, Eq, Debug)]
13pub struct Header {
14    name: String,
15    value: Option<String>
16}
17
18pub fn new_header<K: Into<String>, V: Into<String>>(name: K, value: V) -> Header {
19    Header {
20        name: name.into(),
21        value: Some(value.into())
22    }
23}
24
25impl fmt::Display for Header {
26    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27        write!(f, "{}: {}", self.name, self.value.as_ref().map(|s| s.as_str()).unwrap_or(""))
28    }
29}
30
31/// Representation of an HTTP response.
32#[derive(PartialEq, Eq, Debug)]
33pub struct HttpResponse {
34    version: (u32, u32),
35    status: u32,
36    headers: Vec<Header>,
37    body: Vec<u8>
38}
39
40pub fn new_response(version: (u32, u32), status: u32, headers: Vec<Header>) -> HttpResponse {
41    HttpResponse {
42        version: version,
43        status: status,
44        headers: headers,
45        body: Vec::new()
46    }
47}
48
49impl HttpResponse {
50    /// Returns the status code of this response.
51    pub fn status(&self) -> u32 {
52        self.status
53    }
54
55    /// Returns true if this response has a header with the given `name`
56    /// that matches the expected `value`.
57    ///
58    /// Comparisons are made in a case-insensitive manner.
59    pub fn is<K: AsRef<str>, V: AsRef<str>>(&self, name: K, expected: V) -> bool {
60        self[name.as_ref()].as_ref().map_or(false, |candidate|
61            candidate.eq_ignore_ascii_case(expected.as_ref()))
62    }
63
64    /// Returns true if this response has a header with the given `name`
65    /// that has a comma-separated list of values, and one of those values
66    /// matches the `expected` value.
67    ///
68    /// Comparisons are made in a case-insensitive manner. Each value of the comma-separated
69    /// list is trimmed before comparison.
70    pub fn has<K: AsRef<str>, V: AsRef<str>>(&self, name: K, expected: V) -> bool {
71        self[name.as_ref()].as_ref().map_or(false, |candidate|
72            candidate.split(',').any(|item| item.trim().eq_ignore_ascii_case(expected.as_ref())))
73    }
74
75    /// Returns true if this response has a 1xx Informational status code.
76    pub fn is_informational(&self) -> bool {
77        self.status >= 100 && self.status < 200
78    }
79
80    /// Returns true if this response has a 2xx Successful status code.
81    pub fn is_successful(&self) -> bool {
82        self.status >= 200 && self.status < 300
83    }
84
85    /// Returns true if this response has a 3xx Redirection status code.
86    pub fn is_redirection(&self) -> bool {
87        self.status >= 300 && self.status < 400
88    }
89
90    /// Returns true if this response has a 4xx Client Error status code.
91    pub fn is_client_error(&self) -> bool {
92        self.status >= 400 && self.status < 500
93    }
94
95    /// Returns true if this response isisis a 5xx Server Error status code.
96    pub fn is_server_error(&self) -> bool {
97        self.status >= 500 && self.status < 600
98    }
99}
100
101/// Appends data to this response's body.
102pub fn append<A: AsRef<[u8]>>(res: &mut HttpResponse, buf: A) {
103    res.body.extend_from_slice(buf.as_ref());
104}
105
106const NONE: &'static Option<String> = &None;
107
108impl<'a> Index<&'a str> for HttpResponse {
109    type Output = Option<String>;
110
111    /// Retrieve the header with the given name.
112    ///
113    /// Comparison is made in a case-insensitive manner.
114    fn index(&self, name: &str) -> &Option<String> {
115        self.headers.iter()
116            .find(|header| name.eq_ignore_ascii_case(&header.name))
117            .map(|header| &header.value).unwrap_or(NONE)
118    }
119}
120
121impl fmt::Display for HttpResponse {
122    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
123        writeln!(f, "HTTP/{}.{} {}", self.version.0, self.version.1, self.status)?;
124        for header in &self.headers {
125            writeln!(f, "{}", header)?;
126        }
127        write!(f, "body: {} bytes = [", self.body.len())?;
128        for byte in &self.body[0 .. cmp::min(self.body.len(), 30)] {
129            write!(f, "{}", *byte as char)?;
130        }
131        writeln!(f, "...]")
132    }
133}