httpx 0.7.0 → 0.8.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.
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "digest"
4
+
3
5
  module HTTPX
4
6
  module Plugins
5
7
  #
@@ -179,20 +179,9 @@ module HTTPX
179
179
  super || @state == :connecting || @state == :connected
180
180
  end
181
181
 
182
- def to_io
183
- return super unless @options.proxy
184
-
185
- case @state
186
- when :idle
187
- transition(:connecting)
188
- when :connected
189
- transition(:open)
190
- end
191
- @io.to_io
192
- end
193
-
194
182
  def call
195
183
  super
184
+
196
185
  return unless @options.proxy
197
186
 
198
187
  case @state
@@ -210,11 +199,26 @@ module HTTPX
210
199
  emit(:close)
211
200
  end
212
201
 
202
+ private
203
+
204
+ def connect
205
+ return super unless @options.proxy
206
+
207
+ case @state
208
+ when :idle
209
+ transition(:connecting)
210
+ when :connected
211
+ transition(:open)
212
+ end
213
+ end
214
+
213
215
  def transition(nextstate)
214
216
  return super unless @options.proxy
215
217
 
216
218
  case nextstate
217
219
  when :closing
220
+ # this is a hack so that we can use the super method
221
+ # and it'll thing that the current state is open
218
222
  @state = :open if @state == :connecting
219
223
  end
220
224
  super
@@ -7,6 +7,10 @@ module HTTPX
7
7
  module Proxy
8
8
  module HTTP
9
9
  module ConnectionMethods
10
+ def connecting?
11
+ super || @state == :connecting || @state == :connected
12
+ end
13
+
10
14
  private
11
15
 
12
16
  def transition(nextstate)
@@ -34,7 +38,6 @@ module HTTPX
34
38
  when :idle
35
39
  @parser = ProxyParser.new(@write_buffer, @options)
36
40
  set_parser_callbacks(@parser)
37
- @parser.on(:close) { transition(:closing) }
38
41
  end
39
42
  end
40
43
  super
@@ -48,7 +51,7 @@ module HTTPX
48
51
  #
49
52
  if req.uri.scheme == "https"
50
53
  connect_request = ConnectRequest.new(req.uri, @options)
51
-
54
+ @inflight += 1
52
55
  parser.send(connect_request)
53
56
  else
54
57
  transition(:connected)
@@ -56,6 +59,7 @@ module HTTPX
56
59
  end
57
60
 
58
61
  def __http_on_connect(_, response)
62
+ @inflight -= 1
59
63
  if response.status == 200
60
64
  req = @pending.first
61
65
  request_uri = req.uri
@@ -67,6 +71,7 @@ module HTTPX
67
71
  while (req = pending.shift)
68
72
  req.emit(:response, response)
69
73
  end
74
+ reset
70
75
  end
71
76
  end
72
77
  end
@@ -39,7 +39,7 @@ module HTTPX
39
39
 
40
40
  @parser = nil
41
41
  end
42
- log(level: 1, label: "SOCKS4: ") { "#{nextstate}: #{@write_buffer.to_s.inspect}" } unless nextstate == :open
42
+ log(level: 1) { "SOCKS4: #{nextstate}: #{@write_buffer.to_s.inspect}" } unless nextstate == :open
43
43
  super
44
44
  end
45
45
 
@@ -31,6 +31,10 @@ module HTTPX
31
31
  end
32
32
  end
33
33
 
34
+ def connecting?
35
+ super || @state == :authenticating || @state == :negotiating
36
+ end
37
+
34
38
  private
35
39
 
36
40
  def transition(nextstate)
@@ -60,7 +64,7 @@ module HTTPX
60
64
 
61
65
  @parser = nil
62
66
  end
63
- log(level: 1, label: "SOCKS5: ") { "#{nextstate}: #{@write_buffer.to_s.inspect}" } unless nextstate == :open
67
+ log(level: 1) { "SOCKS5: #{nextstate}: #{@write_buffer.to_s.inspect}" } unless nextstate == :open
64
68
  super
65
69
  end
