1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
|
require 'zlib'
require 'stringio'
##
# Provides a unified callback interface to decompression libraries.
module EventMachine::HttpDecoders
class DecoderError < StandardError
end
class << self
def accepted_encodings
DECODERS.inject([]) { |r,d| r + d.encoding_names }
end
def decoder_for_encoding(encoding)
DECODERS.each { |d|
return d if d.encoding_names.include? encoding
}
nil
end
end
class Base
def self.encoding_names
name = to_s.split('::').last.downcase
[name]
end
##
# chunk_callback:: [Block] To handle a decompressed chunk
def initialize(&chunk_callback)
@chunk_callback = chunk_callback
end
def <<(compressed)
return unless compressed && compressed.size > 0
decompressed = decompress(compressed)
receive_decompressed decompressed
end
def finalize!
decompressed = finalize
receive_decompressed decompressed
end
private
def receive_decompressed(decompressed)
if decompressed && decompressed.size > 0
@chunk_callback.call(decompressed)
end
end
protected
##
# Must return a part of decompressed
def decompress(compressed)
nil
end
##
# May return last part
def finalize
nil
end
end
class Deflate < Base
def decompress(compressed)
begin
@zstream ||= Zlib::Inflate.new(nil)
@zstream.inflate(compressed)
rescue Zlib::Error
raise DecoderError
end
end
def finalize
return nil unless @zstream
begin
r = @zstream.inflate(nil)
@zstream.close
r
rescue Zlib::Error
raise DecoderError
end
end
end
##
# Oneshot decompressor, due to lack of a streaming Gzip reader
# implementation. We may steal code from Zliby to improve this.
#
# For now, do not put `gzip' or `compressed' in your accept-encoding
# header if you expect much data through the :on_response interface.
class GZip < Base
def self.encoding_names
%w(gzip compressed)
end
def decompress(compressed)
@buf ||= ''
@buf += compressed
nil
end
def finalize
begin
Zlib::GzipReader.new(StringIO.new(@buf.to_s)).read
rescue Zlib::Error
raise DecoderError
end
end
end
DECODERS = [Deflate, GZip]
end
|