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,239 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "resolv"
4
+
5
+ module HTTPX
6
+ class TCP
7
+ include Loggable
8
+
9
+ using URIExtensions
10
+
11
+ attr_reader :ip, :port, :addresses, :state, :interests
12
+
13
+ alias_method :host, :ip
14
+
15
+ def initialize(origin, addresses, options)
16
+ @state = :idle
17
+ @keep_open = false
18
+ @addresses = []
19
+ @ip_index = -1
20
+ @ip = nil
21
+ @hostname = origin.host
22
+ @options = options
23
+ @fallback_protocol = @options.fallback_protocol
24
+ @port = origin.port
25
+ @interests = :w
26
+ if @options.io
27
+ @io = case @options.io
28
+ when Hash
29
+ @options.io[origin.authority]
30
+ else
31
+ @options.io
32
+ end
33
+ raise Error, "Given IO objects do not match the request authority" unless @io
34
+
35
+ _, _, _, ip = @io.addr
36
+ @ip = Resolver::Entry.new(ip)
37
+ @addresses << @ip
38
+ @keep_open = true
39
+ @state = :connected
40
+ else
41
+ add_addresses(addresses)
42
+ end
43
+ @ip_index = @addresses.size - 1
44
+ end
45
+
46
+ def socket
47
+ @io
48
+ end
49
+
50
+ def add_addresses(addrs)
51
+ return if addrs.empty?
52
+
53
+ ip_index = @ip_index || (@addresses.size - 1)
54
+ if addrs.first.ipv6?
55
+ # should be the next in line
56
+ @addresses = [*@addresses[0, ip_index], *addrs, *@addresses[ip_index..-1]]
57
+ else
58
+ @addresses.unshift(*addrs)
59
+ end
60
+ @ip_index += addrs.size
61
+ end
62
+
63
+ # eliminates expired entries and returns whether there are still any left.
64
+ def addresses?
65
+ prev_addr_size = @addresses.size
66
+
67
+ @addresses.delete_if(&:expired?).sort! do |addr1, addr2|
68
+ if addr1.ipv6?
69
+ addr2.ipv6? ? 0 : 1
70
+ else
71
+ addr2.ipv6? ? -1 : 0
72
+ end
73
+ end
74
+
75
+ @ip_index = @addresses.size - 1 if prev_addr_size != @addresses.size
76
+
77
+ @addresses.any?
78
+ end
79
+
80
+ def to_io
81
+ @io.to_io
82
+ end
83
+
84
+ def protocol
85
+ @fallback_protocol
86
+ end
87
+
88
+ def connect
89
+ return unless closed?
90
+
91
+ if @addresses.empty?
92
+ # an idle connection trying to connect with no available addresses is a connection
93
+ # out of the initial context which is back to the DNS resolution loop. This may
94
+ # happen in a fiber-aware context where a connection reconnects with expired addresses,
95
+ # and context is passed back to a fiber on the same connection while waiting for the
96
+ # DNS answer.
97
+ log { "tried connecting while resolving, skipping..." }
98
+
99
+ return
100
+ end
101
+
102
+ if !@io || @io.closed?
103
+ transition(:idle)
104
+ @io = build_socket
105
+ end
106
+ try_connect
107
+ rescue Errno::EHOSTUNREACH,
108
+ Errno::ENETUNREACH => e
109
+ @ip_index -= 1
110
+
111
+ raise e if @ip_index.negative?
112
+
113
+ log { "failed connecting to #{@ip} (#{e.message}), evict from cache and trying next..." }
114
+ Resolver.cached_lookup_evict(@hostname, @ip)
115
+
116
+ @io = build_socket
117
+ retry
118
+ rescue Errno::ECONNREFUSED,
119
+ Errno::EADDRNOTAVAIL,
120
+ SocketError,
121
+ IOError => e
122
+ @ip_index -= 1
123
+
124
+ raise e if @ip_index.negative?
125
+
126
+ log { "failed connecting to #{@ip} (#{e.message}), trying next..." }
127
+ @io = build_socket
128
+ retry
129
+ rescue Errno::ETIMEDOUT => e
130
+ @ip_index -= 1
131
+
132
+ raise ConnectTimeoutError.new(@options.timeout[:connect_timeout], e.message) if @ip_index.negative?
133
+
134
+ log { "failed connecting to #{@ip} (#{e.message}), trying next..." }
135
+
136
+ @io = build_socket
137
+ retry
138
+ end
139
+
140
+ def try_connect
141
+ ret = @io.connect_nonblock(Socket.sockaddr_in(@port, @ip.to_s), exception: false)
142
+ log(level: 3, color: :cyan) { "TCP CONNECT: #{ret}..." }
143
+ case ret
144
+ when :wait_readable
145
+ @interests = :r
146
+ return
147
+ when :wait_writable
148
+ @interests = :w
149
+ return
150
+ end
151
+ transition(:connected)
152
+ @interests = :w
153
+ rescue Errno::EALREADY
154
+ @interests = :w
155
+ end
156
+ private :try_connect
157
+
158
+ def read(size, buffer)
159
+ ret = @io.read_nonblock(size, buffer, exception: false)
160
+ if ret == :wait_readable
161
+ buffer.clear
162
+ return 0
163
+ end
164
+ return if ret.nil?
165
+
166
+ log { "READ: #{buffer.bytesize} bytes..." }
167
+ buffer.bytesize
168
+ end
169
+
170
+ def write(buffer)
171
+ siz = @io.write_nonblock(buffer, exception: false)
172
+ return 0 if siz == :wait_writable
173
+ return if siz.nil?
174
+
175
+ log { "WRITE: #{siz} bytes..." }
176
+
177
+ buffer.shift!(siz)
178
+ siz
179
+ end
180
+
181
+ def close
182
+ return if @keep_open || closed?
183
+
184
+ begin
185
+ @io.close
186
+ ensure
187
+ transition(:closed)
188
+ end
189
+ end
190
+
191
+ def connected?
192
+ @state == :connected
193
+ end
194
+
195
+ def closed?
196
+ @state == :idle || @state == :closed
197
+ end
198
+
199
+ # :nocov:
200
+ def inspect
201
+ "#<#{self.class}:#{object_id} " \
202
+ "#{@ip}:#{@port} " \
203
+ "@state=#{@state} " \
204
+ "@hostname=#{@hostname} " \
205
+ "@addresses=#{@addresses} " \
206
+ "@state=#{@state}>"
207
+ end
208
+ # :nocov:
209
+
210
+ private
211
+
212
+ def build_socket
213
+ @ip = @addresses[@ip_index]
214
+ Socket.new(@ip.family, :STREAM, 0)
215
+ end
216
+
217
+ def transition(nextstate)
218
+ case nextstate
219
+ # when :idle
220
+ when :connected
221
+ return unless @state == :idle
222
+ when :closed
223
+ return unless @state == :connected
224
+ end
225
+ do_transition(nextstate)
226
+ end
227
+
228
+ def do_transition(nextstate)
229
+ log(level: 1) { log_transition_state(nextstate) }
230
+ @state = nextstate
231
+ end
232
+
233
+ def log_transition_state(nextstate)
234
+ label = host
235
+ label = "#{label}(##{@io.fileno})" if nextstate == :connected
236
+ "#{label} #{@state} -> #{nextstate}"
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ipaddr"
4
+
5
+ module HTTPX
6
+ class UDP
7
+ include Loggable
8
+
9
+ def initialize(ip, port, options)
10
+ @host = ip
11
+ @port = port
12
+ @io = UDPSocket.new(IPAddr.new(ip).family)
13
+ @options = options
14
+ end
15
+
16
+ def to_io
17
+ @io.to_io
18
+ end
19
+
20
+ def connect; end
21
+
22
+ def connected?
23
+ true
24
+ end
25
+
26
+ def close
27
+ @io.close
28
+ end
29
+
30
+ if RUBY_ENGINE == "jruby"
31
+ # In JRuby, sendmsg_nonblock is not implemented
32
+ def write(buffer)
33
+ siz = @io.send(buffer.to_s, 0, @host, @port)
34
+ log { "WRITE: #{siz} bytes..." }
35
+ buffer.shift!(siz)
36
+ siz
37
+ end
38
+ else
39
+ def write(buffer)
40
+ siz = @io.sendmsg_nonblock(buffer.to_s, 0, Socket.sockaddr_in(@port, @host.to_s), exception: false)
41
+ return 0 if siz == :wait_writable
42
+ return if siz.nil?
43
+
44
+ log { "WRITE: #{siz} bytes..." }
45
+
46
+ buffer.shift!(siz)
47
+ siz
48
+ end
49
+ end
50
+
51
+ def read(size, buffer)
52
+ ret = @io.recvfrom_nonblock(size, 0, buffer, exception: false)
53
+ return 0 if ret == :wait_readable
54
+ return if ret.nil?
55
+
56
+ log { "READ: #{buffer.bytesize} bytes..." }
57
+
58
+ buffer.bytesize
59
+ rescue IOError
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ class UNIX < TCP
5
+ using URIExtensions
6
+
7
+ attr_reader :path
8
+
9
+ alias_method :host, :path
10
+
11
+ def initialize(origin, path, options)
12
+ @addresses = []
13
+ @hostname = origin.host
14
+ @state = :idle
15
+ @options = options
16
+ @fallback_protocol = @options.fallback_protocol
17
+ if @options.io
18
+ @io = case @options.io
19
+ when Hash
20
+ @options.io[origin.authority]
21
+ else
22
+ @options.io
23
+ end
24
+ raise Error, "Given IO objects do not match the request authority" unless @io
25
+
26
+ @path = @io.path
27
+ @keep_open = true
28
+ @state = :connected
29
+ elsif path
30
+ @path = path
31
+ else
32
+ raise Error, "No path given where to store the socket"
33
+ end
34
+ @io ||= build_socket
35
+ end
36
+
37
+ def connect
38
+ return unless closed?
39
+
40
+ begin
41
+ if @io.closed?
42
+ transition(:idle)
43
+ @io = build_socket
44
+ end
45
+ @io.connect_nonblock(Socket.sockaddr_un(@path))
46
+ rescue Errno::EISCONN
47
+ end
48
+ transition(:connected)
49
+ rescue Errno::EINPROGRESS,
50
+ Errno::EALREADY,
51
+ IO::WaitReadable
52
+ end
53
+
54
+ # the path is always explicitly passed, so no point in resolving.
55
+ def addresses?
56
+ true
57
+ end
58
+
59
+ # :nocov:
60
+ def inspect
61
+ "#<#{self.class}:#{object_id} @path=#{@path}) @state=#{@state})>"
62
+ end
63
+ # :nocov:
64
+
65
+ private
66
+
67
+ def build_socket
68
+ Socket.new(Socket::PF_UNIX, :STREAM, 0)
69
+ end
70
+ end
71
+ end
data/lib/httpx/io.rb ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "socket"
4
+ require "httpx/io/udp"
5
+ require "httpx/io/tcp"
6
+ require "httpx/io/unix"
7
+
8
+ begin
9
+ require "httpx/io/ssl"
10
+ rescue LoadError
11
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Loggable
5
+ COLORS = {
6
+ black: 30,
7
+ red: 31,
8
+ green: 32,
9
+ yellow: 33,
10
+ blue: 34,
11
+ magenta: 35,
12
+ cyan: 36,
13
+ white: 37,
14
+ }.freeze
15
+
16
+ USE_DEBUG_LOG = ENV.key?("HTTPX_DEBUG")
17
+
18
+ def log(
19
+ level: @options.debug_level,
20
+ color: nil,
21
+ debug_level: @options.debug_level,
22
+ debug: @options.debug,
23
+ &msg
24
+ )
25
+ return unless debug_level >= level
26
+
27
+ debug_stream = debug || ($stderr if USE_DEBUG_LOG)
28
+
29
+ return unless debug_stream
30
+
31
+ klass = self.class
32
+
33
+ until (class_name = klass.name)
34
+ klass = klass.superclass
35
+ end
36
+
37
+ message = +"(pid:#{Process.pid}, " \
38
+ "tid:#{Thread.current.object_id}, " \
39
+ "fid:#{Fiber.current.object_id}, " \
40
+ "self:#{class_name}##{object_id}) "
41
+ message << msg.call << "\n"
42
+ message = "\e[#{COLORS[color]}m#{message}\e[0m" if color && debug_stream.respond_to?(:isatty) && debug_stream.isatty
43
+ debug_stream << message
44
+ end
45
+
46
+ def log_exception(ex, level: @options.debug_level, color: nil, debug_level: @options.debug_level, debug: @options.debug)
47
+ log(level: level, color: color, debug_level: debug_level, debug: debug) { ex.full_message }
48
+ end
49
+
50
+ def log_redact(text, should_redact = @options.debug_redact)
51
+ return text.to_s unless should_redact
52
+
53
+ "[REDACTED]"
54
+ end
55
+ end
56
+ end