httpx-patched 1.6.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 (336) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +191 -0
  3. data/README.md +162 -0
  4. data/doc/release_notes/0_0_1.md +7 -0
  5. data/doc/release_notes/0_0_2.md +9 -0
  6. data/doc/release_notes/0_0_3.md +9 -0
  7. data/doc/release_notes/0_0_4.md +7 -0
  8. data/doc/release_notes/0_0_5.md +5 -0
  9. data/doc/release_notes/0_10_0.md +66 -0
  10. data/doc/release_notes/0_10_1.md +37 -0
  11. data/doc/release_notes/0_10_2.md +5 -0
  12. data/doc/release_notes/0_11_0.md +74 -0
  13. data/doc/release_notes/0_11_1.md +5 -0
  14. data/doc/release_notes/0_11_2.md +5 -0
  15. data/doc/release_notes/0_11_3.md +5 -0
  16. data/doc/release_notes/0_12_0.md +55 -0
  17. data/doc/release_notes/0_13_0.md +58 -0
  18. data/doc/release_notes/0_13_1.md +5 -0
  19. data/doc/release_notes/0_13_2.md +9 -0
  20. data/doc/release_notes/0_14_0.md +79 -0
  21. data/doc/release_notes/0_14_1.md +7 -0
  22. data/doc/release_notes/0_14_2.md +6 -0
  23. data/doc/release_notes/0_14_3.md +5 -0
  24. data/doc/release_notes/0_14_4.md +5 -0
  25. data/doc/release_notes/0_14_5.md +11 -0
  26. data/doc/release_notes/0_15_0.md +53 -0
  27. data/doc/release_notes/0_15_1.md +8 -0
  28. data/doc/release_notes/0_15_2.md +9 -0
  29. data/doc/release_notes/0_15_3.md +5 -0
  30. data/doc/release_notes/0_15_4.md +5 -0
  31. data/doc/release_notes/0_16_0.md +93 -0
  32. data/doc/release_notes/0_16_1.md +5 -0
  33. data/doc/release_notes/0_17_0.md +49 -0
  34. data/doc/release_notes/0_18_0.md +69 -0
  35. data/doc/release_notes/0_18_1.md +12 -0
  36. data/doc/release_notes/0_18_2.md +10 -0
  37. data/doc/release_notes/0_18_3.md +7 -0
  38. data/doc/release_notes/0_18_4.md +14 -0
  39. data/doc/release_notes/0_18_5.md +10 -0
  40. data/doc/release_notes/0_18_6.md +5 -0
  41. data/doc/release_notes/0_18_7.md +5 -0
  42. data/doc/release_notes/0_19_0.md +39 -0
  43. data/doc/release_notes/0_19_1.md +5 -0
  44. data/doc/release_notes/0_19_2.md +7 -0
  45. data/doc/release_notes/0_19_3.md +6 -0
  46. data/doc/release_notes/0_19_4.md +14 -0
  47. data/doc/release_notes/0_19_5.md +13 -0
  48. data/doc/release_notes/0_19_6.md +5 -0
  49. data/doc/release_notes/0_19_7.md +5 -0
  50. data/doc/release_notes/0_19_8.md +5 -0
  51. data/doc/release_notes/0_1_0.md +9 -0
  52. data/doc/release_notes/0_20_0.md +36 -0
  53. data/doc/release_notes/0_20_1.md +5 -0
  54. data/doc/release_notes/0_20_2.md +7 -0
  55. data/doc/release_notes/0_20_3.md +6 -0
  56. data/doc/release_notes/0_20_4.md +17 -0
  57. data/doc/release_notes/0_20_5.md +3 -0
  58. data/doc/release_notes/0_21_0.md +96 -0
  59. data/doc/release_notes/0_21_1.md +12 -0
  60. data/doc/release_notes/0_22_0.md +13 -0
  61. data/doc/release_notes/0_22_1.md +11 -0
  62. data/doc/release_notes/0_22_2.md +5 -0
  63. data/doc/release_notes/0_22_3.md +55 -0
  64. data/doc/release_notes/0_22_4.md +6 -0
  65. data/doc/release_notes/0_22_5.md +6 -0
  66. data/doc/release_notes/0_23_0.md +42 -0
  67. data/doc/release_notes/0_23_1.md +5 -0
  68. data/doc/release_notes/0_23_2.md +5 -0
  69. data/doc/release_notes/0_23_3.md +6 -0
  70. data/doc/release_notes/0_23_4.md +5 -0
  71. data/doc/release_notes/0_24_0.md +48 -0
  72. data/doc/release_notes/0_24_1.md +12 -0
  73. data/doc/release_notes/0_24_2.md +12 -0
  74. data/doc/release_notes/0_24_3.md +12 -0
  75. data/doc/release_notes/0_24_4.md +18 -0
  76. data/doc/release_notes/0_24_5.md +6 -0
  77. data/doc/release_notes/0_24_6.md +5 -0
  78. data/doc/release_notes/0_24_7.md +10 -0
  79. data/doc/release_notes/0_2_0.md +5 -0
  80. data/doc/release_notes/0_2_1.md +16 -0
  81. data/doc/release_notes/0_3_0.md +12 -0
  82. data/doc/release_notes/0_3_1.md +6 -0
  83. data/doc/release_notes/0_4_0.md +51 -0
  84. data/doc/release_notes/0_4_1.md +3 -0
  85. data/doc/release_notes/0_5_0.md +15 -0
  86. data/doc/release_notes/0_5_1.md +14 -0
  87. data/doc/release_notes/0_6_0.md +5 -0
  88. data/doc/release_notes/0_6_1.md +6 -0
  89. data/doc/release_notes/0_6_2.md +6 -0
  90. data/doc/release_notes/0_6_3.md +13 -0
  91. data/doc/release_notes/0_6_4.md +21 -0
  92. data/doc/release_notes/0_6_5.md +22 -0
  93. data/doc/release_notes/0_6_6.md +19 -0
  94. data/doc/release_notes/0_6_7.md +5 -0
  95. data/doc/release_notes/0_7_0.md +46 -0
  96. data/doc/release_notes/0_8_0.md +27 -0
  97. data/doc/release_notes/0_8_1.md +8 -0
  98. data/doc/release_notes/0_8_2.md +7 -0
  99. data/doc/release_notes/0_9_0.md +38 -0
  100. data/doc/release_notes/1_0_0.md +60 -0
  101. data/doc/release_notes/1_0_1.md +5 -0
  102. data/doc/release_notes/1_0_2.md +7 -0
  103. data/doc/release_notes/1_1_0.md +32 -0
  104. data/doc/release_notes/1_1_1.md +17 -0
  105. data/doc/release_notes/1_1_2.md +12 -0
  106. data/doc/release_notes/1_1_3.md +18 -0
  107. data/doc/release_notes/1_1_4.md +6 -0
  108. data/doc/release_notes/1_1_5.md +12 -0
  109. data/doc/release_notes/1_2_0.md +49 -0
  110. data/doc/release_notes/1_2_1.md +6 -0
  111. data/doc/release_notes/1_2_2.md +10 -0
  112. data/doc/release_notes/1_2_3.md +16 -0
  113. data/doc/release_notes/1_2_4.md +8 -0
  114. data/doc/release_notes/1_2_5.md +7 -0
  115. data/doc/release_notes/1_2_6.md +13 -0
  116. data/doc/release_notes/1_3_0.md +18 -0
  117. data/doc/release_notes/1_3_1.md +17 -0
  118. data/doc/release_notes/1_3_2.md +6 -0
  119. data/doc/release_notes/1_3_3.md +5 -0
  120. data/doc/release_notes/1_3_4.md +6 -0
  121. data/doc/release_notes/1_4_0.md +43 -0
  122. data/doc/release_notes/1_4_1.md +19 -0
  123. data/doc/release_notes/1_4_2.md +20 -0
  124. data/doc/release_notes/1_4_3.md +11 -0
  125. data/doc/release_notes/1_4_4.md +14 -0
  126. data/doc/release_notes/1_5_0.md +126 -0
  127. data/doc/release_notes/1_5_1.md +6 -0
  128. data/doc/release_notes/1_6_0.md +50 -0
  129. data/doc/release_notes/1_6_1.md +17 -0
  130. data/doc/release_notes/1_6_2.md +11 -0
  131. data/lib/httpx/adapters/datadog.rb +359 -0
  132. data/lib/httpx/adapters/faraday.rb +303 -0
  133. data/lib/httpx/adapters/sentry.rb +121 -0
  134. data/lib/httpx/adapters/webmock.rb +175 -0
  135. data/lib/httpx/altsvc.rb +163 -0
  136. data/lib/httpx/base64.rb +27 -0
  137. data/lib/httpx/buffer.rb +61 -0
  138. data/lib/httpx/callbacks.rb +35 -0
  139. data/lib/httpx/chainable.rb +106 -0
  140. data/lib/httpx/connection/http1.rb +399 -0
  141. data/lib/httpx/connection/http2.rb +468 -0
  142. data/lib/httpx/connection.rb +954 -0
  143. data/lib/httpx/domain_name.rb +145 -0
  144. data/lib/httpx/errors.rb +111 -0
  145. data/lib/httpx/extensions.rb +59 -0
  146. data/lib/httpx/headers.rb +176 -0
  147. data/lib/httpx/io/ssl.rb +163 -0
  148. data/lib/httpx/io/tcp.rb +239 -0
  149. data/lib/httpx/io/udp.rb +62 -0
  150. data/lib/httpx/io/unix.rb +71 -0
  151. data/lib/httpx/io.rb +11 -0
  152. data/lib/httpx/loggable.rb +56 -0
  153. data/lib/httpx/options.rb +463 -0
  154. data/lib/httpx/parser/http1.rb +186 -0
  155. data/lib/httpx/plugins/auth/basic.rb +20 -0
  156. data/lib/httpx/plugins/auth/digest.rb +102 -0
  157. data/lib/httpx/plugins/auth/ntlm.rb +35 -0
  158. data/lib/httpx/plugins/auth/socks5.rb +22 -0
  159. data/lib/httpx/plugins/auth.rb +25 -0
  160. data/lib/httpx/plugins/aws_sdk_authentication.rb +111 -0
  161. data/lib/httpx/plugins/aws_sigv4.rb +239 -0
  162. data/lib/httpx/plugins/basic_auth.rb +29 -0
  163. data/lib/httpx/plugins/brotli.rb +50 -0
  164. data/lib/httpx/plugins/callbacks.rb +127 -0
  165. data/lib/httpx/plugins/circuit_breaker/circuit.rb +100 -0
  166. data/lib/httpx/plugins/circuit_breaker/circuit_store.rb +53 -0
  167. data/lib/httpx/plugins/circuit_breaker.rb +147 -0
  168. data/lib/httpx/plugins/content_digest.rb +204 -0
  169. data/lib/httpx/plugins/cookies/cookie.rb +174 -0
  170. data/lib/httpx/plugins/cookies/jar.rb +95 -0
  171. data/lib/httpx/plugins/cookies/set_cookie_parser.rb +143 -0
  172. data/lib/httpx/plugins/cookies.rb +107 -0
  173. data/lib/httpx/plugins/digest_auth.rb +67 -0
  174. data/lib/httpx/plugins/expect.rb +120 -0
  175. data/lib/httpx/plugins/fiber_concurrency.rb +195 -0
  176. data/lib/httpx/plugins/follow_redirects.rb +233 -0
  177. data/lib/httpx/plugins/grpc/call.rb +63 -0
  178. data/lib/httpx/plugins/grpc/grpc_encoding.rb +90 -0
  179. data/lib/httpx/plugins/grpc/message.rb +55 -0
  180. data/lib/httpx/plugins/grpc.rb +282 -0
  181. data/lib/httpx/plugins/h2c.rb +127 -0
  182. data/lib/httpx/plugins/internal_telemetry.rb +107 -0
  183. data/lib/httpx/plugins/ntlm_auth.rb +62 -0
  184. data/lib/httpx/plugins/oauth.rb +183 -0
  185. data/lib/httpx/plugins/persistent.rb +82 -0
  186. data/lib/httpx/plugins/proxy/http.rb +184 -0
  187. data/lib/httpx/plugins/proxy/socks4.rb +135 -0
  188. data/lib/httpx/plugins/proxy/socks5.rb +194 -0
  189. data/lib/httpx/plugins/proxy/ssh.rb +94 -0
  190. data/lib/httpx/plugins/proxy.rb +349 -0
  191. data/lib/httpx/plugins/push_promise.rb +81 -0
  192. data/lib/httpx/plugins/query.rb +35 -0
  193. data/lib/httpx/plugins/rate_limiter.rb +55 -0
  194. data/lib/httpx/plugins/response_cache/file_store.rb +140 -0
  195. data/lib/httpx/plugins/response_cache/store.rb +33 -0
  196. data/lib/httpx/plugins/response_cache.rb +333 -0
  197. data/lib/httpx/plugins/retries.rb +230 -0
  198. data/lib/httpx/plugins/ssrf_filter.rb +145 -0
  199. data/lib/httpx/plugins/stream.rb +183 -0
  200. data/lib/httpx/plugins/stream_bidi.rb +315 -0
  201. data/lib/httpx/plugins/upgrade/h2.rb +64 -0
  202. data/lib/httpx/plugins/upgrade.rb +86 -0
  203. data/lib/httpx/plugins/webdav.rb +86 -0
  204. data/lib/httpx/plugins/xml.rb +76 -0
  205. data/lib/httpx/pmatch_extensions.rb +33 -0
  206. data/lib/httpx/pool.rb +190 -0
  207. data/lib/httpx/punycode.rb +22 -0
  208. data/lib/httpx/request/body.rb +158 -0
  209. data/lib/httpx/request.rb +328 -0
  210. data/lib/httpx/resolver/entry.rb +30 -0
  211. data/lib/httpx/resolver/https.rb +256 -0
  212. data/lib/httpx/resolver/multi.rb +102 -0
  213. data/lib/httpx/resolver/native.rb +547 -0
  214. data/lib/httpx/resolver/resolver.rb +173 -0
  215. data/lib/httpx/resolver/system.rb +255 -0
  216. data/lib/httpx/resolver.rb +189 -0
  217. data/lib/httpx/response/body.rb +242 -0
  218. data/lib/httpx/response/buffer.rb +115 -0
  219. data/lib/httpx/response.rb +304 -0
  220. data/lib/httpx/selector.rb +282 -0
  221. data/lib/httpx/session.rb +612 -0
  222. data/lib/httpx/session_extensions.rb +30 -0
  223. data/lib/httpx/timers.rb +133 -0
  224. data/lib/httpx/transcoder/body.rb +43 -0
  225. data/lib/httpx/transcoder/chunker.rb +115 -0
  226. data/lib/httpx/transcoder/deflate.rb +37 -0
  227. data/lib/httpx/transcoder/form.rb +68 -0
  228. data/lib/httpx/transcoder/gzip.rb +71 -0
  229. data/lib/httpx/transcoder/json.rb +71 -0
  230. data/lib/httpx/transcoder/multipart/decoder.rb +141 -0
  231. data/lib/httpx/transcoder/multipart/encoder.rb +120 -0
  232. data/lib/httpx/transcoder/multipart/mime_type_detector.rb +78 -0
  233. data/lib/httpx/transcoder/multipart/part.rb +35 -0
  234. data/lib/httpx/transcoder/multipart.rb +31 -0
  235. data/lib/httpx/transcoder/utils/body_reader.rb +46 -0
  236. data/lib/httpx/transcoder/utils/deflater.rb +75 -0
  237. data/lib/httpx/transcoder.rb +91 -0
  238. data/lib/httpx/utils.rb +75 -0
  239. data/lib/httpx/version.rb +5 -0
  240. data/lib/httpx.rb +66 -0
  241. data/sig/altsvc.rbs +33 -0
  242. data/sig/buffer.rbs +27 -0
  243. data/sig/callbacks.rbs +15 -0
  244. data/sig/chainable.rbs +55 -0
  245. data/sig/connection/http1.rbs +85 -0
  246. data/sig/connection/http2.rbs +116 -0
  247. data/sig/connection.rbs +169 -0
  248. data/sig/domain_name.rbs +17 -0
  249. data/sig/errors.rbs +69 -0
  250. data/sig/headers.rbs +49 -0
  251. data/sig/httpx.rbs +27 -0
  252. data/sig/io/ssl.rbs +27 -0
  253. data/sig/io/tcp.rbs +72 -0
  254. data/sig/io/udp.rbs +25 -0
  255. data/sig/io/unix.rbs +26 -0
  256. data/sig/io.rbs +3 -0
  257. data/sig/loggable.rbs +17 -0
  258. data/sig/options.rbs +202 -0
  259. data/sig/parser/http1.rbs +59 -0
  260. data/sig/plugins/auth/basic.rbs +17 -0
  261. data/sig/plugins/auth/digest.rbs +25 -0
  262. data/sig/plugins/auth/ntlm.rbs +20 -0
  263. data/sig/plugins/auth/socks5.rbs +18 -0
  264. data/sig/plugins/auth.rbs +13 -0
  265. data/sig/plugins/aws_sdk_authentication.rbs +43 -0
  266. data/sig/plugins/aws_sigv4.rbs +78 -0
  267. data/sig/plugins/basic_auth.rbs +15 -0
  268. data/sig/plugins/brotli.rbs +22 -0
  269. data/sig/plugins/callbacks.rbs +38 -0
  270. data/sig/plugins/circuit_breaker.rbs +71 -0
  271. data/sig/plugins/compression.rbs +57 -0
  272. data/sig/plugins/content_digest.rbs +51 -0
  273. data/sig/plugins/cookies/cookie.rbs +55 -0
  274. data/sig/plugins/cookies/jar.rbs +26 -0
  275. data/sig/plugins/cookies/set_cookie_parser.rbs +22 -0
  276. data/sig/plugins/cookies.rbs +28 -0
  277. data/sig/plugins/digest_auth.rbs +21 -0
  278. data/sig/plugins/expect.rbs +15 -0
  279. data/sig/plugins/fiber_concurrency.rbs +51 -0
  280. data/sig/plugins/follow_redirects.rbs +47 -0
  281. data/sig/plugins/grpc/call.rbs +23 -0
  282. data/sig/plugins/grpc/grpc_encoding.rbs +37 -0
  283. data/sig/plugins/grpc/message.rbs +17 -0
  284. data/sig/plugins/grpc.rbs +65 -0
  285. data/sig/plugins/h2c.rbs +27 -0
  286. data/sig/plugins/ntlm_auth.rbs +21 -0
  287. data/sig/plugins/oauth.rbs +68 -0
  288. data/sig/plugins/persistent.rbs +14 -0
  289. data/sig/plugins/proxy/http.rbs +30 -0
  290. data/sig/plugins/proxy/socks4.rbs +37 -0
  291. data/sig/plugins/proxy/socks5.rbs +49 -0
  292. data/sig/plugins/proxy/ssh.rbs +18 -0
  293. data/sig/plugins/proxy.rbs +70 -0
  294. data/sig/plugins/push_promise.rbs +23 -0
  295. data/sig/plugins/query.rbs +18 -0
  296. data/sig/plugins/rate_limiter.rbs +13 -0
  297. data/sig/plugins/response_cache/file_store.rbs +19 -0
  298. data/sig/plugins/response_cache/store.rbs +13 -0
  299. data/sig/plugins/response_cache.rbs +86 -0
  300. data/sig/plugins/retries.rbs +66 -0
  301. data/sig/plugins/ssrf_filter.rbs +26 -0
  302. data/sig/plugins/stream.rbs +54 -0
  303. data/sig/plugins/stream_bidi.rbs +68 -0
  304. data/sig/plugins/upgrade/h2.rbs +9 -0
  305. data/sig/plugins/upgrade.rbs +29 -0
  306. data/sig/plugins/webdav.rbs +23 -0
  307. data/sig/plugins/xml.rbs +37 -0
  308. data/sig/pool.rbs +51 -0
  309. data/sig/punycode.rbs +5 -0
  310. data/sig/request/body.rbs +34 -0
  311. data/sig/request.rbs +88 -0
  312. data/sig/resolver/entry.rbs +13 -0
  313. data/sig/resolver/https.rbs +45 -0
  314. data/sig/resolver/multi.rbs +32 -0
  315. data/sig/resolver/native.rbs +74 -0
  316. data/sig/resolver/resolver.rbs +64 -0
  317. data/sig/resolver/system.rbs +34 -0
  318. data/sig/resolver.rbs +48 -0
  319. data/sig/response/body.rbs +52 -0
  320. data/sig/response/buffer.rbs +23 -0
  321. data/sig/response.rbs +103 -0
  322. data/sig/selector.rbs +68 -0
  323. data/sig/session.rbs +104 -0
  324. data/sig/timers.rbs +54 -0
  325. data/sig/transcoder/body.rbs +24 -0
  326. data/sig/transcoder/chunker.rbs +49 -0
  327. data/sig/transcoder/deflate.rbs +12 -0
  328. data/sig/transcoder/form.rbs +34 -0
  329. data/sig/transcoder/gzip.rbs +27 -0
  330. data/sig/transcoder/json.rbs +28 -0
  331. data/sig/transcoder/multipart.rbs +103 -0
  332. data/sig/transcoder/utils/body_reader.rbs +15 -0
  333. data/sig/transcoder/utils/deflater.rbs +28 -0
  334. data/sig/transcoder.rbs +43 -0
  335. data/sig/utils.rbs +19 -0
  336. metadata +518 -0
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ #
6
+ # https://gitlab.com/os85/httpx/wikis/OAuth
7
+ #
8
+ module OAuth
9
+ class << self
10
+ def load_dependencies(_klass)
11
+ require_relative "auth/basic"
12
+ end
13
+ end
14
+
15
+ SUPPORTED_GRANT_TYPES = %w[client_credentials refresh_token].freeze
16
+ SUPPORTED_AUTH_METHODS = %w[client_secret_basic client_secret_post].freeze
17
+
18
+ class OAuthSession
19
+ attr_reader :grant_type, :client_id, :client_secret, :access_token, :refresh_token, :scope, :audience
20
+
21
+ def initialize(
22
+ issuer:,
23
+ client_id:,
24
+ client_secret:,
25
+ access_token: nil,
26
+ refresh_token: nil,
27
+ scope: nil,
28
+ audience: nil,
29
+ token_endpoint: nil,
30
+ response_type: nil,
31
+ grant_type: nil,
32
+ token_endpoint_auth_method: nil
33
+ )
34
+ @issuer = URI(issuer)
35
+ @client_id = client_id
36
+ @client_secret = client_secret
37
+ @token_endpoint = URI(token_endpoint) if token_endpoint
38
+ @response_type = response_type
39
+ @scope = case scope
40
+ when String
41
+ scope.split
42
+ when Array
43
+ scope
44
+ end
45
+ @audience = audience
46
+ @access_token = access_token
47
+ @refresh_token = refresh_token
48
+ @token_endpoint_auth_method = String(token_endpoint_auth_method) if token_endpoint_auth_method
49
+ @grant_type = grant_type || (@refresh_token ? "refresh_token" : "client_credentials")
50
+
51
+ unless @token_endpoint_auth_method.nil? || SUPPORTED_AUTH_METHODS.include?(@token_endpoint_auth_method)
52
+ raise Error, "#{@token_endpoint_auth_method} is not a supported auth method"
53
+ end
54
+
55
+ return if SUPPORTED_GRANT_TYPES.include?(@grant_type)
56
+
57
+ raise Error, "#{@grant_type} is not a supported grant type"
58
+ end
59
+
60
+ def token_endpoint
61
+ @token_endpoint || "#{@issuer}/token"
62
+ end
63
+
64
+ def token_endpoint_auth_method
65
+ @token_endpoint_auth_method || "client_secret_basic"
66
+ end
67
+
68
+ def load(http)
69
+ return if @grant_type && @scope
70
+
71
+ metadata = http.get("#{@issuer}/.well-known/oauth-authorization-server").raise_for_status.json
72
+
73
+ @token_endpoint = metadata["token_endpoint"]
74
+ @scope = metadata["scopes_supported"]
75
+ @grant_type = Array(metadata["grant_types_supported"]).find { |gr| SUPPORTED_GRANT_TYPES.include?(gr) }
76
+ @token_endpoint_auth_method = Array(metadata["token_endpoint_auth_methods_supported"]).find do |am|
77
+ SUPPORTED_AUTH_METHODS.include?(am)
78
+ end
79
+ nil
80
+ end
81
+
82
+ def merge(other)
83
+ obj = dup
84
+
85
+ case other
86
+ when OAuthSession
87
+ other.instance_variables.each do |ivar|
88
+ val = other.instance_variable_get(ivar)
89
+ next unless val
90
+
91
+ obj.instance_variable_set(ivar, val)
92
+ end
93
+ when Hash
94
+ other.each do |k, v|
95
+ obj.instance_variable_set(:"@#{k}", v) if obj.instance_variable_defined?(:"@#{k}")
96
+ end
97
+ end
98
+ obj
99
+ end
100
+ end
101
+
102
+ module OptionsMethods
103
+ private
104
+
105
+ def option_oauth_session(value)
106
+ case value
107
+ when Hash
108
+ OAuthSession.new(**value)
109
+ when OAuthSession
110
+ value
111
+ else
112
+ raise TypeError, ":oauth_session must be a #{OAuthSession}"
113
+ end
114
+ end
115
+ end
116
+
117
+ module InstanceMethods
118
+ def oauth_auth(**args)
119
+ with(oauth_session: OAuthSession.new(**args))
120
+ end
121
+
122
+ def with_access_token
123
+ oauth_session = @options.oauth_session
124
+
125
+ oauth_session.load(self)
126
+
127
+ grant_type = oauth_session.grant_type
128
+
129
+ headers = {}
130
+ form_post = {
131
+ "grant_type" => grant_type,
132
+ "scope" => Array(oauth_session.scope).join(" "),
133
+ "audience" => oauth_session.audience,
134
+ }.compact
135
+
136
+ # auth
137
+ case oauth_session.token_endpoint_auth_method
138
+ when "client_secret_post"
139
+ form_post["client_id"] = oauth_session.client_id
140
+ form_post["client_secret"] = oauth_session.client_secret
141
+ when "client_secret_basic"
142
+ headers["authorization"] = Authentication::Basic.new(oauth_session.client_id, oauth_session.client_secret).authenticate
143
+ end
144
+
145
+ case grant_type
146
+ when "client_credentials"
147
+ # do nothing
148
+ when "refresh_token"
149
+ form_post["refresh_token"] = oauth_session.refresh_token
150
+ end
151
+
152
+ token_request = build_request("POST", oauth_session.token_endpoint, headers: headers, form: form_post)
153
+ token_request.headers.delete("authorization") unless oauth_session.token_endpoint_auth_method == "client_secret_basic"
154
+
155
+ token_response = request(token_request)
156
+ token_response.raise_for_status
157
+
158
+ payload = token_response.json
159
+
160
+ access_token = payload["access_token"]
161
+ refresh_token = payload["refresh_token"]
162
+
163
+ with(oauth_session: oauth_session.merge(access_token: access_token, refresh_token: refresh_token))
164
+ end
165
+
166
+ def build_request(*)
167
+ request = super
168
+
169
+ return request if request.headers.key?("authorization")
170
+
171
+ oauth_session = @options.oauth_session
172
+
173
+ return request unless oauth_session && oauth_session.access_token
174
+
175
+ request.headers["authorization"] = "Bearer #{oauth_session.access_token}"
176
+
177
+ request
178
+ end
179
+ end
180
+ end
181
+ register_plugin :oauth, OAuth
182
+ end
183
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ # This plugin implements a session that persists connections over the duration of the process.
6
+ #
7
+ # This will improve connection reuse in a long-running process.
8
+ #
9
+ # One important caveat to note is, although this session might not close connections,
10
+ # other sessions from the same process that don't have this plugin turned on might.
11
+ #
12
+ # This session will still be able to work with it, as if, when expecting a connection
13
+ # terminated by a different session, it will just retry on a new one and keep it open.
14
+ #
15
+ # This plugin is also not recommendable when connecting to >9000 (like, a lot) different origins.
16
+ # So when you use this, make sure that you don't fall into this trap.
17
+ #
18
+ # https://gitlab.com/os85/httpx/wikis/Persistent
19
+ #
20
+ module Persistent
21
+ class << self
22
+ def load_dependencies(klass)
23
+ klass.plugin(:fiber_concurrency)
24
+
25
+ max_retries = if klass.default_options.respond_to?(:max_retries)
26
+ [klass.default_options.max_retries, 1].max
27
+ else
28
+ 1
29
+ end
30
+ klass.plugin(:retries, max_retries: max_retries)
31
+ end
32
+ end
33
+
34
+ def self.extra_options(options)
35
+ options.merge(persistent: true)
36
+ end
37
+
38
+ module InstanceMethods
39
+ def close(*)
40
+ super
41
+
42
+ # traverse other threads and unlink respective selector
43
+ # WARNING: this is not thread safe, make sure that the session isn't being
44
+ # used anymore, or all non-main threads are stopped.
45
+ Thread.list.each do |th|
46
+ store = thread_selector_store(th)
47
+
48
+ next unless store && store.key?(self)
49
+
50
+ selector = store.delete(self)
51
+
52
+ selector_close(selector)
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def repeatable_request?(request, _)
59
+ super || begin
60
+ response = request.response
61
+
62
+ return false unless response && response.is_a?(ErrorResponse)
63
+
64
+ error = response.error
65
+
66
+ Retries::RECONNECTABLE_ERRORS.any? { |klass| error.is_a?(klass) }
67
+ end
68
+ end
69
+
70
+ def retryable_error?(ex)
71
+ super &&
72
+ # under the persistent plugin rules, requests are only retried for connection related errors,
73
+ # which do not include request timeout related errors. This only gets overriden if the end user
74
+ # manually changed +:max_retries+ to something else, which means it is aware of the
75
+ # consequences.
76
+ (!ex.is_a?(RequestTimeoutError) || @options.max_retries != 1)
77
+ end
78
+ end
79
+ end
80
+ register_plugin :persistent, Persistent
81
+ end
82
+ end
@@ -0,0 +1,184 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ module Proxy
6
+ module HTTP
7
+ class << self
8
+ def extra_options(options)
9
+ options.merge(supported_proxy_protocols: options.supported_proxy_protocols + %w[http])
10
+ end
11
+ end
12
+
13
+ module InstanceMethods
14
+ def with_proxy_basic_auth(opts)
15
+ with(proxy: opts.merge(scheme: "basic"))
16
+ end
17
+
18
+ def with_proxy_digest_auth(opts)
19
+ with(proxy: opts.merge(scheme: "digest"))
20
+ end
21
+
22
+ def with_proxy_ntlm_auth(opts)
23
+ with(proxy: opts.merge(scheme: "ntlm"))
24
+ end
25
+
26
+ def fetch_response(request, selector, options)
27
+ response = super
28
+
29
+ if response &&
30
+ response.is_a?(Response) &&
31
+ response.status == 407 &&
32
+ !request.headers.key?("proxy-authorization") &&
33
+ response.headers.key?("proxy-authenticate") && options.proxy.can_authenticate?(response.headers["proxy-authenticate"])
34
+ request.transition(:idle)
35
+ request.headers["proxy-authorization"] =
36
+ options.proxy.authenticate(request, response.headers["proxy-authenticate"])
37
+ send_request(request, selector, options)
38
+ return
39
+ end
40
+
41
+ response
42
+ end
43
+ end
44
+
45
+ module ConnectionMethods
46
+ def connecting?
47
+ super || @state == :connecting || @state == :connected
48
+ end
49
+
50
+ private
51
+
52
+ def handle_transition(nextstate)
53
+ return super unless @options.proxy && @options.proxy.uri.scheme == "http"
54
+
55
+ case nextstate
56
+ when :connecting
57
+ return unless @state == :idle
58
+
59
+ @io.connect
60
+ return unless @io.connected?
61
+
62
+ @parser || begin
63
+ @parser = parser_type(@io.protocol).new(@write_buffer, @options.merge(max_concurrent_requests: 1))
64
+ parser = @parser
65
+ parser.extend(ProxyParser)
66
+ parser.on(:response, &method(:__http_on_connect))
67
+ parser.on(:close) do |force|
68
+ next unless @parser
69
+
70
+ if force
71
+ reset
72
+ emit(:terminate)
73
+ end
74
+ end
75
+ parser.on(:reset) do
76
+ if parser.empty?
77
+ reset
78
+ else
79
+ transition(:closing)
80
+ transition(:closed)
81
+
82
+ parser.reset if @parser
83
+ transition(:idle)
84
+ transition(:connecting)
85
+ end
86
+ end
87
+ __http_proxy_connect(parser)
88
+ end
89
+ return if @state == :connected
90
+ when :connected
91
+ return unless @state == :idle || @state == :connecting
92
+
93
+ case @state
94
+ when :connecting
95
+ parser = @parser
96
+ @parser = nil
97
+ parser.close
98
+ when :idle
99
+ @parser.callbacks.clear
100
+ set_parser_callbacks(@parser)
101
+ end
102
+ end
103
+ super
104
+ end
105
+
106
+ def __http_proxy_connect(parser)
107
+ req = @pending.first
108
+ if req && req.uri.scheme == "https"
109
+ # if the first request after CONNECT is to an https address, it is assumed that
110
+ # all requests in the queue are not only ALL HTTPS, but they also share the certificate,
111
+ # and therefore, will share the connection.
112
+ #
113
+ connect_request = ConnectRequest.new(req.uri, @options)
114
+ @inflight += 1
115
+ parser.send(connect_request)
116
+ else
117
+ handle_transition(:connected)
118
+ end
119
+ end
120
+
121
+ def __http_on_connect(request, response)
122
+ @inflight -= 1
123
+ if response.is_a?(Response) && response.status == 200
124
+ req = @pending.first
125
+ request_uri = req.uri
126
+ @io = ProxySSL.new(@io, request_uri, @options)
127
+ transition(:connected)
128
+ throw(:called)
129
+ elsif response.is_a?(Response) &&
130
+ response.status == 407 &&
131
+ !request.headers.key?("proxy-authorization") &&
132
+ @options.proxy.can_authenticate?(response.headers["proxy-authenticate"])
133
+
134
+ request.transition(:idle)
135
+ request.headers["proxy-authorization"] = @options.proxy.authenticate(request, response.headers["proxy-authenticate"])
136
+ @parser.send(request)
137
+ @inflight += 1
138
+ else
139
+ pending = @pending + @parser.pending
140
+ while (req = pending.shift)
141
+ response.finish!
142
+ req.response = response
143
+ req.emit(:response, response)
144
+ end
145
+ reset
146
+ end
147
+ end
148
+ end
149
+
150
+ module ProxyParser
151
+ def join_headline(request)
152
+ return super if request.verb == "CONNECT"
153
+
154
+ "#{request.verb} #{request.uri} HTTP/#{@version.join(".")}"
155
+ end
156
+
157
+ def set_protocol_headers(request)
158
+ extra_headers = super
159
+
160
+ proxy_params = @options.proxy
161
+ if proxy_params.scheme == "basic"
162
+ # opt for basic auth
163
+ extra_headers["proxy-authorization"] = proxy_params.authenticate(extra_headers)
164
+ end
165
+ extra_headers["proxy-connection"] = extra_headers.delete("connection") if extra_headers.key?("connection")
166
+ extra_headers
167
+ end
168
+ end
169
+
170
+ class ConnectRequest < Request
171
+ def initialize(uri, options)
172
+ super("CONNECT", uri, options)
173
+ @headers.delete("accept")
174
+ end
175
+
176
+ def path
177
+ "#{@uri.hostname}:#{@uri.port}"
178
+ end
179
+ end
180
+ end
181
+ end
182
+ register_plugin :"proxy/http", Proxy::HTTP
183
+ end
184
+ end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "resolv"
4
+ require "ipaddr"
5
+
6
+ module HTTPX
7
+ class Socks4Error < ProxyError; end
8
+
9
+ module Plugins
10
+ module Proxy
11
+ module Socks4
12
+ VERSION = 4
13
+ CONNECT = 1
14
+ GRANTED = 0x5A
15
+ PROTOCOLS = %w[socks4 socks4a].freeze
16
+
17
+ Error = Socks4Error
18
+
19
+ class << self
20
+ def extra_options(options)
21
+ options.merge(supported_proxy_protocols: options.supported_proxy_protocols + PROTOCOLS)
22
+ end
23
+ end
24
+
25
+ module ConnectionMethods
26
+ def interests
27
+ if @state == :connecting
28
+ return @write_buffer.empty? ? :r : :w
29
+ end
30
+
31
+ super
32
+ end
33
+
34
+ private
35
+
36
+ def handle_transition(nextstate)
37
+ return super unless @options.proxy && PROTOCOLS.include?(@options.proxy.uri.scheme)
38
+
39
+ case nextstate
40
+ when :connecting
41
+ return unless @state == :idle
42
+
43
+ @io.connect
44
+ return unless @io.connected?
45
+
46
+ req = @pending.first
47
+ return unless req
48
+
49
+ request_uri = req.uri
50
+ @write_buffer << Packet.connect(@options.proxy, request_uri)
51
+ __socks4_proxy_connect
52
+ when :connected
53
+ return unless @state == :connecting
54
+
55
+ @parser = nil
56
+ end
57
+ log(level: 1) { "SOCKS4: #{nextstate}: #{@write_buffer.to_s.inspect}" } unless nextstate == :open
58
+ super
59
+ end
60
+
61
+ def __socks4_proxy_connect
62
+ @parser = SocksParser.new(@write_buffer, @options)
63
+ @parser.once(:packet, &method(:__socks4_on_packet))
64
+ end
65
+
66
+ def __socks4_on_packet(packet)
67
+ _version, status, _port, _ip = packet.unpack("CCnN")
68
+ if status == GRANTED
69
+ req = @pending.first
70
+ request_uri = req.uri
71
+ @io = ProxySSL.new(@io, request_uri, @options) if request_uri.scheme == "https"
72
+ transition(:connected)
73
+ throw(:called)
74
+ else
75
+ on_socks4_error("socks error: #{status}")
76
+ end
77
+ end
78
+
79
+ def on_socks4_error(message)
80
+ ex = Error.new(message)
81
+ ex.set_backtrace(caller)
82
+ on_error(ex)
83
+ throw(:called)
84
+ end
85
+ end
86
+
87
+ class SocksParser
88
+ include HTTPX::Callbacks
89
+
90
+ def initialize(buffer, options)
91
+ @buffer = buffer
92
+ @options = options
93
+ end
94
+
95
+ def close; end
96
+
97
+ def consume(*); end
98
+
99
+ def empty?
100
+ true
101
+ end
102
+
103
+ def <<(packet)
104
+ emit(:packet, packet)
105
+ end
106
+ end
107
+
108
+ module Packet
109
+ module_function
110
+
111
+ def connect(parameters, uri)
112
+ packet = [VERSION, CONNECT, uri.port].pack("CCn")
113
+
114
+ case parameters.uri.scheme
115
+ when "socks4"
116
+ socks_host = uri.host
117
+ begin
118
+ ip = IPAddr.new(socks_host)
119
+ packet << ip.hton
120
+ rescue IPAddr::InvalidAddressError
121
+ socks_host = Resolv.getaddress(socks_host)
122
+ retry
123
+ end
124
+ packet << [parameters.username].pack("Z*")
125
+ when "socks4a"
126
+ packet << "\x0\x0\x0\x1" << [parameters.username].pack("Z*") << uri.host << "\x0"
127
+ end
128
+ packet
129
+ end
130
+ end
131
+ end
132
+ end
133
+ register_plugin :"proxy/socks4", Proxy::Socks4
134
+ end
135
+ end