httpx 1.5.1 → 1.6.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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/doc/release_notes/1_6_0.md +50 -0
  3. data/lib/httpx/adapters/datadog.rb +23 -13
  4. data/lib/httpx/adapters/faraday.rb +14 -9
  5. data/lib/httpx/adapters/webmock.rb +1 -1
  6. data/lib/httpx/callbacks.rb +1 -1
  7. data/lib/httpx/connection/http1.rb +5 -6
  8. data/lib/httpx/connection/http2.rb +30 -12
  9. data/lib/httpx/connection.rb +17 -24
  10. data/lib/httpx/errors.rb +3 -1
  11. data/lib/httpx/io/ssl.rb +1 -4
  12. data/lib/httpx/io/tcp.rb +25 -16
  13. data/lib/httpx/io/unix.rb +4 -3
  14. data/lib/httpx/loggable.rb +4 -1
  15. data/lib/httpx/options.rb +252 -158
  16. data/lib/httpx/plugins/aws_sdk_authentication.rb +2 -0
  17. data/lib/httpx/plugins/aws_sigv4.rb +2 -0
  18. data/lib/httpx/plugins/callbacks.rb +13 -1
  19. data/lib/httpx/plugins/circuit_breaker.rb +2 -0
  20. data/lib/httpx/plugins/content_digest.rb +2 -0
  21. data/lib/httpx/plugins/cookies.rb +2 -2
  22. data/lib/httpx/plugins/digest_auth.rb +2 -0
  23. data/lib/httpx/plugins/expect.rb +2 -0
  24. data/lib/httpx/plugins/fiber_concurrency.rb +195 -0
  25. data/lib/httpx/plugins/follow_redirects.rb +2 -0
  26. data/lib/httpx/plugins/grpc.rb +2 -0
  27. data/lib/httpx/plugins/h2c.rb +26 -16
  28. data/lib/httpx/plugins/internal_telemetry.rb +0 -49
  29. data/lib/httpx/plugins/ntlm_auth.rb +2 -0
  30. data/lib/httpx/plugins/oauth.rb +2 -0
  31. data/lib/httpx/plugins/persistent.rb +27 -18
  32. data/lib/httpx/plugins/proxy/socks4.rb +1 -1
  33. data/lib/httpx/plugins/proxy/socks5.rb +1 -1
  34. data/lib/httpx/plugins/proxy/ssh.rb +2 -0
  35. data/lib/httpx/plugins/proxy.rb +61 -20
  36. data/lib/httpx/plugins/response_cache/file_store.rb +2 -2
  37. data/lib/httpx/plugins/response_cache.rb +2 -0
  38. data/lib/httpx/plugins/retries.rb +2 -0
  39. data/lib/httpx/plugins/ssrf_filter.rb +2 -2
  40. data/lib/httpx/plugins/stream_bidi.rb +3 -3
  41. data/lib/httpx/plugins/upgrade/h2.rb +11 -1
  42. data/lib/httpx/plugins/upgrade.rb +8 -0
  43. data/lib/httpx/pool.rb +15 -10
  44. data/lib/httpx/request/body.rb +8 -3
  45. data/lib/httpx/request.rb +22 -11
  46. data/lib/httpx/resolver/entry.rb +30 -0
  47. data/lib/httpx/resolver/https.rb +3 -1
  48. data/lib/httpx/resolver/multi.rb +5 -2
  49. data/lib/httpx/resolver/native.rb +15 -6
  50. data/lib/httpx/resolver/resolver.rb +3 -5
  51. data/lib/httpx/resolver/system.rb +1 -1
  52. data/lib/httpx/resolver.rb +34 -21
  53. data/lib/httpx/response/body.rb +1 -1
  54. data/lib/httpx/response/buffer.rb +13 -18
  55. data/lib/httpx/selector.rb +92 -34
  56. data/lib/httpx/session.rb +89 -30
  57. data/lib/httpx/session_extensions.rb +3 -2
  58. data/lib/httpx/transcoder/form.rb +1 -13
  59. data/lib/httpx/transcoder/multipart/mime_type_detector.rb +1 -1
  60. data/lib/httpx/transcoder/multipart.rb +14 -0
  61. data/lib/httpx/transcoder/utils/deflater.rb +1 -1
  62. data/lib/httpx/version.rb +1 -1
  63. data/sig/callbacks.rbs +1 -1
  64. data/sig/chainable.rbs +1 -0
  65. data/sig/connection/http1.rbs +2 -0
  66. data/sig/connection/http2.rbs +4 -0
  67. data/sig/connection.rbs +6 -6
  68. data/sig/errors.rbs +3 -1
  69. data/sig/io/ssl.rbs +1 -1
  70. data/sig/io/tcp.rbs +13 -7
  71. data/sig/io/udp.rbs +7 -2
  72. data/sig/io/unix.rbs +0 -1
  73. data/sig/io.rbs +0 -3
  74. data/sig/options.rbs +63 -10
  75. data/sig/plugins/fiber_concurrency.rbs +51 -0
  76. data/sig/plugins/h2c.rbs +5 -1
  77. data/sig/plugins/persistent.rbs +1 -1
  78. data/sig/plugins/proxy/socks4.rbs +1 -1
  79. data/sig/plugins/proxy/socks5.rbs +1 -1
  80. data/sig/plugins/proxy.rbs +5 -2
  81. data/sig/plugins/ssrf_filter.rbs +1 -1
  82. data/sig/plugins/stream_bidi.rbs +2 -2
  83. data/sig/request.rbs +4 -1
  84. data/sig/resolver/entry.rbs +13 -0
  85. data/sig/resolver/native.rbs +1 -0
  86. data/sig/resolver/resolver.rbs +2 -3
  87. data/sig/resolver/system.rbs +2 -2
  88. data/sig/resolver.rbs +10 -11
  89. data/sig/response.rbs +2 -2
  90. data/sig/selector.rbs +18 -10
  91. data/sig/session.rbs +4 -0
  92. data/sig/transcoder/form.rbs +3 -3
  93. data/sig/transcoder/multipart.rbs +9 -3
  94. metadata +9 -3
