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,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ class Timers
5
+ def initialize
6
+ @intervals = []
7
+ end
8
+
9
+ def after(interval_in_secs, cb = nil, &blk)
10
+ callback = cb || blk
11
+
12
+ raise Error, "timer must have a callback" unless callback
13
+
14
+ # I'm assuming here that most requests will have the same
15
+ # request timeout, as in most cases they share common set of
16
+ # options. A user setting different request timeouts for 100s of
17
+ # requests will already have a hard time dealing with that.
18
+ unless (interval = @intervals.bsearch { |t| t.interval == interval_in_secs })
19
+ interval = Interval.new(interval_in_secs)
20
+ @intervals << interval
21
+ @intervals.sort!
22
+ end
23
+
24
+ interval << callback
25
+
26
+ @next_interval_at = nil
27
+
28
+ Timer.new(interval, callback)
29
+ end
30
+
31
+ def wait_interval
32
+ drop_elapsed!
33
+
34
+ return if @intervals.empty?
35
+
36
+ @next_interval_at = Utils.now
37
+
38
+ @intervals.first.interval
39
+ end
40
+
41
+ def fire(error = nil)
42
+ raise error if error && error.timeout != @intervals.first
43
+ return if @intervals.empty? || !@next_interval_at
44
+
45
+ elapsed_time = Utils.elapsed_time(@next_interval_at)
46
+
47
+ drop_elapsed!(elapsed_time)
48
+
49
+ @intervals = @intervals.drop_while { |interval| interval.elapse(elapsed_time) <= 0 }
50
+
51
+ @next_interval_at = nil if @intervals.empty?
52
+ end
53
+
54
+ private
55
+
56
+ def drop_elapsed!(elapsed_time = 0)
57
+ # check first, if not elapsed, then return
58
+ first_interval = @intervals.first
59
+
60
+ return unless first_interval && first_interval.elapsed?(elapsed_time)
61
+
62
+ # TODO: would be nice to have a drop_while!
63
+ @intervals = @intervals.drop_while { |interval| interval.elapse(elapsed_time) <= 0 }
64
+ end
65
+
66
+ class Timer
67
+ def initialize(interval, callback)
68
+ @interval = interval
69
+ @callback = callback
70
+ end
71
+
72
+ def cancel
73
+ @interval.delete(@callback)
74
+ end
75
+ end
76
+
77
+ class Interval
78
+ include Comparable
79
+
80
+ attr_reader :interval
81
+
82
+ def initialize(interval)
83
+ @interval = interval
84
+ @callbacks = []
85
+ end
86
+
87
+ def <=>(other)
88
+ @interval <=> other.interval
89
+ end
90
+
91
+ def ==(other)
92
+ return @interval == other if other.is_a?(Numeric)
93
+
94
+ @interval == other.to_f # rubocop:disable Lint/FloatComparison
95
+ end
96
+
97
+ def to_f
98
+ Float(@interval)
99
+ end
100
+
101
+ def <<(callback)
102
+ @callbacks << callback
103
+ end
104
+
105
+ def delete(callback)
106
+ @callbacks.delete(callback)
107
+ end
108
+
109
+ def no_callbacks?
110
+ @callbacks.empty?
111
+ end
112
+
113
+ def elapsed?(elapsed = 0)
114
+ (@interval - elapsed) <= 0 || @callbacks.empty?
115
+ end
116
+
117
+ def elapse(elapsed)
118
+ # same as elapsing
119
+ return 0 if @callbacks.empty?
120
+
121
+ @interval -= elapsed
122
+
123
+ if @interval <= 0
124
+ cb = @callbacks.dup
125
+ cb.each(&:call)
126
+ end
127
+
128
+ @interval
129
+ end
130
+ end
131
+ private_constant :Interval
132
+ end
133
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+
5
+ module HTTPX::Transcoder
6
+ module Body
7
+ class Error < HTTPX::Error; end
8
+
9
+ module_function
10
+
11
+ class Encoder < SimpleDelegator
12
+ def initialize(body)
13
+ body = body.open(File::RDONLY, encoding: Encoding::BINARY) if Object.const_defined?(:Pathname) && body.is_a?(Pathname)
14
+ @body = body
15
+ super(body)
16
+ end
17
+
18
+ def bytesize
19
+ if @body.respond_to?(:bytesize)
20
+ @body.bytesize
21
+ elsif @body.respond_to?(:to_ary)
22
+ @body.sum(&:bytesize)
23
+ elsif @body.respond_to?(:size)
24
+ @body.size || Float::INFINITY
25
+ elsif @body.respond_to?(:length)
26
+ @body.length || Float::INFINITY
27
+ elsif @body.respond_to?(:each)
28
+ Float::INFINITY
29
+ else
30
+ raise Error, "cannot determine size of body: #{@body.inspect}"
31
+ end
32
+ end
33
+
34
+ def content_type
35
+ "application/octet-stream"
36
+ end
37
+ end
38
+
39
+ def encode(body)
40
+ Encoder.new(body)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module HTTPX::Transcoder
6
+ module Chunker
7
+ class Error < HTTPX::Error; end
8
+
9
+ CRLF = "\r\n".b
10
+
11
+ class Encoder
12
+ extend Forwardable
13
+
14
+ def initialize(body)
15
+ @raw = body
16
+ end
17
+
18
+ def each
19
+ return enum_for(__method__) unless block_given?
20
+
21
+ @raw.each do |chunk|
22
+ yield "#{chunk.bytesize.to_s(16)}#{CRLF}#{chunk}#{CRLF}"
23
+ end
24
+ yield "0#{CRLF}"
25
+ end
26
+
27
+ def respond_to_missing?(meth, *args)
28
+ @raw.respond_to?(meth, *args) || super
29
+ end
30
+ end
31
+
32
+ class Decoder
33
+ extend Forwardable
34
+
35
+ def_delegator :@buffer, :empty?
36
+
37
+ def_delegator :@buffer, :<<
38
+
39
+ def_delegator :@buffer, :clear
40
+
41
+ def initialize(buffer, trailers = false)
42
+ @buffer = buffer
43
+ @chunk_buffer = "".b
44
+ @finished = false
45
+ @state = :length
46
+ @trailers = trailers
47
+ end
48
+
49
+ def to_s
50
+ @buffer
51
+ end
52
+
53
+ def each
54
+ loop do
55
+ case @state
56
+ when :length
57
+ index = @buffer.index(CRLF)
58
+ return unless index && index.positive?
59
+
60
+ # Read hex-length
61
+ hexlen = @buffer.byteslice(0, index)
62
+ @buffer = @buffer.byteslice(index..-1) || "".b
63
+ hexlen[/\h/] || raise(Error, "wrong chunk size line: #{hexlen}")
64
+ @chunk_length = hexlen.hex
65
+ # check if is last chunk
66
+ @finished = @chunk_length.zero?
67
+ nextstate(:crlf)
68
+ when :crlf
69
+ crlf_size = @finished && !@trailers ? 4 : 2
70
+ # consume CRLF
71
+ return if @buffer.bytesize < crlf_size
72
+ raise Error, "wrong chunked encoding format" unless @buffer.start_with?(CRLF * (crlf_size / 2))
73
+
74
+ @buffer = @buffer.byteslice(crlf_size..-1)
75
+ if @chunk_length.nil?
76
+ nextstate(:length)
77
+ else
78
+ return if @finished
79
+
80
+ nextstate(:data)
81
+ end
82
+ when :data
83
+ chunk = @buffer.byteslice(0, @chunk_length)
84
+ @buffer = @buffer.byteslice(@chunk_length..-1) || "".b
85
+ @chunk_buffer << chunk
86
+ @chunk_length -= chunk.bytesize
87
+ if @chunk_length.zero?
88
+ yield @chunk_buffer unless @chunk_buffer.empty?
89
+ @chunk_buffer.clear
90
+ @chunk_length = nil
91
+ nextstate(:crlf)
92
+ end
93
+ end
94
+ break if @buffer.empty?
95
+ end
96
+ end
97
+
98
+ def finished?
99
+ @finished
100
+ end
101
+
102
+ private
103
+
104
+ def nextstate(state)
105
+ @state = state
106
+ end
107
+ end
108
+
109
+ module_function
110
+
111
+ def encode(chunks)
112
+ Encoder.new(chunks)
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zlib"
4
+ require_relative "utils/deflater"
5
+
6
+ module HTTPX
7
+ module Transcoder
8
+ module Deflate
9
+ class Deflater < Transcoder::Deflater
10
+ def deflate(chunk)
11
+ @deflater ||= Zlib::Deflate.new
12
+
13
+ if chunk.nil?
14
+ unless @deflater.closed?
15
+ last = @deflater.finish
16
+ @deflater.close
17
+ last.empty? ? nil : last
18
+ end
19
+ else
20
+ @deflater.deflate(chunk)
21
+ end
22
+ end
23
+ end
24
+
25
+ module_function
26
+
27
+ def encode(body)
28
+ Deflater.new(body)
29
+ end
30
+
31
+ def decode(response, bytesize: nil)
32
+ bytesize ||= response.headers.key?("content-length") ? response.headers["content-length"].to_i : Float::INFINITY
33
+ GZIP::Inflater.new(bytesize)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+ require "uri"
5
+ require_relative "multipart"
6
+
7
+ module HTTPX
8
+ module Transcoder
9
+ module Form
10
+ module_function
11
+
12
+ PARAM_DEPTH_LIMIT = 32
13
+
14
+ class Encoder
15
+ extend Forwardable
16
+
17
+ def_delegator :@raw, :to_s
18
+
19
+ def_delegator :@raw, :to_str
20
+
21
+ def_delegator :@raw, :bytesize
22
+
23
+ def_delegator :@raw, :==
24
+
25
+ def initialize(form)
26
+ @raw = form.each_with_object("".b) do |(key, val), buf|
27
+ HTTPX::Transcoder.normalize_keys(key, val) do |k, v|
28
+ buf << "&" unless buf.empty?
29
+ buf << URI.encode_www_form_component(k)
30
+ buf << "=#{URI.encode_www_form_component(v.to_s)}" unless v.nil?
31
+ end
32
+ end
33
+ end
34
+
35
+ def content_type
36
+ "application/x-www-form-urlencoded"
37
+ end
38
+ end
39
+
40
+ module Decoder
41
+ module_function
42
+
43
+ def call(response, *)
44
+ URI.decode_www_form(response.to_s).each_with_object({}) do |(field, value), params|
45
+ HTTPX::Transcoder.normalize_query(params, field, value, PARAM_DEPTH_LIMIT)
46
+ end
47
+ end
48
+ end
49
+
50
+ def encode(form)
51
+ Encoder.new(form)
52
+ end
53
+
54
+ def decode(response)
55
+ content_type = response.content_type.mime_type
56
+
57
+ case content_type
58
+ when "application/x-www-form-urlencoded"
59
+ Decoder
60
+ when "multipart/form-data"
61
+ Multipart::Decoder.new(response)
62
+ else
63
+ raise Error, "invalid form mime type (#{content_type})"
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zlib"
4
+
5
+ module HTTPX
6
+ module Transcoder
7
+ module GZIP
8
+ class Deflater < Transcoder::Deflater
9
+ def initialize(body)
10
+ @compressed_chunk = "".b
11
+ super
12
+ end
13
+
14
+ def deflate(chunk)
15
+ @deflater ||= Zlib::GzipWriter.new(self)
16
+
17
+ if chunk.nil?
18
+ unless @deflater.closed?
19
+ @deflater.flush
20
+ @deflater.close
21
+ compressed_chunk
22
+ end
23
+ else
24
+ @deflater.write(chunk)
25
+ compressed_chunk
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def write(chunk)
32
+ @compressed_chunk << chunk
33
+ end
34
+
35
+ def compressed_chunk
36
+ @compressed_chunk.dup
37
+ ensure
38
+ @compressed_chunk.clear
39
+ end
40
+ end
41
+
42
+ class Inflater
43
+ def initialize(bytesize)
44
+ @inflater = Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
45
+ @bytesize = bytesize
46
+ end
47
+
48
+ def call(chunk)
49
+ buffer = @inflater.inflate(chunk)
50
+ @bytesize -= chunk.bytesize
51
+ if @bytesize <= 0
52
+ buffer << @inflater.finish
53
+ @inflater.close
54
+ end
55
+ buffer
56
+ end
57
+ end
58
+
59
+ module_function
60
+
61
+ def encode(body)
62
+ Deflater.new(body)
63
+ end
64
+
65
+ def decode(response, bytesize: nil)
66
+ bytesize ||= response.headers.key?("content-length") ? response.headers["content-length"].to_i : Float::INFINITY
67
+ Inflater.new(bytesize)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module HTTPX::Transcoder
6
+ module JSON
7
+ module_function
8
+
9
+ JSON_REGEX = %r{
10
+ \b
11
+ application/
12
+ # optional vendor specific type
13
+ (?:
14
+ # token as per https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6
15
+ [!#$%&'*+\-.^_`|~0-9a-z]+
16
+ # literal plus sign
17
+ \+
18
+ )?
19
+ json
20
+ \b
21
+ }ix.freeze
22
+
23
+ class Encoder
24
+ extend Forwardable
25
+
26
+ def_delegator :@raw, :to_s
27
+
28
+ def_delegator :@raw, :bytesize
29
+
30
+ def_delegator :@raw, :==
31
+
32
+ def initialize(json)
33
+ @raw = JSON.json_dump(json)
34
+ @charset = @raw.encoding.name.downcase
35
+ end
36
+
37
+ def content_type
38
+ "application/json; charset=#{@charset}"
39
+ end
40
+ end
41
+
42
+ def encode(json)
43
+ Encoder.new(json)
44
+ end
45
+
46
+ def decode(response)
47
+ content_type = response.content_type.mime_type
48
+
49
+ raise HTTPX::Error, "invalid json mime type (#{content_type})" unless JSON_REGEX.match?(content_type)
50
+
51
+ method(:json_load)
52
+ end
53
+
54
+ # rubocop:disable Style/SingleLineMethods
55
+ if defined?(MultiJson)
56
+ def json_load(*args); MultiJson.load(*args); end
57
+ def json_dump(*args); MultiJson.dump(*args); end
58
+ elsif defined?(Oj)
59
+ def json_load(response, *args); Oj.load(response.to_s, *args); end
60
+ def json_dump(obj, options = {}); Oj.dump(obj, { mode: :compat }.merge(options)); end
61
+ elsif defined?(Yajl)
62
+ def json_load(response, *args); Yajl::Parser.new(*args).parse(response.to_s); end
63
+ def json_dump(*args); Yajl::Encoder.encode(*args); end
64
+ else
65
+ require "json"
66
+ def json_load(*args); ::JSON.parse(*args); end
67
+ def json_dump(*args); ::JSON.dump(*args); end
68
+ end
69
+ # rubocop:enable Style/SingleLineMethods
70
+ end
71
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tempfile"
4
+ require "delegate"
5
+
6
+ module HTTPX
7
+ module Transcoder
8
+ module Multipart
9
+ class FilePart < SimpleDelegator
10
+ attr_reader :original_filename, :content_type
11
+
12
+ def initialize(filename, content_type)
13
+ @original_filename = filename
14
+ @content_type = content_type
15
+ @current = nil
16
+ @file = Tempfile.new("httpx", encoding: Encoding::BINARY, mode: File::RDWR)
17
+ super(@file)
18
+ end
19
+ end
20
+
21
+ class Decoder
22
+ include HTTPX::Utils
23
+
24
+ CRLF = "\r\n"
25
+ BOUNDARY_RE = /;\s*boundary=([^;]+)/i.freeze
26
+ MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{CRLF}/ni.freeze
27
+ MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*;\s*name=(#{VALUE})/ni.freeze
28
+ MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{CRLF}]*)/ni.freeze
29
+ WINDOW_SIZE = 2 << 14
30
+
31
+ def initialize(response)
32
+ @boundary = begin
33
+ m = response.headers["content-type"].to_s[BOUNDARY_RE, 1]
34
+ raise Error, "no boundary declared in content-type header" unless m
35
+
36
+ m.strip
37
+ end
38
+ @buffer = "".b
39
+ @parts = {}
40
+ @intermediate_boundary = "--#{@boundary}"
41
+ @state = :idle
42
+ end
43
+
44
+ def call(response, *)
45
+ response.body.each do |chunk|
46
+ @buffer << chunk
47
+
48
+ parse
49
+ end
50
+
51
+ raise Error, "invalid or unsupported multipart format" unless @buffer.empty?
52
+
53
+ @parts
54
+ end
55
+
56
+ private
57
+
58
+ def parse
59
+ case @state
60
+ when :idle
61
+ raise Error, "payload does not start with boundary" unless @buffer.start_with?("#{@intermediate_boundary}#{CRLF}")
62
+
63
+ @buffer = @buffer.byteslice(@intermediate_boundary.bytesize + 2..-1)
64
+
65
+ @state = :part_header
66
+ when :part_header
67
+ idx = @buffer.index("#{CRLF}#{CRLF}")
68
+
69
+ # raise Error, "couldn't parse part headers" unless idx
70
+ return unless idx
71
+
72
+ # @type var head: String
73
+ head = @buffer.byteslice(0..idx + 4 - 1)
74
+
75
+ @buffer = @buffer.byteslice(head.bytesize..-1)
76
+
77
+ content_type = head[MULTIPART_CONTENT_TYPE, 1] || "text/plain"
78
+ if (name = head[MULTIPART_CONTENT_DISPOSITION, 1])
79
+ name = /\A"(.*)"\Z/ =~ name ? Regexp.last_match(1) : name.dup
80
+ name.gsub!(/\\(.)/, "\\1")
81
+ name
82
+ else
83
+ name = head[MULTIPART_CONTENT_ID, 1]
84
+ end
85
+
86
+ filename = HTTPX::Utils.get_filename(head)
87
+
88
+ name = filename || +"#{content_type}[]" if name.nil? || name.empty?
89
+
90
+ @current = name
91
+
92
+ @parts[name] = if filename
93
+ FilePart.new(filename, content_type)
94
+ else
95
+ "".b
96
+ end
97
+
98
+ @state = :part_body
99
+ when :part_body
100
+ part = @parts[@current]
101
+
102
+ body_separator = if part.is_a?(FilePart)
103
+ "#{CRLF}#{CRLF}"
104
+ else
105
+ CRLF
106
+ end
107
+ idx = @buffer.index(body_separator)
108
+
109
+ if idx
110
+ payload = @buffer.byteslice(0..idx - 1)
111
+ @buffer = @buffer.byteslice(idx + body_separator.bytesize..-1)
112
+ part << payload
113
+ part.rewind if part.respond_to?(:rewind)
114
+ @state = :parse_boundary
115
+ else
116
+ part << @buffer
117
+ @buffer.clear
118
+ end
119
+ when :parse_boundary
120
+ raise Error, "payload does not start with boundary" unless @buffer.start_with?(@intermediate_boundary)
121
+
122
+ @buffer = @buffer.byteslice(@intermediate_boundary.bytesize..-1)
123
+
124
+ if @buffer == "--"
125
+ @buffer.clear
126
+ @state = :done
127
+ return
128
+ elsif @buffer.start_with?(CRLF)
129
+ @buffer = @buffer.byteslice(2..-1)
130
+ @state = :part_header
131
+ else
132
+ return
133
+ end
134
+ when :done
135
+ raise Error, "parsing should have been over by now"
136
+ end until @buffer.empty?
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end