httpx 0.21.0 → 1.2.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 (229) 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 +4 -4
  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_20_0.md +1 -1
  15. data/doc/release_notes/0_21_0.md +7 -5
  16. data/doc/release_notes/0_21_1.md +12 -0
  17. data/doc/release_notes/0_22_0.md +13 -0
  18. data/doc/release_notes/0_22_1.md +11 -0
  19. data/doc/release_notes/0_22_2.md +5 -0
  20. data/doc/release_notes/0_22_3.md +55 -0
  21. data/doc/release_notes/0_22_4.md +6 -0
  22. data/doc/release_notes/0_22_5.md +6 -0
  23. data/doc/release_notes/0_23_0.md +42 -0
  24. data/doc/release_notes/0_23_1.md +5 -0
  25. data/doc/release_notes/0_23_2.md +5 -0
  26. data/doc/release_notes/0_23_3.md +6 -0
  27. data/doc/release_notes/0_23_4.md +5 -0
  28. data/doc/release_notes/0_24_0.md +48 -0
  29. data/doc/release_notes/0_24_1.md +12 -0
  30. data/doc/release_notes/0_24_2.md +12 -0
  31. data/doc/release_notes/0_24_3.md +12 -0
  32. data/doc/release_notes/0_24_4.md +18 -0
  33. data/doc/release_notes/0_24_5.md +6 -0
  34. data/doc/release_notes/0_24_6.md +5 -0
  35. data/doc/release_notes/0_24_7.md +10 -0
  36. data/doc/release_notes/1_0_0.md +60 -0
  37. data/doc/release_notes/1_0_1.md +5 -0
  38. data/doc/release_notes/1_0_2.md +7 -0
  39. data/doc/release_notes/1_1_0.md +32 -0
  40. data/doc/release_notes/1_1_1.md +17 -0
  41. data/doc/release_notes/1_1_2.md +12 -0
  42. data/doc/release_notes/1_1_3.md +18 -0
  43. data/doc/release_notes/1_1_4.md +6 -0
  44. data/doc/release_notes/1_1_5.md +12 -0
  45. data/doc/release_notes/1_2_0.md +49 -0
  46. data/doc/release_notes/1_2_1.md +6 -0
  47. data/lib/httpx/adapters/datadog.rb +100 -106
  48. data/lib/httpx/adapters/faraday.rb +143 -107
  49. data/lib/httpx/adapters/sentry.rb +26 -7
  50. data/lib/httpx/adapters/webmock.rb +33 -17
  51. data/lib/httpx/altsvc.rb +61 -24
  52. data/lib/httpx/base64.rb +27 -0
  53. data/lib/httpx/buffer.rb +12 -0
  54. data/lib/httpx/callbacks.rb +5 -3
  55. data/lib/httpx/chainable.rb +54 -39
  56. data/lib/httpx/connection/http1.rb +62 -37
  57. data/lib/httpx/connection/http2.rb +16 -27
  58. data/lib/httpx/connection.rb +213 -120
  59. data/lib/httpx/domain_name.rb +10 -13
  60. data/lib/httpx/errors.rb +34 -2
  61. data/lib/httpx/extensions.rb +4 -134
  62. data/lib/httpx/io/ssl.rb +77 -71
  63. data/lib/httpx/io/tcp.rb +46 -70
  64. data/lib/httpx/io/udp.rb +18 -52
  65. data/lib/httpx/io/unix.rb +6 -13
  66. data/lib/httpx/io.rb +3 -9
  67. data/lib/httpx/loggable.rb +4 -19
  68. data/lib/httpx/options.rb +168 -110
  69. data/lib/httpx/plugins/{authentication → auth}/basic.rb +1 -5
  70. data/lib/httpx/plugins/{authentication → auth}/digest.rb +13 -14
  71. data/lib/httpx/plugins/{authentication → auth}/ntlm.rb +1 -3
  72. data/lib/httpx/plugins/{authentication → auth}/socks5.rb +0 -2
  73. data/lib/httpx/plugins/auth.rb +25 -0
  74. data/lib/httpx/plugins/aws_sdk_authentication.rb +1 -3
  75. data/lib/httpx/plugins/aws_sigv4.rb +5 -6
  76. data/lib/httpx/plugins/basic_auth.rb +29 -0
  77. data/lib/httpx/plugins/brotli.rb +50 -0
  78. data/lib/httpx/plugins/callbacks.rb +91 -0
  79. data/lib/httpx/plugins/circuit_breaker/circuit.rb +40 -16
  80. data/lib/httpx/plugins/circuit_breaker/circuit_store.rb +14 -5
  81. data/lib/httpx/plugins/circuit_breaker.rb +30 -7
  82. data/lib/httpx/plugins/cookies/set_cookie_parser.rb +0 -2
  83. data/lib/httpx/plugins/cookies.rb +20 -10
  84. data/lib/httpx/plugins/{digest_authentication.rb → digest_auth.rb} +11 -12
  85. data/lib/httpx/plugins/expect.rb +15 -13
  86. data/lib/httpx/plugins/follow_redirects.rb +71 -29
  87. data/lib/httpx/plugins/grpc/call.rb +2 -3
  88. data/lib/httpx/plugins/grpc/grpc_encoding.rb +88 -0
  89. data/lib/httpx/plugins/grpc/message.rb +7 -37
  90. data/lib/httpx/plugins/grpc.rb +35 -29
  91. data/lib/httpx/plugins/h2c.rb +25 -18
  92. data/lib/httpx/plugins/internal_telemetry.rb +16 -0
  93. data/lib/httpx/plugins/{ntlm_authentication.rb → ntlm_auth.rb} +7 -5
  94. data/lib/httpx/plugins/oauth.rb +170 -0
  95. data/lib/httpx/plugins/persistent.rb +1 -1
  96. data/lib/httpx/plugins/proxy/http.rb +15 -10
  97. data/lib/httpx/plugins/proxy/socks4.rb +8 -6
  98. data/lib/httpx/plugins/proxy/socks5.rb +10 -8
  99. data/lib/httpx/plugins/proxy.rb +69 -67
  100. data/lib/httpx/plugins/push_promise.rb +1 -1
  101. data/lib/httpx/plugins/rate_limiter.rb +3 -1
  102. data/lib/httpx/plugins/response_cache/file_store.rb +40 -0
  103. data/lib/httpx/plugins/response_cache/store.rb +34 -17
  104. data/lib/httpx/plugins/response_cache.rb +6 -6
  105. data/lib/httpx/plugins/retries.rb +61 -12
  106. data/lib/httpx/plugins/ssrf_filter.rb +142 -0
  107. data/lib/httpx/plugins/stream.rb +27 -32
  108. data/lib/httpx/plugins/upgrade/h2.rb +4 -4
  109. data/lib/httpx/plugins/upgrade.rb +8 -10
  110. data/lib/httpx/plugins/webdav.rb +10 -8
  111. data/lib/httpx/pool.rb +85 -23
  112. data/lib/httpx/punycode.rb +9 -291
  113. data/lib/httpx/request/body.rb +158 -0
  114. data/lib/httpx/request.rb +86 -121
  115. data/lib/httpx/resolver/https.rb +54 -17
  116. data/lib/httpx/resolver/multi.rb +8 -12
  117. data/lib/httpx/resolver/native.rb +163 -70
  118. data/lib/httpx/resolver/resolver.rb +28 -13
  119. data/lib/httpx/resolver/system.rb +15 -10
  120. data/lib/httpx/resolver.rb +38 -16
  121. data/lib/httpx/response/body.rb +242 -0
  122. data/lib/httpx/response/buffer.rb +96 -0
  123. data/lib/httpx/response.rb +113 -211
  124. data/lib/httpx/selector.rb +2 -4
  125. data/lib/httpx/session.rb +91 -64
  126. data/lib/httpx/session_extensions.rb +4 -1
  127. data/lib/httpx/timers.rb +28 -8
  128. data/lib/httpx/transcoder/body.rb +0 -2
  129. data/lib/httpx/transcoder/chunker.rb +0 -1
  130. data/lib/httpx/transcoder/deflate.rb +37 -0
  131. data/lib/httpx/transcoder/form.rb +52 -33
  132. data/lib/httpx/transcoder/gzip.rb +74 -0
  133. data/lib/httpx/transcoder/json.rb +2 -5
  134. data/lib/httpx/transcoder/multipart/decoder.rb +139 -0
  135. data/lib/httpx/{plugins → transcoder}/multipart/encoder.rb +3 -3
  136. data/lib/httpx/{plugins → transcoder}/multipart/mime_type_detector.rb +1 -1
  137. data/lib/httpx/{plugins → transcoder}/multipart/part.rb +3 -2
  138. data/lib/httpx/transcoder/multipart.rb +17 -0
  139. data/lib/httpx/transcoder/utils/body_reader.rb +46 -0
  140. data/lib/httpx/transcoder/utils/deflater.rb +72 -0
  141. data/lib/httpx/transcoder/utils/inflater.rb +19 -0
  142. data/lib/httpx/transcoder/xml.rb +0 -5
  143. data/lib/httpx/transcoder.rb +4 -6
  144. data/lib/httpx/utils.rb +36 -16
  145. data/lib/httpx/version.rb +1 -1
  146. data/lib/httpx.rb +12 -14
  147. data/sig/altsvc.rbs +33 -0
  148. data/sig/buffer.rbs +1 -0
  149. data/sig/callbacks.rbs +3 -3
  150. data/sig/chainable.rbs +10 -9
  151. data/sig/connection/http1.rbs +5 -4
  152. data/sig/connection/http2.rbs +1 -1
  153. data/sig/connection.rbs +46 -24
  154. data/sig/errors.rbs +9 -3
  155. data/sig/httpx.rbs +5 -4
  156. data/sig/io/ssl.rbs +26 -0
  157. data/sig/io/tcp.rbs +60 -0
  158. data/sig/io/udp.rbs +20 -0
  159. data/sig/io/unix.rbs +10 -0
  160. data/sig/options.rbs +28 -12
  161. data/sig/plugins/{authentication → auth}/basic.rbs +0 -2
  162. data/sig/plugins/{authentication → auth}/digest.rbs +2 -1
  163. data/sig/plugins/auth.rbs +13 -0
  164. data/sig/plugins/{basic_authentication.rbs → basic_auth.rbs} +2 -2
  165. data/sig/plugins/brotli.rbs +22 -0
  166. data/sig/plugins/callbacks.rbs +38 -0
  167. data/sig/plugins/circuit_breaker.rbs +13 -3
  168. data/sig/plugins/compression.rbs +6 -4
  169. data/sig/plugins/cookies/jar.rbs +2 -2
  170. data/sig/plugins/cookies.rbs +2 -0
  171. data/sig/plugins/{digest_authentication.rbs → digest_auth.rbs} +2 -2
  172. data/sig/plugins/follow_redirects.rbs +11 -2
  173. data/sig/plugins/grpc/call.rbs +19 -0
  174. data/sig/plugins/grpc/grpc_encoding.rbs +37 -0
  175. data/sig/plugins/grpc/message.rbs +17 -0
  176. data/sig/plugins/grpc.rbs +2 -32
  177. data/sig/plugins/h2c.rbs +1 -1
  178. data/sig/plugins/{ntlm_authentication.rbs → ntlm_auth.rbs} +2 -2
  179. data/sig/plugins/oauth.rbs +54 -0
  180. data/sig/plugins/proxy/socks4.rbs +4 -4
  181. data/sig/plugins/proxy/socks5.rbs +2 -2
  182. data/sig/plugins/proxy/ssh.rbs +1 -1
  183. data/sig/plugins/proxy.rbs +10 -4
  184. data/sig/plugins/response_cache.rbs +12 -3
  185. data/sig/plugins/retries.rbs +28 -8
  186. data/sig/plugins/stream.rbs +24 -17
  187. data/sig/plugins/upgrade.rbs +5 -3
  188. data/sig/pool.rbs +5 -4
  189. data/sig/request/body.rbs +40 -0
  190. data/sig/request.rbs +12 -28
  191. data/sig/resolver/https.rbs +7 -2
  192. data/sig/resolver/native.rbs +10 -4
  193. data/sig/resolver/resolver.rbs +6 -4
  194. data/sig/resolver/system.rbs +2 -0
  195. data/sig/resolver.rbs +9 -5
  196. data/sig/response/body.rbs +53 -0
  197. data/sig/response/buffer.rbs +24 -0
  198. data/sig/response.rbs +17 -38
  199. data/sig/session.rbs +24 -18
  200. data/sig/timers.rbs +17 -7
  201. data/sig/transcoder/body.rbs +4 -3
  202. data/sig/transcoder/deflate.rbs +11 -0
  203. data/sig/transcoder/form.rbs +5 -3
  204. data/sig/transcoder/gzip.rbs +24 -0
  205. data/sig/transcoder/json.rbs +4 -2
  206. data/sig/{plugins → transcoder}/multipart.rbs +3 -12
  207. data/sig/transcoder/utils/body_reader.rbs +15 -0
  208. data/sig/transcoder/utils/deflater.rbs +29 -0
  209. data/sig/transcoder/utils/inflater.rbs +12 -0
  210. data/sig/transcoder/xml.rbs +1 -1
  211. data/sig/transcoder.rbs +22 -7
  212. data/sig/utils.rbs +2 -0
  213. metadata +127 -40
  214. data/lib/httpx/plugins/authentication.rb +0 -20
  215. data/lib/httpx/plugins/basic_authentication.rb +0 -30
  216. data/lib/httpx/plugins/compression/brotli.rb +0 -54
  217. data/lib/httpx/plugins/compression/deflate.rb +0 -49
  218. data/lib/httpx/plugins/compression/gzip.rb +0 -88
  219. data/lib/httpx/plugins/compression.rb +0 -164
  220. data/lib/httpx/plugins/multipart/decoder.rb +0 -187
  221. data/lib/httpx/plugins/multipart.rb +0 -84
  222. data/lib/httpx/registry.rb +0 -85
  223. data/sig/plugins/authentication.rbs +0 -11
  224. data/sig/plugins/compression/brotli.rbs +0 -21
  225. data/sig/plugins/compression/deflate.rbs +0 -17
  226. data/sig/plugins/compression/gzip.rbs +0 -29
  227. data/sig/registry.rbs +0 -13
  228. /data/sig/plugins/{authentication → auth}/ntlm.rbs +0 -0
  229. /data/sig/plugins/{authentication → auth}/socks5.rbs +0 -0