66
70
 
@@ -43,9 +43,9 @@ module HTTPX
43
43
  end
44
44
 
45
45
  def __on_promise_request(parser, stream, h)
46
- log(level: 1, label: "#{stream.id}: ") do
46
+ log(level: 1) do
47
47
  # :nocov:
48
- h.map { |k, v| "-> PROMISE HEADER: #{k}: #{v}" }.join("\n")
48
+ h.map { |k, v| "#{stream.id}: -> PROMISE HEADER: #{k}: #{v}" }.join("\n")
49
49
  # :nocov:
50
50
  end
51
51
  headers = @options.headers_class.new(h)
@@ -63,13 +63,19 @@ module HTTPX
63
63
  def fetch_response(request, connections, options)
64
64
  response = super
65
65
 
66
- retry_on = options.retry_on
67
-
68
- if response.is_a?(ErrorResponse) &&
66
+ if response &&
69
67
  request.retries.positive? &&
70
68
  __repeatable_request?(request, options) &&
71
- __retryable_error?(response.error) &&
72
- (!retry_on || retry_on.call(response))
69
+ (
70
+ # rubocop:disable Style/MultilineTernaryOperator
71
+ options.retry_on ?
72
+ options.retry_on.call(response) :
73
+ (
74
+ response.is_a?(ErrorResponse) && __retryable_error?(response.error)
75
+ )
76
+ # rubocop:enable Style/MultilineTernaryOperator
77
+ )
78
+
73
79
  request.retries -= 1
74
80
  log { "failed to get response, #{request.retries} tries to go..." }
75
81
  request.transition(:idle)
@@ -14,7 +14,7 @@ module HTTPX
14
14
 
15
15
  def initialize
16
16
  @resolvers = {}
17
- @_resolver_monitors = {}
17
+ @_resolver_ios = {}
18
18
  @timers = Timers::Group.new
19
19
  @selector = Selector.new
20
20
  @connections = []
@@ -28,15 +28,13 @@ module HTTPX
28
28
  def next_tick
29
29
  catch(:jump_tick) do
30
30
  timeout = [next_timeout, @timers.wait_interval].compact.min
31
- if timeout.negative?
31
+ if timeout && timeout.negative?
32
32
  @timers.fire
33
33
  throw(:jump_tick)
34
34
  end
35
35
 
36
- @selector.select(timeout) do |monitor|
37
- monitor.io.call
38
- monitor.interests = monitor.io.interests
39
- end
36
+ @selector.select(timeout, &:call)
37
+
40
38
  @timers.fire
41
39
  end
42
40
  rescue StandardError => e
@@ -86,7 +84,7 @@ module HTTPX
86
84
  resolver << connection
87
85
  return if resolver.empty?
88
86
 
89
- @_resolver_monitors[resolver] ||= @selector.register(resolver, :w)
87
+ @_resolver_ios[resolver] ||= @selector.register(resolver)
90
88
  end
91
89
 
92
90
  def on_resolver_connection(connection)
@@ -118,8 +116,7 @@ module HTTPX
118
116
  @resolvers.delete(resolver_type)
119
117
 
120
118
  @selector.deregister(resolver)
121
- monitor = @_resolver_monitors.delete(resolver)
122
- monitor.close if monitor
119
+ @_resolver_ios.delete(resolver)
123
120
  resolver.close unless resolver.closed?
124
121
  end
125
122
 
@@ -128,10 +125,8 @@ module HTTPX
128
125
  # if open, an IO was passed upstream, therefore
129
126
  # consider it connected already.
130
127
  @connected_connections += 1
131
- @selector.register(connection, :rw)
132
- else
133
- @selector.register(connection, :w)
134
128
  end
129
+ @selector.register(connection)
135
130
  connection.on(:close) do
136
131
  unregister_connection(connection)
137
132
  end
@@ -64,7 +64,8 @@ module HTTPX
64
64
 
65
65
  case handler
66
66
  when Symbol, String
67
- const_get(handler)
67
+ obj = const_get(handler)
68
+ @registry[tag] = obj
68
69
  else
