Class: HTTPX::Request::Body

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

Overview

Implementation of the HTTP Request body as a delegator which iterates (responds to each) payload chunks.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(h, options, **params) ⇒ Body

inits the instance with the request headers, options and params, which contain the payload definition. it wraps the given body with the appropriate encoder on initialization.

..., json: { foo: "bar" }) #=> json encoder
..., form: { foo: "bar" }) #=> form urlencoded encoder
..., form: { foo: Pathname.open("path/to/file") }) #=> multipart urlencoded encoder
..., form: { foo: File.open("path/to/file") }) #=> multipart urlencoded encoder
..., form: { body: "bla") }) #=> raw data encoder


28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/httpx/request/body.rb', line 28

def initialize(h, options, **params)
  @headers = h
  @body = self.class.initialize_body(params)
  @options = options.merge(params)

  if @body
    if @options.compress_request_body && @headers.key?("content-encoding")

      @headers.get("content-encoding").each do |encoding|
        @body = self.class.initialize_deflater_body(@body, encoding)
      end
    end

    @headers["content-type"] ||= @body.content_type
    @headers["content-length"] = @body.bytesize unless unbounded_body?
  end

  super(@body)
end

Instance Attribute Details

#optionsObject

Returns the value of attribute options.



18
19
20
# File 'lib/httpx/request/body.rb', line 18

def options
  @options
end

Class Method Details

.initialize_body(params) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/httpx/request/body.rb', line 125

def initialize_body(params)
  if (body = params.delete(:body))
    # @type var body: bodyIO
    Transcoder::Body.encode(body)
  elsif (form = params.delete(:form))
    # @type var form: Transcoder::urlencoded_input
    Transcoder::Form.encode(form)
  elsif (json = params.delete(:json))
    # @type var body: _ToJson
    Transcoder::JSON.encode(json)
  end
end

.initialize_deflater_body(body, encoding) ⇒ Object

returns the body wrapped with the correct deflater accordinng to the given encodisng.



139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/httpx/request/body.rb', line 139

def initialize_deflater_body(body, encoding)
  case encoding
  when "gzip"
    Transcoder::GZIP.encode(body)
  when "deflate"
    Transcoder::Deflate.encode(body)
  when "identity"
    body
  else
    body
  end
end

.new(_, options, body: nil, **params) ⇒ Object



7
8
9
10
11
12
13
14
15
# File 'lib/httpx/request/body.rb', line 7

def new(_, options, body: nil, **params)
  if body.is_a?(self)
    # request derives its options from body
    body.options = options.merge(params)
    return body
  end

  super
end

Instance Method Details

#bytesizeObject

returns the @body payload size in bytes.



87
88
89
90
91
# File 'lib/httpx/request/body.rb', line 87

def bytesize
  return 0 if @body.nil?

  @body.bytesize
end

#chunk!Object

sets the chunked transfer encoding header.



113
114
115
# File 'lib/httpx/request/body.rb', line 113

def chunk!
  @headers.add("transfer-encoding", "chunked")
end

#chunked?Boolean

returns whether the chunked transfer encoding header is set.

Returns:

  • (Boolean)


108
109
110
# File 'lib/httpx/request/body.rb', line 108

def chunked?
  @headers["transfer-encoding"] == "chunked"
end

#closeObject



67
68
69
# File 'lib/httpx/request/body.rb', line 67

def close
  @body.close if @body.respond_to?(:close)
end

#each(&block) ⇒ Object

consumes and yields the request payload in chunks.



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

def each(&block)
  return enum_for(__method__) unless block
  return if @body.nil?

  body = stream(@body)
  if body.respond_to?(:read)
    while (chunk = body.read(16_384))
      block.call(chunk)
    end
    # TODO: use copy_stream once bug is resolved: https://bugs.ruby-lang.org/issues/21131
    # ::IO.copy_stream(body, ProcIO.new(block))
  elsif body.respond_to?(:each)
    body.each(&block)
  else
    block[body.to_s]
  end
end

#empty?Boolean

return true if the body has been fully drained (or does nnot exist).

Returns:

  • (Boolean)


79
80
81
82
83
84
# File 'lib/httpx/request/body.rb', line 79

def empty?
  return true if @body.nil?
  return false if chunked?

  @body.bytesize.zero?
end

#inspectObject

:nocov:



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

def inspect
  "#<#{self.class}:#{object_id} " \
    "#{unbounded_body? ? "stream" : "@bytesize=#{bytesize}"}>"
end

#rewindObject

if the @body is rewindable, it rewinnds it.



72
73
74
75
76
# File 'lib/httpx/request/body.rb', line 72

def rewind
  return if empty?

  @body.rewind if @body.respond_to?(:rewind)
end

#stream(body) ⇒ Object

sets the body to yield using chunked trannsfer encoding format.



94
95
96
97
98
# File 'lib/httpx/request/body.rb', line 94

def stream(body)
  return body unless chunked?

  Transcoder::Chunker.encode(body.enum_for(:each))
end

#unbounded_body?Boolean

returns whether the body yields infinitely.

Returns:

  • (Boolean)


101
102
103
104
105
# File 'lib/httpx/request/body.rb', line 101

def unbounded_body?
  return @unbounded_body if defined?(@unbounded_body)

  @unbounded_body = !@body.nil? && (chunked? || @body.bytesize == Float::INFINITY)
end