Class: HTTPX::Response::Body

Inherits:
Object
  • Object
show all
Includes:
_Reader, _ToS, _ToStr
Defined in:
lib/httpx/response/body.rb,
sig/response/body.rbs

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.

Parameters:



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

def initialize(response, options)
  @response = response
  @headers = response.headers
  @options = options
  @window_size = options.window_size
  @max_response_body_size = options.max_response_body_size
  @encodings = []
  @length = 0
  @buffer = @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

#bufferResponse::Buffer? (readonly)

Returns the value of attribute buffer.

Returns:



14
15
16
# File 'lib/httpx/response/body.rb', line 14

def buffer
  @buffer
end

#encodingEncoding (readonly)

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

Returns:

  • (Encoding)


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

def encoding
  @encoding
end

#encodingsArray[String] (readonly)

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

Returns:

  • (Array[String])


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 & Transcoder::_Inflater

:nodoc:

Parameters:

  • encoding (Encoding, String)
  • response (Response)
  • bytesize: (Integer)

Returns:



236
237
238
239
240
241
242
243
# File 'lib/httpx/response/body.rb', line 236

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



155
156
157
158
159
160
161
162
# File 'lib/httpx/response/body.rb', line 155

def ==(other)
  super || case other
           when Response::Body
             @buffer == other.buffer
           else
             @buffer = other
           end
end

#bytesizeInteger

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

Returns:

  • (Integer)


85
86
87
# File 'lib/httpx/response/body.rb', line 85

def bytesize
  @length
end

#closevoid

This method returns an undefined value.

closes/cleans the buffer, resets everything



146
147
148
149
150
151
152
153
# File 'lib/httpx/response/body.rb', line 146

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

#closed?Boolean

Returns:

  • (Boolean)


49
50
51
# File 'lib/httpx/response/body.rb', line 49

def closed?
  @state == :closed
end

#copy_to(dest) ⇒ void

This method returns an undefined value.

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

Parameters:

  • destination (String, File, _Writer)


131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/httpx/response/body.rb', line 131

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
ensure
  close
end

#decode_chunk(chunk) ⇒ String

passes the chunk through all inflaters to decode it.

Parameters:

  • chunk (String)

Returns:

  • (String)


207
208
209
210
211
212
213
214
215
# File 'lib/httpx/response/body.rb', line 207

def decode_chunk(chunk)
  @inflaters.reverse_each do |inflater|
    chunk = inflater.call(chunk)
  end if @inflaters

  @length += chunk.bytesize

  chunk
end

#eachvoid #eachEnumerable[String]

yields the payload in chunks.

Overloads:

  • #eachvoid

    This method returns an undefined value.

  • #eachEnumerable[String]

    Returns:

    • (Enumerable[String])

Yields:

Yield Parameters:

  • arg0 (String)

Yield Returns:

  • (void)


90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/httpx/response/body.rb', line 90

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)


122
123
124
# File 'lib/httpx/response/body.rb', line 122

def empty?
  @length.zero?
end

#filenameString?

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

Returns:

  • (String, nil)


106
107
108
109
110
# File 'lib/httpx/response/body.rb', line 106

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

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

#initialize_dup(other) ⇒ Object



43
44
45
46
47
# File 'lib/httpx/response/body.rb', line 43

def initialize_dup(other)
  super

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

#initialize_inflatersvoid

This method returns an undefined value.

prepares inflaters for the advertised encodings in "content-encoding" header.



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/httpx/response/body.rb', line 185

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|
    next if encoding == "identity"

    inflater = self.class.initialize_inflater_by_encoding(encoding, @response)

    # do not uncompress if there is no decoder available. In fact, we can't reliably
    # continue decompressing beyond that, so ignore.
    break unless inflater

    @encodings << encoding
    inflater
  end
end

#inspectObject

simplecov:disable



165
166
167
168
169
# File 'lib/httpx/response/body.rb', line 165

def inspect
  "#<#{self.class}:#{object_id} " \
    "@state=#{@state} " \
    "@length=#{@length}>"
end

#read(*args) ⇒ Object

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



72
73
74
75
76
77
78
79
80
81
# File 'lib/httpx/response/body.rb', line 72

def read(*args)
  return unless @buffer

  unless @reader
    rewind
    @reader = @buffer
  end

  @reader.read(*args)
end

#rewindvoid

This method returns an undefined value.

rewinds the response payload buffer.



173
174
175
176
177
178
179
180
# File 'lib/httpx/response/body.rb', line 173

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.



113
114
115
116
117
# File 'lib/httpx/response/body.rb', line 113

def to_s
  return "".b unless @buffer

  @buffer.to_s
end

#transition(nextstate) ⇒ void

This method returns an undefined value.

tries transitioning the body STM to the nextstate.

Parameters:

  • nextstate (Symbol)


218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/httpx/response/body.rb', line 218

def transition(nextstate)
  case nextstate
  when :open
    return unless @state == :idle

    @buffer = Response::Buffer.new(
      threshold_size: @options.body_threshold_size,
      bytesize: @length,
      encoding: @encoding
    )
  when :closed
    return if @state == :closed
  end

  @state = nextstate
end

#write(chunk) ⇒ Integer?

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

Parameters:

  • chunk (String)

Returns:

  • (Integer, nil)

Raises:



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

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

  return 0 if chunk.empty?

  chunk = decode_chunk(chunk)

  raise Error, "maximum response body size exceeded" if @max_response_body_size < @length

  transition(:open)
  @buffer.write(chunk)

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