httpx 1.1.5 → 1.2.0

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -5
  3. data/doc/release_notes/1_2_0.md +49 -0
  4. data/lib/httpx/adapters/webmock.rb +25 -3
  5. data/lib/httpx/altsvc.rb +57 -2
  6. data/lib/httpx/chainable.rb +40 -29
  7. data/lib/httpx/connection/http1.rb +27 -22
  8. data/lib/httpx/connection/http2.rb +7 -3
  9. data/lib/httpx/connection.rb +45 -60
  10. data/lib/httpx/extensions.rb +0 -15
  11. data/lib/httpx/options.rb +84 -27
  12. data/lib/httpx/plugins/aws_sigv4.rb +2 -2
  13. data/lib/httpx/plugins/basic_auth.rb +1 -1
  14. data/lib/httpx/plugins/callbacks.rb +91 -0
  15. data/lib/httpx/plugins/circuit_breaker.rb +2 -0
  16. data/lib/httpx/plugins/cookies.rb +19 -9
  17. data/lib/httpx/plugins/digest_auth.rb +1 -1
  18. data/lib/httpx/plugins/follow_redirects.rb +11 -0
  19. data/lib/httpx/plugins/grpc.rb +2 -2
  20. data/lib/httpx/plugins/h2c.rb +20 -8
  21. data/lib/httpx/plugins/proxy/socks4.rb +2 -2
  22. data/lib/httpx/plugins/proxy/socks5.rb +2 -2
  23. data/lib/httpx/plugins/proxy.rb +14 -32
  24. data/lib/httpx/plugins/rate_limiter.rb +1 -1
  25. data/lib/httpx/plugins/retries.rb +4 -0
  26. data/lib/httpx/plugins/ssrf_filter.rb +142 -0
  27. data/lib/httpx/plugins/stream.rb +1 -1
  28. data/lib/httpx/plugins/upgrade/h2.rb +1 -1
  29. data/lib/httpx/plugins/upgrade.rb +1 -1
  30. data/lib/httpx/plugins/webdav.rb +1 -1
  31. data/lib/httpx/pool.rb +32 -28
  32. data/lib/httpx/request/body.rb +3 -3
  33. data/lib/httpx/request.rb +3 -5
  34. data/lib/httpx/resolver/https.rb +3 -2
  35. data/lib/httpx/resolver/native.rb +1 -0
  36. data/lib/httpx/resolver/resolver.rb +17 -6
  37. data/lib/httpx/session.rb +13 -82
  38. data/lib/httpx/timers.rb +3 -10
  39. data/lib/httpx/transcoder.rb +1 -1
  40. data/lib/httpx/version.rb +1 -1
  41. data/sig/altsvc.rbs +33 -0
  42. data/sig/chainable.rbs +1 -0
  43. data/sig/connection/http1.rbs +2 -1
  44. data/sig/connection.rbs +16 -16
  45. data/sig/options.rbs +10 -2
  46. data/sig/plugins/callbacks.rbs +38 -0
  47. data/sig/plugins/cookies.rbs +2 -0
  48. data/sig/plugins/follow_redirects.rbs +2 -0
  49. data/sig/plugins/proxy/socks4.rbs +2 -1
  50. data/sig/plugins/proxy/socks5.rbs +2 -1
  51. data/sig/plugins/proxy.rbs +11 -1
  52. data/sig/pool.rbs +1 -3
  53. data/sig/resolver/resolver.rbs +3 -1
  54. data/sig/session.rbs +4 -4
  55. metadata +10 -4
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ class ServerSideRequestForgeryError < Error; end
5
+
6
+ module Plugins
7
+ #
8
+ # This plugin adds support for preventing Server-Side Request Forgery attacks.
9
+ #
10
+ # https://gitlab.com/os85/httpx/wikis/Server-Side-Request-Forgery-Filter
11
+ #
12
+ module SsrfFilter
13
+ module IPAddrExtensions
14
+ refine IPAddr do
15
+ def prefixlen
16
+ mask_addr = @mask_addr
17
+ raise "Invalid mask" if mask_addr.zero?
18
+
19
+ mask_addr >>= 1 while (mask_addr & 0x1).zero?
20
+
21
+ length = 0
22
+ while mask_addr & 0x1 == 0x1
23
+ length += 1
24
+ mask_addr >>= 1
25
+ end
26
+
27
+ length
28
+ end
29
+ end
30
+ end
31
+
32
+ using IPAddrExtensions
33
+
34
+ # https://en.wikipedia.org/wiki/Reserved_IP_addresses
35
+ IPV4_BLACKLIST = [
36
+ IPAddr.new("0.0.0.0/8"), # Current network (only valid as source address)
37
+ IPAddr.new("10.0.0.0/8"), # Private network
38
+ IPAddr.new("100.64.0.0/10"), # Shared Address Space
39
+ IPAddr.new("127.0.0.0/8"), # Loopback
40
+ IPAddr.new("169.254.0.0/16"), # Link-local
41
+ IPAddr.new("172.16.0.0/12"), # Private network
42
+ IPAddr.new("192.0.0.0/24"), # IETF Protocol Assignments
43
+ IPAddr.new("192.0.2.0/24"), # TEST-NET-1, documentation and examples
44
+ IPAddr.new("192.88.99.0/24"), # IPv6 to IPv4 relay (includes 2002::/16)
45
+ IPAddr.new("192.168.0.0/16"), # Private network
46
+ IPAddr.new("198.18.0.0/15"), # Network benchmark tests
47
+ IPAddr.new("198.51.100.0/24"), # TEST-NET-2, documentation and examples
48
+ IPAddr.new("203.0.113.0/24"), # TEST-NET-3, documentation and examples
49
+ IPAddr.new("224.0.0.0/4"), # IP multicast (former Class D network)
50
+ IPAddr.new("240.0.0.0/4"), # Reserved (former Class E network)
51
+ IPAddr.new("255.255.255.255"), # Broadcast
52
+ ].freeze
53
+
54
+ IPV6_BLACKLIST = ([
55
+ IPAddr.new("::1/128"), # Loopback
56
+ IPAddr.new("64:ff9b::/96"), # IPv4/IPv6 translation (RFC 6052)
57
+ IPAddr.new("100::/64"), # Discard prefix (RFC 6666)
58
+ IPAddr.new("2001::/32"), # Teredo tunneling
59
+ IPAddr.new("2001:10::/28"), # Deprecated (previously ORCHID)
60
+ IPAddr.new("2001:20::/28"), # ORCHIDv2
61
+ IPAddr.new("2001:db8::/32"), # Addresses used in documentation and example source code
62
+ IPAddr.new("2002::/16"), # 6to4
63
+ IPAddr.new("fc00::/7"), # Unique local address
64
+ IPAddr.new("fe80::/10"), # Link-local address
65
+ IPAddr.new("ff00::/8"), # Multicast
66
+ ] + IPV4_BLACKLIST.flat_map do |ipaddr|
67
+ prefixlen = ipaddr.prefixlen
68
+
69
+ ipv4_compatible = ipaddr.ipv4_compat.mask(96 + prefixlen)
70
+ ipv4_mapped = ipaddr.ipv4_mapped.mask(80 + prefixlen)
71
+
72
+ [ipv4_compatible, ipv4_mapped]
73
+ end).freeze
74
+
75
+ class << self
76
+ def extra_options(options)
77
+ options.merge(allowed_schemes: %w[https http])
78
+ end
79
+
80
+ def unsafe_ip_address?(ipaddr)
81
+ range = ipaddr.to_range
82
+ return true if range.first != range.last
83
+
84
+ return IPV6_BLACKLIST.any? { |r| r.include?(ipaddr) } if ipaddr.ipv6?
85
+
86
+ IPV4_BLACKLIST.any? { |r| r.include?(ipaddr) } # then it's IPv4
87
+ end
88
+ end
89
+
90
+ module OptionsMethods
91
+ def option_allowed_schemes(value)
92
+ Array(value)
93
+ end
94
+ end
95
+
96
+ module InstanceMethods
97
+ def send_requests(*requests)
98
+ responses = requests.map do |request|
99
+ next if @options.allowed_schemes.include?(request.uri.scheme)
100
+
101
+ error = ServerSideRequestForgeryError.new("#{request.uri} URI scheme not allowed")
102
+ error.set_backtrace(caller)
103
+ response = ErrorResponse.new(request, error, request.options)
104
+ request.emit(:response, response)
105
+ response
106
+ end
107
+ allowed_requests = requests.select { |req| responses[requests.index(req)].nil? }
108
+ allowed_responses = super(*allowed_requests)
109
+ allowed_responses.each_with_index do |res, idx|
110
+ req = allowed_requests[idx]
111
+ responses[requests.index(req)] = res
112
+ end
113
+
114
+ responses
115
+ end
116
+ end
117
+
118
+ module ConnectionMethods
119
+ def initialize(*)
120
+ begin
121
+ super
122
+ rescue ServerSideRequestForgeryError => e
123
+ # may raise when IPs are passed as options via :addresses
124
+ throw(:resolve_error, e)
125
+ end
126
+ end
127
+
128
+ def addresses=(addrs)
129
+ addrs = addrs.map { |addr| addr.is_a?(IPAddr) ? addr : IPAddr.new(addr) }
130
+
131
+ addrs.reject!(&SsrfFilter.method(:unsafe_ip_address?))
132
+
133
+ raise ServerSideRequestForgeryError, "#{@origin.host} has no public IP addresses" if addrs.empty?
134
+
135
+ super
136
+ end
137
+ end
138
+ end
139
+
140
+ register_plugin :ssrf_filter, SsrfFilter
141
+ end
142
+ end
@@ -83,7 +83,7 @@ module HTTPX
83
83
  #
