Index: lib/net/http/response.rb =================================================================== --- lib/net/http/response.rb (revision 35779) +++ lib/net/http/response.rb (working copy) @@ -275,11 +275,37 @@ class Net::HTTPResponse private + ## + # Checks for a supported Content-Encoding header and returns the Inflate + # wrapper for this response's socket when zlib is present. If the + # Content-Encoding is unsupported or zlib is missing the plain socket is + # returned. + + def inflater # :nodoc: + return @socket unless Net::HTTP::HAVE_ZLIB + + case self['content-encoding'] + when 'deflate', 'gzip', 'x-gzip' then + self.delete 'content-encoding' + + Inflater.new @socket + when 'none', 'identity' then + self.delete 'content-encoding' + + @socket + else + @socket + end + end + def read_body_0(dest) if chunked? - read_chunked dest + read_chunked dest, inflater return end + + @socket = inflater + clen = content_length() if clen @socket.read clen, dest, true # ignore EOF @@ -291,9 +317,11 @@ class Net::HTTPResponse return end @socket.read_all dest + ensure + @socket.finish if Inflater === @socket end - def read_chunked(dest) + def read_chunked(dest, inflater) len = nil total = 0 while true @@ -303,7 +331,7 @@ class Net::HTTPResponse len = hexlen.hex break if len == 0 begin - @socket.read len, dest + inflater.read len, dest ensure total += len @socket.read 2 # \r\n @@ -319,8 +347,8 @@ class Net::HTTPResponse end def procdest(dest, block) - raise ArgumentError, 'both arg and block given for HTTP method' \ - if dest and block + raise ArgumentError, 'both arg and block given for HTTP method' if + dest and block if block Net::ReadAdapter.new(block) else @@ -328,5 +356,52 @@ class Net::HTTPResponse end end + ## + # Inflater is a wrapper around Net::BufferedIO that transparently inflates + # zlib and gzip streams. + + class Inflater # :nodoc: + + ## + # Creates a new Inflater wrapping +socket+ + + def initialize socket + @socket = socket + # zlib with automatic gzip detection + @inflate = Zlib::Inflate.new(32 + Zlib::MAX_WBITS) + end + + ## + # Reads +clen+ bytes from the socket, inflates them, then writes them to + # +dest+. +ignore_eof+ is passed down to Net::BufferedIO#read + + def read clen, dest, ignore_eof = false + temp_dest = '' + + @socket.read clen, temp_dest, ignore_eof + + dest << @inflate.inflate(temp_dest) + end + + ## + # Reads the rest of the socket, inflates it, then writes it to +dest+. + + def read_all dest + temp_dest = '' + + @socket.read_all temp_dest + + dest << @inflate.inflate(temp_dest) + end + + ## + # Finishes the inflate stream. + + def finish + @inflate.finish + end + + end + end Index: lib/net/http.rb =================================================================== --- lib/net/http.rb (revision 35779) +++ lib/net/http.rb (working copy) @@ -590,7 +590,6 @@ module Net #:nodoc: @use_ssl = false @ssl_context = nil @enable_post_connection_check = true - @compression = nil @sspi_enabled = false SSL_IVNAMES.each do |ivname| instance_variable_set ivname, nil @@ -1034,28 +1033,10 @@ module Net #:nodoc: initheader = initheader.merge({ "accept-encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3" }) - @compression = true end end request(Get.new(path, initheader)) {|r| - if r.key?("content-encoding") and @compression - @compression = nil # Clear it till next set. - the_body = r.read_body dest, &block - case r["content-encoding"] - when "gzip" - r.body= Zlib::GzipReader.new(StringIO.new(the_body), encoding: "ASCII-8BIT").read - r.delete("content-encoding") - when "deflate" - r.body= Zlib::Inflate.inflate(the_body); - r.delete("content-encoding") - when "identity" - ; # nothing needed - else - ; # Don't do anything dramatic, unless we need to later - end - else - r.read_body dest, &block - end + r.read_body dest, &block res = r } res Index: test/net/http/test_httpresponse.rb =================================================================== --- test/net/http/test_httpresponse.rb (revision 35779) +++ test/net/http/test_httpresponse.rb (working copy) @@ -4,7 +4,7 @@ require 'stringio' class HTTPResponseTest < Test::Unit::TestCase def test_singleline_header - io = dummy_io(<