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,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins::Cookies
5
+ # The HTTP Cookie.
6
+ #
7
+ # Contains the single cookie info: name, value and attributes.
8
+ class Cookie
9
+ include Comparable
10
+ # Maximum number of bytes per cookie (RFC 6265 6.1 requires 4096 at
11
+ # least)
12
+ MAX_LENGTH = 4096
13
+
14
+ attr_reader :domain, :path, :name, :value, :created_at
15
+
16
+ def path=(path)
17
+ path = String(path)
18
+ @path = path.start_with?("/") ? path : "/"
19
+ end
20
+
21
+ # See #domain.
22
+ def domain=(domain)
23
+ domain = String(domain)
24
+
25
+ if domain.start_with?(".")
26
+ @for_domain = true
27
+ domain = domain[1..-1]
28
+ end
29
+
30
+ return if domain.empty?
31
+
32
+ @domain_name = DomainName.new(domain)
33
+ # RFC 6265 5.3 5.
34
+ @for_domain = false if @domain_name.domain.nil? # a public suffix or IP address
35
+
36
+ @domain = @domain_name.hostname
37
+ end
38
+
39
+ # Compares the cookie with another. When there are many cookies with
40
+ # the same name for a URL, the value of the smallest must be used.
41
+ def <=>(other)
42
+ # RFC 6265 5.4
43
+ # Precedence: 1. longer path 2. older creation
44
+ (@name <=> other.name).nonzero? ||
45
+ (other.path.length <=> @path.length).nonzero? ||
46
+ (@created_at <=> other.created_at).nonzero? || 0
47
+ end
48
+
49
+ class << self
50
+ def new(cookie, *args)
51
+ return cookie if cookie.is_a?(self)
52
+
53
+ super
54
+ end
55
+
56
+ # Tests if +target_path+ is under +base_path+ as described in RFC
57
+ # 6265 5.1.4. +base_path+ must be an absolute path.
58
+ # +target_path+ may be empty, in which case it is treated as the
59
+ # root path.
60
+ #
61
+ # e.g.
62
+ #
63
+ # path_match?('/admin/', '/admin/index') == true
64
+ # path_match?('/admin/', '/Admin/index') == false
65
+ # path_match?('/admin/', '/admin/') == true
66
+ # path_match?('/admin/', '/admin') == false
67
+ #
68
+ # path_match?('/admin', '/admin') == true
69
+ # path_match?('/admin', '/Admin') == false
70
+ # path_match?('/admin', '/admins') == false
71
+ # path_match?('/admin', '/admin/') == true
72
+ # path_match?('/admin', '/admin/index') == true
73
+ def path_match?(base_path, target_path)
74
+ base_path.start_with?("/") || (return false)
75
+ # RFC 6265 5.1.4
76
+ bsize = base_path.size
77
+ tsize = target_path.size
78
+ return bsize == 1 if tsize.zero? # treat empty target_path as "/"
79
+ return false unless target_path.start_with?(base_path)
80
+ return true if bsize == tsize || base_path.end_with?("/")
81
+
82
+ target_path[bsize] == "/"
83
+ end
84
+ end
85
+
86
+ def initialize(arg, *attrs)
87
+ @created_at = Time.now
88
+
89
+ if attrs.empty?
90
+ attr_hash = Hash.try_convert(arg)
91
+ else
92
+ @name = arg
93
+ @value, attr_hash = attrs
94
+ attr_hash = Hash.try_convert(attr_hash)
95
+ end
96
+
97
+ attr_hash.each do |key, val|
98
+ key = key.downcase.tr("-", "_").to_sym unless key.is_a?(Symbol)
99
+
100
+ case key
101
+ when :domain, :path
102
+ __send__(:"#{key}=", val)
103
+ else
104
+ instance_variable_set(:"@#{key}", val)
105
+ end
106
+ end if attr_hash
107
+
108
+ @path ||= "/"
109
+ raise ArgumentError, "name must be specified" if @name.nil?
110
+
111
+ @name = @name.to_s
112
+ end
113
+
114
+ def expires
115
+ @expires || (@created_at && @max_age ? @created_at + @max_age : nil)
116
+ end
117
+
118
+ def expired?(time = Time.now)
119
+ return false unless expires
120
+
121
+ expires <= time
122
+ end
123
+
124
+ # Returns a string for use in the Cookie header, i.e. `name=value`
125
+ # or `name="value"`.
126
+ def cookie_value
127
+ "#{@name}=#{Scanner.quote(@value.to_s)}"
128
+ end
129
+ alias_method :to_s, :cookie_value
130
+
131
+ # Tests if it is OK to send this cookie to a given `uri`. A
132
+ # RuntimeError is raised if the cookie's domain is unknown.
133
+ def valid_for_uri?(uri)
134
+ uri = URI(uri)
135
+ # RFC 6265 5.4
136
+
137
+ return false if @secure && uri.scheme != "https"
138
+
139
+ acceptable_from_uri?(uri) && Cookie.path_match?(@path, uri.path)
140
+ end
141
+
142
+ private
143
+
144
+ # Tests if it is OK to accept this cookie if it is sent from a given
145
+ # URI/URL, `uri`.
146
+ def acceptable_from_uri?(uri)
147
+ uri = URI(uri)
148
+
149
+ host = DomainName.new(uri.host)
150
+
151
+ # RFC 6265 5.3
152
+ if host.hostname == @domain
153
+ true
154
+ elsif @for_domain # !host-only-flag
155
+ host.cookie_domain?(@domain_name)
156
+ else
157
+ @domain.nil?
158
+ end
159
+ end
160
+
161
+ module Scanner
162
+ RE_BAD_CHAR = /([\x00-\x20\x7F",;\\])/.freeze
163
+
164
+ module_function
165
+
166
+ def quote(s)
167
+ return s unless s.match(RE_BAD_CHAR)
168
+
169
+ "\"#{s.gsub(/([\\"])/, "\\\\\\1")}\""
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins::Cookies
5
+ # The Cookie Jar
6
+ #
7
+ # It holds a bunch of cookies.
8
+ class Jar
9
+ using URIExtensions
10
+
11
+ include Enumerable
12
+
13
+ def initialize_dup(orig)
14
+ super
15
+ @cookies = orig.instance_variable_get(:@cookies).dup
16
+ end
17
+
18
+ def initialize(cookies = nil)
19
+ @cookies = []
20
+
21
+ cookies.each do |elem|
22
+ cookie = case elem
23
+ when Cookie
24
+ elem
25
+ when Array
26
+ Cookie.new(*elem)
27
+ else
28
+ Cookie.new(elem)
29
+ end
30
+
31
+ @cookies << cookie
32
+ end if cookies
33
+ end
34
+
35
+ def parse(set_cookie)
36
+ SetCookieParser.call(set_cookie) do |name, value, attrs|
37
+ add(Cookie.new(name, value, attrs))
38
+ end
39
+ end
40
+
41
+ def add(cookie, path = nil)
42
+ c = cookie.dup
43
+
44
+ c.path = path if path && c.path == "/"
45
+
46
+ # If the user agent receives a new cookie with the same cookie-name, domain-value, and path-value
47
+ # as a cookie that it has already stored, the existing cookie is evicted and replaced with the new cookie.
48
+ @cookies.delete_if { |ck| ck.name == c.name && ck.domain == c.domain && ck.path == c.path }
49
+
50
+ @cookies << c
51
+ end
52
+
53
+ def [](uri)
54
+ each(uri).sort
55
+ end
56
+
57
+ def each(uri = nil, &blk)
58
+ return enum_for(__method__, uri) unless blk
59
+
60
+ return @cookies.each(&blk) unless uri
61
+
62
+ now = Time.now
63
+ tpath = uri.path
64
+
65
+ @cookies.delete_if do |cookie|
66
+ if cookie.expired?(now)
67
+ true
68
+ else
69
+ yield cookie if cookie.valid_for_uri?(uri) && Cookie.path_match?(cookie.path, tpath)
70
+ false
71
+ end
72
+ end
73
+ end
74
+
75
+ def merge(other)
76
+ cookies_dup = dup
77
+
78
+ other.each do |elem|
79
+ cookie = case elem
80
+ when Cookie
81
+ elem
82
+ when Array
83
+ Cookie.new(*elem)
84
+ else
85
+ Cookie.new(elem)
86
+ end
87
+
88
+ cookies_dup.add(cookie)
89
+ end
90
+
91
+ cookies_dup
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "strscan"
4
+ require "time"
5
+
6
+ module HTTPX
7
+ module Plugins::Cookies
8
+ module SetCookieParser
9
+ # Whitespace.
10
+ RE_WSP = /[ \t]+/.freeze
11
+
12
+ # A pattern that matches a cookie name or attribute name which may
13
+ # be empty, capturing trailing whitespace.
14
+ RE_NAME = /(?!#{RE_WSP})[^,;\\"=]*/.freeze
15
+
16
+ RE_BAD_CHAR = /([\x00-\x20\x7F",;\\])/.freeze
17
+
18
+ # A pattern that matches the comma in a (typically date) value.
19
+ RE_COOKIE_COMMA = /,(?=#{RE_WSP}?#{RE_NAME}=)/.freeze
20
+
21
+ module_function
22
+
23
+ def scan_dquoted(scanner)
24
+ s = +""
25
+
26
+ until scanner.eos?
27
+ break if scanner.skip(/"/)
28
+
29
+ if scanner.skip(/\\/)
30
+ s << scanner.getch
31
+ elsif scanner.scan(/[^"\\]+/)
32
+ s << scanner.matched
33
+ end
34
+ end
35
+
36
+ s
37
+ end
38
+
39
+ def scan_value(scanner, comma_as_separator = false)
40
+ value = +""
41
+
42
+ until scanner.eos?
43
+ if scanner.scan(/[^,;"]+/)
44
+ value << scanner.matched
45
+ elsif scanner.skip(/"/)
46
+ # RFC 6265 2.2
47
+ # A cookie-value may be DQUOTE'd.
48
+ value << scan_dquoted(scanner)
49
+ elsif scanner.check(/;/)
50
+ break
51
+ elsif comma_as_separator && scanner.check(RE_COOKIE_COMMA)
52
+ break
53
+ else
54
+ value << scanner.getch
55
+ end
56
+ end
57
+
58
+ value.rstrip!
59
+ value
60
+ end
61
+
62
+ def scan_name_value(scanner, comma_as_separator = false)
63
+ name = scanner.scan(RE_NAME)
64
+ name.rstrip! if name
65
+
66
+ if scanner.skip(/=/)
67
+ value = scan_value(scanner, comma_as_separator)
68
+ else
69
+ scan_value(scanner, comma_as_separator)
70
+ value = nil
71
+ end
72
+ [name, value]
73
+ end
74
+
75
+ def call(set_cookie)
76
+ scanner = StringScanner.new(set_cookie)
77
+
78
+ # RFC 6265 4.1.1 & 5.2
79
+ until scanner.eos?
80
+ start = scanner.pos
81
+ len = nil
82
+
83
+ scanner.skip(RE_WSP)
84
+
85
+ name, value = scan_name_value(scanner, true)
86
+ value = nil if name && name.empty?
87
+
88
+ attrs = {}
89
+
90
+ until scanner.eos?
91
+ if scanner.skip(/,/)
92
+ # The comma is used as separator for concatenating multiple
93
+ # values of a header.
94
+ len = (scanner.pos - 1) - start
95
+ break
96
+ elsif scanner.skip(/;/)
97
+ scanner.skip(RE_WSP)
98
+
99
+ aname, avalue = scan_name_value(scanner, true)
100
+
101
+ next if (aname.nil? || aname.empty?) || value.nil?
102
+
103
+ aname.downcase!
104
+
105
+ case aname
106
+ when "expires"
107
+ next unless avalue
108
+
109
+ # RFC 6265 5.2.1
110
+ (avalue = Time.parse(avalue)) || next
111
+ when "max-age"
112
+ next unless avalue
113
+ # RFC 6265 5.2.2
114
+ next unless /\A-?\d+\z/.match?(avalue)
115
+
116
+ avalue = Integer(avalue)
117
+ when "domain"
118
+ # RFC 6265 5.2.3
119
+ # An empty value SHOULD be ignored.
120
+ next if avalue.nil? || avalue.empty?
121
+ when "path"
122
+ # RFC 6265 5.2.4
123
+ # A relative path must be ignored rather than normalizing it
124
+ # to "/".
125
+ next unless avalue && avalue.start_with?("/")
126
+ when "secure", "httponly"
127
+ # RFC 6265 5.2.5, 5.2.6
128
+ avalue = true
129
+ end
130
+ attrs[aname] = avalue
131
+ end
132
+ end
133
+
134
+ len ||= scanner.pos - start
135
+
136
+ next if len > Cookie::MAX_LENGTH
137
+
138
+ yield(name, value, attrs) if name && !name.empty? && value
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module HTTPX
6
+ module Plugins
7
+ #
8
+ # This plugin implements a persistent cookie jar for the duration of a session.
9
+ #
10
+ # It also adds a *#cookies* helper, so that you can pre-fill the cookies of a session.
11
+ #
12
+ # https://gitlab.com/os85/httpx/wikis/Cookies
13
+ #
14
+ module Cookies
15
+ def self.load_dependencies(*)
16
+ require "httpx/plugins/cookies/jar"
17
+ require "httpx/plugins/cookies/cookie"
18
+ require "httpx/plugins/cookies/set_cookie_parser"
19
+ end
20
+
21
+ module InstanceMethods
22
+ extend Forwardable
23
+
24
+ def_delegator :@options, :cookies
25
+
26
+ def initialize(options = {}, &blk)
27
+ super({ cookies: Jar.new }.merge(options), &blk)
28
+ end
29
+
30
+ def wrap
31
+ return super unless block_given?
32
+
33
+ super do |session|
34
+ old_cookies_jar = @options.cookies.dup
35
+ begin
36
+ yield session
37
+ ensure
38
+ @options = @options.merge(cookies: old_cookies_jar)
39
+ end
40
+ end
41
+ end
42
+
43
+ def build_request(*)
44
+ request = super
45
+ request.headers.set_cookie(request.options.cookies[request.uri])
46
+ request
47
+ end
48
+
49
+ private
50
+
51
+ def set_request_callbacks(request)
52
+ super
53
+ request.on(:response) do |response|
54
+ next unless response && response.respond_to?(:headers) && (set_cookie = response.headers["set-cookie"])
55
+
56
+ log { "cookies: set-cookie is over #{Cookie::MAX_LENGTH}" } if set_cookie.bytesize > Cookie::MAX_LENGTH
57
+
58
+ @options.cookies.parse(set_cookie)
59
+ end
60
+ end
61
+ end
62
+
63
+ module HeadersMethods
64
+ def set_cookie(cookies)
65
+ return if cookies.empty?
66
+
67
+ header_value = cookies.sort.join("; ")
68
+
69
+ add("cookie", header_value)
70
+ end
71
+ end
72
+
73
+ # adds support for the following options:
74
+ #
75
+ # :cookies :: cookie jar for the session (can be a Hash, an Array, an instance of HTTPX::Plugins::Cookies::CookieJar)
76
+ module OptionsMethods
77
+ private
78
+
79
+ def option_headers(*)
80
+ value = super
81
+
82
+ merge_cookie_in_jar(value.delete("cookie"), @cookies) if defined?(@cookies) && value.key?("cookie")
83
+
84
+ value
85
+ end
86
+
87
+ def option_cookies(value)
88
+ jar = value.is_a?(Jar) ? value : Jar.new(value)
89
+
90
+ merge_cookie_in_jar(@headers.delete("cookie"), jar) if defined?(@headers) && @headers.key?("cookie")
91
+
92
+ jar
93
+ end
94
+
95
+ def merge_cookie_in_jar(cookies, jar)
96
+ cookies.each do |ck|
97
+ ck.split(/ *; */).each do |cookie|
98
+ name, value = cookie.split("=", 2)
99
+ jar.add(Cookie.new(name, value))
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ register_plugin :cookies, Cookies
106
+ end
107
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ #
6
+ # This plugin adds helper methods to implement HTTP Digest Auth (https://datatracker.ietf.org/doc/html/rfc7616)
7
+ #
8
+ # https://gitlab.com/os85/httpx/wikis/Auth#digest-auth
9
+ #
10
+ module DigestAuth
11
+ DigestError = Class.new(Error)
12
+
13
+ class << self
14
+ def extra_options(options)
15
+ options.merge(max_concurrent_requests: 1)
16
+ end
17
+
18
+ def load_dependencies(*)
19
+ require_relative "auth/digest"
20
+ end
21
+ end
22
+
23
+ # adds support for the following options:
24
+ #
25
+ # :digest :: instance of HTTPX::Plugins::Authentication::Digest, used to authenticate requests in the session.
26
+ module OptionsMethods
27
+ private
28
+
29
+ def option_digest(value)
30
+ raise TypeError, ":digest must be a #{Authentication::Digest}" unless value.is_a?(Authentication::Digest)
31
+
32
+ value
33
+ end
34
+ end
35
+
36
+ module InstanceMethods
37
+ def digest_auth(user, password, hashed: false)
38
+ with(digest: Authentication::Digest.new(user, password, hashed: hashed))
39
+ end
40
+
41
+ private
42
+
43
+ def send_requests(*requests)
44
+ requests.flat_map do |request|
45
+ digest = request.options.digest
46
+
47
+ next super(request) unless digest
48
+
49
+ probe_response = wrap { super(request).first }
50
+
51
+ return probe_response unless probe_response.is_a?(Response)
52
+
53
+ if probe_response.status == 401 && digest.can_authenticate?(probe_response.headers["www-authenticate"])
54
+ request.transition(:idle)
55
+ request.headers["authorization"] = digest.authenticate(request, probe_response.headers["www-authenticate"])
56
+ super(request)
57
+ else
58
+ probe_response
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ register_plugin :digest_auth, DigestAuth
66
+ end
67
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ #
6
+ # This plugin makes all HTTP/1.1 requests with a body send the "Expect: 100-continue".
7
+ #
8
+ # https://gitlab.com/os85/httpx/wikis/Expect#expect
9
+ #
10
+ module Expect
11
+ EXPECT_TIMEOUT = 2
12
+
13
+ class << self
14
+ def no_expect_store
15
+ @no_expect_store ||= []
16
+ end
17
+
18
+ def extra_options(options)
19
+ options.merge(expect_timeout: EXPECT_TIMEOUT)
20
+ end
21
+ end
22
+
23
+ # adds support for the following options:
24
+ #
25
+ # :expect_timeout :: time (in seconds) to wait for a 100-expect response,
26
+ # before retrying without the Expect header (defaults to <tt>2</tt>).
27
+ # :expect_threshold_size :: min threshold (in bytes) of the request payload to enable the 100-continue negotiation on.
28
+ module OptionsMethods
29
+ private
30
+
31
+ def option_expect_timeout(value)
32
+ seconds = Float(value)
33
+ raise TypeError, ":expect_timeout must be positive" unless seconds.positive?
34
+
35
+ seconds
36
+ end
37
+
38
+ def option_expect_threshold_size(value)
39
+ bytes = Integer(value)
40
+ raise TypeError, ":expect_threshold_size must be positive" unless bytes.positive?
41
+
42
+ bytes
43
+ end
44
+ end
45
+
46
+ module RequestMethods
47
+ def initialize(*)
48
+ super
49
+ return if @body.empty?
50
+
51
+ threshold = @options.expect_threshold_size
52
+ return if threshold && !@body.unbounded_body? && @body.bytesize < threshold
53
+
54
+ return if Expect.no_expect_store.include?(origin)
55
+
56
+ @headers["expect"] = "100-continue"
57
+ end
58
+
59
+ def response=(response)
60
+ if response.is_a?(Response) &&
61
+ response.status == 100 &&
62
+ !@headers.key?("expect") &&
63
+ (@state == :body || @state == :done)
64
+
65
+ # if we're past this point, this means that we just received a 100-Continue response,
66
+ # but the request doesn't have the expect flag, and is already flushing (or flushed) the body.
67
+ #
68
+ # this means that expect was deactivated for this request too soon, i.e. response took longer.
69
+ #
70
+ # so we have to reactivate it again.
71
+ @headers["expect"] = "100-continue"
72
+ @informational_status = 100
73
+ Expect.no_expect_store.delete(origin)
74
+ end
75
+ super
76
+ end
77
+ end
78
+
79
+ module ConnectionMethods
80
+ def send_request_to_parser(request)
81
+ super
82
+
83
+ return unless request.headers["expect"] == "100-continue"
84
+
85
+ expect_timeout = request.options.expect_timeout
86
+
87
+ return if expect_timeout.nil? || expect_timeout.infinite?
88
+
89
+ set_request_timeout(:expect_timeout, request, expect_timeout, :expect, %i[body response]) do
90
+ # expect timeout expired
91
+ if request.state == :expect && !request.expects?
92
+ Expect.no_expect_store << request.origin
93
+ request.headers.delete("expect")
94
+ consume
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ module InstanceMethods
101
+ def fetch_response(request, selector, options)
102
+ response = super
103
+
104
+ return unless response
105
+
106
+ if response.is_a?(Response) && response.status == 417 && request.headers.key?("expect")
107
+ response.close
108
+ request.headers.delete("expect")
109
+ request.transition(:idle)
110
+ send_request(request, selector, options)
111
+ return
112
+ end
113
+
114
+ response
115
+ end
116
+ end
117
+ end
118
+ register_plugin :expect, Expect
119
+ end
120
+ end