84
84
  # This plugin adds support for stream response (text/event-stream).
85
85
  #
86
- # https://gitlab.com/honeyryderchuck/httpx/wikis/Stream
86
+ # https://gitlab.com/os85/httpx/wikis/Stream
87
87
  #
88
88
  module Stream
89
89
  def self.extra_options(options)
@@ -6,7 +6,7 @@ module HTTPX
6
6
  # This plugin adds support for upgrading an HTTP/1.1 connection to HTTP/2
7
7
  # via an Upgrade: h2 response declaration
8
8
  #
9
- # https://gitlab.com/honeyryderchuck/httpx/wikis/Upgrade#h2
9
+ # https://gitlab.com/os85/httpx/wikis/Connection-Upgrade#h2
10
10
  #
11
11
  module H2
12
12
  class << self
@@ -6,7 +6,7 @@ module HTTPX
6
6
  # This plugin helps negotiating a new protocol from an HTTP/1.1 connection, via the
7
7
  # Upgrade header.
8
8
  #
9
- # https://gitlab.com/honeyryderchuck/httpx/wikis/Upgrade
9
+ # https://gitlab.com/os85/httpx/wikis/Upgrade
10
10
  #
11
11
  module Upgrade
12
12
  class << self
@@ -5,7 +5,7 @@ module HTTPX
5
5
  #
