httpx 1.2.6 → 1.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -2
  3. data/doc/release_notes/1_3_0.md +18 -0
  4. data/doc/release_notes/1_3_1.md +17 -0
  5. data/doc/release_notes/1_3_2.md +6 -0
  6. data/doc/release_notes/1_3_3.md +5 -0
  7. data/doc/release_notes/1_3_4.md +6 -0
  8. data/doc/release_notes/1_4_0.md +43 -0
  9. data/doc/release_notes/1_4_1.md +19 -0
  10. data/doc/release_notes/1_4_2.md +20 -0
  11. data/doc/release_notes/1_4_3.md +11 -0
  12. data/doc/release_notes/1_4_4.md +14 -0
  13. data/lib/httpx/adapters/datadog.rb +56 -80
  14. data/lib/httpx/adapters/faraday.rb +5 -2
  15. data/lib/httpx/adapters/webmock.rb +24 -8
  16. data/lib/httpx/callbacks.rb +2 -7
  17. data/lib/httpx/chainable.rb +3 -1
  18. data/lib/httpx/connection/http1.rb +11 -7
  19. data/lib/httpx/connection/http2.rb +57 -34
  20. data/lib/httpx/connection.rb +270 -71
  21. data/lib/httpx/errors.rb +15 -4
  22. data/lib/httpx/io/ssl.rb +6 -3
  23. data/lib/httpx/io/tcp.rb +1 -1
  24. data/lib/httpx/io/unix.rb +1 -1
  25. data/lib/httpx/loggable.rb +17 -10
  26. data/lib/httpx/options.rb +30 -23
  27. data/lib/httpx/plugins/aws_sdk_authentication.rb +3 -0
  28. data/lib/httpx/plugins/aws_sigv4.rb +36 -17
  29. data/lib/httpx/plugins/callbacks.rb +13 -2
  30. data/lib/httpx/plugins/circuit_breaker.rb +11 -5
  31. data/lib/httpx/plugins/content_digest.rb +202 -0
  32. data/lib/httpx/plugins/cookies.rb +9 -6
  33. data/lib/httpx/plugins/digest_auth.rb +3 -0
  34. data/lib/httpx/plugins/expect.rb +10 -4
  35. data/lib/httpx/plugins/follow_redirects.rb +68 -33
  36. data/lib/httpx/plugins/grpc/grpc_encoding.rb +2 -0
  37. data/lib/httpx/plugins/grpc.rb +2 -2
  38. data/lib/httpx/plugins/h2c.rb +23 -20
  39. data/lib/httpx/plugins/internal_telemetry.rb +48 -1
  40. data/lib/httpx/plugins/oauth.rb +1 -1
  41. data/lib/httpx/plugins/persistent.rb +16 -0
  42. data/lib/httpx/plugins/proxy/http.rb +19 -16
  43. data/lib/httpx/plugins/proxy/socks4.rb +1 -1
  44. data/lib/httpx/plugins/proxy/socks5.rb +1 -1
  45. data/lib/httpx/plugins/proxy.rb +96 -85
  46. data/lib/httpx/plugins/retries.rb +28 -10
  47. data/lib/httpx/plugins/ssrf_filter.rb +4 -1
  48. data/lib/httpx/plugins/stream.rb +42 -18
  49. data/lib/httpx/plugins/upgrade.rb +5 -10
  50. data/lib/httpx/plugins/webdav.rb +6 -0
  51. data/lib/httpx/plugins/xml.rb +76 -0
  52. data/lib/httpx/pool.rb +73 -244
  53. data/lib/httpx/request/body.rb +50 -55
  54. data/lib/httpx/request.rb +77 -14
  55. data/lib/httpx/resolver/https.rb +17 -20
  56. data/lib/httpx/resolver/multi.rb +34 -16
  57. data/lib/httpx/resolver/native.rb +140 -61
  58. data/lib/httpx/resolver/resolver.rb +64 -19
  59. data/lib/httpx/resolver/system.rb +32 -16
  60. data/lib/httpx/resolver.rb +21 -14
  61. data/lib/httpx/response/body.rb +12 -1
  62. data/lib/httpx/response.rb +16 -9
  63. data/lib/httpx/selector.rb +170 -91
  64. data/lib/httpx/session.rb +282 -139
  65. data/lib/httpx/timers.rb +17 -2
  66. data/lib/httpx/transcoder/body.rb +15 -29
  67. data/lib/httpx/transcoder/form.rb +2 -0
  68. data/lib/httpx/transcoder/gzip.rb +0 -3
  69. data/lib/httpx/transcoder/json.rb +16 -2
  70. data/lib/httpx/transcoder/multipart/encoder.rb +11 -2
  71. data/lib/httpx/transcoder/multipart/part.rb +1 -1
  72. data/lib/httpx/transcoder/utils/deflater.rb +7 -4
  73. data/lib/httpx/transcoder.rb +0 -1
  74. data/lib/httpx/version.rb +1 -1
  75. data/lib/httpx.rb +20 -21
  76. data/sig/callbacks.rbs +2 -3
  77. data/sig/chainable.rbs +6 -2
  78. data/sig/connection/http1.rbs +2 -2
  79. data/sig/connection/http2.rbs +22 -18
  80. data/sig/connection.rbs +40 -9
  81. data/sig/errors.rbs +9 -3
  82. data/sig/httpx.rbs +3 -3
  83. data/sig/io/tcp.rbs +1 -1
  84. data/sig/io/unix.rbs +1 -1
  85. data/sig/loggable.rbs +4 -2
  86. data/sig/options.rbs +8 -13
  87. data/sig/plugins/aws_sigv4.rbs +8 -2
  88. data/sig/plugins/content_digest.rbs +51 -0
  89. data/sig/plugins/cookies/cookie.rbs +9 -0
  90. data/sig/plugins/follow_redirects.rbs +1 -1
  91. data/sig/plugins/grpc/call.rbs +4 -0
  92. data/sig/plugins/persistent.rbs +4 -1
  93. data/sig/plugins/proxy/http.rbs +3 -0
  94. data/sig/plugins/proxy/socks5.rbs +11 -3
  95. data/sig/plugins/proxy.rbs +18 -9
  96. data/sig/plugins/push_promise.rbs +6 -3
  97. data/sig/plugins/rate_limiter.rbs +2 -0
  98. data/sig/plugins/retries.rbs +1 -1
  99. data/sig/plugins/ssrf_filter.rbs +26 -0
  100. data/sig/plugins/stream.rbs +3 -0
  101. data/sig/plugins/webdav.rbs +23 -0
  102. data/sig/plugins/xml.rbs +37 -0
  103. data/sig/pool.rbs +27 -33
  104. data/sig/request/body.rbs +4 -10
  105. data/sig/request.rbs +14 -1
  106. data/sig/resolver/multi.rbs +26 -1
  107. data/sig/resolver/native.rbs +6 -3
  108. data/sig/resolver/resolver.rbs +22 -3
  109. data/sig/resolver.rbs +5 -1
  110. data/sig/response/body.rbs +2 -2
  111. data/sig/response/buffer.rbs +2 -2
  112. data/sig/response.rbs +9 -4
  113. data/sig/selector.rbs +31 -4
  114. data/sig/session.rbs +54 -20
  115. data/sig/timers.rbs +15 -4
  116. data/sig/transcoder/body.rbs +2 -4
  117. data/sig/transcoder/chunker.rbs +1 -1
  118. data/sig/transcoder/deflate.rbs +1 -0
  119. data/sig/transcoder/form.rbs +8 -0
  120. data/sig/transcoder/gzip.rbs +4 -1
  121. data/sig/transcoder/json.rbs +1 -1
  122. data/sig/transcoder/multipart.rbs +6 -4
  123. data/sig/transcoder/utils/body_reader.rbs +3 -3
  124. data/sig/transcoder/utils/deflater.rbs +2 -3
  125. metadata +32 -14
  126. data/lib/httpx/session2.rb +0 -23
  127. data/lib/httpx/transcoder/utils/inflater.rb +0 -19
  128. data/lib/httpx/transcoder/xml.rb +0 -52
  129. data/sig/transcoder/utils/inflater.rbs +0 -12
  130. data/sig/transcoder/xml.rbs +0 -22