@@ -0,0 +1,195 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ # This plugin makes a session reuse the same selector across all fibers in a given thread.
6
+ #
7
+ # This enables integration with fiber scheduler implementations such as [async](https://github.com/async).
8
+ #
9
+ # # https://gitlab.com/os85/httpx/wikis/FiberConcurrency
10
+ #
11
+ module FiberConcurrency
12
+ def self.subplugins
13
+ {
14
+ h2c: FiberConcurrencyH2C,
15
+ }
16
+ end
17
+
18
+ module InstanceMethods
19
+ private
20
+
21
+ def send_request(request, *)
22
+ request.set_context!
23
+
24
+ super
25
+ end
26
+
27
+ def get_current_selector
28
+ super(&nil) || begin
29
+ return unless block_given?
30
+
31
+ default = yield
32
+
33
+ set_current_selector(default)
34
+
35
+ default
36
+ end
37
+ end
38
+ end
39
+
40
+ module RequestMethods
41
+ # the execution context (fiber) this request was sent on.
42
+ attr_reader :context
43
+
44
+ def initialize(*)
45
+ super
46
+ @context = nil
47
+ end
48
+
49
+ # sets the execution context for this request. the default is the current fiber.
50
+ def set_context!
51
+ @context ||= Fiber.current # rubocop:disable Naming/MemoizedInstanceVariableName
52
+ end
53
+
54
+ # checks whether the current execution context is the one where the request was created.
55
+ def current_context?
56
+ @context == Fiber.current
57
+ end
58
+
59
+ def complete!(response = @response)
60
+ @context = nil
61
+ super
62
+ end
63
+ end
64
+
65
+ module ConnectionMethods
66
+ def current_context?
67
+ @pending.any?(&:current_context?) || (
68
+ @sibling && @sibling.pending.any?(&:current_context?)
69
+ )
70
+ end
71
+
72
+ def interests
73
+ return if connecting? && @pending.none?(&:current_context?)
74
+
75
+ super
76
+ end
77
+
78
+ def send(request)
79
+ # DoH requests bypass the session, so context needs to be set here.
80
+ request.set_context!
81
+
82
+ super
83
+ end
84
+ end
85
+
86
+ module HTTP1Methods
87
+ def interests
88
+ request = @request || @requests.first
89
+
90
+ return unless request
91
+
92
+ return unless request.current_context? || @requests.any?(&:current_context?) || @pending.any?(&:current_context?)
93
+
94
+ super
95
+ end
96
+ end
97
+
98
+ module HTTP2Methods
99
+ def initialize(*)
100
+ super
101
+ @contexts = Hash.new { |hs, k| hs[k] = Set.new }
102
+ end
103
+
104
+ def interests
105
+ if @connection.state == :connected && @handshake_completed && !@contexts.key?(Fiber.current)
106
+ return :w unless @pings.empty?
107
+
108
+ return
109
+ end
110
+
111
+ super
112
+ end
113
+
114
+ def send(request, *)
115
+ add_to_context(request)
116
+
117
+ super
118
+ end
119
+
120
+ private
121
+
122
+ def on_close(_, error, _)
123
+ if error == :http_1_1_required
124
+ # remove all pending requests context
125
+ @pending.each do |req|
126
+ clear_from_context(req)
127
+ end
128
+ end
129
+
130
+ super
131
+ end
132
+
133
+ def on_stream_close(_, request, error)
134
+ clear_from_context(request) if error != :stream_closed && @streams.key?(request)
135
+
136
+ super
137
+ end
138
+
139
+ def teardown(request = nil)
140
+ super
141
+
142
+ if request
143
+ clear_from_context(request)
144
+ else
145
+ @contexts.clear
146
+ end
147
+ end
148
+
149
+ def add_to_context(request)
150
+ @contexts[request.context] << request
151
+ end
152
+
153
+ def clear_from_context(request)
154
+ requests = @contexts[request.context]
155
+
156
+ requests.delete(request)
157
+
158
+ @contexts.delete(request.context) if requests.empty?
159
+ end
160
+ end
161
+
162
+ module NativeResolverMethods
163
+ private
164
+
165
+ def calculate_interests
166
+ return if @queries.empty?
167
+
168
+ return unless @queries.values.any?(&:current_context?) || @connections.any?(&:current_context?)
169
+
170
+ super
171
+ end
172
+ end
173
+
174
+ module SystemResolverMethods
175
+ def interests
176
+ return unless @queries.any? { |_, conn| conn.current_context? }
177
+
178
+ super
179
+ end
180
+ end
181
+
182
+ module FiberConcurrencyH2C
183
+ module HTTP2Methods
184
+ def upgrade(request, *)
185
+ @contexts[request.context] << request
186
+
187
+ super
188
+ end
189
+ end
190
+ end
191
+ end
192
+
193
+ register_plugin :fiber_concurrency, FiberConcurrency
194
+ end
195
+ end
@@ -34,6 +34,8 @@ module HTTPX
34
34
  # (defaults to <tt>false</tt>).