6
6
  # This plugin implements convenience methods for performing WEBDAV requests.
7
7
  #
8
- # https://gitlab.com/honeyryderchuck/httpx/wikis/WEBDAV
8
+ # https://gitlab.com/os85/httpx/wikis/WebDav
9
9
  #
10
10
  module WebDav
11
11
  module InstanceMethods
data/lib/httpx/pool.rb CHANGED
@@ -17,8 +17,6 @@ module HTTPX
17
17
  @timers = Timers.new
18
18
  @selector = Selector.new
19
19
  @connections = []
20
- @eden_connections = []
21
- @connected_connections = 0
22
20
  end
23
21
 
24
22
  def empty?
@@ -45,16 +43,15 @@ module HTTPX
45
43
  connection.emit(:error, e)
46
44
  end
47
45
  rescue Exception # rubocop:disable Lint/RescueException
48
- @connections.each(&:reset)
46
+ @connections.each(&:force_reset)
49
47
  raise
50
48
  end
51
49
 
52
50
  def close(connections = @connections)
53
51
  return if connections.empty?
54
52
 
55
- @eden_connections.clear
56
53
  connections = connections.reject(&:inflight?)
57
- connections.each(&:close)
54
+ connections.each(&:terminate)
58
55
  next_tick until connections.none? { |c| c.state != :idle && @connections.include?(c) }