data/lib/httpx/pool.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "forwardable"
4
3
  require "httpx/selector"
5
4
  require "httpx/connection"
6
5
  require "httpx/resolver"
@@ -8,110 +7,31 @@ require "httpx/resolver"
8
7
  module HTTPX
9
8
  class Pool
10
9
  using ArrayExtensions::FilterMap
11
- extend Forwardable
10
+ using URIExtensions
12
11
 
13
- def_delegator :@timers, :after
12
+ POOL_TIMEOUT = 5
14
13
 
15
- def initialize
16
- @resolvers = {}
17
- @timers = Timers.new
18
- @selector = Selector.new
19
- @connections = []
20
- end
21
-
22
- def wrap
23
- connections = @connections
14
+ # Sets up the connection pool with the given +options+, which can be the following:
15
+ #
16
+ # :max_connections_per_origin :: the maximum number of connections held in the pool pointing to a given origin.
17
+ # :pool_timeout :: the number of seconds to wait for a connection to a given origin (before raising HTTPX::PoolTimeoutError)
18
+ #
19
+ def initialize(options)
20
+ @max_connections_per_origin = options.fetch(:max_connections_per_origin, Float::INFINITY)
21
+ @pool_timeout = options.fetch(:pool_timeout, POOL_TIMEOUT)
22
+ @resolvers = Hash.new { |hs, resolver_type| hs[resolver_type] = [] }
23
+ @resolver_mtx = Thread::Mutex.new
24
24
  @connections = []