35
35
  # :redirect_on :: optional callback which receives the redirect location and can halt the redirect chain if it returns <tt>false</tt>.
36
36
  module OptionsMethods
37
+ private
38
+
37
39
  def option_max_redirects(value)
38
40
  num = Integer(value)
39
41
  raise TypeError, ":max_redirects must be positive" if num.negative?
@@ -70,6 +70,8 @@ module HTTPX
70
70
  end
71
71
 
72
72
  module OptionsMethods
73
+ private
74
+
73
75
  def option_grpc_service(value)
74
76
  String(value)
75
77
  end
@@ -21,24 +21,17 @@ module HTTPX
21
21
  end
22
22
 
23
23
  def extra_options(options)
24
- options.merge(max_concurrent_requests: 1, upgrade_handlers: options.upgrade_handlers.merge("h2c" => self))
24
+ options.merge(
25
+ h2c_class: Class.new(options.http2_class) { include(H2CParser) },
26
+ max_concurrent_requests: 1,
27
+ upgrade_handlers: options.upgrade_handlers.merge("h2c" => self),
28
+ )
25
29
  end
26
30
  end
27
31
 
28
- class H2CParser < Connection::HTTP2
29
- def upgrade(request, response)
30
- # skip checks, it is assumed that this is the first
31
- # request in the connection
32
- stream = @connection.upgrade
33
-
34
- # on_settings
35
- handle_stream(stream, request)
36
- @streams[request] = stream
37
-
38
- # clean up data left behind in the buffer, if the server started
39
- # sending frames
40
- data = response.read
41
- @connection << data
32
+ module OptionsMethods
33
+ def option_h2c_class(value)
34
+ value
42
35
  end
