lib/httpx/response/body.rb in httpx-1.0.2 vs lib/httpx/response/body.rb in httpx-1.1.0

- old
+ new

@@ -1,21 +1,29 @@ # frozen_string_literal: true module HTTPX + # Implementation of the HTTP Response body as a buffer which implements the IO writer protocol + # (for buffering the response payload), the IO reader protocol (for consuming the response payload), + # and can be iterated over (via #each, which yields the payload in chunks). class Response::Body - attr_reader :encoding, :encodings + # the payload encoding (i.e. "utf-8", "ASCII-8BIT") + attr_reader :encoding + # Array of encodings contained in the response "content-encoding" header. + attr_reader :encodings + + # initialized with the corresponding HTTPX::Response +response+ and HTTPX::Options +options+. def initialize(response, options) @response = response @headers = response.headers @options = options - @threshold_size = options.body_threshold_size @window_size = options.window_size @encoding = response.content_type.charset || Encoding::BINARY @encodings = [] @length = 0 @buffer = nil + @reader = nil @state = :idle initialize_inflaters end def initialize_dup(other) @@ -26,10 +34,12 @@ def closed? @state == :closed end + # write the response payload +chunk+ into the buffer. Inflates the chunk when required + # and supported. def write(chunk) return if @state == :closed @inflaters.reverse_each do |inflater| chunk = inflater.call(chunk) @@ -42,10 +52,11 @@ @response.emit(:chunk_received, chunk) size end + # reads a chunk from the payload (implementation of the IO reader protocol). def read(*args) return unless @buffer unless @reader rewind @@ -53,14 +64,17 @@ end @reader.read(*args) end + # size of the decoded response payload. May differ from "content-length" header if + # response was encoded over-the-wire. def bytesize @length end + # yields the payload in chunks. def each return enum_for(__method__) unless block_given? begin if @buffer @@ -72,28 +86,36 @@ ensure close end end + # returns the declared filename in the "contennt-disposition" header, when present. def filename return unless @headers.key?("content-disposition") Utils.get_filename(@headers["content-disposition"]) end + # returns the full response payload as a string. def to_s return "".b unless @buffer @buffer.to_s end alias_method :to_str, :to_s + # whether the payload is empty. def empty? @length.zero? end + # copies the payload to +dest+. + # + # body.copy_to("path/to/file") + # body.copy_to(Pathname.new("path/to/file")) + # body.copy_to(File.new("path/to/file")) def copy_to(dest) return unless @buffer rewind @@ -130,10 +152,11 @@ "@state=#{@state} " \ "@length=#{@length}>" end # :nocov: + # rewinds the response payload buffer. def rewind return unless @buffer # in case there's some reading going on @reader = nil @@ -142,10 +165,12 @@ end private def initialize_inflaters + @inflaters = nil + return unless @headers.key?("content-encoding") return unless @options.decompress_response_body @inflaters = @headers.get("content-encoding").filter_map do |encoding| @@ -166,22 +191,22 @@ case nextstate when :open return unless @state == :idle @buffer = Response::Buffer.new( - threshold_size: @threshold_size, + threshold_size: @options.body_threshold_size, bytesize: @length, encoding: @encoding ) when :closed return if @state == :closed end @state = nextstate end - def _with_same_buffer_pos + def _with_same_buffer_pos # :nodoc: return yield unless @buffer && @buffer.respond_to?(:pos) # @type ivar @buffer: StringIO | Tempfile current_pos = @buffer.pos @buffer.rewind @@ -191,10 +216,10 @@ @buffer.pos = current_pos end end class << self - def initialize_inflater_by_encoding(encoding, response, **kwargs) + def initialize_inflater_by_encoding(encoding, response, **kwargs) # :nodoc: case encoding when "gzip" Transcoder::GZIP.decode(response, **kwargs) when "deflate" Transcoder::Deflate.decode(response, **kwargs)