69
70
  handler
70
71
  end
@@ -58,6 +58,12 @@ module HTTPX
58
58
  @state = :idle
59
59
  end
60
60
 
61
+ def interests
62
+ return :r if @state == :done || @state == :expect
63
+
64
+ :w
65
+ end
66
+
61
67
  # :nocov:
62
68
  if RUBY_VERSION < "2.2"
63
69
  # rubocop: disable Lint/UriEscapeUnescape:
@@ -219,6 +225,7 @@ module HTTPX
219
225
  case nextstate
220
226
  when :idle
221
227
  @response = nil
228
+ @drainer = nil
222
229
  when :headers
223
230
  return unless @state == :idle
224
231
  when :body
@@ -25,7 +25,7 @@ module HTTPX
25
25
 
26
26
  def_delegator :@connections, :empty?
27
27
 
28
- def_delegators :@resolver_connection, :to_io, :call, :interests, :close
28
+ def_delegators :@resolver_connection, :connecting?, :to_io, :call, :close
29
29
 
30
30
  def initialize(options)
31
31
  @options = Options.new(options)
@@ -62,8 +62,20 @@ module HTTPX
62
62
  resolver_connection.closed?
63
63
  end
64
64
 
65
+ def interests
66
+ return if @queries.empty?
67
+
68
+ resolver_connection.__send__(__method__)
69
+ end
70
+
65
71
  private
66
72
 
73
+ def connect
74
+ return if @queries.empty?
75
+
76
+ resolver_connection.__send__(__method__)
77
+ end
78
+
67
79
  def pool
68
80
  Thread.current[:httpx_connection_pool] ||= Pool.new
69
81
  end
@@ -84,7 +96,7 @@ module HTTPX
84
96
 
85
97
  hostname = hostname || @queries.key(connection) || connection.origin.host
86
98
  type = @_record_types[hostname].first
87
- log(label: "resolver: ") { "query #{type} for #{hostname}" }
99
+ log { "resolver: query #{type} for #{hostname}" }
88
100
  begin
89
101
  request = build_request(hostname, type)
90
102
  @requests[request] = connection
@@ -111,7 +123,7 @@ module HTTPX
111
123
  end
112
124
 
113
125
  def on_promise(_, stream)
114
- log(level: 2, label: "#{stream.id}: ") { "refusing stream!" }
126
+ log(level: 2) { "#{stream.id}: refusing stream!" }
115
127
  stream.refuse
116
128
  end
117
129
 
@@ -73,14 +73,6 @@ module HTTPX
73
73
  end
74
74
 
75
75
  def to_io
76
- case @state
77
- when :idle
78
- transition(:open)
79
- when :closed
80
- transition(:idle)
81
- transition(:open)
82
- end
83
- resolve if @queries.empty?
84
76
  @io.to_io
85
77
  end
86
78
 
@@ -93,11 +85,7 @@ module HTTPX
93
85
  rescue Errno::EHOSTUNREACH => e
94
86
  @ns_index += 1
95
87
  if @ns_index < @nameserver.size
96
- log(label: "resolver: ") do
97
- # :nocov:
98
- "failed resolving on nameserver #{@nameserver[@ns_index - 1]} (#{e.message})"
99
- # :nocov:
100
- end
88
+ log { "resolver: failed resolving on nameserver #{@nameserver[@ns_index - 1]} (#{e.message})" }
101
89
  transition(:idle)
102
90
  else
103
91
  handle_error(e)
@@ -107,6 +95,14 @@ module HTTPX
107
95
  end
108
96
 
109
97
  def interests
98
+ case @state
99
+ when :idle
100
+ transition(:open)
101
+ when :closed
102
+ transition(:idle)
103
+ transition(:open)
104
+ end
105
+
110
106
  !@write_buffer.empty? || @queries.empty? ? :w : :r
111
107
  end
112
108
 
@@ -160,11 +156,7 @@ module HTTPX
160
156
  raise NativeResolveError.new(connection, host)
161
157
  else
162
158
  connections << connection