@@ -5,16 +5,16 @@ module HTTPX
5
5
  #
6
6
  # This plugin implements convenience methods for performing WEBDAV requests.
7
7
  #
8
- # https://gitlab.com/honeyryderchuck/httpx/wikis/WEBDAV
8
+ # https://gitlab.com/os85/httpx/wikis/WebDav
9
9
  #
10
10
  module WebDav
11
11
  module InstanceMethods
12
12
  def copy(src, dest)
13
- request(:copy, src, headers: { "destination" => @options.origin.merge(dest) })
13
+ request("COPY", src, headers: { "destination" => @options.origin.merge(dest) })
14
14
  end
15
15
 
16
16
  def move(src, dest)
17
- request(:move, src, headers: { "destination" => @options.origin.merge(dest) })
17
+ request("MOVE", src, headers: { "destination" => @options.origin.merge(dest) })
18
18
  end
19
19
 
20
20
  def lock(path, timeout: nil, &blk)
@@ -30,7 +30,9 @@ module HTTPX
30
30
  "<D:locktype><D:write/></D:locktype>" \
31
31
  "<D:owner>null</D:owner>" \
32
32
  "</D:lockinfo>"
33
- response = request(:lock, path, headers: headers, xml: xml)
33
+ response = request("LOCK", path, headers: headers, xml: xml)
34
+
35
+ return response unless response.is_a?(Response)
34
36
 
