thruster_core/
request.rs

1use std::{io, str, fmt};
2use std::collections::HashMap;
3use serde;
4use bytes::BytesMut;
5use serde_json;
6use smallvec::SmallVec;
7
8use httparse;
9use crate::httplib;
10
11
12pub trait RequestWithParams {
13    fn set_params(&mut self, _: HashMap<String, String>);
14}
15
16///
17/// The request object is the default request object provied by Thruster. If a different
18/// server is used, such as Hyper, then you'll need to reference that server's "request"
19/// documentation instead.
20///
21#[derive(Default)]
22pub struct Request {
23    body: Slice,
24    method: Slice,
25    path: Slice,
26    version: u8,
27    pub headers: SmallVec<[(Slice, Slice); 8]>,
28    data: BytesMut,
29    pub params: HashMap<String, String>
30}
31
32type Slice = (usize, usize);
33
34impl Request {
35    ///
36    /// Create a new, blank, request.
37    ///
38    pub fn new() -> Self {
39      Request  {
40        body: (0,0),
41        method: (0,0),
42        path: (0,0),
43        version: 0,
44        headers: SmallVec::new(),
45        data: BytesMut::new(),
46        params: HashMap::new(),
47      }
48    }
49
50    ///
51    /// Get the raw pointer to the byte array of the request
52    ///
53    pub fn raw_body(&self) -> &[u8] {
54        self.slice(&self.body)
55    }
56
57    ///
58    /// Get the body as a utf8 encoded string
59    ///
60    pub fn body(&self) -> &str {
61        str::from_utf8(self.slice(&self.body)).unwrap()
62    }
63
64    ///
65    /// Get the method as a string
66    ///
67    pub fn method(&self) -> &str {
68        str::from_utf8(self.slice(&self.method)).unwrap()
69    }
70
71    ///
72    /// Get the path as a string ("/some/path")
73    ///
74    pub fn path(&self) -> &str {
75        str::from_utf8(self.slice(&self.path)).unwrap()
76    }
77
78    ///
79    /// Get the HTTP version
80    ///
81    pub fn version(&self) -> u8 {
82        self.version
83    }
84
85    ///
86    /// Get an HashMap of the provided headers. The HashMap is lazily computed, so
87    /// avoid this method unless you need to access headers.
88    ///
89    pub fn headers(&self) -> HashMap<String, String> {
90        let mut header_map = HashMap::new();
91
92        for slice_pair in self.headers.iter() {
93            header_map.insert(
94                str::from_utf8(self.slice(&slice_pair.0)).unwrap().to_owned().to_lowercase(),
95                str::from_utf8(self.slice(&slice_pair.1)).unwrap().to_owned()
96            );
97        }
98
99        header_map
100    }
101
102    ///
103    /// Automatically apply a serde deserialization to the body
104    ///
105    pub fn body_as<'a, T>(&self, body: &'a str) -> serde_json::Result<T>
106        where T: serde::de::Deserialize<'a>
107    {
108        serde_json::from_str(body)
109    }
110
111    ///
112    /// Fetch a piece of the raw body
113    ///
114    fn slice(&self, slice: &Slice) -> &[u8] {
115        &self.data[slice.0..slice.1]
116    }
117
118    ///
119    /// Fetch the params from the route, e.g. The route "/some/:key" when applied to an incoming
120    /// request for "/some/value" will populate `params` with `{ key: "value" }`
121    ///
122    pub fn params(&self) -> &HashMap<String, String> {
123        &self.params
124    }
125}
126
127impl RequestWithParams for Request {
128    fn set_params(&mut self, params: HashMap<String, String>) {
129        self.params = params;
130    }
131}
132
133impl fmt::Debug for Request {
134    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135        write!(f, "<HTTP Request {} {}>", self.method(), self.path())
136    }
137}
138
139
140pub fn decode(buf: &mut BytesMut) -> io::Result<Option<Request>> {
141    // TODO: we should grow this headers array if parsing fails and asks
142    //       for more headers
143    let (method, path, version, headers, amt, body_len) = {
144        let mut headers = [httparse::EMPTY_HEADER; 32];
145        let mut r = httparse::Request::new(&mut headers);
146        let mut body_len: usize = 0;
147        let mut header_vec = SmallVec::new();
148        let status = r.parse(buf).map_err(|e| {
149            let msg = format!("failed to parse http request: {:?}", e);
150            eprintln!("msg: {}", msg);
151            io::Error::new(io::ErrorKind::Other, msg)
152        })?;
153        let amt = match status {
154            httparse::Status::Complete(amt) => amt,
155            httparse::Status::Partial => {
156                return Ok(None)
157            },
158        };
159        let toslice = |a: &[u8]| {
160            let start = a.as_ptr() as usize - buf.as_ptr() as usize;
161            assert!(start < buf.len());
162            (start, start + a.len())
163        };
164        for header in r.headers.iter() {
165            let header_name = httplib::header::CONTENT_LENGTH;
166
167            if header.name == header_name {
168                let value = str::from_utf8(header.value).unwrap_or("0");
169                body_len = value.parse::<usize>().unwrap_or(0);
170            }
171
172            header_vec.push((toslice(header.name.as_bytes()), toslice(header.value)));
173        }
174
175        (toslice(r.method.unwrap().as_bytes()),
176         toslice(r.path.unwrap().as_bytes()),
177         r.version.unwrap(),
178         header_vec,
179         amt,
180         body_len)
181    };
182
183    if amt + body_len != buf.len() {
184        Ok(None)
185    } else {
186        Ok(Request {
187            method,
188            path,
189            version,
190            headers,
191            data: buf.split_to(amt + body_len),
192            body: (amt, amt + body_len),
193            params: HashMap::new()
194        }.into())
195    }
196}