httpx 0.10.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/doc/release_notes/0_10_1.md +39 -0
  3. data/lib/httpx/chainable.rb +7 -6
  4. data/lib/httpx/connection.rb +4 -15
  5. data/lib/httpx/connection/http1.rb +14 -1
  6. data/lib/httpx/connection/http2.rb +11 -12
  7. data/lib/httpx/errors.rb +1 -1
  8. data/lib/httpx/plugins/multipart.rb +15 -1
  9. data/lib/httpx/plugins/proxy.rb +16 -2
  10. data/lib/httpx/plugins/proxy/socks4.rb +14 -16
  11. data/lib/httpx/pool.rb +8 -14
  12. data/lib/httpx/request.rb +1 -1
  13. data/lib/httpx/resolver.rb +0 -2
  14. data/lib/httpx/resolver/https.rb +15 -22
  15. data/lib/httpx/resolver/native.rb +12 -13
  16. data/lib/httpx/resolver/resolver_mixin.rb +4 -2
  17. data/lib/httpx/resolver/system.rb +2 -2
  18. data/lib/httpx/selector.rb +8 -13
  19. data/lib/httpx/session.rb +9 -3
  20. data/lib/httpx/transcoder.rb +18 -0
  21. data/lib/httpx/transcoder/form.rb +9 -1
  22. data/lib/httpx/version.rb +1 -1
  23. data/sig/connection.rbs +84 -1
  24. data/sig/connection/http1.rbs +66 -0
  25. data/sig/connection/http2.rbs +74 -0
  26. data/sig/httpx.rbs +1 -0
  27. data/sig/options.rbs +3 -3
  28. data/sig/plugins/basic_authentication.rbs +1 -1
  29. data/sig/plugins/compression.rbs +1 -1
  30. data/sig/plugins/compression/brotli.rbs +1 -1
  31. data/sig/plugins/compression/deflate.rbs +1 -1
  32. data/sig/plugins/compression/gzip.rbs +1 -1
  33. data/sig/plugins/h2c.rbs +1 -1
  34. data/sig/plugins/multipart.rbs +4 -2
  35. data/sig/plugins/persistent.rbs +1 -1
  36. data/sig/plugins/proxy.rbs +2 -2
  37. data/sig/plugins/proxy/ssh.rbs +1 -1
  38. data/sig/plugins/rate_limiter.rbs +1 -1
  39. data/sig/pool.rbs +36 -2
  40. data/sig/request.rbs +1 -1
  41. data/sig/resolver.rbs +26 -0
  42. data/sig/resolver/https.rbs +49 -0
  43. data/sig/resolver/native.rbs +60 -0
  44. data/sig/resolver/resolver_mixin.rbs +27 -0
  45. data/sig/resolver/system.rbs +17 -0
  46. data/sig/response.rbs +1 -1
  47. data/sig/selector.rbs +20 -0
  48. data/sig/session.rbs +2 -2
  49. data/sig/transcoder.rbs +4 -2
  50. data/sig/transcoder/form.rbs +1 -1
  51. metadata +11 -4
  52. data/lib/httpx/resolver/options.rb +0 -25
  53. data/sig/test.rbs +0 -9
@@ -15,7 +15,6 @@ module HTTPX
15
15
  "AAAA" => Resolv::DNS::Resource::IN::AAAA,
16
16
  }.freeze
17
17
 
18
- # :nocov:
19
18
  DEFAULTS = if RUBY_VERSION < "2.2"
