httpx 0.20.0 → 1.3.1

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