25
-
26
- begin
27
- yield self
28
- ensure
29
- @connections.unshift(*connections)
30
- end
31
- end
32
-
33
- def empty?
34
- @connections.empty?
35
- end
36
-
37
- def next_tick
38
- catch(:jump_tick) do
39
- timeout = next_timeout
40
- if timeout && timeout.negative?
41
- @timers.fire
42
- throw(:jump_tick)
43
- end
44
-
45
- begin
46
- @selector.select(timeout, &:call)
47
- @timers.fire
48
- rescue TimeoutError => e
49
- @timers.fire(e)
50
- end
51
- end
52
- rescue StandardError => e
53
- @connections.each do |connection|
54
- connection.emit(:error, e)
55
- end
56
- rescue Exception # rubocop:disable Lint/RescueException
57
- @connections.each(&:force_reset)
58
- raise
59
- end
60
-
61
- def close(connections = @connections)
62
- return if connections.empty?
63
-
64
- connections = connections.reject(&:inflight?)
65
- connections.each(&:terminate)
66
- next_tick until connections.none? { |c| c.state != :idle && @connections.include?(c) }
67
-
68
- # close resolvers
69
- outstanding_connections = @connections
70
- resolver_connections = @resolvers.each_value.flat_map(&:connections).compact
71
- outstanding_connections -= resolver_connections
72
-
73
- return unless outstanding_connections.empty?
74
-
75
- @resolvers.each_value do |resolver|
76
- resolver.close unless resolver.closed?
77
- end
78
- # for https resolver
79
- resolver_connections.each(&:terminate)
80
- next_tick until resolver_connections.none? { |c| c.state != :idle && @connections.include?(c) }
81
- end
82
-
83
- def init_connection(connection, _options)
84
- connection.timers = @timers
85
- connection.on(:activate) do
86
- select_connection(connection)
87
- end
88
- connection.on(:exhausted) do
89
- case connection.state
90
- when :closed
91
- connection.idling
92
- @connections << connection
93
- select_connection(connection)
94
- when :closing
95
- connection.once(:close) do
96
- connection.idling
97
- @connections << connection
98
- select_connection(connection)
99
- end
100
- end
101
- end
102
- connection.on(:close) do
103
- unregister_connection(connection)
104
- end
105
- connection.on(:terminate) do
106
- unregister_connection(connection, true)
107
- end
108
- resolve_connection(connection) unless connection.family
25
+ @connection_mtx = Thread::Mutex.new
26
+ @origin_counters = Hash.new(0)
27
+ @origin_conds = Hash.new { |hs, orig| hs[orig] = ConditionVariable.new }
109
28
  end
