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,612 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ # Class implementing the APIs being used publicly.
5
+ #
6
+ # HTTPX.get(..) #=> delegating to an internal HTTPX::Session object.
7
+ # HTTPX.plugin(..).get(..) #=> creating an intermediate HTTPX::Session with plugin, then sending the GET request
8
+ class Session
9
+ include Loggable
10
+ include Chainable
11
+
12
+ # initializes the session with a set of +options+, which will be shared by all
13
+ # requests sent from it.
14
+ #
15
+ # When pass a block, it'll yield itself to it, then closes after the block is evaluated.
16
+ def initialize(options = EMPTY_HASH, &blk)
17
+ @options = self.class.default_options.merge(options)
18
+ @persistent = @options.persistent
19
+ @pool = @options.pool_class.new(@options.pool_options)
20
+ @wrapped = false
21
+ @closing = false
22
+ INSTANCES[self] = self if @persistent && @options.close_on_fork && INSTANCES
23
+ wrap(&blk) if blk
24
+ end
25
+
26
+ # Yields itself the block, then closes it after the block is evaluated.
27
+ #
28
+ # session.wrap do |http|
29
+ # http.get("https://wikipedia.com")
30
+ # end # wikipedia connection closes here
31
+ def wrap
32
+ prev_wrapped = @wrapped
33
+ @wrapped = true
34
+ was_initialized = false
35
+ current_selector = get_current_selector do
36
+ selector = Selector.new
37
+
38
+ set_current_selector(selector)
39
+
40
+ was_initialized = true
41
+
42
+ selector
43
+ end
44
+ begin
45
+ yield self
46
+ ensure
47
+ unless prev_wrapped
48
+ if @persistent
49
+ deactivate(current_selector)
50
+ else
51
+ close(current_selector)
52
+ end
53
+ end
54
+ @wrapped = prev_wrapped
55
+ set_current_selector(nil) if was_initialized
56
+ end
57
+ end
58
+
59
+ # closes all the active connections from the session.
60
+ #
61
+ # when called directly without specifying +selector+, all available connections
62
+ # will be picked up from the connection pool and closed. Connections in use
63
+ # by other sessions, or same session in a different thread, will not be reaped.
64
+ def close(selector = Selector.new)
65
+ # throw resolvers away from the pool
66
+ @pool.reset_resolvers
67
+
68
+ # preparing to throw away connections
69
+ while (connection = @pool.pop_connection)
70
+ next if connection.state == :closed
71
+
72
+ select_connection(connection, selector)
73
+ end
74
+
75
+ selector_close(selector)
76
+ end
77
+
78
+ # performs one, or multple requests; it accepts:
79
+ #
80
+ # 1. one or multiple HTTPX::Request objects;
81
+ # 2. an HTTP verb, then a sequence of URIs or URI/options tuples;
82
+ # 3. one or multiple HTTP verb / uri / (optional) options tuples;
83
+ #
84
+ # when present, the set of +options+ kwargs is applied to all of the
85
+ # sent requests.
86
+ #
87
+ # respectively returns a single HTTPX::Response response, or all of them in an Array, in the same order.
88
+ #
89
+ # resp1 = session.request(req1)
90
+ # resp1, resp2 = session.request(req1, req2)
91
+ # resp1 = session.request("GET", "https://server.org/a")
92
+ # resp1, resp2 = session.request("GET", ["https://server.org/a", "https://server.org/b"])
93
+ # resp1, resp2 = session.request(["GET", "https://server.org/a"], ["GET", "https://server.org/b"])
94
+ # resp1 = session.request("POST", "https://server.org/a", form: { "foo" => "bar" })
95
+ # resp1, resp2 = session.request(["POST", "https://server.org/a", form: { "foo" => "bar" }], ["GET", "https://server.org/b"])
96
+ # resp1, resp2 = session.request("GET", ["https://server.org/a", "https://server.org/b"], headers: { "x-api-token" => "TOKEN" })
97
+ #
98
+ def request(*args, **params)
99
+ raise ArgumentError, "must perform at least one request" if args.empty?
100
+
101
+ requests = args.first.is_a?(Request) ? args : build_requests(*args, params)
102
+ responses = send_requests(*requests)
103
+ return responses.first if responses.size == 1
104
+
105
+ responses
106
+ end
107
+
108
+ # returns a HTTP::Request instance built from the HTTP +verb+, the request +uri+, and
109
+ # the optional set of request-specific +options+. This request **must** be sent through
110
+ # the same session it was built from.
111
+ #
112
+ # req = session.build_request("GET", "https://server.com")
113
+ # resp = session.request(req)
114
+ def build_request(verb, uri, params = EMPTY_HASH, options = @options)
115
+ rklass = options.request_class
116
+ request = rklass.new(verb, uri, options, params)
117
+ request.persistent = @persistent
118
+ set_request_callbacks(request)
119
+ request
120
+ end
121
+
122
+ def select_connection(connection, selector)
123
+ pin_connection(connection, selector)
124
+ selector.register(connection)
125
+ end
126
+
127
+ def pin_connection(connection, selector)
128
+ connection.current_session = self
129
+ connection.current_selector = selector
130
+ end
131
+
132
+ alias_method :select_resolver, :select_connection
133
+
134
+ def deselect_connection(connection, selector, cloned = false)
135
+ connection.log(level: 2) do
136
+ "deregistering connection##{connection.object_id}(#{connection.state}) from selector##{selector.object_id}"
137
+ end
138
+ selector.deregister(connection)
139
+
140
+ # when connections coalesce
141
+ return if connection.state == :idle
142
+
143
+ return if cloned
144
+
145
+ return if @closing && connection.state == :closed
146
+
147
+ connection.log(level: 2) { "check-in connection##{connection.object_id}(#{connection.state}) in pool##{@pool.object_id}" }
148
+ @pool.checkin_connection(connection)
149
+ end
150
+
151
+ def deselect_resolver(resolver, selector)
152
+ resolver.log(level: 2) do
153
+ "deregistering resolver##{resolver.object_id}(#{resolver.state}) from selector##{selector.object_id}"
154
+ end
155
+ selector.deregister(resolver)
156
+
157
+ return if @closing && resolver.closed?
158
+
159
+ resolver.log(level: 2) { "check-in resolver##{resolver.object_id}(#{resolver.state}) in pool##{@pool.object_id}" }
160
+ @pool.checkin_resolver(resolver)
161
+ end
162
+
163
+ def try_clone_connection(connection, selector, family)
164
+ connection.family ||= family
165
+
166
+ return connection if connection.family == family
167
+
168
+ new_connection = connection.class.new(connection.origin, connection.options)
169
+
170
+ new_connection.family = family
171
+
172
+ connection.sibling = new_connection
173
+
174
+ do_init_connection(new_connection, selector)
175
+ new_connection
176
+ end
177
+
178
+ # returns the HTTPX::Connection through which the +request+ should be sent through.
179
+ def find_connection(request_uri, selector, options)
180
+ if (connection = selector.find_connection(request_uri, options))
181
+ connection.idling if connection.state == :closed
182
+ connection.log(level: 2) { "found connection##{connection.object_id}(#{connection.state}) in selector##{selector.object_id}" }
183
+ return connection
184
+ end
185
+
186
+ connection = @pool.checkout_connection(request_uri, options)
187
+
188
+ connection.log(level: 2) { "found connection##{connection.object_id}(#{connection.state}) in pool##{@pool.object_id}" }
189
+
190
+ case connection.state
191
+ when :idle
192
+ do_init_connection(connection, selector)
193
+ when :open
194
+ if options.io
195
+ select_connection(connection, selector)
196
+ else
197
+ pin_connection(connection, selector)
198
+ end
199
+ when :closing, :closed
200
+ connection.idling
201
+ if connection.addresses?
202
+ select_connection(connection, selector)
203
+ else
204
+ # if addresses expired, resolve again
205
+ resolve_connection(connection, selector)
206
+ end
207
+ else
208
+ pin_connection(connection, selector)
209
+ end
210
+
211
+ connection
212
+ end
213
+
214
+ private
215
+
216
+ def selector_close(selector)
217
+ begin
218
+ @closing = true
219
+ selector.terminate
220
+ ensure
221
+ @closing = false
222
+ end
223
+ end
224
+
225
+ # tries deactivating connections in the +selector+, deregistering the ones that have been deactivated.
226
+ def deactivate(selector)
227
+ selector.each_connection.select do |c|
228
+ c.deactivate
229
+
230
+ c.state == :inactive
231
+ end.each do |c| # rubocop:disable Style/MultilineBlockChain
232
+ deselect_connection(c, selector)
233
+ end
234
+ end
235
+
236
+ # callback executed when an HTTP/2 promise frame has been received.
237
+ def on_promise(_, stream)
238
+ log(level: 2) { "#{stream.id}: refusing stream!" }
239
+ stream.refuse
240
+ end
241
+
242
+ # returns the corresponding HTTP::Response to the given +request+ if it has been received.
243
+ def fetch_response(request, _selector, _options)
244
+ response = request.response
245
+
246
+ return unless response && response.finished?
247
+
248
+ log(level: 2) { "response fetched" }
249
+
250
+ response
251
+ end
252
+
253
+ # sends the +request+ to the corresponding HTTPX::Connection
254
+ def send_request(request, selector, options = request.options)
255
+ error = begin
256
+ catch(:resolve_error) do
257
+ connection = find_connection(request.uri, selector, options)
258
+ connection.send(request)
259
+ end
260
+ rescue StandardError => e
261
+ e
262
+ end
263
+ return unless error && error.is_a?(Exception)
264
+
265
+ raise error unless error.is_a?(Error)
266
+
267
+ response = ErrorResponse.new(request, error)
268
+ request.response = response
269
+ request.emit(:response, response)
270
+ end
271
+
272
+ # returns a set of HTTPX::Request objects built from the given +args+ and +options+.
273
+ def build_requests(*args, params)
274
+ requests = if args.size == 1
275
+ reqs = args.first
276
+ reqs.map do |verb, uri, ps = EMPTY_HASH|
277
+ request_params = params
278
+ request_params = request_params.merge(ps) unless ps.empty?
279
+ build_request(verb, uri, request_params)
280
+ end
281
+ else
282
+ verb, uris = args
283
+ if uris.respond_to?(:each)
284
+ uris.enum_for(:each).map do |uri, ps = EMPTY_HASH|
285
+ request_params = params
286
+ request_params = request_params.merge(ps) unless ps.empty?
287
+ build_request(verb, uri, request_params)
288
+ end
289
+ else
290
+ [build_request(verb, uris, params)]
291
+ end
292
+ end
293
+ raise ArgumentError, "wrong number of URIs (given 0, expect 1..+1)" if requests.empty?
294
+
295
+ requests
296
+ end
297
+
298
+ def set_request_callbacks(request)
299
+ request.on(:promise, &method(:on_promise))
300
+ end
301
+
302
+ def do_init_connection(connection, selector)
303
+ resolve_connection(connection, selector) unless connection.family
304
+ end
305
+
306
+ # sends an array of HTTPX::Request +requests+, returns the respective array of HTTPX::Response objects.
307
+ def send_requests(*requests)
308
+ selector = get_current_selector { Selector.new }
309
+ begin
310
+ _send_requests(requests, selector)
311
+ receive_requests(requests, selector)
312
+ ensure
313
+ unless @wrapped
314
+ if @persistent
315
+ deactivate(selector)
316
+ else
317
+ close(selector)
318
+ end
319
+ end
320
+ end
321
+ end
322
+
323
+ # sends an array of HTTPX::Request objects
324
+ def _send_requests(requests, selector)
325
+ requests.each do |request|
326
+ send_request(request, selector)
327
+ end
328
+ end
329
+
330
+ # returns the array of HTTPX::Response objects corresponding to the array of HTTPX::Request +requests+.
331
+ def receive_requests(requests, selector)
332
+ responses = [] # : Array[response]
333
+
334
+ # guarantee ordered responses
335
+ loop do
336
+ request = requests.first
337
+
338
+ return responses unless request
339
+
340
+ catch(:coalesced) { selector.next_tick } until (response = fetch_response(request, selector, request.options))
341
+ request.complete!(response)
342
+
343
+ responses << response
344
+ requests.shift
345
+
346
+ break if requests.empty?
347
+
348
+ next unless selector.empty?
349
+
350
+ # in some cases, the pool of connections might have been drained because there was some
351
+ # handshake error, and the error responses have already been emitted, but there was no
352
+ # opportunity to traverse the requests, hence we're returning only a fraction of the errors
353
+ # we were supposed to. This effectively fetches the existing responses and return them.
354
+ exit_from_loop = true
355
+
356
+ requests_to_remove = [] # : Array[Request]
357
+
358
+ requests.each do |req|
359
+ response = fetch_response(req, selector, request.options)
360
+
361
+ if exit_from_loop && response
362
+ req.complete!(response)
363
+ responses << response
364
+ requests_to_remove << req
365
+ else
366
+ # fetch_response may resend requests. when that happens, we need to go back to the initial
367
+ # loop and process the selector. we still do a pass-through on the remainder of requests, so
368
+ # that every request that need to be resent, is resent.
369
+ exit_from_loop = false
370
+
371
+ raise Error, "something went wrong, responses not found and requests not resent" if selector.empty?
372
+ end
373
+ end
374
+
375
+ break if exit_from_loop
376
+
377
+ requests -= requests_to_remove
378
+ end
379
+ responses
380
+ end
381
+
382
+ def resolve_connection(connection, selector)
383
+ if connection.addresses? || connection.open?
384
+ #
385
+ # there are two cases in which we want to activate initialization of
386
+ # connection immediately:
387
+ #
388
+ # 1. when the connection already has addresses, i.e. it doesn't need to
389
+ # resolve a name (not the same as name being an IP, yet)
390
+ # 2. when the connection is initialized with an external already open IO.
391
+ #
392
+ on_resolver_connection(connection, selector)
393
+ return
394
+ end
395
+
396
+ resolver = find_resolver_for(connection, selector)
397
+
398
+ resolver.early_resolve(connection) || resolver.lazy_resolve(connection)
399
+ end
400
+
401
+ def on_resolver_connection(connection, selector)
402
+ from_pool = false
403
+ found_connection = selector.find_mergeable_connection(connection) || begin
404
+ from_pool = true
405
+ @pool.checkout_mergeable_connection(connection)
406
+ end
407
+
408
+ return select_connection(connection, selector) unless found_connection
409
+
410
+ connection.log(level: 2) do
411
+ "try coalescing from #{from_pool ? "pool##{@pool.object_id}" : "selector##{selector.object_id}"} " \
412
+ "(conn##{found_connection.object_id}[#{found_connection.origin}])"
413
+ end
414
+
415
+ coalesce_connections(found_connection, connection, selector, from_pool)
416
+ end
417
+
418
+ def on_resolver_close(resolver, selector)
419
+ return if resolver.closed?
420
+
421
+ deselect_resolver(resolver, selector)
422
+ resolver.close unless resolver.closed?
423
+ end
424
+
425
+ def find_resolver_for(connection, selector)
426
+ if (resolver = selector.find_resolver(connection.options))
427
+ resolver.log(level: 2) { "found resolver##{connection.object_id}(#{connection.state}) in selector##{selector.object_id}" }
428
+ return resolver
429
+ end
430
+
431
+ resolver = @pool.checkout_resolver(connection.options)
432
+ resolver.log(level: 2) { "found resolver##{connection.object_id}(#{connection.state}) in pool##{@pool.object_id}" }
433
+ resolver.current_session = self
434
+ resolver.current_selector = selector
435
+
436
+ resolver
437
+ end
438
+
439
+ # coalesces +conn2+ into +conn1+. if +conn1+ was loaded from the connection pool
440
+ # (it is known via +from_pool+), then it adds its to the +selector+.
441
+ def coalesce_connections(conn1, conn2, selector, from_pool)
442
+ unless conn1.coalescable?(conn2)
443
+ conn2.log(level: 2) { "not coalescing with conn##{conn1.object_id}[#{conn1.origin}])" }
444
+ select_connection(conn2, selector)
445
+ if from_pool
446
+ conn1.log(level: 2) { "check-in connection##{conn1.object_id}(#{conn1.state}) in pool##{@pool.object_id}" }
447
+ @pool.checkin_connection(conn1)
448
+ end
449
+ return false
450
+ end
451
+
452
+ conn2.log(level: 2) { "coalescing with conn##{conn1.object_id}[#{conn1.origin}])" }
453
+ conn2.coalesce!(conn1)
454
+ select_connection(conn1, selector) if from_pool
455
+ conn2.disconnect
456
+ true
457
+ end
458
+
459
+ def get_current_selector
460
+ selector_store[self] || (yield if block_given?)
461
+ end
462
+
463
+ def set_current_selector(selector)
464
+ if selector
465
+ selector_store[self] = selector
466
+ else
467
+ selector_store.delete(self)
468
+ end
469
+ end
470
+
471
+ def selector_store
472
+ th_current = Thread.current
473
+
474
+ thread_selector_store(th_current) || begin
475
+ {}.compare_by_identity.tap do |store|
476
+ th_current.thread_variable_set(:httpx_persistent_selector_store, store)
477
+ end
478
+ end
479
+ end
480
+
481
+ def thread_selector_store(th)
482
+ th.thread_variable_get(:httpx_persistent_selector_store)
483
+ end
484
+
485
+ @default_options = Options.new
486
+ @default_options.freeze
487
+ @plugins = []
488
+
489
+ class << self
490
+ attr_reader :default_options
491
+
492
+ def inherited(klass)
493
+ super
494
+ klass.instance_variable_set(:@default_options, @default_options)
495
+ klass.instance_variable_set(:@plugins, @plugins.dup)
496
+ klass.instance_variable_set(:@callbacks, @callbacks.dup)
497
+ end
498
+
499
+ # returns a new HTTPX::Session instance, with the plugin pointed by +pl+ loaded.
500
+ #
501
+ # session_with_retries = session.plugin(:retries)
502
+ # session_with_custom = session.plugin(CustomPlugin)
503
+ #
504
+ def plugin(pl, options = nil, &block)
505
+ label = pl
506
+ # raise Error, "Cannot add a plugin to a frozen config" if frozen?
507
+ pl = Plugins.load_plugin(pl) if pl.is_a?(Symbol)
508
+ raise ArgumentError, "Invalid plugin type: #{pl.class.inspect}" unless pl.is_a?(Module)
509
+
510
+ if !@plugins.include?(pl)
511
+ @plugins << pl
512
+ pl.load_dependencies(self, &block) if pl.respond_to?(:load_dependencies)
513
+
514
+ @default_options = @default_options.dup
515
+
516
+ include(pl::InstanceMethods) if defined?(pl::InstanceMethods)
517
+ extend(pl::ClassMethods) if defined?(pl::ClassMethods)
518
+
519
+ opts = @default_options
520
+ opts.extend_with_plugin_classes(pl)
521
+
522
+ if defined?(pl::OptionsMethods)
523
+ # when a class gets dup'ed, the #initialize_dup callbacks isn't triggered.
524
+ # moreover, and because #method_added does not get triggered on mixin include,
525
+ # the callback is also forcefully manually called here.
526
+ opts.options_class.instance_variable_set(:@options_names, opts.options_class.options_names.dup)
527
+ (pl::OptionsMethods.instance_methods + pl::OptionsMethods.private_instance_methods - Object.instance_methods).each do |meth|
528
+ opts.options_class.method_added(meth)
529
+ end
530
+ @default_options = opts.options_class.new(opts)
531
+ end
532
+
533
+ @default_options = pl.extra_options(@default_options) if pl.respond_to?(:extra_options)
534
+ @default_options = @default_options.merge(options) if options
535
+
536
+ if pl.respond_to?(:subplugins)
537
+ pl.subplugins.transform_keys(&Plugins.method(:load_plugin)).each do |main_pl, sub_pl|
538
+ # in case the main plugin has already been loaded, then apply subplugin functionality
539
+ # immediately
540
+ next unless @plugins.include?(main_pl)
541
+
542
+ plugin(sub_pl, options, &block)
543
+ end
544
+ end
545
+
546
+ pl.configure(self, &block) if pl.respond_to?(:configure)
547
+
548
+ if label.is_a?(Symbol)
549
+ # in case an already-loaded plugin complements functionality of
550
+ # the plugin currently being loaded, loaded it now
551
+ @plugins.each do |registered_pl|
552
+ next if registered_pl == pl
553
+
554
+ next unless registered_pl.respond_to?(:subplugins)
555
+
556
+ sub_pl = registered_pl.subplugins[label]
557
+
558
+ next unless sub_pl
559
+
560
+ plugin(sub_pl, options, &block)
561
+ end
562
+ end
563
+
564
+ @default_options.freeze
565
+ set_temporary_name("#{superclass}/#{pl}") if respond_to?(:set_temporary_name) # ruby 3.4 only
566
+ elsif options
567
+ # this can happen when two plugins are loaded, an one of them calls the other under the hood,
568
+ # albeit changing some default.
569
+ @default_options = pl.extra_options(@default_options) if pl.respond_to?(:extra_options)
570
+ @default_options = @default_options.merge(options) if options
571
+
572
+ @default_options.freeze
573
+ end
574
+
575
+ self
576
+ end
577
+ end
578
+
579
+ # setup of the support for close_on_fork sessions.
580
+ # adapted from https://github.com/mperham/connection_pool/blob/main/lib/connection_pool.rb#L48
581
+ if Process.respond_to?(:fork)
582
+ INSTANCES = ObjectSpace::WeakMap.new
583
+ private_constant :INSTANCES
584
+
585
+ def self.after_fork
586
+ INSTANCES.each_value(&:close)
587
+ nil
588
+ end
589
+
590
+ if ::Process.respond_to?(:_fork)
591
+ module ForkTracker
592
+ def _fork
593
+ pid = super
594
+ Session.after_fork if pid.zero?
595
+ pid
596
+ end
597
+ end
598
+ Process.singleton_class.prepend(ForkTracker)
599
+ end
600
+ else
601
+ INSTANCES = nil
602
+ private_constant :INSTANCES
603
+
604
+ def self.after_fork
605
+ # noop
606
+ end
607
+ end
608
+ end
609
+
610
+ # session may be overridden by certain adapters.
611
+ S = Session
612
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ unless ENV.keys.grep(/\Ahttps?_proxy\z/i).empty?
5
+ proxy_session = plugin(:proxy)
6
+ remove_const(:Session)
7
+ const_set(:Session, proxy_session.class)
8
+
9
+ # redefine the default options static var, which needs to
10
+ # refresh options_class
11
+ options = proxy_session.class.default_options.to_hash
12
+ original_verbosity = $VERBOSE
13
+ $VERBOSE = nil
14
+ new_options_class = proxy_session.class.default_options.options_class.dup
15
+ const_set(:Options, new_options_class)
16
+ options[:options_class] = Class.new(new_options_class).freeze
17
+ options.freeze
18
+ Options.send(:const_set, :DEFAULT_OPTIONS, options)
19
+ Session.instance_variable_set(:@default_options, Options.new(options))
20
+ $VERBOSE = original_verbosity
21
+ end
22
+
23
+ # :nocov:
24
+ if Session.default_options.debug_level > 2
25
+ proxy_session = plugin(:internal_telemetry)
26
+ remove_const(:Session)
27
+ const_set(:Session, proxy_session.class)
28
+ end
29
+ # :nocov:
30
+ end