43
36
  end
44
37
 
@@ -82,7 +75,7 @@ module HTTPX
82
75
  @inflight -= prev_parser.requests.size
83
76
  end
84
77
 
85
- @parser = H2CParser.new(@write_buffer, @options)
78
+ @parser = request.options.h2c_class.new(@write_buffer, @options)
86
79
  set_parser_callbacks(@parser)
87
80
  @inflight += 1
88
81
  @parser.upgrade(request, response)
@@ -111,6 +104,23 @@ module HTTPX
111
104
  end
112
105
  end
113
106
  end
107
+
108
+ module H2CParser
109
+ def upgrade(request, response)
110
+ # skip checks, it is assumed that this is the first
111
+ # request in the connection
112
+ stream = @connection.upgrade
113
+
114
+ # on_settings
115
+ handle_stream(stream, request)
116
+ @streams[request] = stream
117
+
118
+ # clean up data left behind in the buffer, if the server started
119
+ # sending frames
120
+ data = response.read
121
+ @connection << data
122
+ end
123
+ end
114
124
  end
115
125
  register_plugin(:h2c, H2C)
116
126
  end
@@ -50,15 +50,6 @@ module HTTPX
50
50
  end
51
51
  end
52
52
 
53
- module NativeResolverMethods
54
- def transition(nextstate)
55
- state = @state
56
- val = super
57
- meter_elapsed_time("Resolver::Native: #{state} -> #{nextstate}")
58
- val
59
- end
60
- end
61
-
62
53
  module InstanceMethods
63
54
  def self.included(klass)
64
55
  klass.prepend TrackTimeMethods
@@ -69,13 +60,6 @@ module HTTPX
69
60
  meter_elapsed_time("Session: initializing...")
70
61
  super
71
62
  meter_elapsed_time("Session: initialized!!!")
72
- resolver_type = @options.resolver_class
73
- resolver_type = Resolver.resolver_for(resolver_type)
74
- return unless resolver_type <= Resolver::Native
75
-
76
- resolver_type.prepend TrackTimeMethods
77
- resolver_type.prepend NativeResolverMethods
78
- @options = @options.merge(resolver_class: resolver_type)
79
63
  end
80
64
 
81
65
  def close(*)
@@ -104,33 +88,6 @@ module HTTPX
104
88
  end
105
89
  end
106
90
 
107
- module RequestMethods
108
- def self.included(klass)
109
- klass.prepend Loggable
110
- klass.prepend TrackTimeMethods
111
- super
112
- end
113
-
114
- def transition(nextstate)
115
- prev_state = @state
116
- super
117
- meter_elapsed_time("Request##{object_id}[#{@verb} #{@uri}: #{prev_state}] -> #{@state}") if prev_state != @state
118
- end
119
- end
120
-
121
- module ConnectionMethods
122
- def self.included(klass)
123
- klass.prepend TrackTimeMethods
124
- super
125
- end
126
-
127
- def handle_transition(nextstate)
128
- state = @state
129
- super
130
- meter_elapsed_time("Connection##{object_id}[#{@origin}]: #{state} -> #{nextstate}") if nextstate == @state
131
- end
132
- end
133
-
134
91
  module PoolMethods