110
29
 
111
- def deactivate(connections)
112
- connections.each do |connection|
113
- connection.deactivate
114
- deselect_connection(connection) if connection.state == :inactive
30
+ def pop_connection
31
+ @connection_mtx.synchronize do
32
+ conn = @connections.shift
33
+ @origin_conds.delete(conn.origin) if conn && (@origin_counters[conn.origin.to_s] -= 1).zero?
34
+ conn
115
35
  end
116
36
  end
117
37
 
@@ -119,185 +39,94 @@ module HTTPX
119
39
  # Many hostnames are reachable through the same IP, so we try to
120
40
  # maximize pipelining by opening as few connections as possible.
121
41
  #
122
- def find_connection(uri, options)
123
- conn = @connections.find do |connection|
124
- connection.match?(uri, options)
125
- end
42
+ def checkout_connection(uri, options)
43
+ return checkout_new_connection(uri, options) if options.io
126
44
 
127
- return unless conn
45
+ @connection_mtx.synchronize do
46
+ acquire_connection(uri, options) || begin
47
+ if @origin_counters[uri.origin] == @max_connections_per_origin
128
48
 
129
- case conn.state
130
- when :closed
131
- conn.idling
132
- select_connection(conn)
133
- when :closing
134
- conn.once(:close) do
135
- conn.idling
136
- select_connection(conn)
137
- end
138
- end
49
+ @origin_conds[uri.origin].wait(@connection_mtx, @pool_timeout)
139
50
 
140
- conn
141
- end
142
-
143
- private
144
-
145
- def resolve_connection(connection)
146
- @connections << connection unless @connections.include?(connection)
147
-
148
- if connection.addresses || connection.open?
149
- #
150
- # there are two cases in which we want to activate initialization of
151
- # connection immediately:
152
- #
153
- # 1. when the connection already has addresses, i.e. it doesn't need to
154
- # resolve a name (not the same as name being an IP, yet)
155
- # 2. when the connection is initialized with an external already open IO.
156
- #
157
- connection.once(:connect_error, &connection.method(:handle_error))
158
- on_resolver_connection(connection)
159
- return
160
- end
51
+ return acquire_connection(uri, options) || raise(PoolTimeoutError.new(uri.origin, @pool_timeout))
52
+ end
161
53
 
162
- find_resolver_for(connection) do |resolver|
163
- resolver << try_clone_connection(connection, resolver.family)
164
- next if resolver.empty?
54
+ @origin_counters[uri.origin] += 1
165
55
 
166
- select_connection(resolver)
56
+ checkout_new_connection(uri, options)
57
+ end
167
58
  end
168
59
  end
169
60
 
170
- def try_clone_connection(connection, family)
171
- connection.family ||= family
172
-
173
- return connection if connection.family == family
174
-
175
- new_connection = connection.class.new(connection.origin, connection.options)
176
- new_connection.family = family
61
+ def checkin_connection(connection)
62
+ return if connection.options.io
177
63
 
