22//!
33//! These are responses sent by a `hyper::Server` to clients, after
44//! receiving a request.
5+ use std:: any:: { Any , TypeId } ;
56use std:: marker:: PhantomData ;
7+ use std:: mem;
68use std:: io:: { self , Write } ;
9+ use std:: ptr;
710
811use time:: now_utc;
912
@@ -14,9 +17,10 @@ use status;
1417use net:: { Fresh , Streaming } ;
1518use version;
1619
20+
1721/// The outgoing half for a Tcp connection, created by a `Server` and given to a `Handler`.
1822#[ derive( Debug ) ]
19- pub struct Response < ' a , W = Fresh > {
23+ pub struct Response < ' a , W : Any = Fresh > {
2024 /// The HTTP version of this response.
2125 pub version : version:: HttpVersion ,
2226 // Stream the Response is writing to, not accessible through UnwrittenResponse
@@ -26,10 +30,10 @@ pub struct Response<'a, W = Fresh> {
2630 // The outgoing headers on this response.
2731 headers : header:: Headers ,
2832
29- _marker : PhantomData < W >
33+ _writing : PhantomData < W >
3034}
3135
32- impl < ' a , W > Response < ' a , W > {
36+ impl < ' a , W : Any > Response < ' a , W > {
3337 /// The status of this response.
3438 #[ inline]
3539 pub fn status ( & self ) -> status:: StatusCode { self . status }
@@ -47,31 +51,26 @@ impl<'a, W> Response<'a, W> {
4751 version : version,
4852 body : body,
4953 headers : headers,
50- _marker : PhantomData ,
54+ _writing : PhantomData ,
5155 }
5256 }
5357
5458 /// Deconstruct this Response into its constituent parts.
5559 pub fn deconstruct ( self ) -> ( version:: HttpVersion , HttpWriter < & ' a mut ( Write + ' a ) > ,
5660 status:: StatusCode , header:: Headers ) {
57- ( self . version , self . body , self . status , self . headers )
58- }
59- }
60-
61- impl < ' a > Response < ' a , Fresh > {
62- /// Creates a new Response that can be used to write to a network stream.
63- pub fn new ( stream : & ' a mut ( Write + ' a ) ) -> Response < ' a , Fresh > {
64- Response {
65- status : status:: StatusCode :: Ok ,
66- version : version:: HttpVersion :: Http11 ,
67- headers : header:: Headers :: new ( ) ,
68- body : ThroughWriter ( stream) ,
69- _marker : PhantomData ,
61+ unsafe {
62+ let parts = (
63+ self . version ,
64+ ptr:: read ( & self . body ) ,
65+ self . status ,
66+ ptr:: read ( & self . headers )
67+ ) ;
68+ mem:: forget ( self ) ;
69+ parts
7070 }
7171 }
7272
73- /// Consume this Response<Fresh>, writing the Headers and Status and creating a Response<Streaming>
74- pub fn start ( mut self ) -> io:: Result < Response < ' a , Streaming > > {
73+ fn write_head ( & mut self ) -> io:: Result < Body > {
7574 debug ! ( "writing head: {:?} {:?}" , self . version, self . status) ;
7675 try!( write ! ( & mut self . body, "{} {}{}{}" , self . version, self . status, CR as char , LF as char ) ) ;
7776
@@ -80,19 +79,14 @@ impl<'a> Response<'a, Fresh> {
8079 }
8180
8281
83- let mut chunked = true ;
84- let mut len = 0 ;
82+ let mut body_type = Body :: Chunked ;
8583
86- match self . headers . get :: < header:: ContentLength > ( ) {
87- Some ( cl) => {
88- chunked = false ;
89- len = * * cl;
90- } ,
91- None => ( )
84+ if let Some ( cl) = self . headers . get :: < header:: ContentLength > ( ) {
85+ body_type = Body :: Sized ( * * cl) ;
9286 } ;
9387
9488 // can't do in match above, thanks borrowck
95- if chunked {
89+ if body_type == Body :: Chunked {
9690 let encodings = match self . headers . get_mut :: < header:: TransferEncoding > ( ) {
9791 Some ( & mut header:: TransferEncoding ( ref mut encodings) ) => {
9892 //TODO: check if chunked is already in encodings. use HashSet?
@@ -113,46 +107,208 @@ impl<'a> Response<'a, Fresh> {
113107 try!( write ! ( & mut self . body, "{}" , self . headers) ) ;
114108 try!( write ! ( & mut self . body, "{}" , LINE_ENDING ) ) ;
115109
116- let stream = if chunked {
117- ChunkedWriter ( self . body . into_inner ( ) )
118- } else {
119- SizedWriter ( self . body . into_inner ( ) , len)
110+ Ok ( body_type)
111+ }
112+
113+
114+ }
115+
116+ impl < ' a > Response < ' a , Fresh > {
117+ /// Creates a new Response that can be used to write to a network stream.
118+ #[ inline]
119+ pub fn new ( stream : & ' a mut ( Write + ' a ) ) -> Response < ' a , Fresh > {
120+ Response {
121+ status : status:: StatusCode :: Ok ,
122+ version : version:: HttpVersion :: Http11 ,
123+ headers : header:: Headers :: new ( ) ,
124+ body : ThroughWriter ( stream) ,
125+ _writing : PhantomData ,
126+ }
127+ }
128+
129+ /// Consume this Response<Fresh>, writing the Headers and Status and creating a Response<Streaming>
130+ pub fn start ( mut self ) -> io:: Result < Response < ' a , Streaming > > {
131+ let body_type = try!( self . write_head ( ) ) ;
132+ let ( version, body, status, headers) = self . deconstruct ( ) ;
133+ let stream = match body_type {
134+ Body :: Chunked => ChunkedWriter ( body. into_inner ( ) ) ,
135+ Body :: Sized ( len) => SizedWriter ( body. into_inner ( ) , len)
120136 } ;
121137
122138 // "copy" to change the phantom type
123139 Ok ( Response {
124- version : self . version ,
140+ version : version,
125141 body : stream,
126- status : self . status ,
127- headers : self . headers ,
128- _marker : PhantomData ,
142+ status : status,
143+ headers : headers,
144+ _writing : PhantomData ,
129145 } )
130146 }
131-
132147 /// Get a mutable reference to the status.
133148 #[ inline]
134149 pub fn status_mut ( & mut self ) -> & mut status:: StatusCode { & mut self . status }
135150
136151 /// Get a mutable reference to the Headers.
152+ #[ inline]
137153 pub fn headers_mut ( & mut self ) -> & mut header:: Headers { & mut self . headers }
138154}
139155
156+
140157impl < ' a > Response < ' a , Streaming > {
141158 /// Flushes all writing of a response to the client.
159+ #[ inline]
142160 pub fn end ( self ) -> io:: Result < ( ) > {
143- debug ! ( "ending" ) ;
144- try!( self . body . end ( ) ) ;
161+ trace ! ( "ending" ) ;
162+ let ( _, body, _, _) = self . deconstruct ( ) ;
163+ try!( body. end ( ) ) ;
145164 Ok ( ( ) )
146165 }
147166}
148167
149168impl < ' a > Write for Response < ' a , Streaming > {
169+ #[ inline]
150170 fn write ( & mut self , msg : & [ u8 ] ) -> io:: Result < usize > {
151171 debug ! ( "write {:?} bytes" , msg. len( ) ) ;
152172 self . body . write ( msg)
153173 }
154174
175+ #[ inline]
155176 fn flush ( & mut self ) -> io:: Result < ( ) > {
156177 self . body . flush ( )
157178 }
158179}
180+
181+ #[ derive( PartialEq ) ]
182+ enum Body {
183+ Chunked ,
184+ Sized ( u64 ) ,
185+ }
186+
187+ impl < ' a , T : Any > Drop for Response < ' a , T > {
188+ fn drop ( & mut self ) {
189+ if TypeId :: of :: < T > ( ) == TypeId :: of :: < Fresh > ( ) {
190+ let mut body = match self . write_head ( ) {
191+ Ok ( Body :: Chunked ) => ChunkedWriter ( self . body . get_mut ( ) ) ,
192+ Ok ( Body :: Sized ( len) ) => SizedWriter ( self . body . get_mut ( ) , len) ,
193+ Err ( e) => {
194+ debug ! ( "error dropping request: {:?}" , e) ;
195+ return ;
196+ }
197+ } ;
198+ end ( & mut body) ;
199+ } else {
200+ end ( & mut self . body ) ;
201+ } ;
202+
203+
204+ #[ inline]
205+ fn end < W : Write > ( w : & mut W ) {
206+ match w. write ( & [ ] ) {
207+ Ok ( _) => match w. flush ( ) {
208+ Ok ( _) => debug ! ( "drop successful" ) ,
209+ Err ( e) => debug ! ( "error dropping request: {:?}" , e)
210+ } ,
211+ Err ( e) => debug ! ( "error dropping request: {:?}" , e)
212+ }
213+ }
214+ }
215+ }
216+
217+ #[ cfg( test) ]
218+ mod tests {
219+ use mock:: MockStream ;
220+ use super :: Response ;
221+
222+ macro_rules! lines {
223+ ( $s: ident = $( $line: pat) ,+) => ( {
224+ let s = String :: from_utf8( $s. write) . unwrap( ) ;
225+ let mut lines = s. split_terminator( "\r \n " ) ;
226+
227+ $(
228+ match lines. next( ) {
229+ Some ( $line) => ( ) ,
230+ other => panic!( "line mismatch: {:?} != {:?}" , other, stringify!( $line) )
231+ }
232+ ) +
233+
234+ assert_eq!( lines. next( ) , None ) ;
235+ } )
236+ }
237+
238+ #[ test]
239+ fn test_fresh_start ( ) {
240+ let mut stream = MockStream :: new ( ) ;
241+ {
242+ let res = Response :: new ( & mut stream) ;
243+ res. start ( ) . unwrap ( ) . deconstruct ( ) ;
244+ }
245+
246+ lines ! { stream =
247+ "HTTP/1.1 200 OK" ,
248+ _date,
249+ _transfer_encoding,
250+ ""
251+ }
252+ }
253+
254+ #[ test]
255+ fn test_streaming_end ( ) {
256+ let mut stream = MockStream :: new ( ) ;
257+ {
258+ let res = Response :: new ( & mut stream) ;
259+ res. start ( ) . unwrap ( ) . end ( ) . unwrap ( ) ;
260+ }
261+
262+ lines ! { stream =
263+ "HTTP/1.1 200 OK" ,
264+ _date,
265+ _transfer_encoding,
266+ "" ,
267+ "0" ,
268+ "" // empty zero body
269+ }
270+ }
271+
272+ #[ test]
273+ fn test_fresh_drop ( ) {
274+ use status:: StatusCode ;
275+ let mut stream = MockStream :: new ( ) ;
276+ {
277+ let mut res = Response :: new ( & mut stream) ;
278+ * res. status_mut ( ) = StatusCode :: NotFound ;
279+ }
280+
281+ lines ! { stream =
282+ "HTTP/1.1 404 Not Found" ,
283+ _date,
284+ _transfer_encoding,
285+ "" ,
286+ "0" ,
287+ "" // empty zero body
288+ }
289+ }
290+
291+ #[ test]
292+ fn test_streaming_drop ( ) {
293+ use std:: io:: Write ;
294+ use status:: StatusCode ;
295+ let mut stream = MockStream :: new ( ) ;
296+ {
297+ let mut res = Response :: new ( & mut stream) ;
298+ * res. status_mut ( ) = StatusCode :: NotFound ;
299+ let mut stream = res. start ( ) . unwrap ( ) ;
300+ stream. write_all ( b"foo" ) . unwrap ( ) ;
301+ }
302+
303+ lines ! { stream =
304+ "HTTP/1.1 404 Not Found" ,
305+ _date,
306+ _transfer_encoding,
307+ "" ,
308+ "3" ,
309+ "foo" ,
310+ "0" ,
311+ "" // empty zero body
312+ }
313+ }
314+ }
0 commit comments