lib/em-http/decoders.rb in em-http-request-1.0.3 vs lib/em-http/decoders.rb in em-http-request-1.1.0

- old
+ new

@@ -89,55 +89,164 @@ raise DecoderError end end end - class GZip < Base - def self.encoding_names - %w(gzip compressed) + ## + # Partial implementation of RFC 1952 to extract the deflate stream from a gzip file + class GZipHeader + def initialize + @state = :begin + @data = "" + @pos = 0 end - def decompress(compressed) - @buf ||= LazyStringIO.new - @buf << compressed + def finished? + @state == :finish + end - # Zlib::GzipReader loads input in 2048 byte chunks - if @buf.size > 2048 - @gzip ||= Zlib::GzipReader.new @buf - @gzip.readline + def read(n, buffer) + if (@pos + n) <= @data.size + buffer << @data[@pos..(@pos + n - 1)] + @pos += n + return true + else + return false end end - def finalize - begin - @gzip ||= Zlib::GzipReader.new @buf - @gzip.read - rescue Zlib::Error - raise DecoderError + def readbyte + if (@pos + 1) <= @data.size + @pos += 1 + @data.getbyte(@pos - 1) end end - class LazyStringIO - def initialize(string="") - @stream = string + def eof? + @pos >= @data.size + end + + def extract_stream(compressed) + @data << compressed + pos = @pos + + while !eof? && !finished? + buffer = "" + + case @state + when :begin + break if !read(10, buffer) + + if buffer.getbyte(0) != 0x1f || buffer.getbyte(1) != 0x8b + raise DecoderError.new("magic header not found") + end + + if buffer.getbyte(2) != 0x08 + raise DecoderError.new("unknown compression method") + end + + @flags = buffer.getbyte(3) + if (@flags & 0xe0).nonzero? + raise DecoderError.new("unknown header flags set") + end + + # We don't care about these values, I'm leaving the code for reference + # @time = buffer[4..7].unpack("V")[0] # little-endian uint32 + # @extra_flags = buffer.getbyte(8) + # @os = buffer.getbyte(9) + + @state = :extra_length + + when :extra_length + if (@flags & 0x04).nonzero? + break if !read(2, buffer) + @extra_length = buffer.unpack("v")[0] # little-endian uint16 + @state = :extra + else + @state = :extra + end + + when :extra + if (@flags & 0x04).nonzero? + break if read(@extra_length, buffer) + @state = :name + else + @state = :name + end + + when :name + if (@flags & 0x08).nonzero? + while !(buffer = readbyte).nil? + if buffer == 0 + @state = :comment + break + end + end + else + @state = :comment + end + + when :comment + if (@flags & 0x10).nonzero? + while !(buffer = readbyte).nil? + if buffer == 0 + @state = :hcrc + break + end + end + else + @state = :hcrc + end + + when :hcrc + if (@flags & 0x02).nonzero? + break if !read(2, buffer) + @state = :finish + else + @state = :finish + end + end end - def <<(string) - @stream << string + if finished? + compressed[(@pos - pos)..-1] + else + "" end + end + end - def read(length=nil, buffer=nil) - buffer ||= "" - length ||= 0 - buffer << @stream[0..(length-1)] - @stream = @stream[length..-1] - buffer + class GZip < Base + def self.encoding_names + %w(gzip compressed) + end + + def decompress(compressed) + @header ||= GZipHeader.new + if !@header.finished? + compressed = @header.extract_stream(compressed) end - def size - @stream.size + @zstream ||= Zlib::Inflate.new(-Zlib::MAX_WBITS) + @zstream.inflate(compressed) + rescue Zlib::Error + raise DecoderError + end + + def finalize + if @zstream + if !@zstream.finished? + r = @zstream.finish + end + @zstream.close + r + else + nil end + rescue Zlib::Error + raise DecoderError end + end DECODERS = [Deflate, GZip] end