httpx 0.20.0 → 1.3.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 (250) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +0 -48
  3. data/README.md +54 -45
  4. data/doc/release_notes/0_10_0.md +2 -2
  5. data/doc/release_notes/0_11_0.md +3 -5
  6. data/doc/release_notes/0_12_0.md +5 -5
  7. data/doc/release_notes/0_13_0.md +5 -5
  8. data/doc/release_notes/0_14_0.md +2 -2
  9. data/doc/release_notes/0_16_0.md +3 -3
  10. data/doc/release_notes/0_17_0.md +1 -1
  11. data/doc/release_notes/0_18_0.md +4 -4
  12. data/doc/release_notes/0_18_2.md +1 -1
  13. data/doc/release_notes/0_19_0.md +1 -1
  14. data/doc/release_notes/0_19_8.md +1 -1
  15. data/doc/release_notes/0_20_0.md +2 -2
  16. data/doc/release_notes/0_20_1.md +5 -0
  17. data/doc/release_notes/0_20_2.md +7 -0
  18. data/doc/release_notes/0_20_3.md +6 -0
  19. data/doc/release_notes/0_20_4.md +17 -0
  20. data/doc/release_notes/0_20_5.md +3 -0
  21. data/doc/release_notes/0_21_0.md +96 -0
  22. data/doc/release_notes/0_21_1.md +12 -0
  23. data/doc/release_notes/0_22_0.md +13 -0
  24. data/doc/release_notes/0_22_1.md +11 -0
  25. data/doc/release_notes/0_22_2.md +5 -0
  26. data/doc/release_notes/0_22_3.md +55 -0
  27. data/doc/release_notes/0_22_4.md +6 -0
  28. data/doc/release_notes/0_22_5.md +6 -0
  29. data/doc/release_notes/0_23_0.md +42 -0
  30. data/doc/release_notes/0_23_1.md +5 -0
  31. data/doc/release_notes/0_23_2.md +5 -0
  32. data/doc/release_notes/0_23_3.md +6 -0
  33. data/doc/release_notes/0_23_4.md +5 -0
  34. data/doc/release_notes/0_24_0.md +48 -0
  35. data/doc/release_notes/0_24_1.md +12 -0
  36. data/doc/release_notes/0_24_2.md +12 -0
  37. data/doc/release_notes/0_24_3.md +12 -0
  38. data/doc/release_notes/0_24_4.md +18 -0
  39. data/doc/release_notes/0_24_5.md +6 -0
  40. data/doc/release_notes/0_24_6.md +5 -0
  41. data/doc/release_notes/0_24_7.md +10 -0
  42. data/doc/release_notes/1_0_0.md +60 -0
  43. data/doc/release_notes/1_0_1.md +5 -0
  44. data/doc/release_notes/1_0_2.md +7 -0
  45. data/doc/release_notes/1_1_0.md +32 -0
  46. data/doc/release_notes/1_1_1.md +17 -0
  47. data/doc/release_notes/1_1_2.md +12 -0
  48. data/doc/release_notes/1_1_3.md +18 -0
  49. data/doc/release_notes/1_1_4.md +6 -0
  50. data/doc/release_notes/1_1_5.md +12 -0
  51. data/doc/release_notes/1_2_0.md +49 -0
  52. data/doc/release_notes/1_2_1.md +6 -0
  53. data/doc/release_notes/1_2_2.md +10 -0
  54. data/doc/release_notes/1_2_3.md +16 -0
  55. data/doc/release_notes/1_2_4.md +8 -0
  56. data/doc/release_notes/1_2_5.md +7 -0
  57. data/doc/release_notes/1_2_6.md +13 -0
  58. data/doc/release_notes/1_3_0.md +18 -0
  59. data/doc/release_notes/1_3_1.md +17 -0
  60. data/lib/httpx/adapters/datadog.rb +215 -122
  61. data/lib/httpx/adapters/faraday.rb +145 -107
  62. data/lib/httpx/adapters/sentry.rb +26 -7
  63. data/lib/httpx/adapters/webmock.rb +34 -18
  64. data/lib/httpx/altsvc.rb +63 -26
  65. data/lib/httpx/base64.rb +27 -0
  66. data/lib/httpx/buffer.rb +12 -0
  67. data/lib/httpx/callbacks.rb +5 -3
  68. data/lib/httpx/chainable.rb +54 -39
  69. data/lib/httpx/connection/http1.rb +75 -44
  70. data/lib/httpx/connection/http2.rb +31 -38
  71. data/lib/httpx/connection.rb +287 -117
  72. data/lib/httpx/domain_name.rb +10 -13
  73. data/lib/httpx/errors.rb +52 -2
  74. data/lib/httpx/extensions.rb +24 -131
  75. data/lib/httpx/io/ssl.rb +83 -77
  76. data/lib/httpx/io/tcp.rb +48 -71
  77. data/lib/httpx/io/udp.rb +18 -52
  78. data/lib/httpx/io/unix.rb +10 -15
  79. data/lib/httpx/io.rb +3 -9
  80. data/lib/httpx/loggable.rb +4 -19
  81. data/lib/httpx/options.rb +176 -118
  82. data/lib/httpx/parser/http1.rb +4 -0
  83. data/lib/httpx/plugins/{authentication → auth}/basic.rb +1 -5
  84. data/lib/httpx/plugins/{authentication → auth}/digest.rb +14 -14
  85. data/lib/httpx/plugins/{authentication → auth}/ntlm.rb +1 -3
  86. data/lib/httpx/plugins/{authentication → auth}/socks5.rb +0 -2
  87. data/lib/httpx/plugins/auth.rb +25 -0
  88. data/lib/httpx/plugins/aws_sdk_authentication.rb +4 -3
  89. data/lib/httpx/plugins/aws_sigv4.rb +12 -9
  90. data/lib/httpx/plugins/basic_auth.rb +29 -0
  91. data/lib/httpx/plugins/brotli.rb +50 -0
  92. data/lib/httpx/plugins/callbacks.rb +91 -0
  93. data/lib/httpx/plugins/circuit_breaker/circuit.rb +100 -0
  94. data/lib/httpx/plugins/circuit_breaker/circuit_store.rb +53 -0
  95. data/lib/httpx/plugins/circuit_breaker.rb +148 -0
  96. data/lib/httpx/plugins/cookies/set_cookie_parser.rb +0 -2
  97. data/lib/httpx/plugins/cookies.rb +30 -17
  98. data/lib/httpx/plugins/{digest_authentication.rb → digest_auth.rb} +14 -12
  99. data/lib/httpx/plugins/expect.rb +21 -14
  100. data/lib/httpx/plugins/follow_redirects.rb +140 -41
  101. data/lib/httpx/plugins/grpc/call.rb +2 -3
  102. data/lib/httpx/plugins/grpc/grpc_encoding.rb +88 -0
  103. data/lib/httpx/plugins/grpc/message.rb +7 -37
  104. data/lib/httpx/plugins/grpc.rb +36 -29
  105. data/lib/httpx/plugins/h2c.rb +26 -19
  106. data/lib/httpx/plugins/internal_telemetry.rb +16 -0
  107. data/lib/httpx/plugins/{ntlm_authentication.rb → ntlm_auth.rb} +7 -5
  108. data/lib/httpx/plugins/oauth.rb +175 -0
  109. data/lib/httpx/plugins/persistent.rb +1 -1
  110. data/lib/httpx/plugins/proxy/http.rb +23 -13
  111. data/lib/httpx/plugins/proxy/socks4.rb +9 -7
  112. data/lib/httpx/plugins/proxy/socks5.rb +11 -9
  113. data/lib/httpx/plugins/proxy.rb +80 -61
  114. data/lib/httpx/plugins/push_promise.rb +1 -1
  115. data/lib/httpx/plugins/rate_limiter.rb +5 -1
  116. data/lib/httpx/plugins/response_cache/file_store.rb +40 -0
  117. data/lib/httpx/plugins/response_cache/store.rb +62 -25
  118. data/lib/httpx/plugins/response_cache.rb +105 -12
  119. data/lib/httpx/plugins/retries.rb +87 -17
  120. data/lib/httpx/plugins/ssrf_filter.rb +145 -0
  121. data/lib/httpx/plugins/stream.rb +27 -23
  122. data/lib/httpx/plugins/upgrade/h2.rb +4 -4
  123. data/lib/httpx/plugins/upgrade.rb +8 -10
  124. data/lib/httpx/plugins/webdav.rb +80 -0
  125. data/lib/httpx/pool/synch_pool.rb +93 -0
  126. data/lib/httpx/pool.rb +102 -27
  127. data/lib/httpx/punycode.rb +9 -291
  128. data/lib/httpx/request/body.rb +154 -0
  129. data/lib/httpx/request.rb +130 -146
  130. data/lib/httpx/resolver/https.rb +62 -27
  131. data/lib/httpx/resolver/multi.rb +9 -13
  132. data/lib/httpx/resolver/native.rb +192 -76
  133. data/lib/httpx/resolver/resolver.rb +34 -9
  134. data/lib/httpx/resolver/system.rb +16 -11
  135. data/lib/httpx/resolver.rb +38 -16
  136. data/lib/httpx/response/body.rb +242 -0
  137. data/lib/httpx/response/buffer.rb +96 -0
  138. data/lib/httpx/response.rb +159 -217
  139. data/lib/httpx/selector.rb +9 -4
  140. data/lib/httpx/session.rb +137 -89
  141. data/lib/httpx/session_extensions.rb +4 -1
  142. data/lib/httpx/timers.rb +34 -8
  143. data/lib/httpx/transcoder/body.rb +0 -2
  144. data/lib/httpx/transcoder/chunker.rb +0 -1
  145. data/lib/httpx/transcoder/deflate.rb +37 -0
  146. data/lib/httpx/transcoder/form.rb +52 -33
  147. data/lib/httpx/transcoder/gzip.rb +74 -0
  148. data/lib/httpx/transcoder/json.rb +21 -8
  149. data/lib/httpx/transcoder/multipart/decoder.rb +139 -0
  150. data/lib/httpx/{plugins → transcoder}/multipart/encoder.rb +4 -4
  151. data/lib/httpx/{plugins → transcoder}/multipart/mime_type_detector.rb +1 -1
  152. data/lib/httpx/{plugins → transcoder}/multipart/part.rb +3 -2
  153. data/lib/httpx/transcoder/multipart.rb +17 -0
  154. data/lib/httpx/transcoder/utils/body_reader.rb +46 -0
  155. data/lib/httpx/transcoder/utils/deflater.rb +72 -0
  156. data/lib/httpx/transcoder/utils/inflater.rb +19 -0
  157. data/lib/httpx/transcoder/xml.rb +52 -0
  158. data/lib/httpx/transcoder.rb +5 -6
  159. data/lib/httpx/utils.rb +36 -16
  160. data/lib/httpx/version.rb +1 -1
  161. data/lib/httpx.rb +12 -14
  162. data/sig/altsvc.rbs +33 -0
  163. data/sig/buffer.rbs +2 -1
  164. data/sig/callbacks.rbs +3 -3
  165. data/sig/chainable.rbs +11 -9
  166. data/sig/connection/http1.rbs +8 -7
  167. data/sig/connection/http2.rbs +19 -19
  168. data/sig/connection.rbs +64 -24
  169. data/sig/errors.rbs +22 -3
  170. data/sig/httpx.rbs +5 -4
  171. data/sig/io/ssl.rbs +27 -0
  172. data/sig/io/tcp.rbs +60 -0
  173. data/sig/io/udp.rbs +20 -0
  174. data/sig/io/unix.rbs +27 -0
  175. data/sig/io.rbs +6 -0
  176. data/sig/options.rbs +32 -22
  177. data/sig/parser/http1.rbs +1 -1
  178. data/sig/plugins/{authentication → auth}/basic.rbs +0 -2
  179. data/sig/plugins/{authentication → auth}/digest.rbs +2 -1
  180. data/sig/plugins/auth.rbs +13 -0
  181. data/sig/plugins/{basic_authentication.rbs → basic_auth.rbs} +2 -2
  182. data/sig/plugins/brotli.rbs +22 -0
  183. data/sig/plugins/callbacks.rbs +38 -0
  184. data/sig/plugins/circuit_breaker.rbs +71 -0
  185. data/sig/plugins/compression.rbs +7 -5
  186. data/sig/plugins/cookies/jar.rbs +2 -2
  187. data/sig/plugins/cookies.rbs +2 -0
  188. data/sig/plugins/{digest_authentication.rbs → digest_auth.rbs} +2 -2
  189. data/sig/plugins/follow_redirects.rbs +18 -4
  190. data/sig/plugins/grpc/call.rbs +19 -0
  191. data/sig/plugins/grpc/grpc_encoding.rbs +37 -0
  192. data/sig/plugins/grpc/message.rbs +17 -0
  193. data/sig/plugins/grpc.rbs +7 -32
  194. data/sig/plugins/h2c.rbs +1 -1
  195. data/sig/plugins/{ntlm_authentication.rbs → ntlm_auth.rbs} +2 -2
  196. data/sig/plugins/oauth.rbs +54 -0
  197. data/sig/plugins/proxy/http.rbs +3 -0
  198. data/sig/plugins/proxy/socks4.rbs +9 -6
  199. data/sig/plugins/proxy/socks5.rbs +10 -6
  200. data/sig/plugins/proxy/ssh.rbs +1 -1
  201. data/sig/plugins/proxy.rbs +13 -5
  202. data/sig/plugins/push_promise.rbs +3 -3
  203. data/sig/plugins/rate_limiter.rbs +1 -1
  204. data/sig/plugins/response_cache.rbs +36 -7
  205. data/sig/plugins/retries.rbs +30 -8
  206. data/sig/plugins/stream.rbs +24 -17
  207. data/sig/plugins/upgrade.rbs +5 -3
  208. data/sig/pool.rbs +10 -7
  209. data/sig/request/body.rbs +38 -0
  210. data/sig/request.rbs +15 -24
  211. data/sig/resolver/https.rbs +8 -3
  212. data/sig/resolver/native.rbs +17 -4
  213. data/sig/resolver/resolver.rbs +8 -6
  214. data/sig/resolver/system.rbs +2 -0
  215. data/sig/resolver.rbs +9 -5
  216. data/sig/response/body.rbs +53 -0
  217. data/sig/response/buffer.rbs +24 -0
  218. data/sig/response.rbs +24 -39
  219. data/sig/selector.rbs +1 -1
  220. data/sig/session.rbs +29 -18
  221. data/sig/timers.rbs +18 -8
  222. data/sig/transcoder/body.rbs +4 -3
  223. data/sig/transcoder/deflate.rbs +11 -0
  224. data/sig/transcoder/form.rbs +5 -3
  225. data/sig/transcoder/gzip.rbs +24 -0
  226. data/sig/transcoder/json.rbs +8 -3
  227. data/sig/{plugins → transcoder}/multipart.rbs +15 -19
  228. data/sig/transcoder/utils/body_reader.rbs +15 -0
  229. data/sig/transcoder/utils/deflater.rbs +29 -0
  230. data/sig/transcoder/utils/inflater.rbs +12 -0
  231. data/sig/transcoder/xml.rbs +22 -0
  232. data/sig/transcoder.rbs +24 -9
  233. data/sig/utils.rbs +8 -2
  234. metadata +163 -41
  235. data/lib/httpx/plugins/authentication.rb +0 -20
  236. data/lib/httpx/plugins/basic_authentication.rb +0 -30
  237. data/lib/httpx/plugins/compression/brotli.rb +0 -54
  238. data/lib/httpx/plugins/compression/deflate.rb +0 -49
  239. data/lib/httpx/plugins/compression/gzip.rb +0 -88
  240. data/lib/httpx/plugins/compression.rb +0 -164
  241. data/lib/httpx/plugins/multipart/decoder.rb +0 -187
  242. data/lib/httpx/plugins/multipart.rb +0 -84
  243. data/lib/httpx/registry.rb +0 -85
  244. data/sig/plugins/authentication.rbs +0 -11
  245. data/sig/plugins/compression/brotli.rbs +0 -21
  246. data/sig/plugins/compression/deflate.rbs +0 -17
  247. data/sig/plugins/compression/gzip.rbs +0 -29
  248. data/sig/registry.rbs +0 -12
  249. /data/sig/plugins/{authentication → auth}/ntlm.rbs +0 -0
  250. /data/sig/plugins/{authentication → auth}/socks5.rbs +0 -0
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thread"
4
+
5
+ module HTTPX
6
+ class SynchPool < Pool
7
+ def initialize(options)
8
+ super
9
+
10
+ @connections = ConnectionStore.new(options)
11
+ end
12
+
13
+ # TODO: #wrap
14
+ def find_or_new_connection(uri, options, &blk)
15
+ @connections.find_or_new(uri, options) do |new_conn|
16
+ catch(:coalesced) do
17
+ init_connection(new_conn, options)
18
+ blk.call(new_conn) if blk
19
+ new_conn
20
+ end
21
+ end
22
+ find_connection(uri, options) || new_connection(uri, options, &blk)
23
+ end
24
+
25
+ class ConnectionManager
26
+ include Enumerable
27
+
28
+ def initialize(limit = 3)
29
+ @connections = []
30
+ @used = 0
31
+ @limit = limit
32
+ end
33
+
34
+ def each(*args, &blk)
35
+ @connections.each(*args, &blk)
36
+ end
37
+
38
+ def find_or_new(uri, options, &blk)
39
+ raise "over limit" if @used >= @limit
40
+
41
+ @used += 1
42
+ conn = @connections.find do |connection|
43
+ connection.match?(uri, options)
44
+ end
45
+
46
+ if conn
47
+ @connections.delete(conn)
48
+ else
49
+ conn = options.connection_class.new(uri, options)
50
+ blk[conn]
51
+ end
52
+
53
+ conn
54
+ end
55
+ end
56
+
57
+ class ConnectionStore
58
+ include Enumerable
59
+
60
+ def initialize(options)
61
+ @connections = Hash.new { |hs, k| hs[k] ||= ConnectionManager.new }
62
+ @conn_mtx = Thread::Mutex.new
63
+ @conn_waiter = ConditionVariable.new
64
+ @timeout = Float(options.fetch(:pool_timeout, 5))
65
+ end
66
+
67
+ def each(&block)
68
+ return enum_for(__meth__) unless block
69
+
70
+ @conn_mtx.synchronize do
71
+ @connections.each_value do |conns|
72
+ conns.each(&block)
73
+ end
74
+ end
75
+ end
76
+
77
+ def find_or_new(uri, options, &blk)
78
+ @connections[uri.origin].find_or_new(uri, options, &blk)
79
+ end
80
+
81
+ # def <<(conn)
82
+ # @conn_mtx.synchronize do
83
+ # origin, conns = @connections.find { |_orig, _| conn.origins.include?(origin) }
84
+ # (conns || @connections[conn.origin.to_s]) << conn
85
+ # end
86
+ # end
87
+
88
+ def empty?
89
+ @conn_mtx.synchronize { super }
90
+ end
91
+ end
92
+ end
93
+ end
data/lib/httpx/pool.rb CHANGED
@@ -7,7 +7,7 @@ require "httpx/resolver"
7
7
 
8
8
  module HTTPX
9
9
  class Pool
10
- using ArrayExtensions
10
+ using ArrayExtensions::FilterMap
11
11
  extend Forwardable
12
12
 
13
13
  def_delegator :@timers, :after
@@ -17,7 +17,17 @@ module HTTPX
17
17
  @timers = Timers.new
18
18
  @selector = Selector.new
19
19
  @connections = []
20
- @connected_connections = 0
20
+ end
21
+
22
+ def wrap
23
+ connections = @connections
24
+ @connections = []
25
+
26
+ begin
27
+ yield self
28
+ ensure
29
+ @connections.unshift(*connections)
30
+ end
21
31
  end
22
32
 
23
33
  def empty?
@@ -44,16 +54,15 @@ module HTTPX
44
54
  connection.emit(:error, e)
45
55
  end
46
56
  rescue Exception # rubocop:disable Lint/RescueException
47
- @connections.each(&:reset)
57
+ @connections.each(&:force_reset)
48
58
  raise
49
59
  end
50
60
 
51
61
  def close(connections = @connections)
52
62
  return if connections.empty?
53
63
 
54
- @timers.cancel
55
64
  connections = connections.reject(&:inflight?)
56
- connections.each(&:close)
65
+ connections.each(&:terminate)
57
66
  next_tick until connections.none? { |c| c.state != :idle && @connections.include?(c) }
58
67
 
59
68
  # close resolvers
@@ -67,22 +76,39 @@ module HTTPX
67
76
  resolver.close unless resolver.closed?
68
77
  end
69
78
  # for https resolver
70
- resolver_connections.each(&:close)
79
+ resolver_connections.each(&:terminate)
71
80
  next_tick until resolver_connections.none? { |c| c.state != :idle && @connections.include?(c) }
72
81
  end
73
82
 
74
83
  def init_connection(connection, _options)
75
- resolve_connection(connection)
76
84
  connection.timers = @timers
77
- connection.on(:open) do
78
- @connected_connections += 1
79
- end
80
85
  connection.on(:activate) do
81
86
  select_connection(connection)
82
87
  end
88
+ connection.on(:exhausted) do
89
+ case connection.state
90
+ when :closed
91
+ connection.idling
92
+ @connections << connection
93
+ select_connection(connection)
94
+ when :closing
95
+ connection.once(:close) do
96
+ connection.idling
97
+ @connections << connection
98
+ select_connection(connection)
99
+ end
100
+ end
101
+ end
102
+ connection.on(:close) do
103
+ unregister_connection(connection)
104
+ end
105
+ connection.on(:terminate) do
106
+ unregister_connection(connection, true)
107
+ end
108
+ resolve_connection(connection) unless connection.family
83
109
  end
84
110
 
85
- def deactivate(connections)
111
+ def deactivate(*connections)
86
112
  connections.each do |connection|
87
113
  connection.deactivate
88
114
  deselect_connection(connection) if connection.state == :inactive
@@ -94,9 +120,24 @@ module HTTPX
94
120
  # maximize pipelining by opening as few connections as possible.
95
121
  #
96
122
  def find_connection(uri, options)
97
- @connections.find do |connection|
123
+ conn = @connections.find do |connection|
98
124
  connection.match?(uri, options)
99
125
  end
126
+
127
+ return unless conn
128
+
129
+ case conn.state
130
+ when :closed
131
+ conn.idling
132
+ select_connection(conn)
133
+ when :closing
134
+ conn.once(:close) do
135
+ conn.idling
136
+ select_connection(conn)
137
+ end
138
+ end
139
+
140
+ conn
100
141
  end
101
142
 
102
143
  private
@@ -113,19 +154,61 @@ module HTTPX
113
154
  # resolve a name (not the same as name being an IP, yet)
114
155
  # 2. when the connection is initialized with an external already open IO.
115
156
  #
157
+ connection.once(:connect_error, &connection.method(:handle_error))
116
158
  on_resolver_connection(connection)
117
159
  return
118
160
  end
119
161
 
120
162
  find_resolver_for(connection) do |resolver|
121
- resolver << connection
163
+ resolver << try_clone_connection(connection, resolver.family)
122
164
  next if resolver.empty?
123
165
 
124
166
  select_connection(resolver)
125
167
  end
126
168
  end
127
169
 
170
+ def try_clone_connection(connection, family)
171
+ connection.family ||= family
172
+
173
+ return connection if connection.family == family
174
+
175
+ new_connection = connection.class.new(connection.origin, connection.options)
176
+ new_connection.family = family
177
+
178
+ connection.once(:tcp_open) { new_connection.force_reset }
179
+ connection.once(:connect_error) do |err|
180
+ if new_connection.connecting?
181
+ new_connection.merge(connection)
182
+ connection.emit(:cloned, new_connection)
183
+ connection.force_reset
184
+ else
185
+ connection.__send__(:handle_error, err)
186
+ end
187
+ end
188
+
189
+ new_connection.once(:tcp_open) do |new_conn|
190
+ if new_conn != connection
191
+ new_conn.merge(connection)
192
+ connection.force_reset
193
+ end
194
+ end
195
+ new_connection.once(:connect_error) do |err|
196
+ if connection.connecting?
197
+ # main connection has the requests
198
+ connection.merge(new_connection)
199
+ new_connection.emit(:cloned, connection)
200
+ new_connection.force_reset
201
+ else
202
+ new_connection.__send__(:handle_error, err)
203
+ end
204
+ end
205
+
206
+ init_connection(new_connection, connection.options)
207
+ new_connection
208
+ end
209
+
128
210
  def on_resolver_connection(connection)
211
+ @connections << connection unless @connections.include?(connection)
129
212
  found_connection = @connections.find do |ch|
130
213
  ch != connection && ch.mergeable?(connection)
131
214
  end
@@ -142,9 +225,9 @@ module HTTPX
142
225
  end
143
226
 
144
227
  def on_resolver_error(connection, error)
228
+ return connection.emit(:connect_error, error) if connection.connecting? && connection.callbacks_for?(:connect_error)
229
+
145
230
  connection.emit(:error, error)
146
- # must remove connection by hand, hasn't been started yet
147
- unregister_connection(connection)
148
231
  end
149
232
 
150
233
  def on_resolver_close(resolver)
@@ -158,21 +241,12 @@ module HTTPX
158
241
  end
159
242
 
160
243
  def register_connection(connection)
161
- if connection.state == :open
162
- # if open, an IO was passed upstream, therefore
163
- # consider it connected already.
164
- @connected_connections += 1
165
- end
166
244
  select_connection(connection)
167
- connection.on(:close) do
168
- unregister_connection(connection)
169
- end
170
245
  end
171
246
 
172
- def unregister_connection(connection)
173
- @connections.delete(connection)
247
+ def unregister_connection(connection, cleanup = !connection.used?)
248
+ @connections.delete(connection) if cleanup
174
249
  deselect_connection(connection)
175
- @connected_connections -= 1
176
250
  end
177
251
 
178
252
  def select_connection(connection)
@@ -186,6 +260,7 @@ module HTTPX
186
260
  def coalesce_connections(conn1, conn2)
187
261
  return register_connection(conn2) unless conn1.coalescable?(conn2)
188
262
 
263
+ conn2.emit(:tcp_open, conn1)
189
264
  conn1.merge(conn2)
190
265
  @connections.delete(conn2)
191
266
  end
@@ -201,7 +276,7 @@ module HTTPX
201
276
  def find_resolver_for(connection)
202
277
  connection_options = connection.options
203
278
  resolver_type = connection_options.resolver_class
204
- resolver_type = Resolver.registry(resolver_type) if resolver_type.is_a?(Symbol)
279
+ resolver_type = Resolver.resolver_for(resolver_type)
205
280
 
206
281
  @resolvers[resolver_type] ||= begin
207
282
  resolver_manager = if resolver_type.multi?
@@ -1,304 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
- begin
5
- require "idnx"
4
+ module Punycode
5
+ module_function
6
6
 
7
- module Punycode
8
- module_function
7
+ begin
8
+ require "idnx"
9
9
 
10
10
  def encode_hostname(hostname)
11
11
  Idnx.to_punycode(hostname)
12
12
  end
13
- end
14
-
15
- rescue LoadError
16
- # :nocov:
17
- # -*- coding: utf-8 -*-
18
- #--
19
- # punycode.rb - PunyCode encoder for the Domain Name library
20
- #
21
- # Copyright (C) 2011-2017 Akinori MUSHA, All rights reserved.
22
- #
23
- # Ported from puny.c, a part of VeriSign XCode (encode/decode) IDN
24
- # Library.
25
- #
26
- # Copyright (C) 2000-2002 Verisign Inc., All rights reserved.
27
- #
28
- # Redistribution and use in source and binary forms, with or
29
- # without modification, are permitted provided that the following
30
- # conditions are met:
31
- #
32
- # 1) Redistributions of source code must retain the above copyright
33
- # notice, this list of conditions and the following disclaimer.
34
- #
35
- # 2) Redistributions in binary form must reproduce the above copyright
36
- # notice, this list of conditions and the following disclaimer in
37
- # the documentation and/or other materials provided with the
38
- # distribution.
39
- #
40
- # 3) Neither the name of the VeriSign Inc. nor the names of its
41
- # contributors may be used to endorse or promote products derived
42
- # from this software without specific prior written permission.
43
- #
44
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
45
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
46
- # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
47
- # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
48
- # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
49
- # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
50
- # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
51
- # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
52
- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53
- # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
54
- # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
55
- # POSSIBILITY OF SUCH DAMAGE.
56
- #
57
- # This software is licensed under the BSD open source license. For more
58
- # information visit www.opensource.org.
59
- #
60
- # Authors:
61
- # John Colosi (VeriSign)
62
- # Srikanth Veeramachaneni (VeriSign)
63
- # Nagesh Chigurupati (Verisign)
64
- # Praveen Srinivasan(Verisign)
65
- #++
66
- module Punycode
67
- BASE = 36
68
- TMIN = 1
69
- TMAX = 26
70
- SKEW = 38
71
- DAMP = 700
72
- INITIAL_BIAS = 72
73
- INITIAL_N = 0x80
74
- DELIMITER = "-"
75
-
76
- MAXINT = (1 << 32) - 1
77
-
78
- LOBASE = BASE - TMIN
79
- CUTOFF = LOBASE * TMAX / 2
80
-
81
- RE_NONBASIC = /[^\x00-\x7f]/.freeze
82
-
83
- # Returns the numeric value of a basic code point (for use in
84
- # representing integers) in the range 0 to base-1, or nil if cp
85
- # is does not represent a value.
86
- DECODE_DIGIT = {}.tap do |map|
87
- # ASCII A..Z map to 0..25
88
- # ASCII a..z map to 0..25
89
- (0..25).each { |i| map[65 + i] = map[97 + i] = i }
90
- # ASCII 0..9 map to 26..35
91
- (26..35).each { |i| map[22 + i] = i }
92
- end
93
-
94
- # Returns the basic code point whose value (when used for
95
- # representing integers) is d, which must be in the range 0 to
96
- # BASE-1. The lowercase form is used unless flag is true, in
97
- # which case the uppercase form is used. The behavior is
98
- # undefined if flag is nonzero and digit d has no uppercase
99
- # form.
100
- ENCODE_DIGIT = proc { |d, flag|
101
- (d + 22 + (d < 26 ? 75 : 0) - (flag ? (1 << 5) : 0)).chr
102
- # 0..25 map to ASCII a..z or A..Z
103
- # 26..35 map to ASCII 0..9
104
- }
105
-
106
- DOT = "."
107
- PREFIX = "xn--"
108
-
109
- # Most errors we raise are basically kind of ArgumentError.
110
- class ArgumentError < ::ArgumentError; end
111
- class BufferOverflowError < ArgumentError; end
112
-
113
- module_function
114
-
115
- # Encode a +string+ in Punycode
116
- def encode(string)
117
- input = string.unpack("U*")
118
- output = +""
119
-
120
- # Initialize the state
121
- n = INITIAL_N
122
- delta = 0
123
- bias = INITIAL_BIAS
124
-
125
- # Handle the basic code points
126
- input.each { |cp| output << cp.chr if cp < 0x80 }
127
-
128
- h = b = output.length
129
-
130
- # h is the number of code points that have been handled, b is the
131
- # number of basic code points, and out is the number of characters
132
- # that have been output.
133
-
134
- output << DELIMITER if b > 0
135
-
136
- # Main encoding loop
137
-
138
- while h < input.length
139
- # All non-basic code points < n have been handled already. Find
140
- # the next larger one
141
-
142
- m = MAXINT
143
- input.each do |cp|
144
- m = cp if (n...m) === cp
145
- end
146
-
147
- # Increase delta enough to advance the decoder's <n,i> state to
148
- # <m,0>, but guard against overflow
149
-
150
- delta += (m - n) * (h + 1)
151
- raise BufferOverflowError if delta > MAXINT
152
-
153
- n = m
154
-
155
- input.each do |cp|
156
- # AMC-ACE-Z can use this simplified version instead
157
- if cp < n
158
- delta += 1
159
- raise BufferOverflowError if delta > MAXINT
160
- elsif cp == n
161
- # Represent delta as a generalized variable-length integer
162
- q = delta
163
- k = BASE
164
- loop do
165
- t = k <= bias ? TMIN : k - bias >= TMAX ? TMAX : k - bias
166
- break if q < t
167
-
168
- q, r = (q - t).divmod(BASE - t)
169
- output << ENCODE_DIGIT[t + r, false]
170
- k += BASE
171
- end
172
-
173
- output << ENCODE_DIGIT[q, false]
174
-
175
- # Adapt the bias
176
- delta = h == b ? delta / DAMP : delta >> 1
177
- delta += delta / (h + 1)
178
- bias = 0
179
- while delta > CUTOFF
180
- delta /= LOBASE
181
- bias += BASE
182
- end
183
- bias += (LOBASE + 1) * delta / (delta + SKEW)
184
-
185
- delta = 0
186
- h += 1
187
- end
188
- end
189
-
190
- delta += 1
191
- n += 1
192
- end
193
-
194
- output
195
- end
196
-
197
- # Encode a hostname using IDN/Punycode algorithms
13
+ rescue LoadError
198
14
  def encode_hostname(hostname)