178
- connection.once(:tcp_open) { new_connection.force_reset }
179
- connection.once(:connect_error) do |err|
180
- if new_connection.connecting?
181
- new_connection.merge(connection)
182
- connection.emit(:cloned, new_connection)
183
- connection.force_reset
184
- else
185
- connection.__send__(:handle_error, err)
186
- end
187
- end
64
+ @connection_mtx.synchronize do
65
+ @connections << connection
188
66
 
189
- new_connection.once(:tcp_open) do |new_conn|
190
- if new_conn != connection
191
- new_conn.merge(connection)
192
- connection.force_reset
193
- end
194
- end
195
- new_connection.once(:connect_error) do |err|
196
- if connection.connecting?
197
- # main connection has the requests
198
- connection.merge(new_connection)
199
- new_connection.emit(:cloned, connection)
200
- new_connection.force_reset
201
- else
202
- new_connection.__send__(:handle_error, err)
203
- end
67
+ @origin_conds[connection.origin.to_s].signal
204
68
  end
205
-
206
- init_connection(new_connection, connection.options)
207
- new_connection
208
69
  end
209
70
 
210
- def on_resolver_connection(connection)
211
- @connections << connection unless @connections.include?(connection)
212
- found_connection = @connections.find do |ch|
213
- ch != connection && ch.mergeable?(connection)
214
- end
215
- return register_connection(connection) unless found_connection
71
+ def checkout_mergeable_connection(connection)
72
+ return if connection.options.io
216
73
 
217
- if found_connection.open?
218
- coalesce_connections(found_connection, connection)
219
- throw(:coalesced, found_connection) unless @connections.include?(connection)
220
- else
221
- found_connection.once(:open) do
222
- coalesce_connections(found_connection, connection)
74
+ @connection_mtx.synchronize do
75
+ idx = @connections.find_index do |ch|
76
+ ch != connection && ch.mergeable?(connection)
223
77
  end
78
+ @connections.delete_at(idx) if idx
224
79
  end
225
80
  end
226
81
 
227
- def on_resolver_error(connection, error)
228
- return connection.emit(:connect_error, error) if connection.connecting? && connection.callbacks_for?(:connect_error)
229
-
230
- connection.emit(:error, error)
82
+ def reset_resolvers
83
+ @resolver_mtx.synchronize { @resolvers.clear }
231
84
  end
232
85
 
233
- def on_resolver_close(resolver)
234
- resolver_type = resolver.class
235
- return if resolver.closed?
86
+ def checkout_resolver(options)
87
+ resolver_type = options.resolver_class
88
+ resolver_type = Resolver.resolver_for(resolver_type)
236
89
 
237
- @resolvers.delete(resolver_type)
90
+ @resolver_mtx.synchronize do
91
+ resolvers = @resolvers[resolver_type]
238
92
 
239
- deselect_connection(resolver)
240
- resolver.close unless resolver.closed?
93
+ idx = resolvers.find_index do |res|
94
+ res.options == options
95
+ end
96
+ resolvers.delete_at(idx) if idx
97
+ end || checkout_new_resolver(resolver_type, options)
241
98
  end
242
99
 
243
- def register_connection(connection)
244
- select_connection(connection)
245
- end
100
+ def checkin_resolver(resolver)
101
+ @resolver_mtx.synchronize do
102
+ resolvers = @resolvers[resolver.class]
246
103
 
247
- def unregister_connection(connection, cleanup = !connection.used?)
248
- @connections.delete(connection) if cleanup
249
- deselect_connection(connection)
250
- end
104
+ resolver = resolver.multi
251
105
 
252
- def select_connection(connection)
253
- @selector.register(connection)
106
+ resolvers << resolver unless resolvers.include?(resolver)
107
+ end
254
108
  end
255
109
 
256
- def deselect_connection(connection)
257
- @selector.deregister(connection)
258
- end
110
+ private
259
111
 
260
- def coalesce_connections(conn1, conn2)
261
- return register_connection(conn2) unless conn1.coalescable?(conn2)
112
+ def acquire_connection(uri, options)
113
+ idx = @connections.find_index do |connection|
114
+ connection.match?(uri, options)
115
+ end
262
116
 
