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,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+ require "stringio"
5
+ require "tempfile"
6
+
7
+ module HTTPX
8
+ # wraps and delegates to an internal buffer, which can be a StringIO or a Tempfile.
9
+ class Response::Buffer < SimpleDelegator
10
+ attr_reader :buffer
11
+ protected :buffer
12
+
13
+ # initializes buffer with the +threshold_size+ over which the payload gets buffer to a tempfile,
14
+ # the initial +bytesize+, and the +encoding+.
15
+ def initialize(threshold_size:, bytesize: 0, encoding: Encoding::BINARY)
16
+ @threshold_size = threshold_size
17
+ @bytesize = bytesize
18
+ @encoding = encoding
19
+ @buffer = StringIO.new("".b)
20
+ super(@buffer)
21
+ end
22
+
23
+ def initialize_dup(other)
24
+ super
25
+
26
+ # create new descriptor in READ-ONLY mode
27
+ @buffer =
28
+ case other.buffer
29
+ when StringIO
30
+ StringIO.new(other.buffer.string, mode: File::RDONLY)
31
+ else
32
+ other.buffer.class.new(other.buffer.path, encoding: Encoding::BINARY, mode: File::RDONLY).tap do |temp|
33
+ FileUtils.copy_file(other.buffer.path, temp.path)
34
+ end
35
+ end
36
+ end
37
+
38
+ # size in bytes of the buffered content.
39
+ def size
40
+ @bytesize
41
+ end
42
+
43
+ # writes the +chunk+ into the buffer.
44
+ def write(chunk)
45
+ @bytesize += chunk.bytesize
46
+ try_upgrade_buffer
47
+ @buffer.write(chunk)
48
+ end
49
+
50
+ # returns the buffered content as a string.
51
+ def to_s
52
+ case @buffer
53
+ when StringIO
54
+ begin
55
+ @buffer.string.force_encoding(@encoding)
56
+ rescue ArgumentError
57
+ @buffer.string
58
+ end
59
+ when Tempfile
60
+ rewind
61
+ content = @buffer.read
62
+ begin
63
+ content.force_encoding(@encoding)
64
+ rescue ArgumentError # ex: unknown encoding name - utf
65
+ content
66
+ end
67
+ end
68
+ end
69
+
70
+ # closes the buffer.
71
+ def close
72
+ @buffer.close
73
+ @buffer.unlink if @buffer.respond_to?(:unlink)
74
+ end
75
+
76
+ def ==(other)
77
+ super || begin
78
+ return false unless other.is_a?(Response::Buffer)
79
+
80
+ buffer_pos = @buffer.pos
81
+ other_pos = other.buffer.pos
82
+ @buffer.rewind
83
+ other.buffer.rewind
84
+ begin
85
+ FileUtils.compare_stream(@buffer, other.buffer)
86
+ ensure
87
+ @buffer.pos = buffer_pos
88
+ other.buffer.pos = other_pos
89
+ end
90
+ end
91
+ end
92
+
93
+ private
94
+
95
+ # initializes the buffer into a StringIO, or turns it into a Tempfile when the threshold
96
+ # has been reached.
97
+ def try_upgrade_buffer
98
+ return unless @bytesize > @threshold_size
99
+
100
+ return if @buffer.is_a?(Tempfile)
101
+
102
+ aux = @buffer
103
+
104
+ @buffer = Tempfile.new("httpx", encoding: Encoding::BINARY, mode: File::RDWR)
105
+
106
+ if aux
107
+ aux.rewind
108
+ IO.copy_stream(aux, @buffer)
109
+ aux.close
110
+ end
111
+
112
+ __setobj__(@buffer)
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,304 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "objspace"
4
+ require "stringio"
5
+ require "tempfile"
6
+ require "fileutils"
7
+ require "forwardable"
8
+
9
+ module HTTPX
10
+ # Defines a HTTP response is handled internally, with a few properties exposed as attributes.
11
+ #
12
+ # It delegates the following methods to the corresponding HTTPX::Request:
13
+ #
14
+ # * HTTPX::Request#uri
15
+ # * HTTPX::Request#peer_address
16
+ #
17
+ # It implements (indirectly, via the +body+) the IO write protocol to internally buffer payloads.
18
+ #
19
+ # It implements the IO reader protocol in order for users to buffer/stream it, acts as an enumerable
20
+ # (of payload chunks).
21
+ #
22
+ class Response
23
+ extend Forwardable
24
+ include Callbacks
25
+
26
+ # the HTTP response status code
27
+ attr_reader :status
28
+
29
+ # an HTTPX::Headers object containing the response HTTP headers.
30
+ attr_reader :headers
31
+
32
+ # a HTTPX::Response::Body object wrapping the response body. The following methods are delegated to it:
33
+ #
34
+ # * HTTPX::Response::Body#to_s
35
+ # * HTTPX::Response::Body#to_str
36
+ # * HTTPX::Response::Body#read
37
+ # * HTTPX::Response::Body#copy_to
38
+ # * HTTPX::Response::Body#close
39
+ attr_reader :body
40
+
41
+ # The HTTP protocol version used to fetch the response.
42
+ attr_reader :version
43
+
44
+ # returns the response body buffered in a string.
45
+ def_delegator :@body, :to_s
46
+
47
+ def_delegator :@body, :to_str
48
+
49
+ # implements the IO reader +#read+ interface.
50
+ def_delegator :@body, :read
51
+
52
+ # copies the response body to a different location.
53
+ def_delegator :@body, :copy_to
54
+
55
+ # the corresponding request uri.
56
+ def_delegator :@request, :uri
57
+
58
+ # the IP address of the peer server.
59
+ def_delegator :@request, :peer_address
60
+
61
+ # inits the instance with the corresponding +request+ to this response, an the
62
+ # response HTTP +status+, +version+ and HTTPX::Headers instance of +headers+.
63
+ def initialize(request, status, version, headers)
64
+ @request = request
65
+ @options = request.options
66
+ @version = version
67
+ @status = Integer(status)
68
+ @headers = @options.headers_class.new(headers)
69
+ @body = @options.response_body_class.new(self, @options)
70
+ @finished = complete?
71
+ @content_type = nil
72
+ end
73
+
74
+ # dupped initialization
75
+ def initialize_dup(orig)
76
+ super
77
+ # if a response gets dupped, the body handle must also get dupped to prevent
78
+ # two responses from using the same file handle to read.
79
+ @body = orig.body.dup
80
+ end
81
+
82
+ # closes the respective +@request+ and +@body+.
83
+ def close
84
+ @request.close
85
+ @body.close
86
+ end
87
+
88
+ # merges headers defined in +h+ into the response headers.
89
+ def merge_headers(h)
90
+ @headers = @headers.merge(h)
91
+ end
92
+
93
+ # writes +data+ chunk into the response body.
94
+ def <<(data)
95
+ @body.write(data)
96
+ end
97
+
98
+ # returns the HTTPX::ContentType for the response, as per what's declared in the content-type header.
99
+ #
100
+ # response.content_type #=> #<HTTPX::ContentType:xxx @header_value="text/plain">
101
+ # response.content_type.mime_type #=> "text/plain"
102
+ def content_type
103
+ @content_type ||= ContentType.new(@headers["content-type"])
104
+ end
105
+
106
+ # returns whether the response has been fully fetched.
107
+ def finished?
108
+ @finished
109
+ end
110
+
111
+ # marks the response as finished, freezes the headers.
112
+ def finish!
113
+ @finished = true
114
+ @headers.freeze
115
+ end
116
+
117
+ # returns whether the response contains body payload.
118
+ def bodyless?
119
+ @request.verb == "HEAD" ||
120
+ @status < 200 || # informational response
121
+ @status == 204 ||
122
+ @status == 205 ||
123
+ @status == 304 || begin
124
+ content_length = @headers["content-length"]
125
+ return false if content_length.nil?
126
+
127
+ content_length == "0"
128
+ end
129
+ end
130
+
131
+ def complete?
132
+ bodyless? || (@request.verb == "CONNECT" && @status == 200)
133
+ end
134
+
135
+ # :nocov:
136
+ def inspect
137
+ "#<#{self.class}:#{object_id} " \
138
+ "HTTP/#{version} " \
139
+ "@status=#{@status} " \
140
+ "@headers=#{@headers} " \
141
+ "@body=#{@body.bytesize}>"
142
+ end
143
+ # :nocov:
144
+
145
+ # returns an instance of HTTPX::HTTPError if the response has a 4xx or 5xx
146
+ # status code, or nothing.
147
+ #
148
+ # ok_response.error #=> nil
149
+ # not_found_response.error #=> HTTPX::HTTPError instance, status 404
150
+ def error
151
+ return if @status < 400
152
+
153
+ HTTPError.new(self)
154
+ end
155
+
156
+ # it raises the exception returned by +error+, or itself otherwise.
157
+ #
158
+ # ok_response.raise_for_status #=> ok_response
159
+ # not_found_response.raise_for_status #=> raises HTTPX::HTTPError exception
160
+ def raise_for_status
161
+ return self unless (err = error)
162
+
163
+ raise err
164
+ end
165
+
166
+ # decodes the response payload into a ruby object **if** the payload is valid json.
167
+ #
168
+ # response.json #≈> { "foo" => "bar" } for "{\"foo\":\"bar\"}" payload
169
+ # response.json(symbolize_names: true) #≈> { foo: "bar" } for "{\"foo\":\"bar\"}" payload
170
+ def json(*args)
171
+ decode(Transcoder::JSON, *args)
172
+ end
173
+
174
+ # decodes the response payload into a ruby object **if** the payload is valid
175
+ # "application/x-www-urlencoded" or "multipart/form-data".
176
+ def form
177
+ decode(Transcoder::Form)
178
+ end
179
+
180
+ def xml
181
+ # TODO: remove at next major version.
182
+ warn "DEPRECATION WARNING: calling `.#{__method__}` on plain HTTPX responses is deprecated. " \
183
+ "Use HTTPX.plugin(:xml) sessions and call `.#{__method__}` in its responses instead."
184
+ require "httpx/plugins/xml"
185
+ decode(Plugins::XML::Transcoder)
186
+ end
187
+
188
+ private
189
+
190
+ # decodes the response payload using the given +transcoder+, which implements the decoding logic.
191
+ #
192
+ # +transcoder+ must implement the internal transcoder API, i.e. respond to <tt>decode(HTTPX::Response response)</tt>,
193
+ # which returns a decoder which responds to <tt>call(HTTPX::Response response, **kwargs)</tt>
194
+ def decode(transcoder, *args)
195
+ # TODO: check if content-type is a valid format, i.e. "application/json" for json parsing
196
+
197
+ decoder = transcoder.decode(self)
198
+
199
+ raise Error, "no decoder available for \"#{transcoder}\"" unless decoder
200
+
201
+ @body.rewind
202
+
203
+ decoder.call(self, *args)
204
+ end
205
+ end
206
+
207
+ # Helper class which decodes the HTTP "content-type" header.
208
+ class ContentType
209
+ MIME_TYPE_RE = %r{^([^/]+/[^;]+)(?:$|;)}.freeze
210
+ CHARSET_RE = /;\s*charset=([^;]+)/i.freeze
211
+
212
+ def initialize(header_value)
213
+ @header_value = header_value
214
+ end
215
+
216
+ # returns the mime type declared in the header.
217
+ #
218
+ # ContentType.new("application/json; charset=utf-8").mime_type #=> "application/json"
219
+ def mime_type
220
+ return @mime_type if defined?(@mime_type)
221
+
222
+ m = @header_value.to_s[MIME_TYPE_RE, 1]
223
+ m && @mime_type = m.strip.downcase
224
+ end
225
+
226
+ # returns the charset declared in the header.
227
+ #
228
+ # ContentType.new("application/json; charset=utf-8").charset #=> "utf-8"
229
+ # ContentType.new("text/plain").charset #=> nil
230
+ def charset
231
+ return @charset if defined?(@charset)
232
+
233
+ m = @header_value.to_s[CHARSET_RE, 1]
234
+ m && @charset = m.strip.delete('"')
235
+ end
236
+ end
237
+
238
+ # Wraps an error which has happened while processing an HTTP Request. It has partial
239
+ # public API parity with HTTPX::Response, so users should rely on it to infer whether
240
+ # the returned response is one or the other.
241
+ #
242
+ # response = HTTPX.get("https://some-domain/path") #=> response is HTTPX::Response or HTTPX::ErrorResponse
243
+ # response.raise_for_status #=> raises if it wraps an error
244
+ class ErrorResponse
245
+ include Loggable
246
+ extend Forwardable
247
+
248
+ # the corresponding HTTPX::Request instance.
249
+ attr_reader :request
250
+
251
+ # the HTTPX::Response instance, when there is one (i.e. error happens fetching the response).
252
+ attr_reader :response
253
+
254
+ # the wrapped exception.
255
+ attr_reader :error
256
+
257
+ # the request uri
258
+ def_delegator :@request, :uri
259
+
260
+ # the IP address of the peer server.
261
+ def_delegator :@request, :peer_address
262
+
263
+ def initialize(request, error)
264
+ @request = request
265
+ @response = request.response if request.response.is_a?(Response)
266
+ @error = error
267
+ @options = request.options
268
+ log_exception(@error)
269
+ end
270
+
271
+ # returns the exception full message.
272
+ def to_s
273
+ @error.full_message(highlight: false)
274
+ end
275
+
276
+ # closes the error resources.
277
+ def close
278
+ @response.close if @response
279
+ end
280
+
281
+ # always true for error responses.
282
+ def finished?
283
+ true
284
+ end
285
+
286
+ def finish!; end
287
+
288
+ # raises the wrapped exception.
289
+ def raise_for_status
290
+ raise @error
291
+ end
292
+
293
+ # buffers lost chunks to error response
294
+ def <<(data)
295
+ return unless @response
296
+
297
+ @response << data
298
+ end
299
+ end
300
+ end
301
+
302
+ require_relative "response/body"
303
+ require_relative "response/buffer"
304
+ require_relative "pmatch_extensions" if RUBY_VERSION >= "2.7.0"
@@ -0,0 +1,282 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "io/wait"
4
+
5
+ module HTTPX
6
+ #
7
+ # Implements the selector loop, where it registers and monitors "Selectable" objects.
8
+ #
9
+ # A Selectable object is an object which can calculate the **interests** (<tt>:r</tt>, <tt>:w</tt> or <tt>:rw</tt>,
10
+ # respectively "read", "write" or "read-write") it wants to monitor for, and returns (via <tt>to_io</tt> method) an
11
+ # IO object which can be passed to functions such as IO.select . More exhaustively, a Selectable **must** implement
12
+ # the following methods:
13
+ #
14
+ # state :: returns the state as a Symbol, must return <tt>:closed</tt> when disposed of resources.
15
+ # to_io :: returns the IO object.
16
+ # call :: gets called when the IO is ready.
17
+ # interests :: returns the current interests to monitor for, as described above.
18
+ # timeout :: returns nil or an integer, representing how long to wait for interests.
19
+ # handle_socket_timeout(Numeric) :: called when waiting for interest times out.
20
+ #
21
+ class Selector
22
+ extend Forwardable
23
+
24
+ READABLE = %i[rw r].freeze
25
+ WRITABLE = %i[rw w].freeze
26
+
27
+ private_constant :READABLE
28
+ private_constant :WRITABLE
29
+
30
+ def_delegator :@timers, :after
31
+
32
+ def_delegator :@selectables, :empty?
33
+
34
+ def initialize
35
+ @timers = Timers.new
36
+ @selectables = []
37
+ @is_timer_interval = false
38
+ end
39
+
40
+ def each(&blk)
41
+ @selectables.each(&blk)
42
+ end
43
+
44
+ def next_tick
45
+ catch(:jump_tick) do
46
+ timeout = next_timeout
47
+ if timeout && timeout.negative?
48
+ @timers.fire
49
+ throw(:jump_tick)
50
+ end
51
+
52
+ begin
53
+ select(timeout) do |c|
54
+ c.log(level: 2) { "[#{c.state}] selected#{" after #{timeout} secs" unless timeout.nil?}..." }
55
+
56
+ c.call
57
+ end
58
+
59
+ @timers.fire
60
+ rescue TimeoutError => e
61
+ @timers.fire(e)
62
+ end
63
+ end
64
+ rescue StandardError => e
65
+ each_connection do |c|
66
+ c.emit(:error, e)
67
+ end
68
+ rescue Exception # rubocop:disable Lint/RescueException
69
+ each_connection do |conn|
70
+ conn.force_reset
71
+ conn.disconnect
72
+ end
73
+
74
+ raise
75
+ end
76
+
77
+ def terminate
78
+ # array may change during iteration
79
+ selectables = @selectables.reject(&:inflight?)
80
+
81
+ selectables.delete_if do |sel|
82
+ sel.terminate
83
+ sel.state == :closed
84
+ end
85
+
86
+ until selectables.empty?
87
+ next_tick
88
+
89
+ selectables &= @selectables
90
+ end
91
+ end
92
+
93
+ def find_resolver(options)
94
+ res = @selectables.find do |c|
95
+ c.is_a?(Resolver::Resolver) && options == c.options
96
+ end
97
+
98
+ res.multi if res
99
+ end
100
+
101
+ def each_connection(&block)
102
+ return enum_for(__method__) unless block
103
+
104
+ @selectables.each do |c|
105
+ case c
106
+ when Resolver::Resolver
107
+ c.each_connection(&block)
108
+ when Connection
109
+ yield c
110
+ end
111
+ end
112
+ end
113
+
114
+ def find_connection(request_uri, options)
115
+ each_connection.find do |connection|
116
+ connection.match?(request_uri, options)
117
+ end
118
+ end
119
+
120
+ def find_mergeable_connection(connection)
121
+ each_connection.find do |ch|
122
+ ch != connection && ch.mergeable?(connection)
123
+ end
124
+ end
125
+
126
+ # deregisters +io+ from selectables.
127
+ def deregister(io)
128
+ @selectables.delete(io)
129
+ end
130
+
131
+ # register +io+.
132
+ def register(io)
133
+ return if @selectables.include?(io)
134
+
135
+ @selectables << io
136
+ end
137
+
138
+ private
139
+
140
+ def select(interval, &block)
141
+ has_no_selectables = @selectables.empty?
142
+ # do not cause an infinite loop here.
143
+ #
144
+ # this may happen if timeout calculation actually triggered an error which causes
145
+ # the connections to be reaped (such as the total timeout error) before #select
146
+ # gets called.
147
+ return if interval.nil? && has_no_selectables
148
+
149
+ # @type var r: (selectable | Array[selectable])?
150
+ # @type var w: (selectable | Array[selectable])?
151
+ r, w = nil
152
+
153
+ @selectables.delete_if do |io|
154
+ interests = io.interests
155
+
156
+ is_closed = io.state == :closed
157
+
158
+ next(is_closed) if is_closed
159
+
160
+ io.log(level: 2) { "[#{io.state}] registering for select (#{interests})#{" for #{interval} seconds" unless interval.nil?}" }
161
+
162
+ if READABLE.include?(interests)
163
+ r = r.nil? ? io : (Array(r) << io)
164
+ end
165
+
166
+ if WRITABLE.include?(interests)
167
+ w = w.nil? ? io : (Array(w) << io)
168
+ end
169
+
170
+ is_closed
171
+ end
172
+
173
+ case r
174
+ when Array
175
+ w = Array(w) unless w.nil?
176
+
177
+ select_many(r, w, interval, &block)
178
+ when nil
179
+ case w
180
+ when Array
181
+ select_many(r, w, interval, &block)
182
+ when nil
183
+ return unless interval && has_no_selectables
184
+
185
+ # no selectables
186
+ # TODO: replace with sleep?
187
+ select_many(r, w, interval, &block)
188
+ else
189
+ select_one(w, :w, interval, &block)
190
+ end
191
+
192
+ else
193
+ case w
194
+ when Array
195
+ select_many(Array(r), w, interval, &block)
196
+ when nil
197
+ select_one(r, :r, interval, &block)
198
+ else
199
+ if r == w
200
+ select_one(r, :rw, interval, &block)
201
+ else
202
+ select_many(Array(r), Array(w), interval, &block)
203
+ end
204
+ end
205
+ end
206
+ end
207
+
208
+ def select_many(r, w, interval, &block)
209
+ readers, writers = ::IO.select(r, w, nil, interval)
210
+
211
+ if readers.nil? && writers.nil? && interval
212
+ [*r, *w].each { |io| io.handle_socket_timeout(interval) }
213
+ return
214
+ end
215
+
216
+ if writers
217
+ readers.each do |io|
218
+ yield io
219
+
220
+ # so that we don't yield 2 times
221
+ writers.delete(io)
222
+ end if readers
223
+
224
+ writers.each(&block)
225
+ else
226
+ readers.each(&block) if readers
227
+ end
228
+ end
229
+
230
+ def select_one(io, interests, interval)
231
+ result =
232
+ case interests
233
+ when :r then io.to_io.wait_readable(interval)
234
+ when :w then io.to_io.wait_writable(interval)
235
+ when :rw then rw_wait(io, interval)
236
+ end
237
+
238
+ unless result || interval.nil?
239
+ io.handle_socket_timeout(interval) unless @is_timer_interval
240
+ return
241
+ end
242
+
243
+ yield io
244
+ end
245
+
246
+ def next_timeout
247
+ @is_timer_interval = false
248
+
249
+ timer_interval = @timers.wait_interval
250
+
251
+ connection_interval = @selectables.filter_map(&:timeout).min
252
+
253
+ return connection_interval unless timer_interval
254
+
255
+ if connection_interval.nil? || timer_interval <= connection_interval
256
+ @is_timer_interval = true
257
+
258
+ return timer_interval
259
+ end
260
+
261
+ connection_interval
262
+ end
263
+
264
+ if RUBY_ENGINE == "jruby"
265
+ def rw_wait(io, interval)
266
+ io.to_io.wait(interval, :read_write)
267
+ end
268
+ elsif IO.const_defined?(:READABLE)
269
+ def rw_wait(io, interval)
270
+ io.to_io.wait(IO::READABLE | IO::WRITABLE, interval)
271
+ end
272
+ else
273
+ def rw_wait(io, interval)
274
+ if interval
275
+ io.to_io.wait(interval, :read_write)
276
+ else
277
+ io.to_io.wait(:read_write)
278
+ end
279
+ end
280
+ end
281
+ end
282
+ end