35
37
  return response unless blk && response.status == 200
36
38
 
@@ -44,11 +46,11 @@ module HTTPX
44
46
  end
45
47
 
46
48
  def unlock(path, lock_token)
47
- request(:unlock, path, headers: { "lock-token" => lock_token })
49
+ request("UNLOCK", path, headers: { "lock-token" => lock_token })
48
50
  end
49
51
 
50
52
  def mkcol(dir)
51
- request(:mkcol, dir)
53
+ request("MKCOL", dir)
52
54
  end
53
55
 
54
56
  def propfind(path, xml = nil)
@@ -62,13 +64,13 @@ module HTTPX
62
64
  xml
63
65
  end
64
66
 
65
- request(:propfind, path, headers: { "depth" => "1" }, xml: body)
67
+ request("PROPFIND", path, headers: { "depth" => "1" }, xml: body)
66
68
  end
67
69
 
68
70
  def proppatch(path, xml)
69
71
  body = "<?xml version=\"1.0\"?>" \
70
72
  "<D:propertyupdate xmlns:D=\"DAV:\" xmlns:Z=\"http://ns.example.com/standards/z39.50/\">#{xml}</D:propertyupdate>"
71
- request(:proppatch, path, xml: body)
73
+ request("PROPPATCH", path, xml: body)
72
74
  end
