2
2
//!
3
3
//! These are responses sent by a `hyper::Server` to clients, after
4
4
//! receiving a request.
5
+ use std:: any:: { Any , TypeId } ;
5
6
use std:: marker:: PhantomData ;
7
+ use std:: mem;
6
8
use std:: io:: { self , Write } ;
9
+ use std:: ptr;
7
10
8
11
use time:: now_utc;
9
12
@@ -14,9 +17,10 @@ use status;
14
17
use net:: { Fresh , Streaming } ;
15
18
use version;
16
19
20
+
17
21
/// The outgoing half for a Tcp connection, created by a `Server` and given to a `Handler`.
18
22
#[ derive( Debug ) ]
19
- pub struct Response < ' a , W = Fresh > {
23
+ pub struct Response < ' a , W : Any = Fresh > {
20
24
/// The HTTP version of this response.
21
25
pub version : version:: HttpVersion ,
22
26
// Stream the Response is writing to, not accessible through UnwrittenResponse
@@ -26,10 +30,10 @@ pub struct Response<'a, W = Fresh> {
26
30
// The outgoing headers on this response.
27
31
headers : header:: Headers ,
28
32
29
- _marker : PhantomData < W >
33
+ _writing : PhantomData < W >
30
34
}
31
35
32
- impl < ' a , W > Response < ' a , W > {
36
+ impl < ' a , W : Any > Response < ' a , W > {
33
37
/// The status of this response.
34
38
#[ inline]
35
39
pub fn status ( & self ) -> status:: StatusCode { self . status }
@@ -47,31 +51,26 @@ impl<'a, W> Response<'a, W> {
47
51
version : version,
48
52
body : body,
49
53
headers : headers,
50
- _marker : PhantomData ,
54
+ _writing : PhantomData ,
51
55
}
52
56
}
53
57
54
58
/// Deconstruct this Response into its constituent parts.
55
59
pub fn deconstruct ( self ) -> ( version:: HttpVersion , HttpWriter < & ' a mut ( Write + ' a ) > ,
56
60
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
70
70
}
71
71
}
72
72
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 > {
75
74
debug ! ( "writing head: {:?} {:?}" , self . version, self . status) ;
76
75
try!( write ! ( & mut self . body, "{} {}{}{}" , self . version, self . status, CR as char , LF as char ) ) ;
77
76
@@ -80,19 +79,14 @@ impl<'a> Response<'a, Fresh> {
80
79
}
81
80
82
81
83
- let mut chunked = true ;
84
- let mut len = 0 ;
82
+ let mut body_type = Body :: Chunked ;
85
83
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) ;
92
86
} ;
93
87
94
88
// can't do in match above, thanks borrowck
95
- if chunked {
89
+ if body_type == Body :: Chunked {
96
90
let encodings = match self . headers . get_mut :: < header:: TransferEncoding > ( ) {
97
91
Some ( & mut header:: TransferEncoding ( ref mut encodings) ) => {
98
92
//TODO: check if chunked is already in encodings. use HashSet?
@@ -113,46 +107,208 @@ impl<'a> Response<'a, Fresh> {
113
107
try!( write ! ( & mut self . body, "{}" , self . headers) ) ;
114
108
try!( write ! ( & mut self . body, "{}" , LINE_ENDING ) ) ;
115
109
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)
120
136
} ;
121
137
122
138
// "copy" to change the phantom type
123
139
Ok ( Response {
124
- version : self . version ,
140
+ version : version,
125
141
body : stream,
126
- status : self . status ,
127
- headers : self . headers ,
128
- _marker : PhantomData ,
142
+ status : status,
143
+ headers : headers,
144
+ _writing : PhantomData ,
129
145
} )
130
146
}
131
-
132
147
/// Get a mutable reference to the status.
133
148
#[ inline]
134
149
pub fn status_mut ( & mut self ) -> & mut status:: StatusCode { & mut self . status }
135
150
136
151
/// Get a mutable reference to the Headers.
152
+ #[ inline]
137
153
pub fn headers_mut ( & mut self ) -> & mut header:: Headers { & mut self . headers }
138
154
}
139
155
156
+
140
157
impl < ' a > Response < ' a , Streaming > {
141
158
/// Flushes all writing of a response to the client.
159
+ #[ inline]
142
160
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 ( ) ) ;
145
164
Ok ( ( ) )
146
165
}
147
166
}
148
167
149
168
impl < ' a > Write for Response < ' a , Streaming > {
169
+ #[ inline]
150
170
fn write ( & mut self , msg : & [ u8 ] ) -> io:: Result < usize > {
151
171
debug ! ( "write {:?} bytes" , msg. len( ) ) ;
152
172
self . body . write ( msg)
153
173
}
154
174
175
+ #[ inline]
155
176
fn flush ( & mut self ) -> io:: Result < ( ) > {
156
177
self . body . flush ( )
157
178
}
158
179
}
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