135
92
  def self.included(klass)
136
93
  klass.prepend Loggable
@@ -138,12 +95,6 @@ module HTTPX
138
95
  super
139
96
  end
140
97
 
141
- def checkout_connection(request_uri, options)
142
- super.tap do |connection|
143
- meter_elapsed_time("Pool##{object_id}: checked out connection for Connection##{connection.object_id}[#{connection.origin}]}")
144
- end
145
- end
146
-
147
98
  def checkin_connection(connection)
148
99
  super.tap do
149
100
  meter_elapsed_time("Pool##{object_id}: checked in connection for Connection##{connection.object_id}[#{connection.origin}]}")
@@ -17,6 +17,8 @@ module HTTPX
17
17
  end
18
18
 
19
19
  module OptionsMethods
20
+ private
21
+
20
22
  def option_ntlm(value)
21
23
  raise TypeError, ":ntlm must be a #{Authentication::Ntlm}" unless value.is_a?(Authentication::Ntlm)
22
24
 
@@ -98,6 +98,8 @@ module HTTPX
98
98
  end
99
99
 
100
100
  module OptionsMethods
101
+ private
102
+
101
103
  def option_oauth_session(value)
102
104
  case value
103
105
  when Hash
@@ -18,13 +18,17 @@ module HTTPX
18
18
  # https://gitlab.com/os85/httpx/wikis/Persistent
19
19
  #
20
20
  module Persistent
21
- def self.load_dependencies(klass)
22
- max_retries = if klass.default_options.respond_to?(:max_retries)
23
- [klass.default_options.max_retries, 1].max
24
- else
25
- 1
21
+ class << self
22
+ def load_dependencies(klass)
23
+ klass.plugin(:fiber_concurrency)
24
+
25
+ max_retries = if klass.default_options.respond_to?(:max_retries)
26
+ [klass.default_options.max_retries, 1].max
27
+ else
28
+ 1
29
+ end
30
+ klass.plugin(:retries, max_retries: max_retries)
26
31
  end
27
- klass.plugin(:retries, max_retries: max_retries)
28
32
  end
29
33
 
30
34
  def self.extra_options(options)
@@ -32,6 +36,23 @@ module HTTPX
32
36
  end
33
37
 
34
38
  module InstanceMethods
39
+ def close(*)
40
+ super
41
+
42
+ # traverse other threads and unlink respective selector
43
+ # WARNING: this is not thread safe, make sure that the session isn't being
44
+ # used anymore, or all non-main threads are stopped.
45
+ Thread.list.each do |th|
46
+ store = thread_selector_store(th)
47
+
48
+ next unless store && store.key?(self)
49
+
50
+ selector = store.delete(self)
51
+
52
+ selector_close(selector)
53
+ end
54
+ end
55
+
35
56
  private
36
57
 
37
58
  def repeatable_request?(request, _)
@@ -54,18 +75,6 @@ module HTTPX
54
75
  # consequences.
55
76
  (!ex.is_a?(RequestTimeoutError) || @options.max_retries != 1)
56
77
  end
57
-
58
- def get_current_selector
59
- super(&nil) || begin
60
- return unless block_given?
61
-
62
- default = yield
63
-
64
- set_current_selector(default)
65
-
66
- default
67
- end
68
- end
69
78
  end
70
79
  end
71
80
  register_plugin :persistent, Persistent
@@ -4,7 +4,7 @@ require "resolv"
4
4
  require "ipaddr"
5
5
 
6
6
  module HTTPX
7
- class Socks4Error < HTTPProxyError; end
7
+ class Socks4Error < ProxyError; end
8
8
 
9
9
  module Plugins
10
10
  module Proxy
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
- class Socks5Error < HTTPProxyError; end
4
+ class Socks5Error < ProxyError; end
5
5
 
