Class: HTTPX::Response::Body

Inherits:
Object
  • Object
show all
Defined in:
lib/httpx/response/body.rb

Overview

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).

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(response, options) ⇒ Body

initialized with the corresponding HTTPX::Response response and HTTPX::Options options.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/httpx/response/body.rb', line 15

def initialize(response, options)
  @response = response
  @headers = response.headers
  @options = options
  @window_size = options.window_size
  @encodings = []
  @length = 0
  @buffer = nil
  @reader = nil
  @state = :idle

  # initialize response encoding
  @encoding = if (enc = response.content_type.charset)
    begin
      Encoding.find(enc)
    rescue ArgumentError
      Encoding::BINARY
    end
  else
    Encoding::BINARY
  end

  initialize_inflaters
end

Instance Attribute Details

#encodingObject (readonly)

the payload encoding (i.e. “utf-8”, “ASCII-8BIT”)



9
10
11
# File 'lib/httpx/response/body.rb', line 9

def encoding
  @encoding
end

#encodingsObject (readonly)

Array of encodings contained in the response “content-encoding” header.



12
13
14
# File 'lib/httpx/response/body.rb', line 12

def encodings
  @encodings
end

Class Method Details

.initialize_inflater_by_encoding(encoding, response, **kwargs) ⇒ Object

:nodoc:



243
244
245
246
247
248
249
250
# File 'lib/httpx/response/body.rb', line 243

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)
  end
end

Instance Method Details

#==(other) ⇒ Object



150
151
152
153
154
155
156
157
158
# File 'lib/httpx/response/body.rb', line 150

def ==(other)
  object_id == other.object_id || begin
    if other.respond_to?(:read)
      _with_same_buffer_pos { FileUtils.compare_stream(@buffer, other) }
    else
      to_s == other.to_s
    end
  end
end

#bytesizeObject

size of the decoded response payload. May differ from “content-length” header if response was encoded over-the-wire.



82
83
84
# File 'lib/httpx/response/body.rb', line 82

def bytesize
  @length
end

#closeObject

closes/cleans the buffer, resets everything



141
142
143
144
145
146
147
148
# File 'lib/httpx/response/body.rb', line 141

def close
  if @buffer
    @buffer.close
    @buffer = nil
  end
  @length = 0
  transition(:closed)
end

#closed?Boolean

Returns:

  • (Boolean)


46
47
48
# File 'lib/httpx/response/body.rb', line 46

def closed?
  @state == :closed
end

#copy_to(dest) ⇒ Object

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"))


128
129
130
131
132
133
134
135
136
137
138
# File 'lib/httpx/response/body.rb', line 128

def copy_to(dest)
  return unless @buffer

  rewind

  if dest.respond_to?(:path) && @buffer.respond_to?(:path)
    FileUtils.mv(@buffer.path, dest.path)
  else
    ::IO.copy_stream(@buffer, dest)
  end
end

#eachObject

yields the payload in chunks.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/httpx/response/body.rb', line 87

def each
  return enum_for(__method__) unless block_given?

  begin
    if @buffer
      rewind
      while (chunk = @buffer.read(@window_size))
        yield(chunk.force_encoding(@encoding))
      end
    end
  ensure
    close
  end
end

#empty?Boolean

whether the payload is empty.

Returns:

  • (Boolean)


119
120
121
# File 'lib/httpx/response/body.rb', line 119

def empty?
  @length.zero?
end

#filenameObject

returns the declared filename in the “contennt-disposition” header, when present.



103
104
105
106
107
# File 'lib/httpx/response/body.rb', line 103

def filename
  return unless @headers.key?("content-disposition")

  Utils.get_filename(@headers["content-disposition"])
end

#initialize_dup(other) ⇒ Object



40
41
42
43
44
# File 'lib/httpx/response/body.rb', line 40

def initialize_dup(other)
  super

  @buffer = other.instance_variable_get(:@buffer).dup
end

#inspectObject

:nocov:



161
162
163
164
165
# File 'lib/httpx/response/body.rb', line 161

def inspect
  "#<HTTPX::Response::Body:#{object_id} " \
    "@state=#{@state} " \
    "@length=#{@length}>"
end

#read(*args) ⇒ Object

reads a chunk from the payload (implementation of the IO reader protocol).



69
70
71
72
73
74
75
76
77
78
# File 'lib/httpx/response/body.rb', line 69

def read(*args)
  return unless @buffer

  unless @reader
    rewind
    @reader = @buffer
  end

  @reader.read(*args)
end

#rewindObject

rewinds the response payload buffer.



169
170
171
172
173
174
175
176
# File 'lib/httpx/response/body.rb', line 169

def rewind
  return unless @buffer

  # in case there's some reading going on
  @reader = nil

  @buffer.rewind
end

#to_sObject Also known as: to_str

returns the full response payload as a string.



110
111
112
113
114
# File 'lib/httpx/response/body.rb', line 110

def to_s
  return "".b unless @buffer

  @buffer.to_s
end

#write(chunk) ⇒ Object

write the response payload chunk into the buffer. Inflates the chunk when required and supported.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/httpx/response/body.rb', line 52

def write(chunk)
  return if @state == :closed

  return 0 if chunk.empty?

  chunk = decode_chunk(chunk)

  size = chunk.bytesize
  @length += size
  transition(:open)
  @buffer.write(chunk)

  @response.emit(:chunk_received, chunk)
  size
end