hyper_sync/server/
request.rs

1//! Server Requests
2//!
3//! These are requests that a `hyper_sync::Server` receives, and include its method,
4//! target URI, headers, and message body.
5use std::io::{self, Read};
6use std::net::SocketAddr;
7use std::time::Duration;
8
9use buffer::BufReader;
10use net::NetworkStream;
11use version::{HttpVersion};
12use method::Method;
13use header::{Headers, ContentLength, TransferEncoding};
14use http::h1::{self, Incoming, HttpReader};
15use http::h1::HttpReader::{SizedReader, ChunkedReader, EmptyReader};
16use uri::RequestUri;
17
18/// A request bundles several parts of an incoming `NetworkStream`, given to a `Handler`.
19pub struct Request<'a, 'b: 'a> {
20    /// The IP address of the remote connection.
21    pub remote_addr: SocketAddr,
22    /// The `Method`, such as `Get`, `Post`, etc.
23    pub method: Method,
24    /// The headers of the incoming request.
25    pub headers: Headers,
26    /// The target request-uri for this request.
27    pub uri: RequestUri,
28    /// The version of HTTP for this request.
29    pub version: HttpVersion,
30    body: HttpReader<&'a mut BufReader<&'b mut NetworkStream>>
31}
32
33
34impl<'a, 'b: 'a> Request<'a, 'b> {
35    /// Create a new Request, reading the StartLine and Headers so they are
36    /// immediately useful.
37    pub fn new(stream: &'a mut BufReader<&'b mut NetworkStream>, addr: SocketAddr)
38        -> ::Result<Request<'a, 'b>> {
39
40        let Incoming { version, subject: (method, uri), headers } = try!(h1::parse_request(stream));
41        debug!("Request Line: {:?} {:?} {:?}", method, uri, version);
42        debug!("{:?}", headers);
43
44        let body = if headers.has::<ContentLength>() {
45            match headers.get::<ContentLength>() {
46                Some(&ContentLength(len)) => SizedReader(stream, len),
47                None => unreachable!()
48            }
49        } else if headers.has::<TransferEncoding>() {
50            todo!("check for Transfer-Encoding: chunked");
51            ChunkedReader(stream, None)
52        } else {
53            EmptyReader(stream)
54        };
55
56        Ok(Request {
57            remote_addr: addr,
58            method: method,
59            uri: uri,
60            headers: headers,
61            version: version,
62            body: body
63        })
64    }
65
66    /// Set the read timeout of the underlying NetworkStream.
67    #[inline]
68    pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
69        self.body.get_ref().get_ref().set_read_timeout(timeout)
70    }
71
72    /// Get a reference to the underlying `NetworkStream`.
73    #[inline]
74    pub fn downcast_ref<T: NetworkStream>(&self) -> Option<&T> {
75        self.body.get_ref().get_ref().downcast_ref()
76    }
77
78    /// Get a reference to the underlying Ssl stream, if connected
79    /// over HTTPS.
80    ///
81    /// This is actually just an alias for `downcast_ref`.
82    #[inline]
83    pub fn ssl<T: NetworkStream>(&self) -> Option<&T> {
84        self.downcast_ref()
85    }
86
87    /// Deconstruct a Request into its constituent parts.
88    #[inline]
89    pub fn deconstruct(self) -> (SocketAddr, Method, Headers,
90                                 RequestUri, HttpVersion,
91                                 HttpReader<&'a mut BufReader<&'b mut NetworkStream>>) {
92        (self.remote_addr, self.method, self.headers,
93         self.uri, self.version, self.body)
94    }
95}
96
97impl<'a, 'b> Read for Request<'a, 'b> {
98    #[inline]
99    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
100        self.body.read(buf)
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use buffer::BufReader;
107    use header::{Host, TransferEncoding, Encoding};
108    use net::NetworkStream;
109    use mock::MockStream;
110    use super::Request;
111
112    use std::io::{self, Read};
113    use std::net::SocketAddr;
114
115    fn sock(s: &str) -> SocketAddr {
116        s.parse().unwrap()
117    }
118
119    fn read_to_string(mut req: Request) -> io::Result<String> {
120        let mut s = String::new();
121        try!(req.read_to_string(&mut s));
122        Ok(s)
123    }
124
125    #[test]
126    fn test_get_empty_body() {
127        let mut mock = MockStream::with_input(b"\
128            GET / HTTP/1.1\r\n\
129            Host: example.domain\r\n\
130            \r\n\
131            I'm a bad request.\r\n\
132        ");
133
134        // FIXME: Use Type ascription
135        let mock: &mut NetworkStream = &mut mock;
136        let mut stream = BufReader::new(mock);
137
138        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
139        assert_eq!(read_to_string(req).unwrap(), "".to_owned());
140    }
141
142    #[test]
143    fn test_get_with_body() {
144        let mut mock = MockStream::with_input(b"\
145            GET / HTTP/1.1\r\n\
146            Host: example.domain\r\n\
147            Content-Length: 19\r\n\
148            \r\n\
149            I'm a good request.\r\n\
150        ");
151
152        // FIXME: Use Type ascription
153        let mock: &mut NetworkStream = &mut mock;
154        let mut stream = BufReader::new(mock);
155
156        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
157        assert_eq!(read_to_string(req).unwrap(), "I'm a good request.".to_owned());
158    }
159
160    #[test]
161    fn test_head_empty_body() {
162        let mut mock = MockStream::with_input(b"\
163            HEAD / HTTP/1.1\r\n\
164            Host: example.domain\r\n\
165            \r\n\
166            I'm a bad request.\r\n\
167        ");
168
169        // FIXME: Use Type ascription
170        let mock: &mut NetworkStream = &mut mock;
171        let mut stream = BufReader::new(mock);
172
173        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
174        assert_eq!(read_to_string(req).unwrap(), "".to_owned());
175    }
176
177    #[test]
178    fn test_post_empty_body() {
179        let mut mock = MockStream::with_input(b"\
180            POST / HTTP/1.1\r\n\
181            Host: example.domain\r\n\
182            \r\n\
183            I'm a bad request.\r\n\
184        ");
185
186        // FIXME: Use Type ascription
187        let mock: &mut NetworkStream = &mut mock;
188        let mut stream = BufReader::new(mock);
189
190        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
191        assert_eq!(read_to_string(req).unwrap(), "".to_owned());
192    }
193
194    #[test]
195    fn test_parse_chunked_request() {
196        let mut mock = MockStream::with_input(b"\
197            POST / HTTP/1.1\r\n\
198            Host: example.domain\r\n\
199            Transfer-Encoding: chunked\r\n\
200            \r\n\
201            1\r\n\
202            q\r\n\
203            2\r\n\
204            we\r\n\
205            2\r\n\
206            rt\r\n\
207            0\r\n\
208            \r\n"
209        );
210
211        // FIXME: Use Type ascription
212        let mock: &mut NetworkStream = &mut mock;
213        let mut stream = BufReader::new(mock);
214
215        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
216
217        // The headers are correct?
218        match req.headers.get::<Host>() {
219            Some(host) => {
220                assert_eq!("example.domain", host.hostname());
221            },
222            None => panic!("Host header expected!"),
223        };
224        match req.headers.get::<TransferEncoding>() {
225            Some(encodings) => {
226                assert_eq!(1, encodings.len());
227                assert_eq!(Encoding::Chunked, encodings[0]);
228            }
229            None => panic!("Transfer-Encoding: chunked expected!"),
230        };
231        // The content is correctly read?
232        assert_eq!(read_to_string(req).unwrap(), "qwert".to_owned());
233    }
234
235    /// Tests that when a chunk size is not a valid radix-16 number, an error
236    /// is returned.
237    #[test]
238    fn test_invalid_chunk_size_not_hex_digit() {
239        let mut mock = MockStream::with_input(b"\
240            POST / HTTP/1.1\r\n\
241            Host: example.domain\r\n\
242            Transfer-Encoding: chunked\r\n\
243            \r\n\
244            X\r\n\
245            1\r\n\
246            0\r\n\
247            \r\n"
248        );
249
250        // FIXME: Use Type ascription
251        let mock: &mut NetworkStream = &mut mock;
252        let mut stream = BufReader::new(mock);
253
254        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
255
256        assert!(read_to_string(req).is_err());
257    }
258
259    /// Tests that when a chunk size contains an invalid extension, an error is
260    /// returned.
261    #[test]
262    fn test_invalid_chunk_size_extension() {
263        let mut mock = MockStream::with_input(b"\
264            POST / HTTP/1.1\r\n\
265            Host: example.domain\r\n\
266            Transfer-Encoding: chunked\r\n\
267            \r\n\
268            1 this is an invalid extension\r\n\
269            1\r\n\
270            0\r\n\
271            \r\n"
272        );
273
274        // FIXME: Use Type ascription
275        let mock: &mut NetworkStream = &mut mock;
276        let mut stream = BufReader::new(mock);
277
278        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
279
280        assert!(read_to_string(req).is_err());
281    }
282
283    /// Tests that when a valid extension that contains a digit is appended to
284    /// the chunk size, the chunk is correctly read.
285    #[test]
286    fn test_chunk_size_with_extension() {
287        let mut mock = MockStream::with_input(b"\
288            POST / HTTP/1.1\r\n\
289            Host: example.domain\r\n\
290            Transfer-Encoding: chunked\r\n\
291            \r\n\
292            1;this is an extension with a digit 1\r\n\
293            1\r\n\
294            0\r\n\
295            \r\n"
296        );
297
298        // FIXME: Use Type ascription
299        let mock: &mut NetworkStream = &mut mock;
300        let mut stream = BufReader::new(mock);
301
302        let req = Request::new(&mut stream, sock("127.0.0.1:80")).unwrap();
303
304        assert_eq!(read_to_string(req).unwrap(), "1".to_owned());
305    }
306
307}