163
- log(label: "resolver: ") do
164
- # :nocov:
165
- "timeout after #{prev_timeout}s, retry(#{timeouts.first}) #{host}..."
166
- # :nocov:
167
- end
159
+ log { "resolver: timeout after #{prev_timeout}s, retry(#{timeouts.first}) #{host}..." }
168
160
  end
169
161
  end
170
162
  @queries = queries
@@ -174,14 +166,11 @@ module HTTPX
174
166
  def dread(wsize = @resolver_options.packet_size)
175
167
  loop do
176
168
  siz = @io.read(wsize, @read_buffer)
177
- unless siz
178
- emit(:close)
179
- return
180
- end
181
- return if siz.zero?
169
+ return unless siz && siz.positive?
182
170
 
183
- log(label: "resolver: ") { "READ: #{siz} bytes..." }
171
+ log { "resolver: READ: #{siz} bytes..." }
184
172
  parse(@read_buffer)
173
+ return if @state == :closed
185
174
  end
186
175
  end
187
176
 
@@ -190,12 +179,10 @@ module HTTPX
190
179
  return if @write_buffer.empty?
191
180
 
192
181
  siz = @io.write(@write_buffer)
193
- unless siz
194
- emit(:close)
195
- return
196
- end
197
- log(label: "resolver: ") { "WRITE: #{siz} bytes..." }
198
- return if siz.zero?
182
+ return unless siz && siz.positive?
183
+
184
+ log { "resolver: WRITE: #{siz} bytes..." }
185
+ return if @state == :closed
199
186
  end
200
187
  end
201
188
 
@@ -253,7 +240,7 @@ module HTTPX
253
240
  hostname = hostname || @queries.key(connection) || connection.origin.host
254
241
  @queries[hostname] = connection
255
242
  type = @_record_types[hostname].first
256
- log(label: "resolver: ") { "query #{type} for #{hostname}" }
243
+ log { "resolver: query #{type} for #{hostname}" }
257
244
  begin
258
245
  @write_buffer << Resolver.encode_dns_query(hostname, type: RECORD_TYPES[type])
259
246
  rescue Resolv::DNS::EncodeError => e
@@ -269,7 +256,7 @@ module HTTPX
269
256
  uri = URI::Generic.build(scheme: "udp", port: port)
270
257
  uri.hostname = ip
271
258
  type = IO.registry(uri.scheme)
272
- log(label: "resolver: ") { "server: #{uri}..." }
259
+ log { "resolver: server: #{uri}..." }
273
260
  @io = type.new(uri, [IPAddr.new(ip)], @options)
274
261
  end
275
262
 
@@ -285,8 +272,11 @@ module HTTPX
285
272
  return unless @state == :idle
286
273
 
287
274
  build_socket
275
+
288
276
  @io.connect
289
277
  return unless @io.connected?
278
+
279
+ resolve if @queries.empty?
290
280
  when :closed
291
281
  return unless @state == :open
292
282
 
@@ -6,7 +6,7 @@ module HTTPX
6
6
  @options = options
7
7
  end
8
8
 
9
- def method_missing(m, *args, &block)
9
+ def method_missing(m, *, &block)
10
10
  if @options.key?(m)
11
11
  @options[m]
12
12
  else
@@ -14,7 +14,7 @@ module HTTPX
14
14
  end
15
15
  end
16
16
 
17
- def respond_to_missing?(m)
17
+ def respond_to_missing?(m, *)
18
18
  @options.key?(m) || super
19
19
  end
20
20
 
@@ -30,7 +30,7 @@ module HTTPX
30
30
  addresses.map! do |address|
31
31
  address.is_a?(IPAddr) ? address : IPAddr.new(address.to_s)
32
32
  end
33
- log(label: "resolver: ") { "answer #{connection.origin.host}: #{addresses.inspect}" }
33
+ log { "resolver: answer #{connection.origin.host}: #{addresses.inspect}" }
34
34
  connection.addresses = addresses
35
35
  catch(:coalesced) { emit(:resolve, connection) }
36
36
  end