59
56
 
60
57
  # close resolvers
@@ -68,22 +65,36 @@ module HTTPX
68
65
  resolver.close unless resolver.closed?
69
66
  end
70
67
  # for https resolver
71
- resolver_connections.each(&:close)
68
+ resolver_connections.each(&:terminate)
72
69
  next_tick until resolver_connections.none? { |c| c.state != :idle && @connections.include?(c) }
73
70
  end
74
71
 
75
72
  def init_connection(connection, _options)
76
- resolve_connection(connection) unless connection.family
77
73
  connection.timers = @timers
78
- connection.on(:open) do
79
- @connected_connections += 1
80
- end
81
74
  connection.on(:activate) do
82
75
  select_connection(connection)
83
76
  end
77
+ connection.on(:exhausted) do
78
+ case connection.state
79
+ when :closed
80
+ connection.idling
81
+ @connections << connection
82
+ select_connection(connection)
83
+ when :closing
84
+ connection.once(:close) do
85
+ connection.idling
86
+ @connections << connection
87
+ select_connection(connection)
88
+ end
89
+ end
90
+ end
84
91
  connection.on(:close) do
85
92
  unregister_connection(connection)
86
93
  end
94
+ connection.on(:terminate) do
95
+ unregister_connection(connection, true)
96
+ end
97
+ resolve_connection(connection) unless connection.family
87
98
  end
88
99
 
89
100
  def deactivate(connections)
@@ -102,16 +113,15 @@ module HTTPX
102
113
  connection.match?(uri, options)
103
114
  end
104
115
 
105
- unless conn
106
- @eden_connections.delete_if do |connection|
107
- is_expired = connection.expired?
108
- conn = connection if conn.nil? && !is_expired && connection.match?(uri, options)
109
- is_expired
110
- end
116
+ return unless conn
111
117
 
112
- if conn
118
+ case conn.state
119
+ when :closed
120
+ conn.idling
121
+ select_connection(conn)
122
+ when :closing
123
+ conn.once(:close) do
113
124
  conn.idling
114
- @connections << conn
115
125
  select_connection(conn)
116
126
  end
117
127
  end
@@ -151,7 +161,7 @@ module HTTPX
151
161
 
152
162
  return connection if connection.family == family
153
163
 
154
- new_connection = connection.class.new(connection.type, connection.origin, connection.options)
164
+ new_connection = connection.class.new(connection.origin, connection.options)
155
165
  new_connection.family = family
156
166
 
157
167
  connection.once(:tcp_open) { new_connection.force_reset }
@@ -218,18 +228,12 @@ module HTTPX
218
228
  end
219
229
 
220
230
  def register_connection(connection)
221
- if connection.state == :open
222
- # if open, an IO was passed upstream, therefore
223
- # consider it connected already.
224
- @connected_connections += 1
225
- end
226
231
  select_connection(connection)
227
232
  end
228
233
 
229
- def unregister_connection(connection)
230
- @connections.delete(connection)
231
- @eden_connections << connection if connection.used? && !@eden_connections.include?(connection)
232
- @connected_connections -= 1 if deselect_connection(connection)
234
+ def unregister_connection(connection, cleanup = !connection.used?)
235
+ @connections.delete(connection) if cleanup
236
+ deselect_connection(connection)
233
237
  end
234
238
 
235
239
  def select_connection(connection)
@@ -70,9 +70,9 @@ module HTTPX
70
70
 
71
71
  # sets the body to yield using chunked trannsfer encoding format.
72
72
  def stream(body)
73
- encoded = body
74
- encoded = Transcoder::Chunker.encode(body.enum_for(:each)) if chunked?
75
- encoded
73
+ return body unless chunked?
74
+
75
+ Transcoder::Chunker.encode(body.enum_for(:each))
76
76
  end
77
77
 
78
78
  # returns whether the body yields infinitely.
data/lib/httpx/request.rb CHANGED
@@ -127,11 +127,9 @@ module HTTPX
127
127
  return
128
128
  end
129
129
 
130
- if response.status >= 103
131
- # 103 Early Hints advertises resources in document to browsers.
132
- # not very relevant for an HTTP client, discard.
133
- return
134
- end
130
+ # 103 Early Hints advertises resources in document to browsers.
131
+ # not very relevant for an HTTP client, discard.
132
+ return if response.status >= 103
135
133
  end
136
134
 
137
135
  @response = response
@@ -27,7 +27,7 @@ module HTTPX
27
27
  use_get: false,
28
28
  }.freeze
29
29
 
30
- def_delegators :@resolver_connection, :state, :connecting?, :to_io, :call, :close
30
+ def_delegators :@resolver_connection, :state, :connecting?, :to_io, :call, :close, :terminate
31
31
 
32
32
  def initialize(_, options)
33
33
  super
@@ -50,6 +50,7 @@ module HTTPX
50
50
  if @uri_addresses.empty?
51
51
  ex = ResolveError.new("Can't resolve DNS server #{@uri.host}")
52
52
  ex.set_backtrace(caller)
53
+ connection.force_reset
53
54
  throw(:resolve_error, ex)
54
55
  end
55
56
 
@@ -67,7 +68,7 @@ module HTTPX
67
68
  def resolver_connection
68
69
  @resolver_connection ||= @pool.find_connection(@uri, @options) || begin
69
70
  @building_connection = true
70
- connection = @options.connection_class.new("ssl", @uri, @options.merge(ssl: { alpn_protocols: %w[h2] }))
71
+ connection = @options.connection_class.new(@uri, @options.merge(ssl: { alpn_protocols: %w[h2] }))
71
72
  @pool.init_connection(connection, @options)
72
73
  # only explicity emit addresses if connection didn't pre-resolve, i.e. it's not an IP.
73
74
  emit_addresses(connection, @family, @uri_addresses) unless connection.addresses
@@ -88,6 +88,7 @@ module HTTPX
88
88
  if @nameserver.nil?
89
89
  ex = ResolveError.new("No available nameserver")
90
90
  ex.set_backtrace(caller)
91
+ connection.force_reset
91
92
  throw(:resolve_error, ex)
92
93
  else
93
94
  @connections << connection
@@ -38,6 +38,8 @@ module HTTPX
38
38
 
39
39
  def close; end
40
40
 
41
+ alias_method :terminate, :close
42
+
41
43
  def closed?
42
44
  true
43
45
  end
@@ -65,20 +67,29 @@ module HTTPX
65
67
  unless connection.state == :closed ||
66
68
  # double emission check
67
69
  (connection.addresses && addresses.intersect?(connection.addresses))
68
- emit_resolved_connection(connection, addresses)
70
+ emit_resolved_connection(connection, addresses, early_resolve)
69
71
  end
70
72
  end
71
73
  else
72
- emit_resolved_connection(connection, addresses)
74
+ emit_resolved_connection(connection, addresses, early_resolve)
73
75
  end
74
76
  end
75
77
 
76
78
  private
77
79
 
78
- def emit_resolved_connection(connection, addresses)
79
- connection.addresses = addresses
80
-
81
- emit(:resolve, connection)
80
+ def emit_resolved_connection(connection, addresses, early_resolve)
81
+ begin
82
+ connection.addresses = addresses
83
+
84
+ emit(:resolve, connection)
85
+ rescue StandardError => e
86
+ if early_resolve
87
+ connection.force_reset
88
+ throw(:resolve_error, e)
89
+ else
90
+ emit(:error, connection, e)
91
+ end
92
+ end
82
93
  end
83
94
 
84
95
  def early_resolve(connection, hostname: connection.origin.host)
data/lib/httpx/session.rb CHANGED
@@ -8,7 +8,6 @@ module HTTPX
8
8
  class Session
9
9
  include Loggable
10
10
  include Chainable
11
- include Callbacks
12
11
 
13
12
  EMPTY_HASH = {}.freeze
14
13
 
@@ -85,33 +84,7 @@ module HTTPX
85
84
  options = @options.merge(options) unless options.is_a?(Options)
86
85
  request = rklass.new(verb, uri, options)
87
86
  request.persistent = @persistent
88
- request.on(:response, &method(:on_response).curry(2)[request])
89
- request.on(:promise, &method(:on_promise))
90
-
91
- request.on(:headers) do
92
- emit(:request_started, request)
93
- end
94
- request.on(:body_chunk) do |chunk|
95
- emit(:request_body_chunk, request, chunk)
96
- end
97
- request.on(:done) do
98
- emit(:request_completed, request)
99
- end
100
-
101
- request.on(:response_started) do |res|
102
- if res.is_a?(Response)
103
- emit(:response_started, request, res)
104
- res.on(:chunk_received) do |chunk|
105
- emit(:response_body_chunk, request, res, chunk)
106
- end
107
- else
108
- emit(:request_error, request, res.error)
109
- end
110
- end
111
- request.on(:response) do |res|
112
- emit(:response_completed, request, res)
113
- end
114
-
87
+ set_request_callbacks(request)
115
88
  request
116
89
  end
117
90
 
@@ -143,7 +116,7 @@ module HTTPX
143
116
  def find_connection(request, connections, options)
144
117
  uri = request.uri
145
118
 
146
- connection = pool.find_connection(uri, options) || build_connection(uri, options)
119
+ connection = pool.find_connection(uri, options) || init_connection(uri, options)
147
120
  unless connections.nil? || connections.include?(connection)
148
121
  connections << connection
149
122
  set_connection_callbacks(connection, connections, options)
@@ -179,15 +152,6 @@ module HTTPX
179
152
  other_connection = build_altsvc_connection(connection, connections, alt_origin, origin, alt_params, options)
180
153
  connections << other_connection if other_connection
181
154
  end
182
- connection.only(:exhausted) do
183
- other_connection = connection.create_idle
184
- other_connection.merge(connection)
185
- catch(:coalesced) do
186
- pool.init_connection(other_connection, options)
187
- end
188
- set_connection_callbacks(other_connection, connections, options)
189
- connections << other_connection
190
- end
191
155
  end
192
156
 
193
157
  # returns an HTTPX::Connection for the negotiated Alternative Service (or none).
@@ -202,24 +166,19 @@ module HTTPX
202
166
 
203
167
  alt_options = options.merge(ssl: options.ssl.merge(hostname: URI(origin).host))
204
168
 
205
- connection = pool.find_connection(alt_origin, alt_options) || build_connection(alt_origin, alt_options)
169
+ connection = pool.find_connection(alt_origin, alt_options) || init_connection(alt_origin, alt_options)
170
+
206
171
  # advertised altsvc is the same origin being used, ignore
207
172
  return if connection == existing_connection
208
173
 