73
75
  # %i[ orderpatch acl report search]
74
76
  end
data/lib/httpx/pool.rb CHANGED
@@ -17,7 +17,6 @@ module HTTPX
17
17
  @timers = Timers.new
18
18
  @selector = Selector.new
19
19
  @connections = []
20
- @connected_connections = 0
21
20
  end
22
21
 
23
22
  def empty?
@@ -44,16 +43,15 @@ module HTTPX
44
43
  connection.emit(:error, e)
45
44
  end
46
45
  rescue Exception # rubocop:disable Lint/RescueException
47
- @connections.each(&:reset)
46
+ @connections.each(&:force_reset)
48
47
  raise
49
48
  end
50
49
 
51
50
  def close(connections = @connections)
52
51
  return if connections.empty?
53
52
 
54
- @timers.cancel
55
53
  connections = connections.reject(&:inflight?)
56
- connections.each(&:close)
54
+ connections.each(&:terminate)
57
55
  next_tick until connections.none? { |c| c.state != :idle && @connections.include?(c) }
58
56
 
59
57
  # close resolvers
@@ -67,22 +65,36 @@ module HTTPX
67
65
  resolver.close unless resolver.closed?
68
66
  end
69
67
  # for https resolver
70
- resolver_connections.each(&:close)
68
+ resolver_connections.each(&:terminate)
71
69
  next_tick until resolver_connections.none? { |c| c.state != :idle && @connections.include?(c) }
