httpx 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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