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,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Transcoder::Multipart
5
+ class Encoder
6
+ attr_reader :bytesize
7
+
8
+ def initialize(form)
9
+ @boundary = ("-" * 21) << SecureRandom.hex(21)
10
+ @part_index = 0
11
+ @buffer = "".b
12
+
13
+ @form = form
14
+ @bytesize = 0
15
+ @parts = to_parts(form)
16
+ end
17
+
18
+ def content_type
19
+ "multipart/form-data; boundary=#{@boundary}"
20
+ end
21
+
22
+ def to_s
23
+ read || ""
24
+ ensure
25
+ rewind
26
+ end
27
+
28
+ def read(length = nil, outbuf = nil)
29
+ data = String(outbuf).clear.force_encoding(Encoding::BINARY) if outbuf
30
+ data ||= "".b
31
+
32
+ read_chunks(data, length)
33
+
34
+ data unless length && data.empty?
35
+ end
36
+
37
+ def rewind
38
+ form = @form.each_with_object([]) do |(key, val), aux|
39
+ if val.respond_to?(:path) && val.respond_to?(:reopen) && val.respond_to?(:closed?) && val.closed?
40
+ # @type var val: File
41
+ val = val.reopen(val.path, File::RDONLY)
42
+ end
43
+ val.rewind if val.respond_to?(:rewind)
44
+ aux << [key, val]
45
+ end
46
+ @form = form
47
+ @bytesize = 0
48
+ @parts = to_parts(form)
49
+ @part_index = 0
50
+ end
51
+
52
+ private
53
+
54
+ def to_parts(form)
55
+ params = form.each_with_object([]) do |(key, val), aux|
56
+ Transcoder.normalize_keys(key, val, MULTIPART_VALUE_COND) do |k, v|
57
+ next if v.nil?
58
+
59
+ value, content_type, filename = Part.call(v)
60
+
61
+ header = header_part(k, content_type, filename)
62
+ @bytesize += header.size
63
+ aux << header
64
+
65
+ @bytesize += value.size
66
+ aux << value
67
+
68
+ delimiter = StringIO.new("\r\n")
69
+ @bytesize += delimiter.size
70
+ aux << delimiter
71
+ end
72
+ end
73
+ final_delimiter = StringIO.new("--#{@boundary}--\r\n")
74
+ @bytesize += final_delimiter.size
75
+ params << final_delimiter
76
+
77
+ params
78
+ end
79
+
80
+ def header_part(key, content_type, filename)
81
+ header = "--#{@boundary}\r\n".b
82
+ header << "Content-Disposition: form-data; name=#{key.inspect}".b
83
+ header << "; filename=#{filename.inspect}" if filename
84
+ header << "\r\nContent-Type: #{content_type}\r\n\r\n"
85
+ StringIO.new(header)
86
+ end
87
+
88
+ def read_chunks(buffer, length = nil)
89
+ while @part_index < @parts.size
90
+ chunk = read_from_part(length)
91
+
92
+ next unless chunk
93
+
94
+ buffer << chunk.force_encoding(Encoding::BINARY)
95
+
96
+ next unless length
97
+
98
+ length -= chunk.bytesize
99
+
100
+ break if length.zero?
101
+ end
102
+ end
103
+
104
+ # if there's a current part to read from, tries to read a chunk.
105
+ def read_from_part(max_length = nil)
106
+ part = @parts[@part_index]
107
+
108
+ chunk = part.read(max_length, @buffer)
109
+
110
+ return chunk if chunk && !chunk.empty?
111
+
112
+ part.close if part.respond_to?(:close)
113
+
114
+ @part_index += 1
115
+
116
+ nil
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Transcoder::Multipart
5
+ module MimeTypeDetector
6
+ module_function
7
+
8
+ DEFAULT_MIMETYPE = "application/octet-stream"
9
+
10
+ # inspired by https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/determine_mime_type.rb
11
+ if defined?(FileMagic)
12
+ MAGIC_NUMBER = 256 * 1024
13
+
14
+ def call(file, _)
15
+ return nil if file.eof? # FileMagic returns "application/x-empty" for empty files
16
+
17
+ mime = FileMagic.open(FileMagic::MAGIC_MIME_TYPE) do |filemagic|
18
+ filemagic.buffer(file.read(MAGIC_NUMBER))
19
+ end
20
+
21
+ file.rewind
22
+
23
+ mime
24
+ end
25
+ elsif defined?(Marcel)
26
+ def call(file, filename)
27
+ return nil if file.eof? # marcel returns "application/octet-stream" for empty files
28
+
29
+ Marcel::MimeType.for(file, name: filename)
30
+ end
31
+
32
+ elsif defined?(MimeMagic)
33
+
34
+ def call(file, _)
35
+ mime = MimeMagic.by_magic(file)
36
+ mime.type if mime
37
+ end
38
+
39
+ elsif system("which file", out: File::NULL)
40
+ require "open3"
41
+
42
+ def call(file, _)
43
+ return if file.eof? # file command returns "application/x-empty" for empty files
44
+
45
+ Open3.popen3(*%w[file --mime-type --brief -]) do |stdin, stdout, stderr, thread|
46
+ begin
47
+ IO.copy_stream(file, stdin.binmode)
48
+ rescue Errno::EPIPE
49
+ end
50
+ file.rewind
51
+ stdin.close
52
+
53
+ status = thread.value
54
+
55
+ # call to file command failed
56
+ if status.nil? || !status.success?
57
+ $stderr.print(stderr.read)
58
+ else
59
+
60
+ output = stdout.read.strip
61
+
62
+ if output.include?("cannot open")
63
+ $stderr.print(output)
64
+ else
65
+ output
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ else
72
+
73
+ def call(_, _); end
74
+
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Transcoder::Multipart
5
+ module Part
6
+ module_function
7
+
8
+ def call(value)
9
+ # take out specialized objects of the way
10
+ if value.respond_to?(:filename) && value.respond_to?(:content_type) && value.respond_to?(:read)
11
+ return value, value.content_type, value.filename
12
+ end
13
+
14
+ content_type = filename = nil
15
+
16
+ if value.is_a?(Hash)
17
+ content_type = value[:content_type]
18
+ filename = value[:filename]
19
+ value = value[:body]
20
+ end
21
+
22
+ value = value.open(File::RDONLY, encoding: Encoding::BINARY) if Object.const_defined?(:Pathname) && value.is_a?(Pathname)
23
+
24
+ if value.respond_to?(:path) && value.respond_to?(:read)
25
+ # either a File, a Tempfile, or something else which has to quack like a file
26
+ filename ||= File.basename(value.path)
27
+ content_type ||= MimeTypeDetector.call(value, filename) || "application/octet-stream"
28
+ [value, content_type, filename]
29
+ else
30
+ [StringIO.new(value.to_s), content_type || "text/plain", filename]
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "multipart/encoder"
4
+ require_relative "multipart/decoder"
5
+ require_relative "multipart/part"
6
+ require_relative "multipart/mime_type_detector"
7
+
8
+ module HTTPX::Transcoder
9
+ module Multipart
10
+ MULTIPART_VALUE_COND = lambda do |value|
11
+ value.respond_to?(:read) ||
12
+ (value.respond_to?(:to_hash) &&
13
+ value.key?(:body) &&
14
+ (value.key?(:filename) || value.key?(:content_type)))
15
+ end
16
+
17
+ module_function
18
+
19
+ def multipart?(form_data)
20
+ form_data.any? do |_, v|
21
+ Multipart::MULTIPART_VALUE_COND.call(v) ||
22
+ (v.respond_to?(:to_ary) && v.to_ary.any?(&Multipart::MULTIPART_VALUE_COND)) ||
23
+ (v.respond_to?(:to_hash) && v.to_hash.any? { |_, e| Multipart::MULTIPART_VALUE_COND.call(e) })
24
+ end
25
+ end
26
+
27
+ def encode(form_data)
28
+ Encoder.new(form_data)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+
5
+ module HTTPX
6
+ module Transcoder
7
+ class BodyReader
8
+ def initialize(body)
9
+ @body = if body.respond_to?(:read)
10
+ body.rewind if body.respond_to?(:rewind)
11
+ body
12
+ elsif body.respond_to?(:each)
13
+ body.enum_for(:each)
14
+ else
15
+ StringIO.new(body.to_s)
16
+ end
17
+ end
18
+
19
+ def bytesize
20
+ return @body.bytesize if @body.respond_to?(:bytesize)
21
+
22
+ Float::INFINITY
23
+ end
24
+
25
+ def read(length = nil, outbuf = nil)
26
+ return @body.read(length, outbuf) if @body.respond_to?(:read)
27
+
28
+ begin
29
+ chunk = @body.next
30
+ if outbuf
31
+ outbuf.clear.force_encoding(Encoding::BINARY)
32
+ outbuf << chunk
33
+ else
34
+ outbuf = chunk
35
+ end
36
+ outbuf unless length && outbuf.empty?
37
+ rescue StopIteration
38
+ end
39
+ end
40
+
41
+ def close
42
+ @body.close if @body.respond_to?(:close)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "body_reader"
4
+
5
+ module HTTPX
6
+ module Transcoder
7
+ class Deflater
8
+ attr_reader :content_type
9
+
10
+ def initialize(body)
11
+ @content_type = body.content_type
12
+ @body = BodyReader.new(body)
13
+ @closed = false
14
+ end
15
+
16
+ def bytesize
17
+ buffer_deflate!
18
+
19
+ @buffer.size
20
+ end
21
+
22
+ def read(length = nil, outbuf = nil)
23
+ return @buffer.read(length, outbuf) if @buffer
24
+
25
+ return if @closed
26
+
27
+ chunk = @body.read(length)
28
+
29
+ compressed_chunk = deflate(chunk)
30
+
31
+ return unless compressed_chunk
32
+
33
+ if outbuf
34
+ outbuf.clear.force_encoding(Encoding::BINARY)
35
+ outbuf << compressed_chunk
36
+ else
37
+ compressed_chunk
38
+ end
39
+ end
40
+
41
+ def close
42
+ return if @closed
43
+
44
+ @buffer.close if @buffer
45
+
46
+ @body.close
47
+
48
+ @closed = true
49
+ end
50
+
51
+ def rewind
52
+ return unless @buffer
53
+
54
+ @buffer.rewind
55
+ end
56
+
57
+ private
58
+
59
+ # rubocop:disable Naming/MemoizedInstanceVariableName
60
+ def buffer_deflate!
61
+ return @buffer if defined?(@buffer)
62
+
63
+ buffer = Response::Buffer.new(
64
+ threshold_size: Options::MAX_BODY_THRESHOLD_SIZE
65
+ )
66
+ IO.copy_stream(self, buffer)
67
+
68
+ buffer.rewind if buffer.respond_to?(:rewind)
69
+
70
+ @buffer = buffer
71
+ end
72
+ # rubocop:enable Naming/MemoizedInstanceVariableName
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Transcoder
5
+ module_function
6
+
7
+ def normalize_keys(key, value, cond = nil, &block)
8
+ if cond && cond.call(value)
9
+ block.call(key.to_s, value)
10
+ elsif value.respond_to?(:to_ary)
11
+ if value.empty?
12
+ block.call("#{key}[]")
13
+ else
14
+ value.to_ary.each do |element|
15
+ normalize_keys("#{key}[]", element, cond, &block)
16
+ end
17
+ end
18
+ elsif value.respond_to?(:to_hash)
19
+ value.to_hash.each do |child_key, child_value|
20
+ normalize_keys("#{key}[#{child_key}]", child_value, cond, &block)
21
+ end
22
+ else
23
+ block.call(key.to_s, value)
24
+ end
25
+ end
26
+
27
+ # based on https://github.com/rack/rack/blob/d15dd728440710cfc35ed155d66a98dc2c07ae42/lib/rack/query_parser.rb#L82
28
+ def normalize_query(params, name, v, depth)
29
+ raise Error, "params depth surpasses what's supported" if depth <= 0
30
+
31
+ name =~ /\A[\[\]]*([^\[\]]+)\]*/
32
+ k = Regexp.last_match(1) || ""
33
+ after = Regexp.last_match ? Regexp.last_match.post_match : ""
34
+
35
+ if k.empty?
36
+ return Array(v) if !v.empty? && name == "[]"
37
+
38
+ return
39
+ end
40
+
41
+ case after
42
+ when ""
43
+ params[k] = v
44
+ when "["
45
+ params[name] = v
46
+ when "[]"
47
+ params[k] ||= []
48
+ raise Error, "expected Array (got #{params[k].class}) for param '#{k}'" unless params[k].is_a?(Array)
49
+
50
+ params[k] << v
51
+ when /^\[\]\[([^\[\]]+)\]$/, /^\[\](.+)$/
52
+ child_key = Regexp.last_match(1)
53
+ params[k] ||= []
54
+ raise Error, "expected Array (got #{params[k].class}) for param '#{k}'" unless params[k].is_a?(Array)
55
+
56
+ if params[k].last.is_a?(Hash) && !params_hash_has_key?(params[k].last, child_key)
57
+ normalize_query(params[k].last, child_key, v, depth - 1)
58
+ else
59
+ params[k] << normalize_query({}, child_key, v, depth - 1)
60
+ end
61
+ else
62
+ params[k] ||= {}
63
+ raise Error, "expected Hash (got #{params[k].class}) for param '#{k}'" unless params[k].is_a?(Hash)
64
+
65
+ params[k] = normalize_query(params[k], after, v, depth - 1)
66
+ end
67
+
68
+ params
69
+ end
70
+
71
+ def params_hash_has_key?(hash, key)
72
+ return false if key.include?("[]")
73
+
74
+ key.split(/[\[\]]+/).inject(hash) do |h, part|
75
+ next h if part == ""
76
+ return false unless h.is_a?(Hash) && h.key?(part)
77
+
78
+ h[part]
79
+ end
80
+
81
+ true
82
+ end
83
+ end
84
+ end
85
+
86
+ require "httpx/transcoder/body"
87
+ require "httpx/transcoder/form"
88
+ require "httpx/transcoder/json"
89
+ require "httpx/transcoder/chunker"
90
+ require "httpx/transcoder/deflate"
91
+ require "httpx/transcoder/gzip"
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Utils
5
+ using URIExtensions
6
+
7
+ TOKEN = %r{[^\s()<>,;:\\"/\[\]?=]+}.freeze
8
+ VALUE = /"(?:\\"|[^"])*"|#{TOKEN}/.freeze
9
+ FILENAME_REGEX = /\s*filename=(#{VALUE})/.freeze
10
+ FILENAME_EXTENSION_REGEX = /\s*filename\*=(#{VALUE})/.freeze
11
+
12
+ module_function
13
+
14
+ def now
15
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
16
+ end
17
+
18
+ def elapsed_time(monotonic_timestamp)
19
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - monotonic_timestamp
20
+ end
21
+
22
+ # The value of this field can be either an HTTP-date or a number of
23
+ # seconds to delay after the response is received.
24
+ def parse_retry_after(retry_after)
25
+ # first: bet on it being an integer
26
+ Integer(retry_after)
27
+ rescue ArgumentError
28
+ # Then it's a datetime
29
+ time = Time.httpdate(retry_after)
30
+ time - Time.now
31
+ end
32
+
33
+ def get_filename(header, _prefix_regex = nil)
34
+ filename = nil
35
+ case header
36
+ when FILENAME_REGEX
37
+ filename = Regexp.last_match(1)
38
+ filename = Regexp.last_match(1) if filename =~ /^"(.*)"$/
39
+ when FILENAME_EXTENSION_REGEX
40
+ filename = Regexp.last_match(1)
41
+ encoding, _, filename = filename.split("'", 3)
42
+ end
43
+
44
+ return unless filename
45
+
46
+ filename = URI::DEFAULT_PARSER.unescape(filename) if filename.scan(/%.?.?/).all? { |s| /%[0-9a-fA-F]{2}/.match?(s) }
47
+
48
+ filename.scrub!
49
+
50
+ filename = filename.gsub(/\\(.)/, '\1') unless /\\[^\\"]/.match?(filename)
51
+
52
+ filename.force_encoding ::Encoding.find(encoding) if encoding
53
+
54
+ filename
55
+ end
56
+
57
+ URIParser = URI::RFC2396_Parser.new
58
+
59
+ def to_uri(uri)
60
+ return URI(uri) unless uri.is_a?(String) && !uri.ascii_only?
61
+
62
+ uri = URI(URIParser.escape(uri))
63
+
64
+ non_ascii_hostname = URIParser.unescape(uri.host)
65
+
66
+ non_ascii_hostname.force_encoding(Encoding::UTF_8)
67
+
68
+ idna_hostname = Punycode.encode_hostname(non_ascii_hostname)
69
+
70
+ uri.host = idna_hostname
71
+ uri.non_ascii_hostname = non_ascii_hostname
72
+ uri
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ VERSION = "1.6.2.1"
5
+ end
data/lib/httpx.rb ADDED
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "httpx/version"
4
+
5
+ # Top-Level Namespace
6
+ #
7
+ module HTTPX
8
+ EMPTY = [].freeze
9
+ EMPTY_HASH = {}.freeze
10
+
11
+ # All plugins should be stored under this module/namespace. Can register and load
12
+ # plugins.
13
+ #
14
+ module Plugins
15
+ @plugins = {}
16
+ @plugins_mutex = Thread::Mutex.new
17
+
18
+ # Loads a plugin based on a name. If the plugin hasn't been loaded, tries to load
19
+ # it from the load path under "httpx/plugins/" directory.
20
+ #
21
+ def self.load_plugin(name)
22
+ h = @plugins
23
+ m = @plugins_mutex
24
+ unless (plugin = m.synchronize { h[name] })
25
+ require "httpx/plugins/#{name}"
26
+ raise "Plugin #{name} hasn't been registered" unless (plugin = m.synchronize { h[name] })
27
+ end
28
+ plugin
29
+ end
30
+
31
+ # Registers a plugin (+mod+) in the central store indexed by +name+.
32
+ #
33
+ def self.register_plugin(name, mod)
34
+ h = @plugins
35
+ m = @plugins_mutex
36
+ m.synchronize { h[name] = mod }
37
+ end
38
+ end
39
+ end
40
+
41
+ require "httpx/extensions"
42
+
43
+ require "httpx/errors"
44
+ require "httpx/utils"
45
+ require "httpx/punycode"
46
+ require "httpx/domain_name"
47
+ require "httpx/altsvc"
48
+ require "httpx/callbacks"
49
+ require "httpx/loggable"
50
+ require "httpx/transcoder"
51
+ require "httpx/timers"
52
+ require "httpx/pool"
53
+ require "httpx/headers"
54
+ require "httpx/request"
55
+ require "httpx/response"
56
+ require "httpx/options"
57
+ require "httpx/chainable"
58
+
59
+ require "httpx/session"
60
+ require "httpx/session_extensions"
61
+
62
+ # load integrations when possible
63
+
64
+ require "httpx/adapters/datadog" if defined?(DDTrace) || defined?(Datadog::Tracing)
65
+ require "httpx/adapters/sentry" if defined?(Sentry)
66
+ require "httpx/adapters/webmock" if defined?(WebMock)
data/sig/altsvc.rbs ADDED
@@ -0,0 +1,33 @@
1
+ module HTTPX
2
+ module AltSvc
3
+ module ConnectionMixin
4
+
5
+ def send: (Request request) -> void
6
+
7
+ def match?: (URI::Generic uri, Options options) -> bool
8
+
9
+ private
10
+
11
+ def match_altsvcs?: (URI::Generic uri) -> bool
12
+
13
+ def match_altsvc_options?: (URI::Generic uri, Options options) -> bool
14
+ end
15
+
16
+ type altsvc_params = Hash[String, untyped]
17
+
18
+ def self?.cached_altsvc: (String origin) -> Array[altsvc_params]
19
+
20
+ def self?.cached_altsvc_set: (String origin, altsvc_params) -> void
21
+
22
+ def self?.lookup: (String origin, Integer | Float ttl) -> Array[altsvc_params]
23
+
24
+ def self?.emit: (Request request, response response) { (http_uri alt_origin, String origin, altsvc_params alt_params) -> void } -> void
25
+
26
+ def self?.parse: (String altsvc) { (http_uri alt_origin, altsvc_params alt_params) -> void } -> void
27
+ | (String altsvc) -> Enumerable[[http_uri, altsvc_params]]
28
+
29
+ def self?.parse_altsvc_scheme: (String alt_proto) -> String?
30
+
31
+ def self.parse_altsvc_origin: (string alt_proto, String alt_origin) -> http_uri?
32
+ end
33
+ end
data/sig/buffer.rbs ADDED
@@ -0,0 +1,27 @@
1
+ module HTTPX
2
+ class Buffer
3
+ extend Forwardable
4
+
5
+ include _ToS
6
+ include _ToStr
7
+
8
+ @buffer: String
9
+
10
+ attr_reader limit: Integer
11
+
12
+ def full?: () -> bool
13
+ def shift!: (Integer) -> void
14
+ def capacity: () -> Integer
15
+
16
+ # delegated
17
+ def <<: (String data) -> String
18
+ def empty?: () -> bool
19
+ def bytesize: () -> (Integer | Float)
20
+ def clear: () -> void
21
+ def replace: (string) -> void
22
+
23
+ private
24
+
25
+ def initialize: (Integer limit) -> untyped
26
+ end
27
+ end