httpx 1.5.1 → 1.6.1

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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/doc/release_notes/1_6_0.md +50 -0
  3. data/doc/release_notes/1_6_1.md +17 -0
  4. data/lib/httpx/adapters/datadog.rb +23 -13
  5. data/lib/httpx/adapters/faraday.rb +14 -9
  6. data/lib/httpx/adapters/webmock.rb +1 -1
  7. data/lib/httpx/callbacks.rb +1 -1
  8. data/lib/httpx/connection/http1.rb +5 -6
  9. data/lib/httpx/connection/http2.rb +34 -18
  10. data/lib/httpx/connection.rb +19 -26
  11. data/lib/httpx/errors.rb +3 -1
  12. data/lib/httpx/io/ssl.rb +1 -4
  13. data/lib/httpx/io/tcp.rb +52 -21
  14. data/lib/httpx/io/unix.rb +4 -3
  15. data/lib/httpx/loggable.rb +4 -1
  16. data/lib/httpx/options.rb +248 -160
  17. data/lib/httpx/plugins/aws_sdk_authentication.rb +2 -0
  18. data/lib/httpx/plugins/aws_sigv4.rb +2 -0
  19. data/lib/httpx/plugins/callbacks.rb +13 -1
  20. data/lib/httpx/plugins/circuit_breaker.rb +2 -0
  21. data/lib/httpx/plugins/content_digest.rb +2 -0
  22. data/lib/httpx/plugins/cookies.rb +2 -2
  23. data/lib/httpx/plugins/digest_auth.rb +2 -0
  24. data/lib/httpx/plugins/expect.rb +2 -0
  25. data/lib/httpx/plugins/fiber_concurrency.rb +195 -0
  26. data/lib/httpx/plugins/follow_redirects.rb +2 -0
  27. data/lib/httpx/plugins/grpc.rb +2 -0
  28. data/lib/httpx/plugins/h2c.rb +26 -16
  29. data/lib/httpx/plugins/internal_telemetry.rb +0 -49
  30. data/lib/httpx/plugins/ntlm_auth.rb +2 -0
  31. data/lib/httpx/plugins/oauth.rb +10 -2
  32. data/lib/httpx/plugins/persistent.rb +27 -18
  33. data/lib/httpx/plugins/proxy/socks4.rb +1 -1
  34. data/lib/httpx/plugins/proxy/socks5.rb +1 -1
  35. data/lib/httpx/plugins/proxy/ssh.rb +2 -0
  36. data/lib/httpx/plugins/proxy.rb +61 -20
  37. data/lib/httpx/plugins/response_cache/file_store.rb +2 -2
  38. data/lib/httpx/plugins/response_cache.rb +2 -0
  39. data/lib/httpx/plugins/retries.rb +2 -0
  40. data/lib/httpx/plugins/ssrf_filter.rb +2 -2
  41. data/lib/httpx/plugins/stream_bidi.rb +3 -3
  42. data/lib/httpx/plugins/upgrade/h2.rb +11 -1
  43. data/lib/httpx/plugins/upgrade.rb +8 -0
  44. data/lib/httpx/pool.rb +15 -10
  45. data/lib/httpx/request/body.rb +8 -3
  46. data/lib/httpx/request.rb +22 -11
  47. data/lib/httpx/resolver/entry.rb +30 -0
  48. data/lib/httpx/resolver/https.rb +3 -1
  49. data/lib/httpx/resolver/multi.rb +16 -3
  50. data/lib/httpx/resolver/native.rb +15 -6
  51. data/lib/httpx/resolver/resolver.rb +15 -11
  52. data/lib/httpx/resolver/system.rb +5 -3
  53. data/lib/httpx/resolver.rb +49 -21
  54. data/lib/httpx/response/body.rb +1 -1
  55. data/lib/httpx/response/buffer.rb +13 -18
  56. data/lib/httpx/selector.rb +92 -34
  57. data/lib/httpx/session.rb +89 -30
  58. data/lib/httpx/session_extensions.rb +3 -2
  59. data/lib/httpx/transcoder/form.rb +1 -13
  60. data/lib/httpx/transcoder/multipart/mime_type_detector.rb +1 -1
  61. data/lib/httpx/transcoder/multipart.rb +14 -0
  62. data/lib/httpx/transcoder/utils/deflater.rb +1 -1
  63. data/lib/httpx/version.rb +1 -1
  64. data/sig/callbacks.rbs +1 -1
  65. data/sig/chainable.rbs +1 -0
  66. data/sig/connection/http1.rbs +2 -0
  67. data/sig/connection/http2.rbs +5 -1
  68. data/sig/connection.rbs +6 -6
  69. data/sig/errors.rbs +3 -1
  70. data/sig/io/ssl.rbs +1 -1
  71. data/sig/io/tcp.rbs +13 -7
  72. data/sig/io/udp.rbs +7 -2
  73. data/sig/io/unix.rbs +0 -1
  74. data/sig/io.rbs +0 -3
  75. data/sig/options.rbs +64 -11
  76. data/sig/plugins/fiber_concurrency.rbs +51 -0
  77. data/sig/plugins/h2c.rbs +5 -1
  78. data/sig/plugins/oauth.rbs +15 -1
  79. data/sig/plugins/persistent.rbs +1 -1
  80. data/sig/plugins/proxy/socks4.rbs +1 -1
  81. data/sig/plugins/proxy/socks5.rbs +1 -1
  82. data/sig/plugins/proxy.rbs +5 -2
  83. data/sig/plugins/ssrf_filter.rbs +1 -1
  84. data/sig/plugins/stream_bidi.rbs +2 -2
  85. data/sig/request.rbs +4 -1
  86. data/sig/resolver/entry.rbs +13 -0
  87. data/sig/resolver/native.rbs +1 -0
  88. data/sig/resolver/resolver.rbs +2 -3
  89. data/sig/resolver/system.rbs +2 -2
  90. data/sig/resolver.rbs +12 -11
  91. data/sig/response.rbs +2 -2
  92. data/sig/selector.rbs +18 -10
  93. data/sig/session.rbs +4 -0
  94. data/sig/transcoder/form.rbs +3 -3
  95. data/sig/transcoder/multipart.rbs +9 -3
  96. metadata +11 -3