72
70
  end
73
71
 
74
72
  def init_connection(connection, _options)
75
- resolve_connection(connection)
76
73
  connection.timers = @timers
77
- connection.on(:open) do
78
- @connected_connections += 1
79
- end
80
74
  connection.on(:activate) do
81
75
  select_connection(connection)
82
76
  end
77
+ connection.on(:exhausted) do
78
+ case connection.state
79
+ when :closed
80
+ connection.idling
81
+ @connections << connection
82
+ select_connection(connection)
83
+ when :closing
84
+ connection.once(:close) do
85
+ connection.idling
86
+ @connections << connection
87
+ select_connection(connection)
88
+ end
89
+ end
90
+ end
83
91
  connection.on(:close) do
84
92
  unregister_connection(connection)
85
93
  end
94
+ connection.on(:terminate) do
95
+ unregister_connection(connection, true)
96
+ end
97
+ resolve_connection(connection) unless connection.family
86
98
  end
87
99
 
88
100
  def deactivate(connections)
@@ -97,9 +109,24 @@ module HTTPX
97
109
  # maximize pipelining by opening as few connections as possible.
98
110
  #
99
111
  def find_connection(uri, options)
100
- @connections.find do |connection|
112
+ conn = @connections.find do |connection|
101
113
  connection.match?(uri, options)
102
114
  end
115
+
116
+ return unless conn
117
+
118
+ case conn.state
119
+ when :closed
120
+ conn.idling
121
+ select_connection(conn)
122
+ when :closing
123
+ conn.once(:close) do
124
+ conn.idling
125
+ select_connection(conn)
126
+ end
127
+ end
128
+
129
+ conn
103
130
  end