6
6
  module Plugins
7
7
  module Proxy
@@ -13,6 +13,8 @@ module HTTPX
13
13
  end
14
14
 
15
15
  module OptionsMethods
16
+ private
17
+
16
18
  def option_proxy(value)
17
19
  Hash[value]
18
20
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
- class HTTPProxyError < ConnectionError; end
4
+ class ProxyError < ConnectionError; end
5
5
 
6
6
  module Plugins
7
7
  #
@@ -15,7 +15,8 @@ module HTTPX
15
15
  # https://gitlab.com/os85/httpx/wikis/Proxy
16
16
  #
17
17
  module Proxy
18
- Error = HTTPProxyError
18
+ class ProxyConnectionError < ProxyError; end
19
+
19
20
  PROXY_ERRORS = [TimeoutError, IOError, SystemCallError, Error].freeze
20
21
 
21
22
  class << self
@@ -28,6 +29,12 @@ module HTTPX
28
29
  def extra_options(options)
29
30
  options.merge(supported_proxy_protocols: [])
30
31
  end
32
+
33
+ def subplugins
34
+ {
35
+ retries: ProxyRetries,
36
+ }
37
+ end
31
38
  end
32
39
 
33
40
  class Parameters
@@ -135,6 +142,8 @@ module HTTPX
135
142
  # :proxy :: proxy options defining *:uri*, *:username*, *:password* or
136
143
  # *:scheme* (i.e. <tt>{ uri: "http://proxy" }</tt>)
137
144
  module OptionsMethods
145
+ private
146
+
138
147
  def option_proxy(value)
139
148
  value.is_a?(Parameters) ? value : Parameters.new(**Hash[value])
140
149
  end
@@ -160,9 +169,9 @@ module HTTPX
160
169
 
161
170
  next_proxy = proxy.uri
162
171
 
163
- raise Error, "Failed to connect to proxy" unless next_proxy
172
+ raise ProxyError, "Failed to connect to proxy" unless next_proxy
164
173
 
165
- raise Error,
174
+ raise ProxyError,
166
175
  "#{next_proxy.scheme}: unsupported proxy protocol" unless options.supported_proxy_protocols.include?(next_proxy.scheme)
167
176
 
168
177
  if (no_proxy = proxy.no_proxy)
@@ -179,20 +188,28 @@ module HTTPX
179
188
  private
180
189
 
181
190
  def fetch_response(request, selector, options)
182
- response = super
183
-
184
- if response.is_a?(ErrorResponse) && proxy_error?(request, response, options)
185
- options.proxy.shift
186
-
187
- # return last error response if no more proxies to try
188
- return response if options.proxy.uri.nil?
189
-
190
- log { "failed connecting to proxy, trying next..." }
191
- request.transition(:idle)
192
- send_request(request, selector, options)
193
- return
191
+ response = request.response # in case it goes wrong later
192
+
193
+ begin
194
+ response = super
195
+
196
+ if response.is_a?(ErrorResponse) && proxy_error?(request, response, options)
197
+ options.proxy.shift
198
+
199
+ # return last error response if no more proxies to try
200
+ return response if options.proxy.uri.nil?
201
+
202
+ log { "failed connecting to proxy, trying next..." }
203
+ request.transition(:idle)
204
+ send_request(request, selector, options)
205
+ return
206
+ end
207
+ response
208
+ rescue ProxyError
209
+ # may happen if coupled with retries, and there are no more proxies to try, in which case
210
+ # it'll end up here
211
+ response
194
212
  end
195
- response
196
213
  end
197
214
 
198
215
  def proxy_error?(_request, response, options)
@@ -203,15 +220,15 @@ module HTTPX
203
220
  when NativeResolveError
204
221
  proxy_uri = URI(options.proxy.uri)
205
222
 
206
- peer = error.connection.peer
223
+ unresolved_host = error.host
207
224
 
208
225
  # failed resolving proxy domain