data/lib/httpx/io/tcp.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "resolv"
4
- require "ipaddr"
5
4
 
6
5
  module HTTPX
7
6
  class TCP
@@ -15,7 +14,10 @@ module HTTPX
15
14
 
16
15
  def initialize(origin, addresses, options)
17
16
  @state = :idle
17
+ @keep_open = false
18
18
  @addresses = []
19
+ @ip_index = -1
20
+ @ip = nil
19
21
  @hostname = origin.host
20
22
  @options = options
21
23
  @fallback_protocol = @options.fallback_protocol
@@ -30,7 +32,8 @@ module HTTPX
30
32
  end
31
33
  raise Error, "Given IO objects do not match the request authority" unless @io
32
34
 
33
- _, _, _, @ip = @io.addr
35
+ _, _, _, ip = @io.addr
36
+ @ip = Resolver::Entry.new(ip)
34
37
  @addresses << @ip
35
38
  @keep_open = true
36
39
  @state = :connected
@@ -47,16 +50,31 @@ module HTTPX
47
50
  def add_addresses(addrs)
48
51
  return if addrs.empty?
49
52
 
50
- addrs = addrs.map { |addr| addr.is_a?(IPAddr) ? addr : IPAddr.new(addr) }
51
-
52
53
  ip_index = @ip_index || (@addresses.size - 1)
53
54
  if addrs.first.ipv6?
54
55
  # should be the next in line
55
56
  @addresses = [*@addresses[0, ip_index], *addrs, *@addresses[ip_index..-1]]
56
57
  else
57
58
  @addresses.unshift(*addrs)
58
- @ip_index += addrs.size if @ip_index
59
59
  end
60
+ @ip_index += addrs.size
61
+ end
62
+
63
+ # eliminates expired entries and returns whether there are still any left.
64
+ def addresses?
65
+ prev_addr_size = @addresses.size
66
+
67
+ @addresses.delete_if(&:expired?).sort! do |addr1, addr2|
68
+ if addr1.ipv6?
69
+ addr2.ipv6? ? 0 : 1
70
+ else
71
+ addr2.ipv6? ? -1 : 0
72
+ end
73
+ end
74
+
75
+ @ip_index = @addresses.size - 1 if prev_addr_size != @addresses.size
76
+
77
+ @addresses.any?
60
78
  end
61
79
 
62
80
  def to_io
@@ -70,27 +88,51 @@ module HTTPX
70
88
  def connect
71
89
  return unless closed?
72
90
 
91
+ if @addresses.empty?
92
+ # an idle connection trying to connect with no available addresses is a connection
93
+ # out of the initial context which is back to the DNS resolution loop. This may
94
+ # happen in a fiber-aware context where a connection reconnects with expired addresses,
95
+ # and context is passed back to a fiber on the same connection while waiting for the
96
+ # DNS answer.
97
+ log { "tried connecting while resolving, skipping..." }
98
+
99
+ return
100
+ end
101
+
73
102
  if !@io || @io.closed?
74
103
  transition(:idle)
75
104
  @io = build_socket
76
105
  end
77
106
  try_connect
107
+ rescue Errno::EHOSTUNREACH,
108
+ Errno::ENETUNREACH => e
109
+ @ip_index -= 1
110
+
111
+ raise e if @ip_index.negative?
112
+
113
+ log { "failed connecting to #{@ip} (#{e.message}), evict from cache and trying next..." }
114
+ Resolver.cached_lookup_evict(@hostname, @ip)
115
+
116
+ @io = build_socket
117
+ retry
78
118
  rescue Errno::ECONNREFUSED,
79
119
  Errno::EADDRNOTAVAIL,
80
- Errno::EHOSTUNREACH,
81
120
  SocketError,
82
121
  IOError => e
83
- raise e if @ip_index <= 0
122
+ @ip_index -= 1
123
+
124
+ raise e if @ip_index.negative?
84
125
 
85
126
  log { "failed connecting to #{@ip} (#{e.message}), trying next..." }
86
- @ip_index -= 1
87
127
  @io = build_socket
88
128
  retry
89
129
  rescue Errno::ETIMEDOUT => e
90
- raise ConnectTimeoutError.new(@options.timeout[:connect_timeout], e.message) if @ip_index <= 0
130
+ @ip_index -= 1
131
+
132
+ raise ConnectTimeoutError.new(@options.timeout[:connect_timeout], e.message) if @ip_index.negative?
91
133
 
92
134
  log { "failed connecting to #{@ip} (#{e.message}), trying next..." }
93
- @ip_index -= 1
135
+
94
136
  @io = build_socket
95
137
  retry
96
138
  end
@@ -154,17 +196,6 @@ module HTTPX
154
196
  @state == :idle || @state == :closed
155
197
  end
156
198
 
157
- def expired?
158
- # do not mess with external sockets
159
- return false if @options.io
160
-
161
- return true unless @addresses
162
-
163
- resolver_addresses = Resolver.nolookup_resolve(@hostname)
164
-
165
- (Array(resolver_addresses) & @addresses).empty?
166
- end
167
-
168
199
  # :nocov:
169
200
  def inspect
170
201
  "#<#{self.class}:#{object_id} " \
data/lib/httpx/io/unix.rb CHANGED
@@ -48,11 +48,12 @@ module HTTPX
48
48
  transition(:connected)
49
49
  rescue Errno::EINPROGRESS,
50
50
  Errno::EALREADY,
51
- ::IO::WaitReadable
51
+ IO::WaitReadable
52
52
  end
53
53
 
54
- def expired?
55
- false
54
+ # the path is always explicitly passed, so no point in resolving.
55
+ def addresses?
56
+ true
56
57
  end
57
58
 
58
59
  # :nocov:
@@ -34,7 +34,10 @@ module HTTPX
34
34
  klass = klass.superclass
35
35
  end
36
36
 
37
- message = +"(pid:#{Process.pid} tid:#{Thread.current.object_id}, self:#{class_name}##{object_id}) "
37
+ message = +"(pid:#{Process.pid}, " \
38
+ "tid:#{Thread.current.object_id}, " \
39
+ "fid:#{Fiber.current.object_id}, " \
40
+ "self:#{class_name}##{object_id}) "
38
41
  message << msg.call << "\n"
39
42
  message = "\e[#{COLORS[color]}m#{message}\e[0m" if color && debug_stream.respond_to?(:isatty) && debug_stream.isatty
40
43
  debug_stream << message