11use std:: fmt;
22use std:: io:: { self , Read } ;
33use std:: sync:: { Arc , Mutex } ;
4+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
45
56use hyper:: client:: IntoUrl ;
6- use hyper:: header:: { Headers , ContentType , Location , Referer , UserAgent , Accept } ;
7+ use hyper:: header:: { Headers , ContentType , Location , Referer , UserAgent , Accept , ContentEncoding , Encoding , ContentLength } ;
78use hyper:: method:: Method ;
89use hyper:: status:: StatusCode ;
910use hyper:: version:: HttpVersion ;
@@ -38,10 +39,16 @@ impl Client {
3839 inner : Arc :: new ( ClientRef {
3940 hyper : client,
4041 redirect_policy : Mutex :: new ( RedirectPolicy :: default ( ) ) ,
42+ auto_ungzip : AtomicBool :: new ( true ) ,
4143 } ) ,
4244 } )
4345 }
4446
47+ /// Enable auto gzip decompression by checking the ContentEncoding response header.
48+ pub fn gzip ( & mut self , enable : bool ) {
49+ self . inner . auto_ungzip . store ( enable, Ordering :: Relaxed ) ;
50+ }
51+
4552 /// Set a `RedirectPolicy` for this client.
4653 pub fn redirect ( & mut self , policy : RedirectPolicy ) {
4754 * self . inner . redirect_policy . lock ( ) . unwrap ( ) = policy;
@@ -94,13 +101,15 @@ impl fmt::Debug for Client {
94101 fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
95102 f. debug_struct ( "Client" )
96103 . field ( "redirect_policy" , & self . inner . redirect_policy )
104+ . field ( "auto_ungzip" , & self . inner . auto_ungzip )
97105 . finish ( )
98106 }
99107}
100108
101109struct ClientRef {
102110 hyper : :: hyper:: Client ,
103111 redirect_policy : Mutex < RedirectPolicy > ,
112+ auto_ungzip : AtomicBool ,
104113}
105114
106115fn new_hyper_client ( ) -> :: Result < :: hyper:: Client > {
@@ -268,7 +277,7 @@ impl RequestBuilder {
268277 loc
269278 } else {
270279 return Ok ( Response {
271- inner : res
280+ inner : Decoder :: from_hyper_response ( res, client . auto_ungzip . load ( Ordering :: Relaxed ) )
272281 } ) ;
273282 }
274283 } ;
@@ -282,14 +291,14 @@ impl RequestBuilder {
282291 } else {
283292 debug ! ( "redirect_policy disallowed redirection to '{}'" , loc) ;
284293 return Ok ( Response {
285- inner : res
294+ inner : Decoder :: from_hyper_response ( res, client . auto_ungzip . load ( Ordering :: Relaxed ) )
286295 } )
287296 }
288297 } ,
289298 Err ( e) => {
290299 debug ! ( "Location header had invalid URI: {:?}" , e) ;
291300 return Ok ( Response {
292- inner : res
301+ inner : Decoder :: from_hyper_response ( res, client . auto_ungzip . load ( Ordering :: Relaxed ) )
293302 } )
294303 }
295304 } ;
@@ -299,7 +308,7 @@ impl RequestBuilder {
299308 //TODO: removeSensitiveHeaders(&mut headers, &url);
300309 } else {
301310 return Ok ( Response {
302- inner : res
311+ inner : Decoder :: from_hyper_response ( res, client . auto_ungzip . load ( Ordering :: Relaxed ) )
303312 } ) ;
304313 }
305314 }
@@ -318,26 +327,56 @@ impl fmt::Debug for RequestBuilder {
318327
319328/// A Response to a submitted `Request`.
320329pub struct Response {
321- inner : :: hyper:: client:: Response ,
330+ inner : Decoder ,
331+ }
332+
333+ impl fmt:: Debug for Response {
334+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
335+ return match & self . inner {
336+ & Decoder :: PlainText ( ref hyper_response) => {
337+ f. debug_struct ( "Response" )
338+ . field ( "status" , & hyper_response. status )
339+ . field ( "headers" , & hyper_response. headers )
340+ . field ( "version" , & hyper_response. version )
341+ . finish ( )
342+ } ,
343+ & Decoder :: Gzip { ref status, ref version, ref headers, ..} => {
344+ f. debug_struct ( "Response" )
345+ . field ( "status" , & status)
346+ . field ( "headers" , & headers)
347+ . field ( "version" , & version)
348+ . finish ( )
349+ }
350+ }
351+ }
322352}
323353
324354impl Response {
325355 /// Get the `StatusCode`.
326356 #[ inline]
327357 pub fn status ( & self ) -> & StatusCode {
328- & self . inner . status
358+ match & self . inner {
359+ & Decoder :: PlainText ( ref hyper_response) => & hyper_response. status ,
360+ & Decoder :: Gzip { ref status, ..} => status
361+ }
329362 }
330363
331364 /// Get the `Headers`.
332365 #[ inline]
333366 pub fn headers ( & self ) -> & Headers {
334- & self . inner . headers
367+ match & self . inner {
368+ & Decoder :: PlainText ( ref hyper_response) => & hyper_response. headers ,
369+ & Decoder :: Gzip { ref headers, ..} => headers
370+ }
335371 }
336372
337373 /// Get the `HttpVersion`.
338374 #[ inline]
339375 pub fn version ( & self ) -> & HttpVersion {
340- & self . inner . version
376+ match & self . inner {
377+ & Decoder :: PlainText ( ref hyper_response) => & hyper_response. version ,
378+ & Decoder :: Gzip { ref version, ..} => version
379+ }
341380 }
342381
343382 /// Try and deserialize the response body as JSON.
@@ -347,6 +386,72 @@ impl Response {
347386 }
348387}
349388
389+ enum Decoder {
390+ /// A `PlainText` decoder just returns the response content as is.
391+ PlainText ( :: hyper:: client:: Response ) ,
392+ /// A `Gzip` decoder will uncompress the gziped response content before returning it.
393+ Gzip {
394+ decoder : :: libflate:: gzip:: Decoder < :: hyper:: client:: Response > ,
395+ headers : :: hyper:: header:: Headers ,
396+ version : :: hyper:: version:: HttpVersion ,
397+ status : :: hyper:: status:: StatusCode ,
398+ }
399+ }
400+
401+ impl Decoder {
402+ /// Constructs a Decoder from a hyper request.
403+ ///
404+ /// A decoder is just a wrapper around the hyper request that knows
405+ /// how to decode the content body of the request.
406+ ///
407+ /// Uses the correct variant by inspecting the Content-Encoding header.
408+ fn from_hyper_response ( res : :: hyper:: client:: Response , check_gzip : bool ) -> Self {
409+ if !check_gzip {
410+ return Decoder :: PlainText ( res) ;
411+ }
412+
413+ let mut is_gzip = false ;
414+ match res. headers . get :: < ContentEncoding > ( ) {
415+ Some ( encoding_types) => {
416+ if encoding_types. contains ( & Encoding :: Gzip ) {
417+ is_gzip = true ;
418+ }
419+ if let Some ( content_length) = res. headers . get :: < ContentLength > ( ) {
420+ if content_length. 0 == 0 {
421+ warn ! ( "GZipped response with content-length of 0" ) ;
422+ is_gzip = false ;
423+ }
424+ }
425+ }
426+ _ => { }
427+ }
428+
429+ if is_gzip {
430+ return Decoder :: Gzip {
431+ status : res. status . clone ( ) ,
432+ version : res. version . clone ( ) ,
433+ headers : res. headers . clone ( ) ,
434+ decoder : :: libflate:: gzip:: Decoder :: new ( res) . unwrap ( ) ,
435+ } ;
436+ } else {
437+ return Decoder :: PlainText ( res) ;
438+ }
439+ }
440+ }
441+
442+ impl Read for Decoder {
443+ fn read ( & mut self , buf : & mut [ u8 ] ) -> io:: Result < usize > {
444+ match self {
445+ & mut Decoder :: PlainText ( ref mut hyper_response) => {
446+ hyper_response. read ( buf)
447+ } ,
448+ & mut Decoder :: Gzip { ref mut decoder, ..} => {
449+ decoder. read ( buf)
450+ }
451+ }
452+ }
453+ }
454+
350455/// Read the body of the Response.
351456impl Read for Response {
352457 #[ inline]
@@ -355,16 +460,6 @@ impl Read for Response {
355460 }
356461}
357462
358- impl fmt:: Debug for Response {
359- fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
360- f. debug_struct ( "Response" )
361- . field ( "status" , self . status ( ) )
362- . field ( "headers" , self . headers ( ) )
363- . field ( "version" , self . version ( ) )
364- . finish ( )
365- }
366- }
367-
368463#[ cfg( test) ]
369464mod tests {
370465 use super :: * ;
0 commit comments