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,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+ require "securerandom"
5
+ require "digest"
6
+
7
+ module HTTPX
8
+ module Plugins
9
+ module Authentication
10
+ class Digest
11
+ def initialize(user, password, hashed: false, **)
12
+ @user = user
13
+ @password = password
14
+ @nonce = 0
15
+ @hashed = hashed
16
+ end
17
+
18
+ def can_authenticate?(authenticate)
19
+ authenticate && /Digest .*/.match?(authenticate)
20
+ end
21
+
22
+ def authenticate(request, authenticate)
23
+ "Digest #{generate_header(request.verb, request.path, authenticate)}"
24
+ end
25
+
26
+ private
27
+
28
+ def generate_header(meth, uri, authenticate)
29
+ # discard first token, it's Digest
30
+ auth_info = authenticate[/^(\w+) (.*)/, 2]
31
+
32
+ params = auth_info.split(/ *, */)
33
+ .to_h { |val| val.split("=", 2) }
34
+ .transform_values { |v| v.delete("\"") }
35
+ nonce = params["nonce"]
36
+ nc = next_nonce
37
+
38
+ # verify qop
39
+ qop = params["qop"]
40
+
41
+ if params["algorithm"] =~ /(.*?)(-sess)?$/
42
+ alg = Regexp.last_match(1)
43
+ algorithm = ::Digest.const_get(alg)
44
+ raise DigestError, "unknown algorithm \"#{alg}\"" unless algorithm
45
+
46
+ sess = Regexp.last_match(2)
47
+ else
48
+ algorithm = ::Digest::MD5
49
+ end
50
+
51
+ if qop || sess
52
+ cnonce = make_cnonce
53
+ nc = format("%<nonce>08x", nonce: nc)
54
+ end
55
+
56
+ a1 = if sess
57
+ [
58
+ (@hashed ? @password : algorithm.hexdigest("#{@user}:#{params["realm"]}:#{@password}")),
59
+ nonce,
60
+ cnonce,
61
+ ].join ":"
62
+ else
63
+ @hashed ? @password : "#{@user}:#{params["realm"]}:#{@password}"
64
+ end
65
+
66
+ ha1 = algorithm.hexdigest(a1)
67
+ ha2 = algorithm.hexdigest("#{meth}:#{uri}")
68
+ request_digest = [ha1, nonce]
69
+ request_digest.push(nc, cnonce, qop) if qop
70
+ request_digest << ha2
71
+ request_digest = request_digest.join(":")
72
+
73
+ header = [
74
+ %(username="#{@user}"),
75
+ %(nonce="#{nonce}"),
76
+ %(uri="#{uri}"),
77
+ %(response="#{algorithm.hexdigest(request_digest)}"),
78
+ ]
79
+ header << %(realm="#{params["realm"]}") if params.key?("realm")
80
+ header << %(algorithm=#{params["algorithm"]}) if params.key?("algorithm")
81
+ header << %(cnonce="#{cnonce}") if cnonce
82
+ header << %(nc=#{nc})
83
+ header << %(qop=#{qop}) if qop
84
+ header << %(opaque="#{params["opaque"]}") if params.key?("opaque")
85
+ header.join ", "
86
+ end
87
+
88
+ def make_cnonce
89
+ ::Digest::MD5.hexdigest [
90
+ Time.now.to_i,
91
+ Process.pid,
92
+ SecureRandom.random_number(2**32),
93
+ ].join ":"
94
+ end
95
+
96
+ def next_nonce
97
+ @nonce += 1
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "httpx/base64"
4
+ require "ntlm"
5
+
6
+ module HTTPX
7
+ module Plugins
8
+ module Authentication
9
+ class Ntlm
10
+ def initialize(user, password, domain: nil)
11
+ @user = user
12
+ @password = password
13
+ @domain = domain
14
+ end
15
+
16
+ def can_authenticate?(authenticate)
17
+ authenticate && /NTLM .*/.match?(authenticate)
18
+ end
19
+
20
+ def negotiate
21
+ "NTLM #{NTLM.negotiate(domain: @domain).to_base64}"
22
+ end
23
+
24
+ def authenticate(_req, www)
25
+ challenge = www[/NTLM (.*)/, 1]
26
+
27
+ challenge = Base64.decode64(challenge)
28
+ ntlm_challenge = NTLM.authenticate(challenge, @user, @domain, @password).to_base64
29
+
30
+ "NTLM #{ntlm_challenge}"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ module Authentication
6
+ class Socks5
7
+ def initialize(user, password, **)
8
+ @user = user
9
+ @password = password
10
+ end
11
+
12
+ def can_authenticate?(*)
13
+ @user && @password
14
+ end
15
+
16
+ def authenticate(*)
17
+ [0x01, @user.bytesize, @user, @password.bytesize, @password].pack("CCA*CA*")
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ #
6
+ # This plugin adds a shim +authorization+ method to the session, which will fill
7
+ # the HTTP Authorization header, and another, +bearer_auth+, which fill the "Bearer " prefix
8
+ # in its value.
9
+ #
10
+ # https://gitlab.com/os85/httpx/wikis/Auth#auth
11
+ #
12
+ module Auth
13
+ module InstanceMethods
14
+ def authorization(token)
15
+ with(headers: { "authorization" => token })
16
+ end
17
+
18
+ def bearer_auth(token)
19
+ authorization("Bearer #{token}")
20
+ end
21
+ end
22
+ end
23
+ register_plugin :auth, Auth
24
+ end
25
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ #
6
+ # This plugin applies AWS Sigv4 to requests, using the AWS SDK credentials and configuration.
7
+ #
8
+ # It requires the "aws-sdk-core" gem.
9
+ #
10
+ module AwsSdkAuthentication
11
+ # Mock configuration, to be used only when resolving credentials
12
+ class Configuration
13
+ attr_reader :profile
14
+
15
+ def initialize(profile)
16
+ @profile = profile
17
+ end
18
+
19
+ def respond_to_missing?(*)
20
+ true
21
+ end
22
+
23
+ def method_missing(*); end
24
+ end
25
+
26
+ #
27
+ # encapsulates access to an AWS SDK credentials store.
28
+ #
29
+ class Credentials
30
+ def initialize(aws_credentials)
31
+ @aws_credentials = aws_credentials
32
+ end
33
+
34
+ def username
35
+ @aws_credentials.access_key_id
36
+ end
37
+
38
+ def password
39
+ @aws_credentials.secret_access_key
40
+ end
41
+
42
+ def security_token
43
+ @aws_credentials.session_token
44
+ end
45
+ end
46
+
47
+ class << self
48
+ def load_dependencies(_klass)
49
+ require "aws-sdk-core"
50
+ end
51
+
52
+ def configure(klass)
53
+ klass.plugin(:aws_sigv4)
54
+ end
55
+
56
+ def extra_options(options)
57
+ options.merge(max_concurrent_requests: 1)
58
+ end
59
+
60
+ def credentials(profile)
61
+ mock_configuration = Configuration.new(profile)
62
+ Credentials.new(Aws::CredentialProviderChain.new(mock_configuration).resolve)
63
+ end
64
+
65
+ def region(profile)
66
+ # https://github.com/aws/aws-sdk-ruby/blob/version-3/gems/aws-sdk-core/lib/aws-sdk-core/plugins/regional_endpoint.rb#L62
67
+ keys = %w[AWS_REGION AMAZON_REGION AWS_DEFAULT_REGION]
68
+ env_region = ENV.values_at(*keys).compact.first
69
+ env_region = nil if env_region == ""
70
+ cfg_region = Aws.shared_config.region(profile: profile)
71
+ env_region || cfg_region
72
+ end
73
+ end
74
+
75
+ # adds support for the following options:
76
+ #
77
+ # :aws_profile :: AWS account profile to retrieve credentials from.
78
+ module OptionsMethods
79
+ private
80
+
81
+ def option_aws_profile(value)
82
+ String(value)
83
+ end
84
+ end
85
+
86
+ module InstanceMethods
87
+ #
88
+ # aws_authentication
89
+ # aws_authentication(credentials: Aws::Credentials.new('akid', 'secret'))
90
+ # aws_authentication()
91
+ #
92
+ def aws_sdk_authentication(
93
+ credentials: AwsSdkAuthentication.credentials(@options.aws_profile),
94
+ region: AwsSdkAuthentication.region(@options.aws_profile),
95
+ **options
96
+ )
97
+
98
+ aws_sigv4_authentication(
99
+ credentials: credentials,
100
+ region: region,
101
+ provider_prefix: "aws",
102
+ header_provider_field: "amz",
103
+ **options
104
+ )
105
+ end
106
+ alias_method :aws_auth, :aws_sdk_authentication
107
+ end
108
+ end
109
+ register_plugin :aws_sdk_authentication, AwsSdkAuthentication
110
+ end
111
+ end
@@ -0,0 +1,239 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ #
6
+ # This plugin adds AWS Sigv4 authentication.
7
+ #
8
+ # https://docs.aws.amazon.com/IAM/latest/UserGuide/signing-elements.html
9
+ #
10
+ # https://gitlab.com/os85/httpx/wikis/AWS-SigV4
11
+ #
12
+ module AWSSigV4
13
+ Credentials = Struct.new(:username, :password, :security_token)
14
+
15
+ # Signs requests using the AWS sigv4 signing.
16
+ class Signer
17
+ def initialize(
18
+ service:,
19
+ region:,
20
+ credentials: nil,
21
+ username: nil,
22
+ password: nil,
23
+ security_token: nil,
24
+ provider_prefix: "aws",
25
+ header_provider_field: "amz",
26
+ unsigned_headers: [],
27
+ apply_checksum_header: true,
28
+ algorithm: "SHA256"
29
+ )
30
+ @credentials = credentials || Credentials.new(username, password, security_token)
31
+ @service = service
32
+ @region = region
33
+
34
+ @unsigned_headers = Set.new(unsigned_headers.map(&:downcase))
35
+ @unsigned_headers << "authorization"
36
+ @unsigned_headers << "x-amzn-trace-id"
37
+ @unsigned_headers << "expect"
38
+
39
+ @apply_checksum_header = apply_checksum_header
40
+ @provider_prefix = provider_prefix
41
+ @header_provider_field = header_provider_field
42
+
43
+ @algorithm = algorithm
44
+ end
45
+
46
+ def sign!(request)
47
+ lower_provider_prefix = "#{@provider_prefix}4"
48
+ upper_provider_prefix = lower_provider_prefix.upcase
49
+
50
+ downcased_algorithm = @algorithm.downcase
51
+
52
+ datetime = (request.headers["x-#{@header_provider_field}-date"] ||= Time.now.utc.strftime("%Y%m%dT%H%M%SZ"))
53
+ date = datetime[0, 8]
54
+
55
+ content_hashed = request.headers["x-#{@header_provider_field}-content-#{downcased_algorithm}"] || hexdigest(request.body)
56
+
57
+ request.headers["x-#{@header_provider_field}-content-#{downcased_algorithm}"] ||= content_hashed if @apply_checksum_header
58
+ request.headers["x-#{@header_provider_field}-security-token"] ||= @credentials.security_token if @credentials.security_token
59
+
60
+ signature_headers = request.headers.each.reject do |k, _|
61
+ @unsigned_headers.include?(k)
62
+ end
63
+ # aws sigv4 needs to declare the host, regardless of protocol version
64
+ signature_headers << ["host", request.authority] unless request.headers.key?("host")
65
+ signature_headers.sort_by!(&:first)
66
+
67
+ signed_headers = signature_headers.map(&:first).join(";")
68
+
69
+ canonical_headers = signature_headers.map do |k, v|
70
+ # eliminate whitespace between value fields, unless it's a quoted value
71
+ "#{k}:#{v.start_with?("\"") && v.end_with?("\"") ? v : v.gsub(/\s+/, " ").strip}\n"
72
+ end.join
73
+
74
+ # canonical request
75
+ creq = "#{request.verb}" \
76
+ "\n#{request.canonical_path}" \
77
+ "\n#{request.canonical_query}" \
78
+ "\n#{canonical_headers}" \
79
+ "\n#{signed_headers}" \
80
+ "\n#{content_hashed}"
81
+
82
+ credential_scope = "#{date}" \
83
+ "/#{@region}" \
84
+ "/#{@service}" \
85
+ "/#{lower_provider_prefix}_request"
86
+
87
+ algo_line = "#{upper_provider_prefix}-HMAC-#{@algorithm}"
88
+ # string to sign
89
+ sts = "#{algo_line}" \
90
+ "\n#{datetime}" \
91
+ "\n#{credential_scope}" \
92
+ "\n#{OpenSSL::Digest.new(@algorithm).hexdigest(creq)}"
93
+
94
+ # signature
95
+ k_date = hmac("#{upper_provider_prefix}#{@credentials.password}", date)
96
+ k_region = hmac(k_date, @region)
97
+ k_service = hmac(k_region, @service)
98
+ k_credentials = hmac(k_service, "#{lower_provider_prefix}_request")
99
+ sig = hexhmac(k_credentials, sts)
100
+
101
+ credential = "#{@credentials.username}/#{credential_scope}"
102
+ # apply signature
103
+ request.headers["authorization"] =
104
+ "#{algo_line} " \
105
+ "Credential=#{credential}, " \
106
+ "SignedHeaders=#{signed_headers}, " \
107
+ "Signature=#{sig}"
108
+ end
109
+
110
+ private
111
+
112
+ def hexdigest(value)
113
+ digest = OpenSSL::Digest.new(@algorithm)
114
+
115
+ if value.respond_to?(:read)
116
+ if value.respond_to?(:to_path)
117
+ # files, pathnames
118
+ digest.file(value.to_path).hexdigest
119
+ else
120
+ # gzipped request bodies
121
+ raise Error, "request body must be rewindable" unless value.respond_to?(:rewind)
122
+
123
+ buffer = Tempfile.new("httpx", encoding: Encoding::BINARY, mode: File::RDWR)
124
+ begin
125
+ IO.copy_stream(value, buffer)
126
+ buffer.flush
127
+
128
+ digest.file(buffer.to_path).hexdigest
129
+ ensure
130
+ value.rewind
131
+ buffer.close
132
+ buffer.unlink
133
+ end
134
+ end
135
+ else
136
+ # error on endless generators
137
+ raise Error, "hexdigest for endless enumerators is not supported" if value.unbounded_body?
138
+
139
+ mb_buffer = value.each.with_object("".b) do |chunk, b|
140
+ b << chunk
141
+ break if b.bytesize >= 1024 * 1024
142
+ end
143
+
144
+ digest.hexdigest(mb_buffer)
145
+ end
146
+ end
147
+
148
+ def hmac(key, value)
149
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new(@algorithm), key, value)
150
+ end
151
+
152
+ def hexhmac(key, value)
153
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new(@algorithm), key, value)
154
+ end
155
+ end
156
+
157
+ class << self
158
+ def load_dependencies(*)
159
+ require "set"
160
+ require "digest/sha2"
161
+ require "cgi/escape"
162
+ end
163
+
164
+ def configure(klass)
165
+ klass.plugin(:expect)
166
+ end
167
+ end
168
+
169
+ # adds support for the following options:
170
+ #
171
+ # :sigv4_signer :: instance of HTTPX::Plugins::AWSSigV4 used to sign requests.
172
+ module OptionsMethods
173
+ private
174
+
175
+ def option_sigv4_signer(value)
176
+ value.is_a?(Signer) ? value : Signer.new(value)
177
+ end
178
+ end
179
+
180
+ module InstanceMethods
181
+ def aws_sigv4_authentication(**options)
182
+ with(sigv4_signer: Signer.new(**options))
183
+ end
184
+
185
+ def build_request(*)
186
+ request = super
187
+
188
+ return request if request.headers.key?("authorization")
189
+
190
+ signer = request.options.sigv4_signer
191
+
192
+ return request unless signer
193
+
194
+ signer.sign!(request)
195
+
196
+ request
197
+ end
198
+ end
199
+
200
+ module RequestMethods
201
+ def canonical_path
202
+ path = uri.path.dup
203
+ path << "/" if path.empty?
204
+ path.gsub(%r{[^/]+}) { |part| CGI.escape(part.encode("UTF-8")).gsub("+", "%20").gsub("%7E", "~") }
205
+ end
206
+
207
+ def canonical_query
208
+ params = query.split("&")
209
+ # params = params.map { |p| p.match(/=/) ? p : p + '=' }
210
+ # From: https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html#create-canonical-request
211
+ # Sort the parameter names by character code point in ascending order.
212
+ # Parameters with duplicate names should be sorted by value.
213
+ #
214
+ # Default sort <=> in JRuby will swap members
215
+ # occasionally when <=> is 0 (considered still sorted), but this
216
+ # causes our normalized query string to not match the sent querystring.
217
+ # When names match, we then sort by their values. When values also
218
+ # match then we sort by their original order
219
+ params.each.with_index.sort do |a, b|
220
+ a, a_offset = a
221
+ b, b_offset = b
222
+ a_name, a_value = a.split("=", 2)
223
+ b_name, b_value = b.split("=", 2)
224
+ if a_name == b_name
225
+ if a_value == b_value
226
+ a_offset <=> b_offset
227
+ else
228
+ a_value <=> b_value
229
+ end
230
+ else
231
+ a_name <=> b_name
232
+ end
233
+ end.map(&:first).join("&")
234
+ end
235
+ end
236
+ end
237
+ register_plugin :aws_sigv4, AWSSigV4
238
+ end
239
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ #
6
+ # This plugin adds helper methods to implement HTTP Basic Auth (https://datatracker.ietf.org/doc/html/rfc7617)
7
+ #
8
+ # https://gitlab.com/os85/httpx/wikis/Auth#basic-auth
9
+ #
10
+ module BasicAuth
11
+ class << self
12
+ def load_dependencies(_klass)
13
+ require_relative "auth/basic"
14
+ end
15
+
16
+ def configure(klass)
17
+ klass.plugin(:auth)
18
+ end
19
+ end
20
+
21
+ module InstanceMethods
22
+ def basic_auth(user, password)
23
+ authorization(Authentication::Basic.new(user, password).authenticate)
24
+ end
25
+ end
26
+ end
27
+ register_plugin :basic_auth, BasicAuth
28
+ end
29
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ module Brotli
6
+ class Deflater < Transcoder::Deflater
7
+ def deflate(chunk)
8
+ return unless chunk
9
+
10
+ ::Brotli.deflate(chunk)
11
+ end
12
+ end
13
+
14
+ module RequestBodyClassMethods
15
+ def initialize_deflater_body(body, encoding)
16
+ return Brotli.encode(body) if encoding == "br"
17
+
18
+ super
19
+ end
20
+ end
21
+
22
+ module ResponseBodyClassMethods
23
+ def initialize_inflater_by_encoding(encoding, response, **kwargs)
24
+ return Brotli.decode(response, **kwargs) if encoding == "br"
25
+
26
+ super
27
+ end
28
+ end
29
+
30
+ module_function
31
+
32
+ def load_dependencies(*)
33
+ require "brotli"
34
+ end
35
+
36
+ def self.extra_options(options)
37
+ options.merge(supported_compression_formats: %w[br] + options.supported_compression_formats)
38
+ end
39
+
40
+ def encode(body)
41
+ Deflater.new(body)
42
+ end
43
+
44
+ def decode(_response, **)
45
+ ::Brotli.method(:inflate)
46
+ end
47
+ end
48
+ register_plugin :brotli, Brotli
49
+ end
50
+ end