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,315 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ #
6
+ # This plugin adds support for bidirectional HTTP/2 streams.
7
+ #
8
+ # https://gitlab.com/os85/httpx/wikis/StreamBidi
9
+ #
10
+ # It is required that the request body allows chunk to be buffered, (i.e., responds to +#<<(chunk)+).
11
+ module StreamBidi
12
+ # Extension of the Connection::HTTP2 class, which adds functionality to
13
+ # deal with a request that can't be drained and must be interleaved with
14
+ # the response streams.
15
+ #
16
+ # The streams keeps send DATA frames while there's data; when they're ain't,
17
+ # the stream is kept open; it must be explicitly closed by the end user.
18
+ #
19
+ class HTTP2Bidi < Connection::HTTP2
20
+ def initialize(*)
21
+ super
22
+ @lock = Thread::Mutex.new
23
+ end
24
+
25
+ %i[close empty? exhausted? send <<].each do |lock_meth|
26
+ class_eval(<<-METH, __FILE__, __LINE__ + 1)
27
+ # lock.aware version of +#{lock_meth}+
28
+ def #{lock_meth}(*) # def close(*)
29
+ return super if @lock.owned?
30
+
31
+ # small race condition between
32
+ # checking for ownership and
33
+ # acquiring lock.
34
+ # TODO: fix this at the parser.
35
+ @lock.synchronize { super }
36
+ end
37
+ METH
38
+ end
39
+
40
+ private
41
+
42
+ %i[join_headers join_trailers join_body].each do |lock_meth|
43
+ class_eval(<<-METH, __FILE__, __LINE__ + 1)
44
+ # lock.aware version of +#{lock_meth}+
45
+ private def #{lock_meth}(*) # private def join_headers(*)
46
+ return super if @lock.owned?
47
+
48
+ # small race condition between
49
+ # checking for ownership and
50
+ # acquiring lock.
51
+ # TODO: fix this at the parser.
52
+ @lock.synchronize { super }
53
+ end
54
+ METH
55
+ end
56
+
57
+ def handle_stream(stream, request)
58
+ request.on(:body) do
59
+ next unless request.headers_sent
60
+
61
+ handle(request, stream)
62
+
63
+ emit(:flush_buffer)
64
+ end
65
+ super
66
+ end
67
+
68
+ # when there ain't more chunks, it makes the buffer as full.
69
+ def send_chunk(request, stream, chunk, next_chunk)
70
+ super
71
+
72
+ return if next_chunk
73
+
74
+ request.transition(:waiting_for_chunk)
75
+ throw(:buffer_full)
76
+ end
77
+
78
+ # sets end-stream flag when the request is closed.
79
+ def end_stream?(request, next_chunk)
80
+ request.closed? && next_chunk.nil?
81
+ end
82
+ end
83
+
84
+ # BidiBuffer is a Buffer which can be receive data from threads othr
85
+ # than the thread of the corresponding Connection/Session.
86
+ #
87
+ # It synchronizes access to a secondary internal +@oob_buffer+, which periodically
88
+ # is reconciled to the main internal +@buffer+.
89
+ class BidiBuffer < Buffer
90
+ def initialize(*)
91
+ super
92
+ @parent_thread = Thread.current
93
+ @oob_mutex = Thread::Mutex.new
94
+ @oob_buffer = "".b
95
+ end
96
+
97
+ # buffers the +chunk+ to be sent
98
+ def <<(chunk)
99
+ return super if Thread.current == @parent_thread
100
+
101
+ @oob_mutex.synchronize { @oob_buffer << chunk }
102
+ end
103
+
104
+ # reconciles the main and secondary buffer (which receives data from other threads).
105
+ def rebuffer
106
+ raise Error, "can only rebuffer while waiting on a response" unless Thread.current == @parent_thread
107
+
108
+ @oob_mutex.synchronize do
109
+ @buffer << @oob_buffer
110
+ @oob_buffer.clear
111
+ end
112
+ end
113
+ end
114
+
115
+ # Proxy to wake up the session main loop when one
116
+ # of the connections has buffered data to write. It abides by the HTTPX::_Selectable API,
117
+ # which allows it to be registered in the selector alongside actual HTTP-based
118
+ # HTTPX::Connection objects.
119
+ class Signal
120
+ def initialize
121
+ @closed = false
122
+ @pipe_read, @pipe_write = IO.pipe
123
+ end
124
+
125
+ def state
126
+ @closed ? :closed : :open
127
+ end
128
+
129
+ # noop
130
+ def log(**, &_); end
131
+
132
+ def to_io
133
+ @pipe_read.to_io
134
+ end
135
+
136
+ def wakeup
137
+ return if @closed
138
+
139
+ @pipe_write.write("\0")
140
+ end
141
+
142
+ def call
143
+ return if @closed
144
+
145
+ @pipe_read.readpartial(1)
146
+ end
147
+
148
+ def interests
149
+ return if @closed
150
+
151
+ :r
152
+ end
153
+
154
+ def timeout; end
155
+
156
+ def terminate
157
+ @pipe_write.close
158
+ @pipe_read.close
159
+ @closed = true
160
+ end
161
+
162
+ # noop (the owner connection will take of it)
163
+ def handle_socket_timeout(interval); end
164
+ end
165
+
166
+ class << self
167
+ def load_dependencies(klass)
168
+ klass.plugin(:stream)
169
+ end
170
+
171
+ def extra_options(options)
172
+ options.merge(fallback_protocol: "h2")
173
+ end
174
+ end
175
+
176
+ module InstanceMethods
177
+ def initialize(*)
178
+ @signal = Signal.new
179
+ super
180
+ end
181
+
182
+ def close(selector = Selector.new)
183
+ @signal.terminate
184
+ selector.deregister(@signal)
185
+ super(selector)
186
+ end
187
+
188
+ def select_connection(connection, selector)
189
+ super
190
+ selector.register(@signal)
191
+ connection.signal = @signal
192
+ end
193
+
194
+ def deselect_connection(connection, *)
195
+ super
196
+ connection.signal = nil
197
+ end
198
+ end
199
+
200
+ # Adds synchronization to request operations which may buffer payloads from different
201
+ # threads.
202
+ module RequestMethods
203
+ attr_accessor :headers_sent
204
+
205
+ def initialize(*)
206
+ super
207
+ @headers_sent = false
208
+ @closed = false
209
+ @mutex = Thread::Mutex.new
210
+ end
211
+
212
+ def closed?
213
+ @closed
214
+ end
215
+
216
+ def can_buffer?
217
+ super && @state != :waiting_for_chunk
218
+ end
219
+
220
+ # overrides state management transitions to introduce an intermediate
221
+ # +:waiting_for_chunk+ state, which the request transitions to once payload
222
+ # is buffered.
223
+ def transition(nextstate)
224
+ headers_sent = @headers_sent
225
+
226
+ case nextstate
227
+ when :waiting_for_chunk
228
+ return unless @state == :body
229
+ when :body
230
+ case @state
231
+ when :headers
232
+ headers_sent = true
233
+ when :waiting_for_chunk
234
+ # HACK: to allow super to pass through
235
+ @state = :headers
236
+ end
237
+ end
238
+
239
+ super.tap do
240
+ # delay setting this up until after the first transition to :body
241
+ @headers_sent = headers_sent
242
+ end
243
+ end
244
+
245
+ def <<(chunk)
246
+ @mutex.synchronize do
247
+ if @drainer
248
+ @body.clear if @body.respond_to?(:clear)
249
+ @drainer = nil
250
+ end
251
+ @body << chunk
252
+
253
+ transition(:body)
254
+ end
255
+ end
256
+
257
+ def close
258
+ @mutex.synchronize do
259
+ return if @closed
260
+
261
+ @closed = true
262
+ end
263
+
264
+ # last chunk to send which ends the stream
265
+ self << ""
266
+ end
267
+ end
268
+
269
+ module RequestBodyMethods
270
+ def initialize(*, **)
271
+ super
272
+ @headers.delete("content-length")
273
+ end
274
+
275
+ def empty?
276
+ false
277
+ end
278
+ end
279
+
280
+ # overrides the declaration of +@write_buffer+, which is now a thread-safe buffer
281
+ # responding to the same API.
282
+ module ConnectionMethods
283
+ attr_writer :signal
284
+
285
+ def initialize(*)
286
+ super
287
+ @write_buffer = BidiBuffer.new(@options.buffer_size)
288
+ end
289
+
290
+ # rebuffers the +@write_buffer+ before calculating interests.
291
+ def interests
292
+ @write_buffer.rebuffer
293
+
294
+ super
295
+ end
296
+
297
+ private
298
+
299
+ def parser_type(protocol)
300
+ return HTTP2Bidi if protocol == "h2"
301
+
302
+ super
303
+ end
304
+
305
+ def set_parser_callbacks(parser)
306
+ super
307
+ parser.on(:flush_buffer) do
308
+ @signal.wakeup if @signal
309
+ end
310
+ end
311
+ end
312
+ end
313
+ register_plugin :stream_bidi, StreamBidi
314
+ end
315
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ #
6
+ # This plugin adds support for upgrading an HTTP/1.1 connection to HTTP/2
7
+ # via an Upgrade: h2 response declaration
8
+ #
9
+ # https://gitlab.com/os85/httpx/wikis/Connection-Upgrade#h2
10
+ #
11
+ module H2
12
+ class << self
13
+ def extra_options(options)
14
+ options.merge(upgrade_handlers: options.upgrade_handlers.merge("h2" => self))
15
+ end
16
+
17
+ def call(connection, _request, _response)
18
+ connection.upgrade_to_h2
19
+ end
20
+ end
21
+
22
+ module ConnectionMethods
23
+ using URIExtensions
24
+
25
+ def interests
26
+ return super unless connecting? && @parser
27
+
28
+ connect
29
+
30
+ return @io.interests if connecting?
31
+
32
+ super
33
+ end
34
+
35
+ def upgrade_to_h2
36
+ prev_parser = @parser
37
+
38
+ if prev_parser
39
+ prev_parser.reset
40
+ @inflight -= prev_parser.requests.size
41
+ end
42
+
43
+ @parser = @options.http2_class.new(@write_buffer, @options)
44
+ set_parser_callbacks(@parser)
45
+ @upgrade_protocol = "h2"
46
+
47
+ # what's happening here:
48
+ # a deviation from the state machine is done to perform the actions when a
49
+ # connection is closed, without transitioning, so the connection is kept in the pool.
50
+ # the state is reset to initial, so that the socket reconnect works out of the box,
51
+ # while the parser is already here.
52
+ purge_after_closed
53
+ transition(:idle)
54
+
55
+ prev_parser.requests.each do |req|
56
+ req.transition(:idle)
57
+ send(req)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ register_plugin(:"upgrade/h2", H2)
63
+ end
64
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ #
6
+ # This plugin helps negotiating a new protocol from an HTTP/1.1 connection, via the
7
+ # Upgrade header.
8
+ #
9
+ # https://gitlab.com/os85/httpx/wikis/Upgrade
10
+ #
11
+ module Upgrade
12
+ class << self
13
+ def configure(klass)
14
+ klass.plugin(:"upgrade/h2")
15
+ end
16
+
17
+ def extra_options(options)
18
+ options.merge(upgrade_handlers: {})
19
+ end
20
+ end
21
+
22
+ module OptionsMethods
23
+ private
24
+
25
+ def option_upgrade_handlers(value)
26
+ raise TypeError, ":upgrade_handlers must be a Hash" unless value.is_a?(Hash)
27
+
28
+ value
29
+ end
30
+ end
31
+
32
+ module InstanceMethods
33
+ def fetch_response(request, selector, options)
34
+ response = super
35
+
36
+ if response
37
+ return response unless response.is_a?(Response)
38
+
39
+ return response unless response.headers.key?("upgrade")
40
+
41
+ upgrade_protocol = response.headers["upgrade"].split(/ *, */).first
42
+
43
+ return response unless upgrade_protocol && options.upgrade_handlers.key?(upgrade_protocol)
44
+
45
+ protocol_handler = options.upgrade_handlers[upgrade_protocol]
46
+
47
+ return response unless protocol_handler
48
+
49
+ log { "upgrading to #{upgrade_protocol}..." }
50
+ connection = find_connection(request.uri, selector, options)
51
+
52
+ # do not upgrade already upgraded connections
53
+ return if connection.upgrade_protocol == upgrade_protocol
54
+
55
+ protocol_handler.call(connection, request, response)
56
+
57
+ # keep in the loop if the server is switching, unless
58
+ # the connection has been hijacked, in which case you want
59
+ # to terminante immediately
60
+ return if response.status == 101 && !connection.hijacked
61
+ end
62
+
63
+ response
64
+ end
65
+ end
66
+
67
+ module ConnectionMethods
68
+ attr_reader :upgrade_protocol, :hijacked
69
+
70
+ def initialize(*)
71
+ super
72
+
73
+ @upgrade_protocol = nil
74
+ end
75
+
76
+ def hijack_io
77
+ @hijacked = true
78
+
79
+ # connection is taken away from selector and not given back to the pool.
80
+ @current_session.deselect_connection(self, @current_selector, true)
81
+ end
82
+ end
83
+ end
84
+ register_plugin(:upgrade, Upgrade)
85
+ end
86
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ #
6
+ # This plugin implements convenience methods for performing WEBDAV requests.
7
+ #
8
+ # https://gitlab.com/os85/httpx/wikis/WebDav
9
+ #
10
+ module WebDav
11
+ def self.configure(klass)
12
+ klass.plugin(:xml)
13
+ end
14
+
15
+ module InstanceMethods
16
+ def copy(src, dest)
17
+ request("COPY", src, headers: { "destination" => @options.origin.merge(dest) })
18
+ end
19
+
20
+ def move(src, dest)
21
+ request("MOVE", src, headers: { "destination" => @options.origin.merge(dest) })
22
+ end
23
+
24
+ def lock(path, timeout: nil, &blk)
25
+ headers = {}
26
+ headers["timeout"] = if timeout && timeout.positive?
27
+ "Second-#{timeout}"
28
+ else
29
+ "Infinite, Second-4100000000"
30
+ end
31
+ xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" \
32
+ "<D:lockinfo xmlns:D=\"DAV:\">" \
33
+ "<D:lockscope><D:exclusive/></D:lockscope>" \
34
+ "<D:locktype><D:write/></D:locktype>" \
35
+ "<D:owner>null</D:owner>" \
36
+ "</D:lockinfo>"
37
+ response = request("LOCK", path, headers: headers, xml: xml)
38
+
39
+ return response unless response.is_a?(Response)
40
+
41
+ return response unless blk && response.status == 200
42
+
43
+ lock_token = response.headers["lock-token"]
44
+
45
+ begin
46
+ blk.call(response)
47
+ ensure
48
+ unlock(path, lock_token)
49
+ end
50
+
51
+ response
52
+ end
53
+
54
+ def unlock(path, lock_token)
55
+ request("UNLOCK", path, headers: { "lock-token" => lock_token })
56
+ end
57
+
58
+ def mkcol(dir)
59
+ request("MKCOL", dir)
60
+ end
61
+
62
+ def propfind(path, xml = nil)
63
+ body = case xml
64
+ when :acl
65
+ '<?xml version="1.0" encoding="utf-8" ?><D:propfind xmlns:D="DAV:"><D:prop><D:owner/>' \
66
+ "<D:supported-privilege-set/><D:current-user-privilege-set/><D:acl/></D:prop></D:propfind>"
67
+ when nil
68
+ '<?xml version="1.0" encoding="utf-8"?><DAV:propfind xmlns:DAV="DAV:"><DAV:allprop/></DAV:propfind>'
69
+ else
70
+ xml
71
+ end
72
+
73
+ request("PROPFIND", path, headers: { "depth" => "1" }, xml: body)
74
+ end
75
+
76
+ def proppatch(path, xml)
77
+ body = "<?xml version=\"1.0\"?>" \
78
+ "<D:propertyupdate xmlns:D=\"DAV:\" xmlns:Z=\"http://ns.example.com/standards/z39.50/\">#{xml}</D:propertyupdate>"
79
+ request("PROPPATCH", path, xml: body)
80
+ end
81
+ # %i[ orderpatch acl report search]
82
+ end
83
+ end
84
+ register_plugin(:webdav, WebDav)
85
+ end
86
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ #
6
+ # This plugin supports request XML encoding/response decoding using the nokogiri gem.
7
+ #
8
+ # https://gitlab.com/os85/httpx/wikis/XML
9
+ #
10
+ module XML
11
+ MIME_TYPES = %r{\b(application|text)/(.+\+)?xml\b}.freeze
12
+ module Transcoder
13
+ module_function
14
+
15
+ class Encoder
16
+ def initialize(xml)
17
+ @raw = xml
18
+ end
19
+
20
+ def content_type
21
+ charset = @raw.respond_to?(:encoding) && @raw.encoding ? @raw.encoding.to_s.downcase : "utf-8"
22
+ "application/xml; charset=#{charset}"
23
+ end
24
+
25
+ def bytesize
26
+ @raw.to_s.bytesize
27
+ end
28
+
29
+ def to_s
30
+ @raw.to_s
31
+ end
32
+ end
33
+
34
+ def encode(xml)
35
+ Encoder.new(xml)
36
+ end
37
+
38
+ def decode(response)
39
+ content_type = response.content_type.mime_type
40
+
41
+ raise HTTPX::Error, "invalid form mime type (#{content_type})" unless MIME_TYPES.match?(content_type)
42
+
43
+ Nokogiri::XML.method(:parse)
44
+ end
45
+ end
46
+
47
+ class << self
48
+ def load_dependencies(*)
49
+ require "nokogiri"
50
+ end
51
+ end
52
+
53
+ module ResponseMethods
54
+ # decodes the response payload into a Nokogiri::XML::Node object **if** the payload is valid
55
+ # "application/xml" (requires the "nokogiri" gem).
56
+ def xml
57
+ decode(Transcoder)
58
+ end
59
+ end
60
+
61
+ module RequestBodyClassMethods
62
+ # ..., xml: Nokogiri::XML::Node #=> xml encoder
63
+ def initialize_body(params)
64
+ if (xml = params.delete(:xml))
65
+ # @type var xml: Nokogiri::XML::Node | String
66
+ return Transcoder.encode(xml)
67
+ end
68
+
69
+ super
70
+ end
71
+ end
72
+ end
73
+
74
+ register_plugin(:xml, XML)
75
+ end
76
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module ResponsePatternMatchExtensions
5
+ def deconstruct
6
+ [@status, @headers, @body]
7
+ end
8
+
9
+ def deconstruct_keys(_keys)
10
+ { status: @status, headers: @headers, body: @body }
11
+ end
12
+ end
13
+
14
+ module ErrorResponsePatternMatchExtensions
15
+ def deconstruct
16
+ [@error]
17
+ end
18
+
19
+ def deconstruct_keys(_keys)
20
+ { error: @error }
21
+ end
22
+ end
23
+
24
+ module HeadersPatternMatchExtensions
25
+ def deconstruct
26
+ to_a
27
+ end
28
+ end
29
+
30
+ Headers.include HeadersPatternMatchExtensions
31
+ Response.include ResponsePatternMatchExtensions
32
+ ErrorResponse.include ErrorResponsePatternMatchExtensions
33
+ end