263
- conn2.emit(:tcp_open, conn1)
264
- conn1.merge(conn2)
265
- @connections.delete(conn2)
117
+ @connections.delete_at(idx) if idx
266
118
  end
267
119
 
268
- def next_timeout
269
- [
270
- @timers.wait_interval,
271
- *@resolvers.values.reject(&:closed?).filter_map(&:timeout),
272
- *@connections.filter_map(&:timeout),
273
- ].compact.min
120
+ def checkout_new_connection(uri, options)
121
+ options.connection_class.new(uri, options)
274
122
  end
275
123
 
276
- def find_resolver_for(connection)
277
- connection_options = connection.options
278
- resolver_type = connection_options.resolver_class
279
- resolver_type = Resolver.resolver_for(resolver_type)
280
-
281
- @resolvers[resolver_type] ||= begin
282
- resolver_manager = if resolver_type.multi?
283
- Resolver::Multi.new(resolver_type, connection_options)
284
- else
285
- resolver_type.new(connection_options)
286
- end
287
- resolver_manager.on(:resolve, &method(:on_resolver_connection))
288
- resolver_manager.on(:error, &method(:on_resolver_error))
289
- resolver_manager.on(:close, &method(:on_resolver_close))
290
- resolver_manager
291
- end
292
-
293
- manager = @resolvers[resolver_type]
294
-
295
- (manager.is_a?(Resolver::Multi) && manager.early_resolve(connection)) || manager.resolvers.each do |resolver|
296
- resolver.pool = self
297
- yield resolver
124
+ def checkout_new_resolver(resolver_type, options)
125
+ if resolver_type.multi?
126
+ Resolver::Multi.new(resolver_type, options)
127
+ else
128
+ resolver_type.new(options)
298
129
  end
299
-
300
- manager
301
130
  end
302
131
  end
303
132
  end
@@ -4,30 +4,44 @@ module HTTPX
4
4
  # Implementation of the HTTP Request body as a delegator which iterates (responds to +each+) payload chunks.
5
5
  class Request::Body < SimpleDelegator
6
6
  class << self
7
- def new(_, options)
8
- return options.body if options.body.is_a?(self)
7
+ def new(_, options, body: nil, **params)
8
+ if body.is_a?(self)
9
+ # request derives its options from body
10
+ body.options = options.merge(params)
11
+ return body
12
+ end
9
13
 
10
14
  super
11
15
  end
12
16
  end
13
17
 
14
- # inits the instance with the request +headers+ and +options+, which contain the payload definition.
15
- def initialize(headers, options)
16
- @headers = headers
18
+ attr_accessor :options
17
19
 
18
- # forego compression in the Range request case
19
- if @headers.key?("range")
20
- @headers.delete("accept-encoding")
21
- else
22
- @headers["accept-encoding"] ||= options.supported_compression_formats
23
- end
20
+ # inits the instance with the request +headers+, +options+ and +params+, which contain the payload definition.
21
+ # it wraps the given body with the appropriate encoder on initialization.
22
+ #
23
+ # ..., json: { foo: "bar" }) #=> json encoder
24
+ # ..., form: { foo: "bar" }) #=> form urlencoded encoder
25
+ # ..., form: { foo: Pathname.open("path/to/file") }) #=> multipart urlencoded encoder
26
+ # ..., form: { foo: File.open("path/to/file") }) #=> multipart urlencoded encoder
27
+ # ..., form: { body: "bla") }) #=> raw data encoder
28
+ def initialize(h, options, **params)
29
+ @headers = h
30
+ @body = self.class.initialize_body(params)
31
+ @options = options.merge(params)
24
32
 
25
- initialize_body(options)
33
+ if @body
34
+ if @options.compress_request_body && @headers.key?("content-encoding")
26
35
 
27
- return if @body.nil?
36
+ @headers.get("content-encoding").each do |encoding|
37
+ @body = self.class.initialize_deflater_body(@body, encoding)
38
+ end
39
+ end
40
+
41
+ @headers["content-type"] ||= @body.content_type
42
+ @headers["content-length"] = @body.bytesize unless unbounded_body?
43
+ end
28
44
 
29
- @headers["content-type"] ||= @body.content_type
30
- @headers["content-length"] = @body.bytesize unless unbounded_body?
31
45
  super(@body)
32
46
  end
33
47
 
@@ -38,7 +52,11 @@ module HTTPX
38
52
 
39
53
  body = stream(@body)
40
54
  if body.respond_to?(:read)
41
- ::IO.copy_stream(body, ProcIO.new(block))
55
+ while (chunk = body.read(16_384))
56
+ block.call(chunk)
57
+ end
58
+ # TODO: use copy_stream once bug is resolved: https://bugs.ruby-lang.org/issues/21131
59
+ # ::IO.copy_stream(body, ProcIO.new(block))
42
60
  elsif body.respond_to?(:each)
43
61
  body.each(&block)
44
62
  else
@@ -46,6 +64,10 @@ module HTTPX
46
64
  end
47
65
  end
48
66
 
67
+ def close
68
+ @body.close if @body.respond_to?(:close)
69
+ end
70
+
49
71
  # if the +@body+ is rewindable, it rewinnds it.
50
72
  def rewind
51
73
  return if empty?
@@ -99,34 +121,20 @@ module HTTPX
99
121
  end
100
122
  # :nocov:
101
123
 
102
- private
103
-
104
- # wraps the given body with the appropriate encoder.
105
- #
106
- # ..., json: { foo: "bar" }) #=> json encoder
107
- # ..., form: { foo: "bar" }) #=> form urlencoded encoder
108
- # ..., form: { foo: Pathname.open("path/to/file") }) #=> multipart urlencoded encoder
109
- # ..., form: { foo: File.open("path/to/file") }) #=> multipart urlencoded encoder
110
- # ..., form: { body: "bla") }) #=> raw data encoder
111
- def initialize_body(options)
112
- @body = if options.body
113
- Transcoder::Body.encode(options.body)
114
- elsif options.form
115
- Transcoder::Form.encode(options.form)
116
- elsif options.json
117
- Transcoder::JSON.encode(options.json)
118
- elsif options.xml
119
- Transcoder::Xml.encode(options.xml)
120
- end
121
-
122
- return unless @body && options.compress_request_body && @headers.key?("content-encoding")
123
-
124
- @headers.get("content-encoding").each do |encoding|
125
- @body = self.class.initialize_deflater_body(@body, encoding)
124
+ class << self
125
+ def initialize_body(params)
126
+ if (body = params.delete(:body))
127
+ # @type var body: bodyIO
128
+ Transcoder::Body.encode(body)
129
+ elsif (form = params.delete(:form))
130
+ # @type var form: Transcoder::urlencoded_input
131
+ Transcoder::Form.encode(form)
132
+ elsif (json = params.delete(:json))
133
+ # @type var body: _ToJson
134
+ Transcoder::JSON.encode(json)
135
+ end
126
136
  end
127
- end
128
137
 
129
- class << self
130
138
  # returns the +body+ wrapped with the correct deflater accordinng to the given +encodisng+.
131
139
  def initialize_deflater_body(body, encoding)
132
140
  case encoding
@@ -142,17 +150,4 @@ module HTTPX
142
150
  end
143
151
  end
144
152
  end
145
-
146
- # Wrapper yielder which can be used with functions which expect an IO writer.
147
- class ProcIO
148
- def initialize(block)
149
- @block = block
150
- end
151
-
152
- # Implementation the IO write protocol, which yield the given chunk to +@block+.
153
- def write(data)
154
- @block.call(data.dup)
155
- data.bytesize
156
- end
157
- end
158
153
  end