added transcoders for content-encoding; added logic in request body to iteratively apply them; there's still a problem: how to close all buffers in the most logical way

This commit is contained in:
HoneyryderChuck 2017-12-22 15:06:23 +02:00
parent 2f67f0ad15
commit ca96f6dfe1
2 changed files with 150 additions and 51 deletions

View File

@ -1,57 +1,6 @@
# frozen_string_literal: true
module HTTPX
module Transcoder
module GZIP
module_function
def encode(payload)
end
def decode(io)
Zlib::GzipReader.new(io, window_size: 32 + Zlib::MAX_WBITS)
end
end
module Deflate
module_function
class Decoder
def initialize(io)
@io = io
@inflater = Zlib::Inflate.new(32 + Zlib::MAX_WBITS)
@buffer = StringIO.new
end
def rewind
@buffer.rewind
end
def read(*args)
return @buffer.read(*args) if @io.eof?
chunk = @io.read(*args)
inflated_chunk = @inflater.inflate(chunk)
inflated_chunk << @inflater.finish if @io.eof?
@buffer << chunk
inflated_chunk
end
def close
@io.close
@io.unlink if @io.respond_to?(:unlink)
@inflater.close
end
end
def decode(io)
Decoder.new(io)
end
end
register "gzip", GZIP
register "deflate", Deflate
end
module Plugins
module Compression
ACCEPT_ENCODING = %w[gzip deflate].freeze
@ -94,7 +43,154 @@ module HTTPX
buffer
end
end
class CompressEncoder
attr_reader :content_type
def initialize(raw)
@content_type = raw.content_type
@raw = raw.respond_to?(:read) ? raw : StringIO.new(raw.to_s)
@buffer = StringIO.new("".b, File::RDWR)
end
def each(&blk)
return enum_for(__method__) unless block_given?
unless @buffer.size.zero?
@buffer.rewind
return @buffer.each(&blk)
end
compress(&blk)
end
def to_s
compress
@buffer.rewind
@buffer.read
end
def bytesize
compress
@buffer.size
end
def close
# @buffer.close
end
end
module GZIPTranscoder
class Encoder < CompressEncoder
def write(chunk)
@compressed_chunk = chunk
end
private
def compressed_chunk
compressed = @compressed_chunk
compressed
ensure
@compressed_chunk = nil
end
def compress
return unless @buffer.size.zero?
@raw.rewind
begin
gzip = Zlib::GzipWriter.new(self)
while chunk = @raw.read(16_384)
gzip.write(chunk)
gzip.flush
compressed = compressed_chunk
@buffer << compressed
yield compressed if block_given?
end
ensure
gzip.close
end
end
end
module_function
def encode(payload)
Encoder.new(payload)
end
def decode(io)
Zlib::GzipReader.new(io, window_size: 32 + Zlib::MAX_WBITS)
end
end
module DeflateTranscoder
class Encoder < CompressEncoder
private
def compress
return unless @buffer.size.zero?
@raw.rewind
begin
deflater = Zlib::Deflate.new(Zlib::BEST_COMPRESSION,
Zlib::MAX_WBITS,
Zlib::MAX_MEM_LEVEL,
Zlib::HUFFMAN_ONLY)
while chunk = @raw.read(16_384)
compressed = deflater.deflate(chunk)
@buffer << compressed
yield compressed if block_given?
end
last = deflater.finish
@buffer << last
yield last if block_given?
ensure
deflater.close
end
end
end
module_function
class Decoder
def initialize(io)
@io = io
@inflater = Zlib::Inflate.new(32 + Zlib::MAX_WBITS)
@buffer = StringIO.new
end
def rewind
@buffer.rewind
end
def read(*args)
return @buffer.read(*args) if @io.eof?
chunk = @io.read(*args)
inflated_chunk = @inflater.inflate(chunk)
inflated_chunk << @inflater.finish if @io.eof?
@buffer << chunk
inflated_chunk
end
def close
@io.close
@io.unlink if @io.respond_to?(:unlink)
@inflater.close
end
end
def encode(payload)
Encoder.new(payload)
end
def decode(io)
Decoder.new(io)
end
end
Transcoder.register "gzip", GZIPTranscoder
Transcoder.register "deflate", DeflateTranscoder
end
register_plugin :compression, Compression
end
end

View File

@ -102,6 +102,9 @@ module HTTPX
Transcoder.registry("json").encode(options.json)
end
return if @body.nil?
@headers.get("content-encoding").each do |encoding|
@body = Transcoder.registry(encoding).encode(@body)
end
@headers["content-type"] ||= @body.content_type
@headers["content-length"] ||= @body.bytesize unless chunked?
end