104
131
 
105
132
  private
@@ -116,19 +143,59 @@ module HTTPX
116
143
  # resolve a name (not the same as name being an IP, yet)
117
144
  # 2. when the connection is initialized with an external already open IO.
118
145
  #
146
+ connection.once(:connect_error, &connection.method(:handle_error))
119
147
  on_resolver_connection(connection)
120
148
  return
121
149
  end
122
150
 
123
151
  find_resolver_for(connection) do |resolver|
124
- resolver << connection
152
+ resolver << try_clone_connection(connection, resolver.family)
125
153
  next if resolver.empty?
126
154
 
127
155
  select_connection(resolver)
128
156
  end
129
157
  end
130
158
 
159
+ def try_clone_connection(connection, family)
160
+ connection.family ||= family
161
+
162
+ return connection if connection.family == family
163
+
164
+ new_connection = connection.class.new(connection.origin, connection.options)
165
+ new_connection.family = family
166
+
167
+ connection.once(:tcp_open) { new_connection.force_reset }
168
+ connection.once(:connect_error) do |err|
169
+ if new_connection.connecting?
170
+ new_connection.merge(connection)
171
+ connection.force_reset
172
+ else
173
+ connection.__send__(:handle_error, err)
174
+ end
175
+ end
176
+
177
+ new_connection.once(:tcp_open) do |new_conn|
178
+ if new_conn != connection
179
+ new_conn.merge(connection)
180
+ connection.force_reset
181
+ end
182
+ end
183
+ new_connection.once(:connect_error) do |err|
184
+ if connection.connecting?
185
+ # main connection has the requests
186
+ connection.merge(new_connection)
187
+ new_connection.force_reset
188
+ else
189
+ new_connection.__send__(:handle_error, err)
190
+ end
191
+ end
192
+
193
+ init_connection(new_connection, connection.options)
194
+ new_connection
195
+ end
196
+
131
197
  def on_resolver_connection(connection)
198
+ @connections << connection unless @connections.include?(connection)
132
199
  found_connection = @connections.find do |ch|
133
200
  ch != connection && ch.mergeable?(connection)
134
201
  end
@@ -145,6 +212,8 @@ module HTTPX
145
212
  end
146
213
 
147
214
  def on_resolver_error(connection, error)
215
+ return connection.emit(:connect_error, error) if connection.connecting? && connection.callbacks_for?(:connect_error)
216
+
148
217
  connection.emit(:error, error)
149
218
  end
150
219
 
@@ -159,20 +228,12 @@ module HTTPX
159
228
  end
160
229
 
161
230
  def register_connection(connection)
162
- if connection.state == :open
163
- # if open, an IO was passed upstream, therefore
164
- # consider it connected already.
165
- @connected_connections += 1
166
- end
167
231
  select_connection(connection)
168
- connection.on(:close) do
169
- unregister_connection(connection)
170
- end
171
232
  end
172
233
 
173
- def unregister_connection(connection)
174
- @connections.delete(connection)
175
- @connected_connections -= 1 if deselect_connection(connection)
234
+ def unregister_connection(connection, cleanup = !connection.used?)
235
+ @connections.delete(connection) if cleanup
236
+ deselect_connection(connection)
176
237
  end
177
238
 
178
239
  def select_connection(connection)
@@ -186,6 +247,7 @@ module HTTPX
186
247
  def coalesce_connections(conn1, conn2)
187
248
  return register_connection(conn2) unless conn1.coalescable?(conn2)
188
249
 
250
+ conn2.emit(:tcp_open, conn1)
189
251
  conn1.merge(conn2)
190
252
  @connections.delete(conn2)
191
253
  end
@@ -201,7 +263,7 @@ module HTTPX
201
263
  def find_resolver_for(connection)
202
264
  connection_options = connection.options
203
265
  resolver_type = connection_options.resolver_class
204
- resolver_type = Resolver.registry(resolver_type) if resolver_type.is_a?(Symbol)
266
+ resolver_type = Resolver.resolver_for(resolver_type)
205
267
 
206
268
  @resolvers[resolver_type] ||= begin
207
269
  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