199
- hostname.match(RE_NONBASIC) || (return hostname)
200
-
201
- hostname.split(DOT).map do |name|
202
- if name.match(RE_NONBASIC)
203
- PREFIX + encode(name)
204
- else
205
- name
206
- end
207
- end.join(DOT)
208
- end
209
-
210
- # Decode a +string+ encoded in Punycode
211
- def decode(string)
212
- # Initialize the state
213
- n = INITIAL_N
214
- i = 0
215
- bias = INITIAL_BIAS
216
-
217
- if j = string.rindex(DELIMITER)
218
- b = string[0...j]
219
-
220
- b.match(RE_NONBASIC) &&
221
- raise(ArgumentError, "Illegal character is found in basic part: #{string.inspect}")
222
-
223
- # Handle the basic code points
224
-
225
- output = b.unpack("U*")
226
- u = string[(j + 1)..-1]
227
- else
228
- output = []
229
- u = string
230
- end
231
-
232
- # Main decoding loop: Start just after the last delimiter if any
233
- # basic code points were copied; start at the beginning
234
- # otherwise.
235
-
236
- input = u.unpack("C*")
237
- input_length = input.length
238
- h = 0
239
- out = output.length
240
-
241
- while h < input_length
242
- # Decode a generalized variable-length integer into delta,
243
- # which gets added to i. The overflow checking is easier
244
- # if we increase i as we go, then subtract off its starting
245
- # value at the end to obtain delta.
246
-
247
- oldi = i
248
- w = 1
249
- k = BASE
250
-
251
- loop do
252
- (digit = DECODE_DIGIT[input[h]]) ||
253
- raise(ArgumentError, "Illegal character is found in non-basic part: #{string.inspect}")
254
- h += 1
255
- i += digit * w
256
- raise BufferOverflowError if i > MAXINT
257
-
258
- t = k <= bias ? TMIN : k - bias >= TMAX ? TMAX : k - bias
259
- break if digit < t
260
-
261
- w *= BASE - t
262
- raise BufferOverflowError if w > MAXINT
263
-
264
- k += BASE
265
- (h < input_length) || raise(ArgumentError, "Malformed input given: #{string.inspect}")
266
- end
267
-
268
- # Adapt the bias
269
- delta = oldi == 0 ? i / DAMP : (i - oldi) >> 1
270
- delta += delta / (out + 1)
271
- bias = 0
272
- while delta > CUTOFF
273
- delta /= LOBASE
274
- bias += BASE
275
- end
276
- bias += (LOBASE + 1) * delta / (delta + SKEW)
277
-
278
- # i was supposed to wrap around from out+1 to 0, incrementing
279
- # n each time, so we'll fix that now:
280
-
281
- q, i = i.divmod(out + 1)
282
- n += q
283
- raise BufferOverflowError if n > MAXINT
284
-
285
- # Insert n at position i of the output:
286
-
287
- output[i, 0] = n
288
-
289
- out += 1
290
- i += 1
291
- end
292
- output.pack("U*")
293
- end
15
+ warn "#{hostname} cannot be converted to punycode. Install the " \
16
+ "\"idnx\" gem: https://github.com/HoneyryderChuck/idnx"
294
17
 
295
- # Decode a hostname using IDN/Punycode algorithms
296
- def decode_hostname(hostname)
297
- hostname.gsub(/(\A|#{Regexp.quote(DOT)})#{Regexp.quote(PREFIX)}([^#{Regexp.quote(DOT)}]*)/o) do
298
- Regexp.last_match(1) << decode(Regexp.last_match(2))
299
- end
18
+ hostname
300
19
  end
301
20
  end
302
- # :nocov:
303
21
  end
304
- end
22
+ end