20
19
  {
21
20
  **Resolv::DNS::Config.default_config_hash,
@@ -44,7 +43,6 @@ module HTTPX
44
43
  false
45
44
  end
46
45
  end if DEFAULTS[:nameserver]
47
- # :nocov:
48
46
 
49
47
  DNS_PORT = 53
50
48
 
@@ -53,15 +51,15 @@ module HTTPX
53
51
  def initialize(options)
54
52
  @options = Options.new(options)
55
53
  @ns_index = 0
56
- @resolver_options = Resolver::Options.new(DEFAULTS.merge(@options.resolver_options || {}))
57
- @nameserver = @resolver_options.nameserver
58
- @_timeouts = Array(@resolver_options.timeouts)
54
+ @resolver_options = DEFAULTS.merge(@options.resolver_options)
55
+ @nameserver = @resolver_options[:nameserver]
56
+ @_timeouts = Array(@resolver_options[:timeouts])
59
57
  @timeouts = Hash.new { |timeouts, host| timeouts[host] = @_timeouts.dup }
60
- @_record_types = Hash.new { |types, host| types[host] = @resolver_options.record_types.dup }
58
+ @_record_types = Hash.new { |types, host| types[host] = @resolver_options[:record_types].dup }
61
59
  @connections = []
62
60
  @queries = {}
63
61
  @read_buffer = "".b
64
- @write_buffer = Buffer.new(@resolver_options.packet_size)
62
+ @write_buffer = Buffer.new(@resolver_options[:packet_size])
65
63
  @state = :idle
66
64
  end
67
65
 
@@ -111,9 +109,9 @@ module HTTPX
111
109
  return if early_resolve(connection)
112
110
 
113
111
  if @nameserver.nil?
114
- ex = ResolveError.new("Can't resolve #{connection.origin.host}: no nameserver")
112
+ ex = ResolveError.new("No available nameserver")
115
113
  ex.set_backtrace(caller)
116
- emit(:error, connection, ex)
114
+ throw(:resolve_error, ex)
117
115
  else
118
116
  @connections << connection
119
117
  resolve
@@ -164,7 +162,7 @@ module HTTPX
164
162
  connections.each { |ch| resolve(ch) }
165
163
  end
166
164
 
167
- def dread(wsize = @resolver_options.packet_size)
165
+ def dread(wsize = @resolver_options[:packet_size])
168
166
  loop do
169
167
  siz = @io.read(wsize, @read_buffer)
170
168
  return unless siz && siz.positive?
@@ -199,13 +197,14 @@ module HTTPX
199
197
  end
200
198
  end
201
199
 
202
- if addresses.empty?
200
+ if addresses.nil? || addresses.empty?
203
201
  hostname, connection = @queries.first
204
202
  @_record_types[hostname].shift
205
203
  if @_record_types[hostname].empty?
206
204
  @queries.delete(hostname)
207
205
  @_record_types.delete(hostname)
208
206
  @connections.delete(connection)
207
+
209
208
  raise NativeResolveError.new(connection, hostname)
210
209
  end
211
210
  else
@@ -223,7 +222,7 @@ module HTTPX
223
222
  end
224
223
  else
225
224
  @connections.delete(connection)
226
- Resolver.cached_lookup_set(connection.origin.host, addresses) if @resolver_options.cache
225
+ Resolver.cached_lookup_set(connection.origin.host, addresses) if @resolver_options[:cache]
227
226
  emit_addresses(connection, addresses.map { |addr| addr["data"] })
228
227
  end
229
228
  end
@@ -243,7 +242,7 @@ module HTTPX
243
242
  log { "resolver: resolve IDN #{connection.origin.non_ascii_hostname} as #{hostname}" } if connection.origin.non_ascii_hostname
244
243
  end
245
244
  @queries[hostname] = connection
246
- type = @_record_types[hostname].first
245
+ type = @_record_types[hostname].first || "A"
247
246
  log { "resolver: query #{type} for #{hostname}" }
248
247
  begin
249
248
  @write_buffer << Resolver.encode_dns_query(hostname, type: RECORD_TYPES[type])
@@ -38,7 +38,7 @@ module HTTPX
38
38
  def early_resolve(connection, hostname: connection.origin.host)
39
39
  addresses = connection.addresses ||
40
40
  ip_resolve(hostname) ||
41
- (@resolver_options.cache && Resolver.cached_lookup(hostname)) ||
41
+ (@resolver_options[:cache] && Resolver.cached_lookup(hostname)) ||
42
42
  system_resolve(hostname)
43
43
  return unless addresses
44
44
 
@@ -57,11 +57,13 @@ module HTTPX
57
57
  ips.map { |ip| IPAddr.new(ip) }
58
58
  end
59
59
 
60
- def emit_resolve_error(connection, hostname, ex = nil)
60
+ def emit_resolve_error(connection, hostname = connection.origin.host, ex = nil)
61
61
  emit(:error, connection, resolve_error(hostname, ex))
62
62
  end
63
63
 
64
64
  def resolve_error(hostname, ex = nil)
65
+ return ex if ex.is_a?(ResolveError)
66
+
65
67
  message = ex ? ex.message : "Can't resolve #{hostname}"
66
68
  error = ResolveError.new(message)
67
69
  error.set_backtrace(ex ? ex.backtrace : caller)
@@ -14,9 +14,9 @@ module HTTPX
14
14
 
15
15
  def initialize(options)
16
16
  @options = Options.new(options)
17
- @resolver_options = Resolver::Options.new(@options.resolver_options)
17
+ @resolver_options = @options.resolver_options
18
18
  @state = :idle
19
- resolv_options = @resolver_options.to_h
19
+ resolv_options = @resolver_options.dup
20
20
  timeouts = resolv_options.delete(:timeouts)
21
21
  resolv_options.delete(:cache)
22
22
  @resolver = Resolv::DNS.new(resolv_options.empty? ? nil : resolv_options)
@@ -4,19 +4,14 @@ require "io/wait"
4
4
 
5
5
  module IOExtensions
6
6
  refine IO do
7
- def wait(timeout = nil, mode = :read)
8
- case mode
9
- when :read
10
- wait_readable(timeout)
11
- when :write
12
- wait_writable(timeout)
13
- when :read_write
14
- r, w = IO.select([self], [self], nil, timeout)
15
-
16
- return unless r || w
17
-
18
- self
19
- end
7
+ # provides a fallback for rubies where IO#wait isn't implemented,
8
+ # but IO#wait_readable and IO#wait_writable are.
9
+ def wait(timeout = nil, _mode = :read_write)
10
+ r, w = IO.select([self], [self], nil, timeout)
11
+
12
+ return unless r || w
13
+
14
+ self
20
15
  end
21
16
  end
22
17
  end
@@ -77,10 +77,16 @@ module HTTPX
77
77
  end
78
78
 
79
79
  def set_connection_callbacks(connection, connections, options)
80
- connection.on(:uncoalesce) do |uncoalesced_uri|
81
- other_connection = build_connection(uncoalesced_uri, options)
80
+ connection.on(:misdirected) do |misdirected_request|
81
+ other_connection = connection.create_idle(ssl: { alpn_protocols: %w[http/1.1] })
82
+ other_connection.merge(connection)
83
+ catch(:coalesced) do
84
+ pool.init_connection(other_connection, options)
85
+ end
86
+ set_connection_callbacks(other_connection, connections, options)
82
87
  connections << other_connection
83
- connection.unmerge(other_connection)
88
+ misdirected_request.transition(:idle)
89
+ other_connection.send(misdirected_request)
84
90
  end
85
91
  connection.on(:altsvc) do |alt_origin, origin, alt_params|
86
92
  other_connection = build_altsvc_connection(connection, connections, alt_origin, origin, alt_params, options)
@@ -3,6 +3,24 @@
3
3
  module HTTPX
4
4
  module Transcoder
5
5
  extend Registry
6
+
7
+ def self.normalize_keys(key, value, &block)
8
+ if value.respond_to?(:to_ary)
9
+ if value.empty?
10
+ block.call("#{key}[]")
11
+ else
12
+ value.to_ary.each do |element|
13
+ normalize_keys("#{key}[]", element, &block)
14
+ end
15
+ end
16
+ elsif value.respond_to?(:to_hash)
17
+ value.to_hash.each do |child_key, child_value|
18
+ normalize_keys("#{key}[#{child_key}]", child_value, &block)
19
+ end
20
+ else
21
+ block.call(key.to_s, value)
22
+ end
23
+ end
6
24
  end
7
25
  end
8
26
 
@@ -12,10 +12,18 @@ module HTTPX::Transcoder
12
12
 
13
13
  def_delegator :@raw, :to_s
14
14
 
15
+ def_delegator :@raw, :to_str
16
+
15
17
  def_delegator :@raw, :bytesize
16
18
 
17
19
  def initialize(form)
18
- @raw = URI.encode_www_form(form)
20
+ @raw = form.each_with_object("".b) do |(key, val), buf|
21
+ HTTPX::Transcoder.normalize_keys(key, val) do |k, v|
22
+ buf << "&" unless buf.empty?
23
+ buf << URI.encode_www_form_component(k)
24
+ buf << "=#{URI.encode_www_form_component(v.to_s)}" unless v.nil?
25
+ end
26
+ end
19
27
  end
20
28
 
21
29
  def content_type
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
- VERSION = "0.10.0"
4
+ VERSION = "0.10.1"
5
5
  end
@@ -1,2 +1,85 @@
1
- class HTTPX::Connection
1
+ module HTTPX
2
+ class Connection
3
+ interface _Parser
4
+
5
+ def on: (Symbol) { (*untyped) -> void } -> void
6
+ def empty?: () -> bool
7
+ def close: () -> void
8
+ def consume: () -> void
9
+ def <<: (string) -> void
10
+ end
11
+
12
+ include Loggable
13
+ include Callbacks
14
+ extend HTTPX::Registry[String, Class]
15
+
16
+ attr_reader origin: generic_uri
17
+ attr_reader state: Symbol
18
+ attr_reader pending: Array[Request]
19
+ attr_reader options: options
20
+
21
+ def addresses: () -> Array[ipaddr]?
22
+
23
+ def addresses=: (Array[ipaddr]) -> void
24
+
25
+ def match?: (generic_uri, options) -> bool
26
+
27
+ def mergeable?: (Connection) -> bool
28
+
29
+ def coalescable?: (Connection) -> bool
30
+
31
+ def create_idle: (options) -> Connection
32
+ | () -> Connection
33
+
34
+ def merge: (Connection) -> void
35
+
36
+ def purge_pending: () { (Request) -> void } -> void
37
+
38
+ def match_altsvcs?: (generic_uri) -> bool
39
+
40
+ def connecting?: () -> bool
41
+ def inflight?: () -> boolish
42
+
43
+ def interests: () -> io_interests?
44
+
45
+ def to_io: () -> _ToIO
46
+
47
+ def call: () -> void
48
+
49
+ def close: () -> void
50
+ def reset: () -> void
51
+
52
+ def send: (Request) -> void
53
+
54
+ def timeout: () -> Numeric?
55
+
56
+ private
57
+
58
+ def initialize: (String, generic_uri, options) -> untyped
59
+
60
+ def connect: () -> void
61
+
62
+ def exhausted?: () -> boolish
63
+
64
+ def consume: () -> void
65
+
66
+ def send_pending: () -> void
67
+
68
+ def parser: () -> _Parser
69
+
70
+ def build_parser: () -> _Parser
71
+ | (String) -> _Parser
72
+
73
+ def set_parser_callbacks: (_Parser) -> void
74
+
75
+ def transition: (Symbol) -> void
76
+
77
+ def handle_response: () -> void
78
+
79
+ def on_error: (StandardError) -> void
80
+
81
+ def handle_error: (StandardError) -> void
82
+
83
+ def total_timeout: () -> Timers::Timer?
84
+ end
2
85
  end
@@ -0,0 +1,66 @@
1
+ module HTTPX
2
+ class Connection::HTTP1
3
+ include Callbacks
4
+ include Loggable
5
+
6
+ attr_reader pending: Array[Request]
7
+
8
+ @options: Options
9
+ @max_concurrent_requests: Integer
10
+ @max_requests: Integer
11
+ @parser: HTTP1
12
+ @buffer: Buffer
13
+
14
+ def interests: () -> io_interests?
15
+
16
+ def reset: () -> void
17
+
18
+ def close: () -> void
19
+
20
+ def empty?: () -> bool
21
+
22
+ def exhausted?: () -> bool
23
+
24
+ def <<: (String) -> void
25
+
26
+ def send: (Request) -> void
27
+
28
+ def consume: () -> void
29
+
30
+ def handle_error: (StandardError ex) -> void
31
+
32
+ def on_headers: (Hash[String, Array[String]] headers) -> void
33
+
34
+ def on_trailers: (Array[String, String] headers) -> void
35
+
36
+ def on_data: (string chunk) -> void
37
+
38
+ def on_complete: () -> void
39
+
40
+ def dispatch: () -> void
41
+
42
+ def ping: () -> void
43
+
44
+ private
45
+
46
+ def initialize: (Buffer, options) -> untyped
47
+
48
+ def manage_connection: (Response) -> void
49
+
50
+ def disable: () -> void
51
+
52
+ def disable_pipelining: () -> void
53
+
54
+ def set_request_headers: (Request) -> void
55
+
56
+ def headline_uri: (Request) -> String
57
+
58
+ def handle: (Request request) -> void
59
+
60
+ def join_headers: (Request request) -> void
61
+
62
+ def join_body: (Request request) -> void
63
+
64
+ def capitalized: (String field) -> String
65
+ end
66
+ end
@@ -1,4 +1,78 @@
1
1
  module HTTPX
2
2
  class Connection::HTTP2
3
+ include Callbacks
4
+ include Loggable
5
+
6
+ attr_reader streams: Hash[HTTP2Next::Stream, Response]
7
+ attr_reader pending: Array[Request]
8
+
9
+ @options: Options
10
+ @max_concurrent_requests: Integer
11
+ @max_requests: Integer
12
+ @drains: Hash[Request, String]
13
+ @pings: Array[String]
14
+ @buffer: Buffer
15
+
16
+ def interests: () -> io_interests
17
+
18
+ def close: () -> void
19
+
20
+ def empty?: () -> bool
21
+
22
+ def exhausted?: () -> bool
23
+
24
+ def <<: (String) -> void
25
+
26
+ def send: (Request) -> void
27
+
28
+ def consume: () -> void
29
+
30
+ def handle_error: (StandardError ex) -> void
31
+
32
+ def ping: () -> void
33
+
34
+ alias reset init_connection
35
+
36
+ private
37
+
38
+ def initialize: (Buffer, options) -> untyped
39
+
40
+ def send_pending: () -> void
41
+
42
+ def headline_uri: (Request) -> String
43
+
44
+ def set_request_headers: (Request) -> void
45
+
46
+ def handle: (Request request, HTTP2Next::Stream stream) -> void
47
+
48
+ def init_connection: () -> void
49
+
50
+ def handle_stream: (HTTP2Next::Stream stream, Request request) -> void
51
+
52
+ def join_headers: (HTTP2Next::Stream stream, Request request) -> void
53
+
54
+ def join_body: (HTTP2Next::Stream stream, Request request) -> void
55
+
56
+
57
+ # def on_stream_headers: (HTTP2Next::Stream stream, Request request, Array[String, String] headers) -> void
58
+
59
+ # def on_stream_data: (HTTP2Next::Stream stream, Request request, string data) -> void
60
+
61
+ # def on_stream_close: (HTTP2Next::Stream stream, Request request, Symbol? error) -> void
62
+
63
+ def on_frame: (string bytes) -> void
64
+
65
+ def on_settings: (*untyped) -> void
66
+
67
+ def on_close: (Integer last_frame, Symbol? error, String? payload) -> void
68
+
69
+ def on_frame_sent: (HTTP2Next::frame) -> void
70
+ def on_frame_received: (HTTP2Next::frame) -> void
71
+
72
+ def on_promise: (HTTP2Next::Stream) -> void
73
+
74
+ def on_origin: (String) -> void
75
+
76
+ def on_pong: (string ping) -> void
3
77
  end
4
78
  end