Skip to content

Instantly share code, notes, and snippets.

@Teshootub7
Created November 17, 2010 01:20
Show Gist options
  • Save Teshootub7/702845 to your computer and use it in GitHub Desktop.
Save Teshootub7/702845 to your computer and use it in GitHub Desktop.
Patch to add gzip support for httpclient
diff --git a/lib/httpclient.rb b/lib/httpclient.rb
index 11a4da4..475fa51 100644
--- a/lib/httpclient.rb
+++ b/lib/httpclient.rb
@@ -317,6 +317,8 @@ class HTTPClient
# An array of response HTTP String (not a HTTP message body) which is used
# for loopback test. See test/* to see how to use it.
attr_proxy(:test_loopback_http_response)
+ # Decompress a compressed (with gzip or deflate) content body transparently. false by default.
+ attr_proxy(:transparent_gzip_decompression, true)
# Default extheader for PROPFIND request.
PROPFIND_DEFAULT_EXTHEADER = { 'Depth' => '0' }
diff --git a/lib/httpclient/session.rb b/lib/httpclient/session.rb
index 16ab037..ac8ef59 100644
--- a/lib/httpclient/session.rb
+++ b/lib/httpclient/session.rb
@@ -13,6 +13,7 @@
require 'socket'
require 'thread'
require 'stringio'
+require 'zlib'
require 'httpclient/timeout'
require 'httpclient/ssl_config'
@@ -106,6 +107,8 @@ class HTTPClient
attr_reader :test_loopback_http_response
+ attr_accessor :transparent_gzip_decompression
+
def initialize(client)
@client = client
@proxy = client.proxy
@@ -128,6 +131,8 @@ class HTTPClient
@ssl_config = nil
@test_loopback_http_response = []
+ @transparent_gzip_decompression = false
+
@sess_pool = []
@sess_pool_mutex = Mutex.new
end
@@ -185,6 +190,7 @@ class HTTPClient
sess.ssl_config = @ssl_config
sess.debug_dev = @debug_dev
sess.test_loopback_http_response = @test_loopback_http_response
+ sess.transparent_gzip_decompression = @transparent_gzip_decompression
end
sess
end
@@ -482,6 +488,8 @@ class HTTPClient
attr_reader :ssl_peer_cert
attr_accessor :test_loopback_http_response
+ attr_accessor :transparent_gzip_decompression
+
def initialize(client, dest, agent_name, from)
@client = client
@dest = dest
@@ -515,6 +523,8 @@ class HTTPClient
@socket = nil
@readbuf = nil
+
+ @transparent_gzip_decompression = false
end
# Send a request to the server
@@ -586,6 +596,20 @@ class HTTPClient
begin
read_header if @state == :META
return nil if @state != :DATA
+ if @gzipped and @transparent_gzip_decompression
+ # zlib itself has a functionality to decompress gzip stream.
+ # - zlib 1.2.5 Manual
+ # http://www.zlib.net/manual.html#Advanced
+ # > windowBits can also be greater than 15 for optional gzip decoding. Add 32 to
+ # > windowBits to enable zlib and gzip decoding with automatic header detection,
+ # > or add 16 to decode only the gzip format
+ inflate_stream = Zlib::Inflate.new( Zlib::MAX_WBITS + 32 )
+ original_block = block
+ block = Proc.new do
+ |buf|
+ original_block.call( inflate_stream.inflate( buf ) )
+ end
+ end
if @chunked
read_body_chunked(&block)
elsif @content_length
@@ -621,6 +645,9 @@ class HTTPClient
if @from
req.header.set('From', @from)
end
+ if @transparent_gzip_decompression
+ req.header.set('Accept-Encoding', 'gzip,deflate')
+ end
req.header.set('Date', Time.now.httpdate)
end
@@ -724,6 +751,7 @@ class HTTPClient
def read_header
@content_length = nil
@chunked = false
+ @gzipped = false
@chunk_length = 0
parse_header
# Header of the request has been parsed.
@@ -798,6 +826,9 @@ class HTTPClient
key = key.downcase
if key == 'content-length'
@content_length = value.to_i
+ elsif key == 'content-encoding' and ( value.downcase == 'gzip' or
+ value.downcase == 'x-gzip' or value.downcase == 'deflate' )
+ @gzipped = true
elsif key == 'transfer-encoding' and value.downcase == 'chunked'
@chunked = true
@chunk_length = 0
diff --git a/test/test_httpclient.rb b/test/test_httpclient.rb
index 94129c2..26286b2 100644
--- a/test/test_httpclient.rb
+++ b/test/test_httpclient.rb
@@ -472,6 +472,17 @@ EOS
assert(called)
end
+ def test_get_gzipped_content
+ @client.transparent_gzip_decompression = false
+ assert_not_equal('hello', @client.get_content(@url + 'compressed?enc=gzip'))
+ assert_equal("\x1f\x8b\x08\x00\x1a\x96\xe0\x4c\x00\x03\xcb\x48\xcd\xc9\xc9\x07\x00\x86\xa6\x10\x36\x05\x00\x00\x00",
+ @client.get_content(@url + 'compressed?enc=gzip'))
+ @client.transparent_gzip_decompression = true
+ assert_equal('hello', @client.get_content(@url + 'compressed?enc=gzip'))
+ assert_equal('hello', @client.get_content(@url + 'compressed?enc=deflate'))
+ @client.transparent_gzip_decompression = false
+ end
+
def test_get_content_with_block
@client.get_content(@url + 'hello') do |str|
assert_equal('hello', str)
@@ -1119,7 +1130,7 @@ private
:AccessLog => [],
:DocumentRoot => File.dirname(File.expand_path(__FILE__))
)
- [:hello, :sleep, :servlet_redirect, :redirect1, :redirect2, :redirect3, :redirect_self, :relative_redirect, :chunked, :largebody, :status].each do |sym|
+ [:hello, :sleep, :servlet_redirect, :redirect1, :redirect2, :redirect3, :redirect_self, :relative_redirect, :chunked, :largebody, :status, :compressed].each do |sym|
@server.mount(
"/#{sym}",
WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc)
@@ -1236,6 +1247,16 @@ private
res.body = "a" * 1000 * 1000
end
+ def do_compressed(req, res)
+ if req.query['enc'] == 'gzip'
+ res['content-encoding'] = 'gzip'
+ res.body = "\x1f\x8b\x08\x00\x1a\x96\xe0\x4c\x00\x03\xcb\x48\xcd\xc9\xc9\x07\x00\x86\xa6\x10\x36\x05\x00\x00\x00"
+ elsif req.query['enc'] == 'deflate'
+ res['content-encoding'] = 'deflate'
+ res.body = "\x78\x9c\xcb\x48\xcd\xc9\xc9\x07\x00\x06\x2c\x02\x15"
+ end
+ end
+
def do_status(req, res)
res.status = req.query['status'].to_i
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment