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,399 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "httpx/parser/http1"
4
+
5
+ module HTTPX
6
+ class Connection::HTTP1
7
+ include Callbacks
8
+ include Loggable
9
+
10
+ MAX_REQUESTS = 200
11
+ CRLF = "\r\n"
12
+
13
+ attr_reader :pending, :requests
14
+
15
+ attr_accessor :max_concurrent_requests
16
+
17
+ def initialize(buffer, options)
18
+ @options = options
19
+ @max_concurrent_requests = @options.max_concurrent_requests || MAX_REQUESTS
20
+ @max_requests = @options.max_requests
21
+ @parser = Parser::HTTP1.new(self)
22
+ @buffer = buffer
23
+ @version = [1, 1]
24
+ @pending = []
25
+ @requests = []
26
+ @handshake_completed = false
27
+ end
28
+
29
+ def timeout
30
+ @options.timeout[:operation_timeout]
31
+ end
32
+
33
+ def interests
34
+ request = @request || @requests.first
35
+
36
+ return unless request
37
+
38
+ return :w if request.interests == :w || !@buffer.empty?
39
+
40
+ :r
41
+ end
42
+
43
+ def reset
44
+ @max_requests = @options.max_requests || MAX_REQUESTS
45
+ @parser.reset!
46
+ @handshake_completed = false
47
+ @pending.concat(@requests) unless @requests.empty?
48
+ end
49
+
50
+ def close
51
+ reset
52
+ emit(:close, true)
53
+ end
54
+
55
+ def exhausted?
56
+ !@max_requests.positive?
57
+ end
58
+
59
+ def empty?
60
+ # this means that for every request there's an available
61
+ # partial response, so there are no in-flight requests waiting.
62
+ @requests.empty? || (
63
+ # checking all responses can be time-consuming. Alas, as in HTTP/1, responses
64
+ # do not come out of order, we can get away with checking first and last.
65
+ !@requests.first.response.nil? &&
66
+ (@requests.size == 1 || !@requests.last.response.nil?)
67
+ )
68
+ end
69
+
70
+ def <<(data)
71
+ @parser << data
72
+ end
73
+
74
+ def send(request)
75
+ unless @max_requests.positive?
76
+ @pending << request
77
+ return
78
+ end
79
+
80
+ return if @requests.include?(request)
81
+
82
+ @requests << request
83
+ @pipelining = true if @requests.size > 1
84
+ end
85
+
86
+ def consume
87
+ requests_limit = [@max_requests, @requests.size].min
88
+ concurrent_requests_limit = [@max_concurrent_requests, requests_limit].min
89
+ @requests.each_with_index do |request, idx|
90
+ break if idx >= concurrent_requests_limit
91
+ next unless request.can_buffer?
92
+
93
+ handle(request)
94
+ end
95
+ end
96
+
97
+ # HTTP Parser callbacks
98
+ #
99
+ # must be public methods, or else they won't be reachable
100
+
101
+ def on_start
102
+ log(level: 2) { "parsing begins" }
103
+ end
104
+
105
+ def on_headers(h)
106
+ @request = @requests.first
107
+
108
+ return if @request.response
109
+
110
+ log(level: 2) { "headers received" }
111
+ headers = @request.options.headers_class.new(h)
112
+ response = @request.options.response_class.new(@request,
113
+ @parser.status_code,
114
+ @parser.http_version.join("."),
115
+ headers)
116
+ log(color: :yellow) { "-> HEADLINE: #{response.status} HTTP/#{@parser.http_version.join(".")}" }
117
+ log(color: :yellow) { response.headers.each.map { |f, v| "-> HEADER: #{f}: #{log_redact(v)}" }.join("\n") }
118
+
119
+ @request.response = response
120
+ on_complete if response.finished?
121
+ end
122
+
123
+ def on_trailers(h)
124
+ return unless @request
125
+
126
+ response = @request.response
127
+ log(level: 2) { "trailer headers received" }
128
+
129
+ log(color: :yellow) { h.each.map { |f, v| "-> HEADER: #{f}: #{log_redact(v.join(", "))}" }.join("\n") }
130
+ response.merge_headers(h)
131
+ end
132
+
133
+ def on_data(chunk)
134
+ request = @request
135
+
136
+ return unless request
137
+
138
+ log(color: :green) { "-> DATA: #{chunk.bytesize} bytes..." }
139
+ log(level: 2, color: :green) { "-> #{log_redact(chunk.inspect)}" }
140
+ response = request.response
141
+
142
+ response << chunk
143
+ rescue StandardError => e
144
+ error_response = ErrorResponse.new(request, e)
145
+ request.response = error_response
146
+ dispatch
147
+ end
148
+
149
+ def on_complete
150
+ request = @request
151
+
152
+ return unless request
153
+
154
+ log(level: 2) { "parsing complete" }
155
+ dispatch
156
+ end
157
+
158
+ def dispatch
159
+ request = @request
160
+
161
+ if request.expects?
162
+ @parser.reset!
163
+ return handle(request)
164
+ end
165
+
166
+ @request = nil
167
+ @requests.shift
168
+ response = request.response
169
+ emit(:response, request, response)
170
+
171
+ if @parser.upgrade?
172
+ response << @parser.upgrade_data
173
+ throw(:called)
174
+ end
175
+
176
+ @parser.reset!
177
+ @max_requests -= 1
178
+ if response.is_a?(ErrorResponse)
179
+ disable
180
+ else
181
+ manage_connection(request, response)
182
+ end
183
+
184
+ if exhausted?
185
+ @pending.concat(@requests)
186
+ @requests.clear
187
+
188
+ emit(:exhausted)
189
+ else
190
+ send(@pending.shift) unless @pending.empty?
191
+ end
192
+ end
193
+
194
+ def handle_error(ex, request = nil)
195
+ if (ex.is_a?(EOFError) || ex.is_a?(TimeoutError)) && @request && @request.response &&
196
+ !@request.response.headers.key?("content-length") &&
197
+ !@request.response.headers.key?("transfer-encoding")
198
+ # if the response does not contain a content-length header, the server closing the
199
+ # connnection is the indicator of response consumed.
200
+ # https://greenbytes.de/tech/webdav/rfc2616.html#rfc.section.4.4
201
+ catch(:called) { on_complete }
202
+ return
203
+ end
204
+
205
+ if @pipelining
206
+ catch(:called) { disable }
207
+ else
208
+ @requests.each do |req|
209
+ next if request && request == req
210
+
211
+ emit(:error, req, ex)
212
+ end
213
+ @pending.each do |req|
214
+ next if request && request == req
215
+
216
+ emit(:error, req, ex)
217
+ end
218
+ end
219
+ end
220
+
221
+ def ping
222
+ reset
223
+ emit(:reset)
224
+ emit(:exhausted)
225
+ end
226
+
227
+ def waiting_for_ping?
228
+ false
229
+ end
230
+
231
+ private
232
+
233
+ def manage_connection(request, response)
234
+ connection = response.headers["connection"]
235
+ case connection
236
+ when /keep-alive/i
237
+ if @handshake_completed
238
+ if @max_requests.zero?
239
+ @pending.concat(@requests)
240
+ @requests.clear
241
+ emit(:exhausted)
242
+ end
243
+ return
244
+ end
245
+
246
+ keep_alive = response.headers["keep-alive"]
247
+ return unless keep_alive
248
+
249
+ parameters = Hash[keep_alive.split(/ *, */).map do |pair|
250
+ pair.split(/ *= */, 2)
251
+ end]
252
+ @max_requests = parameters["max"].to_i - 1 if parameters.key?("max")
253
+
254
+ if parameters.key?("timeout")
255
+ keep_alive_timeout = parameters["timeout"].to_i
256
+ emit(:timeout, keep_alive_timeout)
257
+ end
258
+ @handshake_completed = true
259
+ when /close/i
260
+ disable
261
+ when nil
262
+ # In HTTP/1.1, it's keep alive by default
263
+ return if response.version == "1.1" && request.headers["connection"] != "close"
264
+
265
+ disable
266
+ end
267
+ end
268
+
269
+ def disable
270
+ disable_pipelining
271
+ reset
272
+ emit(:reset)
273
+ throw(:called)
274
+ end
275
+
276
+ def disable_pipelining
277
+ return if @requests.empty?
278
+ # do not disable pipelining if already set to 1 request at a time
279
+ return if @max_concurrent_requests == 1
280
+
281
+ @requests.each do |r|
282
+ r.transition(:idle)
283
+
284
+ # when we disable pipelining, we still want to try keep-alive.
285
+ # only when keep-alive with one request fails, do we fallback to
286
+ # connection: close.
287
+ r.headers["connection"] = "close" if @max_concurrent_requests == 1
288
+ end
289
+ # server doesn't handle pipelining, and probably
290
+ # doesn't support keep-alive. Fallback to send only
291
+ # 1 keep alive request.
292
+ @max_concurrent_requests = 1
293
+ @pipelining = false
294
+ end
295
+
296
+ def set_protocol_headers(request)
297
+ if !request.headers.key?("content-length") &&
298
+ request.body.bytesize == Float::INFINITY
299
+ request.body.chunk!
300
+ end
301
+
302
+ extra_headers = {}
303
+
304
+ unless request.headers.key?("connection")
305
+ connection_value = if request.persistent?
306
+ # when in a persistent connection, the request can't be at
307
+ # the edge of a renegotiation
308
+ if @requests.index(request) + 1 < @max_requests
309
+ "keep-alive"
310
+ else
311
+ "close"
312
+ end
313
+ else
314
+ # when it's not a persistent connection, it sets "Connection: close" always
315
+ # on the last request of the possible batch (either allowed max requests,
316
+ # or if smaller, the size of the batch itself)
317
+ requests_limit = [@max_requests, @requests.size].min
318
+ if request == @requests[requests_limit - 1]
319
+ "close"
320
+ else
321
+ "keep-alive"
322
+ end
323
+ end
324
+
325
+ extra_headers["connection"] = connection_value
326
+ end
327
+ extra_headers["host"] = request.authority unless request.headers.key?("host")
328
+ extra_headers
329
+ end
330
+
331
+ def handle(request)
332
+ catch(:buffer_full) do
333
+ request.transition(:headers)
334
+ join_headers(request) if request.state == :headers
335
+ request.transition(:body)
336
+ join_body(request) if request.state == :body
337
+ request.transition(:trailers)
338
+ # HTTP/1.1 trailers should only work for chunked encoding
339
+ join_trailers(request) if request.body.chunked? && request.state == :trailers
340
+ request.transition(:done)
341
+ end
342
+ end
343
+
344
+ def join_headline(request)
345
+ "#{request.verb} #{request.path} HTTP/#{@version.join(".")}"
346
+ end
347
+
348
+ def join_headers(request)
349
+ headline = join_headline(request)
350
+ @buffer << headline << CRLF
351
+ log(color: :yellow) { "<- HEADLINE: #{headline.chomp.inspect}" }
352
+ extra_headers = set_protocol_headers(request)
353
+ join_headers2(request.headers.each(extra_headers))
354
+ log { "<- " }
355
+ @buffer << CRLF
356
+ end
357
+
358
+ def join_body(request)
359
+ return if request.body.empty?
360
+
361
+ while (chunk = request.drain_body)
362
+ log(color: :green) { "<- DATA: #{chunk.bytesize} bytes..." }
363
+ log(level: 2, color: :green) { "<- #{log_redact(chunk.inspect)}" }
364
+ @buffer << chunk
365
+ throw(:buffer_full, request) if @buffer.full?
366
+ end
367
+
368
+ return unless (error = request.drain_error)
369
+
370
+ raise error
371
+ end
372
+
373
+ def join_trailers(request)
374
+ return unless request.trailers? && request.callbacks_for?(:trailers)
375
+
376
+ join_headers2(request.trailers)
377
+ log { "<- " }
378
+ @buffer << CRLF
379
+ end
380
+
381
+ def join_headers2(headers)
382
+ headers.each do |field, value|
383
+ field = capitalized(field)
384
+ log(color: :yellow) { "<- HEADER: #{[field, log_redact(value)].join(": ")}" }
385
+ @buffer << "#{field}: #{value}#{CRLF}"
386
+ end
387
+ end
388
+
389
+ UPCASED = {
390
+ "www-authenticate" => "WWW-Authenticate",
391
+ "http2-settings" => "HTTP2-Settings",
392
+ "content-md5" => "Content-MD5",
393
+ }.freeze
394
+
395
+ def capitalized(field)
396
+ UPCASED[field] || field.split("-").map(&:capitalize).join("-")
397
+ end
398
+ end
399
+ end