22//!
33//! These are requests that a `hyper::Server` receives, and include its method,
44//! target URI, headers, and message body.
5- use std:: old_io:: IoResult ;
5+ use std:: old_io:: { self , IoResult } ;
66use std:: old_io:: net:: ip:: SocketAddr ;
77
88use { HttpResult } ;
@@ -11,7 +11,7 @@ use method::Method::{self, Get, Head};
1111use header:: { Headers , ContentLength , TransferEncoding } ;
1212use http:: { read_request_line} ;
1313use http:: HttpReader ;
14- use http:: HttpReader :: { SizedReader , ChunkedReader , EmptyReader } ;
14+ use http:: HttpReader :: { SizedReader , ChunkedReader } ;
1515use uri:: RequestUri ;
1616
1717/// A request bundles several parts of an incoming `NetworkStream`, given to a `Handler`.
@@ -26,7 +26,7 @@ pub struct Request<'a> {
2626 pub uri : RequestUri ,
2727 /// The version of HTTP for this request.
2828 pub version : HttpVersion ,
29- body : HttpReader < & ' a mut ( Reader + ' a ) >
29+ body : Body < HttpReader < & ' a mut ( Reader + ' a ) > >
3030}
3131
3232
@@ -39,18 +39,19 @@ impl<'a> Request<'a> {
3939 let headers = try!( Headers :: from_raw ( & mut stream) ) ;
4040 debug ! ( "{:?}" , headers) ;
4141
42- let body = if method == Get || method == Head {
43- EmptyReader ( stream)
44- } else if headers. has :: < ContentLength > ( ) {
45- match headers. get :: < ContentLength > ( ) {
46- Some ( & ContentLength ( len) ) => SizedReader ( stream, len) ,
47- None => unreachable ! ( )
48- }
42+ let body = if let Some ( len) = headers. get :: < ContentLength > ( ) {
43+ SizedReader ( stream, * * len)
4944 } else if headers. has :: < TransferEncoding > ( ) {
5045 todo ! ( "check for Transfer-Encoding: chunked" ) ;
5146 ChunkedReader ( stream, None )
5247 } else {
53- EmptyReader ( stream)
48+ SizedReader ( stream, 0 )
49+ } ;
50+
51+ let body = if method == Get || method == Head {
52+ Body :: Empty ( body)
53+ } else {
54+ Body :: NonEmpty ( body)
5455 } ;
5556
5657 Ok ( Request {
@@ -68,13 +69,31 @@ impl<'a> Request<'a> {
6869 RequestUri , HttpVersion ,
6970 HttpReader < & ' a mut ( Reader + ' a ) > , ) {
7071 ( self . remote_addr , self . method , self . headers ,
71- self . uri , self . version , self . body )
72+ self . uri , self . version , self . body . into_inner ( ) )
7273 }
7374}
7475
7576impl < ' a > Reader for Request < ' a > {
77+ #[ inline]
7678 fn read ( & mut self , buf : & mut [ u8 ] ) -> IoResult < usize > {
77- self . body . read ( buf)
79+ match self . body {
80+ Body :: Empty ( ..) => Err ( old_io:: standard_error ( old_io:: EndOfFile ) ) ,
81+ Body :: NonEmpty ( ref mut r) => r. read ( buf)
82+ }
83+ }
84+ }
85+
86+ enum Body < R > {
87+ Empty ( R ) ,
88+ NonEmpty ( R ) ,
89+ }
90+
91+ impl < R > Body < R > {
92+ fn into_inner ( self ) -> R {
93+ match self {
94+ Body :: Empty ( r) => r,
95+ Body :: NonEmpty ( r) => r
96+ }
7897 }
7998}
8099
@@ -95,8 +114,9 @@ mod tests {
95114 let mut stream = MockStream :: with_input ( b"\
96115 GET / HTTP/1.1\r \n \
97116 Host: example.domain\r \n \
117+ Content-Length: 18\r \n \
98118 \r \n \
99- I'm a bad request.\r \n \
119+ I'm a bad request.\
100120 ") ;
101121
102122 let mut req = Request :: new ( & mut stream, sock ( "127.0.0.1:80" ) ) . unwrap ( ) ;
@@ -108,16 +128,17 @@ mod tests {
108128 let mut stream = MockStream :: with_input ( b"\
109129 HEAD / HTTP/1.1\r \n \
110130 Host: example.domain\r \n \
131+ Content-Length: 18\r \n \
111132 \r \n \
112- I'm a bad request.\r \n \
133+ I'm a bad request.\
113134 ") ;
114135
115136 let mut req = Request :: new ( & mut stream, sock ( "127.0.0.1:80" ) ) . unwrap ( ) ;
116137 assert_eq ! ( req. read_to_string( ) , Ok ( "" . to_string( ) ) ) ;
117138 }
118139
119140 #[ test]
120- fn test_post_empty_body ( ) {
141+ fn test_post_body_with_no_content_length ( ) {
121142 let mut stream = MockStream :: with_input ( b"\
122143 POST / HTTP/1.1\r \n \
123144 Host: example.domain\r \n \
@@ -129,6 +150,20 @@ mod tests {
129150 assert_eq ! ( req. read_to_string( ) , Ok ( "" . to_string( ) ) ) ;
130151 }
131152
153+ #[ test]
154+ fn test_unexpected_body_drains_upon_drop ( ) {
155+ let mut stream = MockStream :: with_input ( b"\
156+ GET / HTTP/1.1\r \n \
157+ Host: example.domain\r \n \
158+ Content-Length: 18\r \n \
159+ \r \n \
160+ I'm a bad request.\
161+ ") ;
162+
163+ Request :: new ( & mut stream, sock ( "127.0.0.1:80" ) ) . unwrap ( ) . read_to_string ( ) . unwrap ( ) ;
164+ assert ! ( stream. read. eof( ) ) ;
165+ }
166+
132167 #[ test]
133168 fn test_parse_chunked_request ( ) {
134169 let mut stream = MockStream :: with_input ( b"\
0 commit comments