209
- peer.host == proxy_uri.host && peer.port == proxy_uri.port
226
+ unresolved_host == proxy_uri.host
210
227
  when ResolveError
211
228
  proxy_uri = URI(options.proxy.uri)
212
229
 
213
230
  error.message.end_with?(proxy_uri.to_s)
214
- when *PROXY_ERRORS
231
+ when ProxyConnectionError
215
232
  # timeout errors connecting to proxy
216
233
  true
217
234
  else
@@ -251,6 +268,14 @@ module HTTPX
251
268
  when :connecting
252
269
  consume
253
270
  end
271
+ rescue *PROXY_ERRORS => e
272
+ if connecting?
273
+ error = ProxyConnectionError.new(e.message)
274
+ error.set_backtrace(e.backtrace)
275
+ raise error
276
+ end
277
+
278
+ raise e
254
279
  end
255
280
 
256
281
  def reset
@@ -292,13 +317,29 @@ module HTTPX
292
317
  end
293
318
  super
294
319
  end
320
+
321
+ def purge_after_closed
322
+ super
323
+ @io = @io.proxy_io if @io.respond_to?(:proxy_io)
324
+ end
325
+ end
326
+
327
+ module ProxyRetries
328
+ module InstanceMethods
329
+ def retryable_error?(ex)
330
+ super || ex.is_a?(ProxyConnectionError)
331
+ end
332
+ end
295
333
  end
296
334
  end
297
335
  register_plugin :proxy, Proxy
298
336
  end
299
337
 
300
338
  class ProxySSL < SSL
339
+ attr_reader :proxy_io
340
+
301
341
  def initialize(tcp, request_uri, options)
342
+ @proxy_io = tcp
302
343
  @io = tcp.to_io
303
344
  super(request_uri, tcp.addresses, options)
304
345
  @hostname = request_uri.host
@@ -81,7 +81,7 @@ module HTTPX::Plugins
81
81
 
82
82
  response.body.rewind
83
83
 
84
- ::IO.copy_stream(response.body, f)
84
+ IO.copy_stream(response.body, f)
85
85
  end
86
86
  end
87
87
 
@@ -131,7 +131,7 @@ module HTTPX::Plugins
131
131
  response.original_request = original_request
132
132
  response.finish!
133
133
 
134
- ::IO.copy_stream(f, response.body)
134
+ IO.copy_stream(f, response.body)
135
135
 
136
136
  response
137
137
  end
@@ -67,6 +67,8 @@ module HTTPX
67
67
  # * +#clear() -> void+)
68
68
  #
69
69
  module OptionsMethods
70
+ private
71
+
70
72
  def option_response_cache_store(value)
71
73
  case value
72
74
  when :store
@@ -57,6 +57,8 @@ module HTTPX
57
57
  # :retry_on :: callable which alternatively defines a different rule for when a response is to be retried
58
58
  # (i.e. <tt>->(res) { ... }</tt>).
59
59
  module OptionsMethods
60
+ private
61
+
60
62
  def option_retry_after(value)
61
63
  # return early if callable
62
64
  unless value.respond_to?(:call)
@@ -91,6 +91,8 @@ module HTTPX
91
91
  #
92
92
  # :allowed_schemes :: list of URI schemes allowed (defaults to <tt>["https", "http"]</tt>)
93
93
  module OptionsMethods
94
+ private
95
+
94
96
  def option_allowed_schemes(value)
95
97
  Array(value)
96
98
  end
@@ -129,8 +131,6 @@ module HTTPX
129
131
  end
130
132
 
131
133
  def addresses=(addrs)
132
- addrs = addrs.map { |addr| addr.is_a?(IPAddr) ? addr : IPAddr.new(addr) }
133
-
134
134
  addrs.reject!(&SsrfFilter.method(:unsafe_ip_address?))
135
135
 
136
136
  raise ServerSideRequestForgeryError, "#{@origin.host} has no public IP addresses" if addrs.empty?