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,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sentry-ruby"
4
+
5
+ module HTTPX::Plugins
6
+ module Sentry
7
+ module Tracer
8
+ module_function
9
+
10
+ def call(request)
11
+ sentry_span = start_sentry_span
12
+
13
+ return unless sentry_span
14
+
15
+ set_sentry_trace_header(request, sentry_span)
16
+
17
+ request.on(:response, &method(:finish_sentry_span).curry(3)[sentry_span, request])
18
+ end
19
+
20
+ def start_sentry_span
21
+ return unless ::Sentry.initialized? && (span = ::Sentry.get_current_scope.get_span)
22
+ return if span.sampled == false
23
+
24
+ span.start_child(op: "httpx.client", start_timestamp: ::Sentry.utc_now.to_f)
25
+ end
26
+
27
+ def set_sentry_trace_header(request, sentry_span)
28
+ return unless sentry_span
29
+
30
+ config = ::Sentry.configuration
31
+ url = request.uri.to_s
32
+
33
+ return unless config.propagate_traces && config.trace_propagation_targets.any? { |target| url.match?(target) }
34
+
35
+ trace = ::Sentry.get_current_client.generate_sentry_trace(sentry_span)
36
+ request.headers[::Sentry::SENTRY_TRACE_HEADER_NAME] = trace if trace
37
+ end
38
+
39
+ def finish_sentry_span(span, request, response)
40
+ return unless ::Sentry.initialized?
41
+
42
+ record_sentry_breadcrumb(request, response)
43
+ record_sentry_span(request, response, span)
44
+ end
45
+
46
+ def record_sentry_breadcrumb(req, res)
47
+ return unless ::Sentry.configuration.breadcrumbs_logger.include?(:http_logger)
48
+
49
+ request_info = extract_request_info(req)
50
+
51
+ data = if res.is_a?(HTTPX::ErrorResponse)
52
+ { error: res.error.message, **request_info }
53
+ else
54
+ { status: res.status, **request_info }
55
+ end
56
+
57
+ crumb = ::Sentry::Breadcrumb.new(
58
+ level: :info,
59
+ category: "httpx",
60
+ type: :info,
61
+ data: data
62
+ )
63
+ ::Sentry.add_breadcrumb(crumb)
64
+ end
65
+
66
+ def record_sentry_span(req, res, sentry_span)
67
+ return unless sentry_span
68
+
69
+ request_info = extract_request_info(req)
70
+ sentry_span.set_description("#{request_info[:method]} #{request_info[:url]}")
71
+ if res.is_a?(HTTPX::ErrorResponse)
72
+ sentry_span.set_data(:error, res.error.message)
73
+ else
74
+ sentry_span.set_data(:status, res.status)
75
+ end
76
+ sentry_span.set_timestamp(::Sentry.utc_now.to_f)
77
+ end
78
+
79
+ def extract_request_info(req)
80
+ uri = req.uri
81
+
82
+ result = {
83
+ method: req.verb,
84
+ }
85
+
86
+ if ::Sentry.configuration.send_default_pii
87
+ uri += "?#{req.query}" unless req.query.empty?
88
+ result[:body] = req.body.to_s unless req.body.empty? || req.body.unbounded_body?
89
+ end
90
+
91
+ result[:url] = uri.to_s
92
+
93
+ result
94
+ end
95
+ end
96
+
97
+ module RequestMethods
98
+ def __sentry_enable_trace!
99
+ return if @__sentry_enable_trace
100
+
101
+ Tracer.call(self)
102
+ @__sentry_enable_trace = true
103
+ end
104
+ end
105
+
106
+ module ConnectionMethods
107
+ def send(request)
108
+ request.__sentry_enable_trace!
109
+
110
+ super
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ Sentry.register_patch(:httpx) do
117
+ sentry_session = HTTPX.plugin(HTTPX::Plugins::Sentry)
118
+
119
+ HTTPX.send(:remove_const, :Session)
120
+ HTTPX.send(:const_set, :Session, sentry_session.class)
121
+ end
@@ -0,0 +1,175 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebMock
4
+ module HttpLibAdapters
5
+ require "net/http/status"
6
+ HTTP_REASONS = Net::HTTP::STATUS_CODES
7
+
8
+ #
9
+ # HTTPX plugin for webmock.
10
+ #
11
+ # Requests are "hijacked" at the session, before they're distributed to a connection.
12
+ #
13
+ module Plugin
14
+ class << self
15
+ def build_webmock_request_signature(request)
16
+ uri = WebMock::Util::URI.heuristic_parse(request.uri)
17
+ uri.query = request.query
18
+ uri.path = uri.normalized_path.gsub("[^:]//", "/")
19
+
20
+ WebMock::RequestSignature.new(
21
+ request.verb.downcase.to_sym,
22
+ uri.to_s,
23
+ body: request.body.to_s,
24
+ headers: request.headers.to_h
25
+ )
26
+ end
27
+
28
+ def build_webmock_response(_request, response)
29
+ webmock_response = WebMock::Response.new
30
+ webmock_response.status = [response.status, HTTP_REASONS[response.status]]
31
+ webmock_response.body = response.body.to_s
32
+ webmock_response.headers = response.headers.to_h
33
+ webmock_response
34
+ end
35
+
36
+ def build_from_webmock_response(request, webmock_response)
37
+ return build_error_response(request, HTTPX::TimeoutError.new(1, "Timed out")) if webmock_response.should_timeout
38
+
39
+ return build_error_response(request, webmock_response.exception) if webmock_response.exception
40
+
41
+ request.options.response_class.new(request,
42
+ webmock_response.status[0],
43
+ "2.0",
44
+ webmock_response.headers).tap do |res|
45
+ res.mocked = true
46
+ end
47
+ end
48
+
49
+ def build_error_response(request, exception)
50
+ HTTPX::ErrorResponse.new(request, exception)
51
+ end
52
+ end
53
+
54
+ module InstanceMethods
55
+ private
56
+
57
+ def do_init_connection(connection, selector)
58
+ super
59
+
60
+ connection.once(:unmock_connection) do
61
+ next unless connection.current_session == self
62
+
63
+ unless connection.addresses?
64
+ # reset Happy Eyeballs, fail early
65
+ connection.sibling = nil
66
+
67
+ deselect_connection(connection, selector)
68
+ end
69
+ resolve_connection(connection, selector)
70
+ end
71
+ end
72
+ end
73
+
74
+ module ResponseMethods
75
+ attr_accessor :mocked
76
+
77
+ def initialize(*)
78
+ super
79
+ @mocked = false
80
+ end
81
+ end
82
+
83
+ module ResponseBodyMethods
84
+ def decode_chunk(chunk)
85
+ return chunk if @response.mocked
86
+
87
+ super
88
+ end
89
+ end
90
+
91
+ module ConnectionMethods
92
+ def initialize(*)
93
+ super
94
+ @mocked = true
95
+ end
96
+
97
+ def open?
98
+ return true if @mocked
99
+
100
+ super
101
+ end
102
+
103
+ def interests
104
+ return if @mocked
105
+
106
+ super
107
+ end
108
+
109
+ def terminate
110
+ force_reset
111
+ end
112
+
113
+ def send(request)
114
+ request_signature = Plugin.build_webmock_request_signature(request)
115
+ WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
116
+
117
+ if (mock_response = WebMock::StubRegistry.instance.response_for_request(request_signature))
118
+ response = Plugin.build_from_webmock_response(request, mock_response)
119
+ WebMock::CallbackRegistry.invoke_callbacks({ lib: :httpx }, request_signature, mock_response)
120
+ log { "mocking #{request.uri} with #{mock_response.inspect}" }
121
+ request.transition(:headers)
122
+ request.transition(:body)
123
+ request.transition(:trailers)
124
+ request.transition(:done)
125
+ response.finish!
126
+ request.response = response
127
+ request.emit(:response, response)
128
+ request_signature.headers = request.headers.to_h
129
+
130
+ response << mock_response.body.dup unless response.is_a?(HTTPX::ErrorResponse)
131
+ elsif WebMock.net_connect_allowed?(request_signature.uri)
132
+ if WebMock::CallbackRegistry.any_callbacks?
133
+ request.on(:response) do |resp|
134
+ unless resp.is_a?(HTTPX::ErrorResponse)
135
+ webmock_response = Plugin.build_webmock_response(request, resp)
136
+ WebMock::CallbackRegistry.invoke_callbacks(
137
+ { lib: :httpx, real_request: true }, request_signature,
138
+ webmock_response
139
+ )
140
+ end
141
+ end
142
+ end
143
+ @mocked = false
144
+ emit(:unmock_connection, self)
145
+ super
146
+ else
147
+ raise WebMock::NetConnectNotAllowedError, request_signature
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ class HttpxAdapter < HttpLibAdapter
154
+ adapter_for :httpx
155
+
156
+ class << self
157
+ def enable!
158
+ @original_session ||= HTTPX::Session
159
+
160
+ webmock_session = HTTPX.plugin(Plugin)
161
+
162
+ HTTPX.send(:remove_const, :Session)
163
+ HTTPX.send(:const_set, :Session, webmock_session.class)
164
+ end
165
+
166
+ def disable!
167
+ return unless @original_session
168
+
169
+ HTTPX.send(:remove_const, :Session)
170
+ HTTPX.send(:const_set, :Session, @original_session)
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "strscan"
4
+
5
+ module HTTPX
6
+ module AltSvc
7
+ # makes connections able to accept requests destined to primary service.
8
+ module ConnectionMixin
9
+ using URIExtensions
10
+
11
+ def send(request)
12
+ request.headers["alt-used"] = @origin.authority if @parser && !@write_buffer.full? && match_altsvcs?(request.uri)
13
+
14
+ super
15
+ end
16
+
17
+ def match?(uri, options)
18
+ return false if !used? && (@state == :closing || @state == :closed)
19
+
20
+ match_altsvcs?(uri) && match_altsvc_options?(uri, options)
21
+ end
22
+
23
+ private
24
+
25
+ # checks if this is connection is an alternative service of
26
+ # +uri+
27
+ def match_altsvcs?(uri)
28
+ @origins.any? { |origin| altsvc_match?(uri, origin) } ||
29
+ AltSvc.cached_altsvc(@origin).any? do |altsvc|
30
+ origin = altsvc["origin"]
31
+ altsvc_match?(origin, uri.origin)
32
+ end
33
+ end
34
+
35
+ def match_altsvc_options?(uri, options)
36
+ return @options == options unless @options.ssl.all? do |k, v|
37
+ v == (k == :hostname ? uri.host : options.ssl[k])
38
+ end
39
+
40
+ @options.options_equals?(options, Options::REQUEST_BODY_IVARS + %i[@ssl])
41
+ end
42
+
43
+ def altsvc_match?(uri, other_uri)
44
+ other_uri = URI(other_uri)
45
+
46
+ uri.origin == other_uri.origin || begin
47
+ case uri.scheme
48
+ when "h2"
49
+ (other_uri.scheme == "https" || other_uri.scheme == "h2") &&
50
+ uri.host == other_uri.host &&
51
+ uri.port == other_uri.port
52
+ else
53
+ false
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ @altsvc_mutex = Thread::Mutex.new
60
+ @altsvcs = Hash.new { |h, k| h[k] = [] }
61
+
62
+ module_function
63
+
64
+ def cached_altsvc(origin)
65
+ now = Utils.now
66
+ @altsvc_mutex.synchronize do
67
+ lookup(origin, now)
68
+ end
69
+ end
70
+
71
+ def cached_altsvc_set(origin, entry)
72
+ now = Utils.now
73
+ @altsvc_mutex.synchronize do
74
+ return if @altsvcs[origin].any? { |altsvc| altsvc["origin"] == entry["origin"] }
75
+
76
+ entry["TTL"] = Integer(entry["ma"]) + now if entry.key?("ma")
77
+ @altsvcs[origin] << entry
78
+ entry
79
+ end
80
+ end
81
+
82
+ def lookup(origin, ttl)
83
+ return [] unless @altsvcs.key?(origin)
84
+
85
+ @altsvcs[origin] = @altsvcs[origin].select do |entry|
86
+ !entry.key?("TTL") || entry["TTL"] > ttl
87
+ end
88
+ @altsvcs[origin].reject { |entry| entry["noop"] }
89
+ end
90
+
91
+ def emit(request, response)
92
+ return unless response.respond_to?(:headers)
93
+ # Alt-Svc
94
+ return unless response.headers.key?("alt-svc")
95
+
96
+ origin = request.origin
97
+ host = request.uri.host
98
+
99
+ altsvc = response.headers["alt-svc"]
100
+
101
+ # https://datatracker.ietf.org/doc/html/rfc7838#section-3
102
+ # A field value containing the special value "clear" indicates that the
103
+ # origin requests all alternatives for that origin to be invalidated
104
+ # (including those specified in the same response, in case of an
105
+ # invalid reply containing both "clear" and alternative services).
106
+ if altsvc == "clear"
107
+ @altsvc_mutex.synchronize do
108
+ @altsvcs[origin].clear
109
+ end
110
+
111
+ return
112
+ end
113
+
114
+ parse(altsvc) do |alt_origin, alt_params|
115
+ alt_origin.host ||= host
116
+ yield(alt_origin, origin, alt_params)
117
+ end
118
+ end
119
+
120
+ def parse(altsvc)
121
+ return enum_for(__method__, altsvc) unless block_given?
122
+
123
+ scanner = StringScanner.new(altsvc)
124
+ until scanner.eos?
125
+ alt_service = scanner.scan(/[^=]+=("[^"]+"|[^;,]+)/)
126
+
127
+ alt_params = []
128
+ loop do
129
+ alt_param = scanner.scan(/[^=]+=("[^"]+"|[^;,]+)/)
130
+ alt_params << alt_param.strip if alt_param
131
+ scanner.skip(/;/)
132
+ break if scanner.eos? || scanner.scan(/ *, */)
133
+ end
134
+ alt_params = Hash[alt_params.map { |field| field.split("=", 2) }]
135
+
136
+ alt_proto, alt_authority = alt_service.split("=", 2)
137
+ alt_origin = parse_altsvc_origin(alt_proto, alt_authority)
138
+ return unless alt_origin
139
+
140
+ yield(alt_origin, alt_params.merge("proto" => alt_proto))
141
+ end
142
+ end
143
+
144
+ def parse_altsvc_scheme(alt_proto)
145
+ case alt_proto
146
+ when "h2c"
147
+ "http"
148
+ when "h2"
149
+ "https"
150
+ end
151
+ end
152
+
153
+ def parse_altsvc_origin(alt_proto, alt_origin)
154
+ alt_scheme = parse_altsvc_scheme(alt_proto)
155
+
156
+ return unless alt_scheme
157
+
158
+ alt_origin = alt_origin[1..-2] if alt_origin.start_with?("\"") && alt_origin.end_with?("\"")
159
+
160
+ URI.parse("#{alt_scheme}://#{alt_origin}")
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ if RUBY_VERSION < "3.3.0"
4
+ require "base64"
5
+ elsif !defined?(Base64)
6
+ module HTTPX
7
+ # require "base64" will not be a default gem after ruby 3.4.0
8
+ module Base64
9
+ module_function
10
+
11
+ def decode64(str)
12
+ str.unpack1("m")
13
+ end
14
+
15
+ def strict_encode64(bin)
16
+ [bin].pack("m0")
17
+ end
18
+
19
+ def urlsafe_encode64(bin, padding: true)
20
+ str = strict_encode64(bin)
21
+ str.chomp!("==") or str.chomp!("=") unless padding
22
+ str.tr!("+/", "-_")
23
+ str
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module HTTPX
6
+ # Internal class to abstract a string buffer, by wrapping a string and providing the
7
+ # minimum possible API and functionality required.
8
+ #
9
+ # buffer = Buffer.new(640)
10
+ # buffer.full? #=> false
11
+ # buffer << "aa"
12
+ # buffer.capacity #=> 638
13
+ #
14
+ class Buffer
15
+ extend Forwardable
16
+
17
+ def_delegator :@buffer, :to_s
18
+
19
+ def_delegator :@buffer, :to_str
20
+
21
+ def_delegator :@buffer, :empty?
22
+
23
+ def_delegator :@buffer, :bytesize
24
+
25
+ def_delegator :@buffer, :clear
26
+
27
+ def_delegator :@buffer, :replace
28
+
29
+ attr_reader :limit
30
+
31
+ if RUBY_VERSION >= "3.4.0"
32
+ def initialize(limit)
33
+ @buffer = String.new("", encoding: Encoding::BINARY, capacity: limit)
34
+ @limit = limit
35
+ end
36
+
37
+ def <<(chunk)
38
+ @buffer.append_as_bytes(chunk)
39
+ end
40
+ else
41
+ def initialize(limit)
42
+ @buffer = "".b
43
+ @limit = limit
44
+ end
45
+
46
+ def_delegator :@buffer, :<<
47
+ end
48
+
49
+ def full?
50
+ @buffer.bytesize >= @limit
51
+ end
52
+
53
+ def capacity
54
+ @limit - @buffer.bytesize
55
+ end
56
+
57
+ def shift!(fin)
58
+ @buffer = @buffer.byteslice(fin..-1) || "".b
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Callbacks
5
+ def on(type, &action)
6
+ callbacks(type) << action
7
+ action
8
+ end
9
+
10
+ def once(type, &block)
11
+ on(type) do |*args, &callback|
12
+ block.call(*args, &callback)
13
+ :delete
14
+ end
15
+ end
16
+
17
+ def emit(type, *args)
18
+ log { "emit #{type.inspect} callbacks" } if respond_to?(:log)
19
+ callbacks(type).delete_if { |pr| :delete == pr.call(*args) } # rubocop:disable Style/YodaCondition
20
+ end
21
+
22
+ def callbacks_for?(type)
23
+ @callbacks && @callbacks.key?(type) && @callbacks[type].any?
24
+ end
25
+
26
+ protected
27
+
28
+ def callbacks(type = nil)
29
+ return @callbacks unless type
30
+
31
+ @callbacks ||= Hash.new { |h, k| h[k] = [] }
32
+ @callbacks[type]
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ # Session mixin, implements most of the APIs that the users call.
5
+ # delegates to a default session when extended.
6
+ module Chainable
7
+ %w[head get post put delete trace options connect patch].each do |meth|
8
+ class_eval(<<-MOD, __FILE__, __LINE__ + 1)
9
+ def #{meth}(*uri, **options) # def get(*uri, **options)
10
+ request("#{meth.upcase}", uri, **options) # request("GET", uri, **options)
11
+ end # end
12
+ MOD
13
+ end
14
+
15
+ # delegates to the default session (see HTTPX::Session#request).
16
+ def request(*args, **options)
17
+ branch(default_options).request(*args, **options)
18
+ end
19
+
20
+ def accept(type)
21
+ with(headers: { "accept" => String(type) })
22
+ end
23
+
24
+ # delegates to the default session (see HTTPX::Session#wrap).
25
+ def wrap(&blk)
26
+ branch(default_options).wrap(&blk)
27
+ end
28
+
29
+ # returns a new instance loaded with the +pl+ plugin and +options+.
30
+ def plugin(pl, options = nil, &blk)
31
+ klass = is_a?(S) ? self.class : Session
32
+ klass = Class.new(klass)
33
+ klass.instance_variable_set(:@default_options, klass.default_options.merge(default_options))
34
+ klass.plugin(pl, options, &blk).new
35
+ end
36
+
37
+ # returns a new instance loaded with +options+.
38
+ def with(options, &blk)
39
+ branch(default_options.merge(options), &blk)
40
+ end
41
+
42
+ private
43
+
44
+ # returns default instance of HTTPX::Options.
45
+ def default_options
46
+ @options || Session.default_options
47
+ end
48
+
49
+ # returns a default instance of HTTPX::Session.
50
+ def branch(options, &blk)
51
+ return self.class.new(options, &blk) if is_a?(S)
52
+
53
+ Session.new(options, &blk)
54
+ end
55
+
56
+ def method_missing(meth, *args, **options, &blk)
57
+ case meth
58
+ when /\Awith_(.+)/
59
+
60
+ option = Regexp.last_match(1)
61
+
62
+ return super unless option
63
+
64
+ with(option.to_sym => args.first || options)
65
+ when /\Aon_(.+)/
66
+ callback = Regexp.last_match(1)
67
+
68
+ return super unless %w[
69
+ connection_opened connection_closed
70
+ request_error
71
+ request_started request_body_chunk request_completed
72
+ response_started response_body_chunk response_completed
73
+ ].include?(callback)
74
+
75
+ warn "DEPRECATION WARNING: calling `.#{meth}` on plain HTTPX sessions is deprecated. " \
76
+ "Use `HTTPX.plugin(:callbacks).#{meth}` instead."
77
+
78
+ plugin(:callbacks).__send__(meth, *args, **options, &blk)
79
+ else
80
+ super
81
+ end
82
+ end
83
+
84
+ def respond_to_missing?(meth, *)
85
+ case meth
86
+ when /\Awith_(.+)/
87
+ option = Regexp.last_match(1)
88
+
89
+ default_options.respond_to?(option) || super
90
+ when /\Aon_(.+)/
91
+ callback = Regexp.last_match(1)
92
+
93
+ %w[
94
+ connection_opened connection_closed
95
+ request_error
96
+ request_started request_body_chunk request_completed
97
+ response_started response_body_chunk response_completed
98
+ ].include?(callback) || super
99
+ else
100
+ super
101
+ end
102
+ end
103
+ end
104
+
105
+ extend Chainable
106
+ end