async_http_client/
response.rs1use std::ascii::AsciiExt;
4use std::cmp;
5use std::fmt;
6use std::ops::Index;
7
8#[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#[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 pub fn status(&self) -> u32 {
52 self.status
53 }
54
55 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 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 pub fn is_informational(&self) -> bool {
77 self.status >= 100 && self.status < 200
78 }
79
80 pub fn is_successful(&self) -> bool {
82 self.status >= 200 && self.status < 300
83 }
84
85 pub fn is_redirection(&self) -> bool {
87 self.status >= 300 && self.status < 400
88 }
89
90 pub fn is_client_error(&self) -> bool {
92 self.status >= 400 && self.status < 500
93 }
94
95 pub fn is_server_error(&self) -> bool {
97 self.status >= 500 && self.status < 600
98 }
99}
100
101pub 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 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}