174
+ connection.extend(AltSvc::ConnectionMixin) unless connection.is_a?(AltSvc::ConnectionMixin)
175
+
209
176
  set_connection_callbacks(connection, connections, alt_options)
210
177
 
211
178
  log(level: 1) { "#{origin} alt-svc: #{alt_origin}" }
212
179
 
213
- # get uninitialized requests
214
- # incidentally, all requests will be re-routed to the first
215
- # advertised alt-svc, which incidentally follows the spec.
216
- existing_connection.purge_pending do |request|
217
- request.origin == origin &&
218
- request.state == :idle &&
219
- !request.headers.key?("alt-used")
220
- end
221
-
222
180
  connection.merge(existing_connection)
181
+ existing_connection.terminate
223
182
  connection
224
183
  rescue UnsupportedSchemeError
225
184
  altsvc["noop"] = true
@@ -250,30 +209,13 @@ module HTTPX
250
209
  requests
251
210
  end
252
211
 
253
- # returns a new HTTPX::Connection object for the given +uri+ and set of +options+.
254
- def build_connection(uri, options)
255
- type = options.transport || begin
256
- case uri.scheme
257
- when "http"
258
- "tcp"
259
- when "https"
260
- "ssl"
261
- else
262
- raise UnsupportedSchemeError, "#{uri}: #{uri.scheme}: unsupported URI scheme"
263
- end
264
- end
265
- init_connection(type, uri, options)
212
+ def set_request_callbacks(request)
213
+ request.on(:response, &method(:on_response).curry(2)[request])
214
+ request.on(:promise, &method(:on_promise))
266
215
  end
267
216
 
268
- def init_connection(type, uri, options)
269
- connection = options.connection_class.new(type, uri, options)
270
- connection.on(:open) do
271
- emit(:connection_opened, connection.origin, connection.io.socket)
272
- # only run close callback if it opened
273
- end
274
- connection.on(:close) do
275
- emit(:connection_closed, connection.origin, connection.io.socket) if connection.used?
276
- end
217
+ def init_connection(uri, options)
218
+ connection = options.connection_class.new(uri, options)
277
219
  catch(:coalesced) do
278
220
  pool.init_connection(connection, options)
279
221
  connection
@@ -369,19 +311,8 @@ module HTTPX
369
311
  extend(pl::ClassMethods) if defined?(pl::ClassMethods)
370
312
 
371
313
  opts = @default_options
372
- opts.request_class.__send__(:include, pl::RequestMethods) if defined?(pl::RequestMethods)
373
- opts.request_class.extend(pl::RequestClassMethods) if defined?(pl::RequestClassMethods)
374
- opts.response_class.__send__(:include, pl::ResponseMethods) if defined?(pl::ResponseMethods)
375
- opts.response_class.extend(pl::ResponseClassMethods) if defined?(pl::ResponseClassMethods)
376
- opts.headers_class.__send__(:include, pl::HeadersMethods) if defined?(pl::HeadersMethods)
377
- opts.headers_class.extend(pl::HeadersClassMethods) if defined?(pl::HeadersClassMethods)
378
- opts.request_body_class.__send__(:include, pl::RequestBodyMethods) if defined?(pl::RequestBodyMethods)
379
- opts.request_body_class.extend(pl::RequestBodyClassMethods) if defined?(pl::RequestBodyClassMethods)
380
- opts.response_body_class.__send__(:include, pl::ResponseBodyMethods) if defined?(pl::ResponseBodyMethods)
381
- opts.response_body_class.extend(pl::ResponseBodyClassMethods) if defined?(pl::ResponseBodyClassMethods)
382
- opts.connection_class.__send__(:include, pl::ConnectionMethods) if defined?(pl::ConnectionMethods)
314
+ opts.extend_with_plugin_classes(pl)
383
315
  if defined?(pl::OptionsMethods)
384
- opts.options_class.__send__(:include, pl::OptionsMethods)
385
316
 
386
317
  (pl::OptionsMethods.instance_methods - Object.instance_methods).each do |meth|
387
318
  opts.options_class.method_added(meth)
data/lib/httpx/timers.rb CHANGED
@@ -81,16 +81,9 @@ module HTTPX
81
81
  @callbacks << callback
82
82
  end
83
83
 
84
- if RUBY_ENGINE == "jruby" && JRUBY_VERSION < "9.4.5.0"
85
- # https://github.com/jruby/jruby/issues/7976
86
- def delete(callback)
87
- @callbacks.delete(callback)
88
- end
89
- else
90
- def delete(callback)
91
- @callbacks.delete(callback)
92
- @on_empty.call if @callbacks.empty?
93
- end
84
+ def delete(callback)
85
+ @callbacks.delete(callback)
86
+ @on_empty.call if @callbacks.empty?
94
87
  end
95
88
 
96
89
  def no_callbacks?
@@ -5,7 +5,7 @@ module HTTPX
5
5
  module_function
6
6
 
7
7
  def normalize_keys(key, value, cond = nil, &block)
8
- if (cond && cond.call(value))
8
+ if cond && cond.call(value)
9
9
  block.call(key.to_s, value)
10
10
  elsif value.respond_to?(:to_ary)
11
11
  if value.empty?
data/lib/httpx/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
- VERSION = "1.1.5"
4
+ VERSION = "1.2.0"
5
5
  end
data/sig/altsvc.rbs ADDED
@@ -0,0 +1,33 @@
1
+ module HTTPX
2
+ module AltSvc
3
+ module ConnectionMixin
4
+
5
+ def send: (Request request) -> void
6
+
7
+ def match?: (URI::Generic uri, Options options) -> bool
8
+
9
+ private
10
+
11
+ def match_altsvcs?: (URI::Generic uri) -> bool
12
+
13
+ def match_altsvc_options?: (URI::Generic uri, Options options) -> bool
14
+ end
15
+
16
+ type altsvc_params = Hash[String, untyped]
17
+
18
+ def self?.cached_altsvc: (String origin) -> Array[altsvc_params]
19
+
20
+ def self?.cached_altsvc_set: (String origin, altsvc_params) -> void
21
+
22
+ def self?.lookup: (String origin, Integer | Float ttl) -> Array[altsvc_params]
23
+
24
+ def self?.emit: (Request request, response response) { (http_uri alt_origin, String origin, altsvc_params alt_params) -> void } -> void
25
+
26
+ def self?.parse: (String altsvc) { (http_uri alt_origin, altsvc_params alt_params) -> void } -> void
27
+ | (String altsvc) -> Enumerable[[http_uri, altsvc_params]]
28
+
29
+ def self?.parse_altsvc_scheme: (String alt_proto) -> String?
30
+
31
+ def self.parse_altsvc_origin: (string alt_proto, String alt_origin) -> http_uri?
32
+ end
33
+ end
data/sig/chainable.rbs CHANGED
@@ -34,6 +34,7 @@ module HTTPX
34
34
  | (:response_cache, ?options) -> Plugins::sessionResponseCache
35
35
  | (:circuit_breaker, ?options) -> Plugins::sessionCircuitBreaker
36
36
  | (:oauth, ?options) -> Plugins::sessionOAuth
37
+ | (:callbacks, ?options) -> Plugins::sessionCallbacks
37
38
  | (Symbol | Module, ?options) { (Class) -> void } -> Session
38
39
  | (Symbol | Module, ?options) -> Session
39
40
 
@@ -10,8 +10,9 @@ module HTTPX
10
10
  attr_reader pending: Array[Request]
11
11
  attr_reader requests: Array[Request]
12
12
 
13
+ attr_accessor max_concurrent_requests: Integer
14
+
13
15
  @options: Options
14
- @max_concurrent_requests: Integer
15
16
  @max_requests: Integer
16
17
  @parser: Parser::HTTP1
17
18
  @buffer: Buffer