httpx 0.20.0 → 1.3.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 (250) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +0 -48
  3. data/README.md +54 -45
  4. data/doc/release_notes/0_10_0.md +2 -2
  5. data/doc/release_notes/0_11_0.md +3 -5
  6. data/doc/release_notes/0_12_0.md +5 -5
  7. data/doc/release_notes/0_13_0.md +5 -5
  8. data/doc/release_notes/0_14_0.md +2 -2
  9. data/doc/release_notes/0_16_0.md +3 -3
  10. data/doc/release_notes/0_17_0.md +1 -1
  11. data/doc/release_notes/0_18_0.md +4 -4
  12. data/doc/release_notes/0_18_2.md +1 -1
  13. data/doc/release_notes/0_19_0.md +1 -1
  14. data/doc/release_notes/0_19_8.md +1 -1
  15. data/doc/release_notes/0_20_0.md +2 -2
  16. data/doc/release_notes/0_20_1.md +5 -0
  17. data/doc/release_notes/0_20_2.md +7 -0
  18. data/doc/release_notes/0_20_3.md +6 -0
  19. data/doc/release_notes/0_20_4.md +17 -0
  20. data/doc/release_notes/0_20_5.md +3 -0
  21. data/doc/release_notes/0_21_0.md +96 -0
  22. data/doc/release_notes/0_21_1.md +12 -0
  23. data/doc/release_notes/0_22_0.md +13 -0
  24. data/doc/release_notes/0_22_1.md +11 -0
  25. data/doc/release_notes/0_22_2.md +5 -0
  26. data/doc/release_notes/0_22_3.md +55 -0
  27. data/doc/release_notes/0_22_4.md +6 -0
  28. data/doc/release_notes/0_22_5.md +6 -0
  29. data/doc/release_notes/0_23_0.md +42 -0
  30. data/doc/release_notes/0_23_1.md +5 -0
  31. data/doc/release_notes/0_23_2.md +5 -0
  32. data/doc/release_notes/0_23_3.md +6 -0
  33. data/doc/release_notes/0_23_4.md +5 -0
  34. data/doc/release_notes/0_24_0.md +48 -0
  35. data/doc/release_notes/0_24_1.md +12 -0
  36. data/doc/release_notes/0_24_2.md +12 -0
  37. data/doc/release_notes/0_24_3.md +12 -0
  38. data/doc/release_notes/0_24_4.md +18 -0
  39. data/doc/release_notes/0_24_5.md +6 -0
  40. data/doc/release_notes/0_24_6.md +5 -0
  41. data/doc/release_notes/0_24_7.md +10 -0
  42. data/doc/release_notes/1_0_0.md +60 -0
  43. data/doc/release_notes/1_0_1.md +5 -0
  44. data/doc/release_notes/1_0_2.md +7 -0
  45. data/doc/release_notes/1_1_0.md +32 -0
  46. data/doc/release_notes/1_1_1.md +17 -0
  47. data/doc/release_notes/1_1_2.md +12 -0
  48. data/doc/release_notes/1_1_3.md +18 -0
  49. data/doc/release_notes/1_1_4.md +6 -0
  50. data/doc/release_notes/1_1_5.md +12 -0
  51. data/doc/release_notes/1_2_0.md +49 -0
  52. data/doc/release_notes/1_2_1.md +6 -0
  53. data/doc/release_notes/1_2_2.md +10 -0
  54. data/doc/release_notes/1_2_3.md +16 -0
  55. data/doc/release_notes/1_2_4.md +8 -0
  56. data/doc/release_notes/1_2_5.md +7 -0
  57. data/doc/release_notes/1_2_6.md +13 -0
  58. data/doc/release_notes/1_3_0.md +18 -0
  59. data/doc/release_notes/1_3_1.md +17 -0
  60. data/lib/httpx/adapters/datadog.rb +215 -122
  61. data/lib/httpx/adapters/faraday.rb +145 -107
  62. data/lib/httpx/adapters/sentry.rb +26 -7
  63. data/lib/httpx/adapters/webmock.rb +34 -18
  64. data/lib/httpx/altsvc.rb +63 -26
  65. data/lib/httpx/base64.rb +27 -0
  66. data/lib/httpx/buffer.rb +12 -0
  67. data/lib/httpx/callbacks.rb +5 -3
  68. data/lib/httpx/chainable.rb +54 -39
  69. data/lib/httpx/connection/http1.rb +75 -44
  70. data/lib/httpx/connection/http2.rb +31 -38
  71. data/lib/httpx/connection.rb +287 -117
  72. data/lib/httpx/domain_name.rb +10 -13
  73. data/lib/httpx/errors.rb +52 -2
  74. data/lib/httpx/extensions.rb +24 -131
  75. data/lib/httpx/io/ssl.rb +83 -77
  76. data/lib/httpx/io/tcp.rb +48 -71
  77. data/lib/httpx/io/udp.rb +18 -52
  78. data/lib/httpx/io/unix.rb +10 -15
  79. data/lib/httpx/io.rb +3 -9
  80. data/lib/httpx/loggable.rb +4 -19
  81. data/lib/httpx/options.rb +176 -118
  82. data/lib/httpx/parser/http1.rb +4 -0
  83. data/lib/httpx/plugins/{authentication → auth}/basic.rb +1 -5
  84. data/lib/httpx/plugins/{authentication → auth}/digest.rb +14 -14
  85. data/lib/httpx/plugins/{authentication → auth}/ntlm.rb +1 -3
  86. data/lib/httpx/plugins/{authentication → auth}/socks5.rb +0 -2
  87. data/lib/httpx/plugins/auth.rb +25 -0
  88. data/lib/httpx/plugins/aws_sdk_authentication.rb +4 -3
  89. data/lib/httpx/plugins/aws_sigv4.rb +12 -9
  90. data/lib/httpx/plugins/basic_auth.rb +29 -0
  91. data/lib/httpx/plugins/brotli.rb +50 -0
  92. data/lib/httpx/plugins/callbacks.rb +91 -0
  93. data/lib/httpx/plugins/circuit_breaker/circuit.rb +100 -0
  94. data/lib/httpx/plugins/circuit_breaker/circuit_store.rb +53 -0
  95. data/lib/httpx/plugins/circuit_breaker.rb +148 -0
  96. data/lib/httpx/plugins/cookies/set_cookie_parser.rb +0 -2
  97. data/lib/httpx/plugins/cookies.rb +30 -17
  98. data/lib/httpx/plugins/{digest_authentication.rb → digest_auth.rb} +14 -12
  99. data/lib/httpx/plugins/expect.rb +21 -14
  100. data/lib/httpx/plugins/follow_redirects.rb +140 -41
  101. data/lib/httpx/plugins/grpc/call.rb +2 -3
  102. data/lib/httpx/plugins/grpc/grpc_encoding.rb +88 -0
  103. data/lib/httpx/plugins/grpc/message.rb +7 -37
  104. data/lib/httpx/plugins/grpc.rb +36 -29
  105. data/lib/httpx/plugins/h2c.rb +26 -19
  106. data/lib/httpx/plugins/internal_telemetry.rb +16 -0
  107. data/lib/httpx/plugins/{ntlm_authentication.rb → ntlm_auth.rb} +7 -5
  108. data/lib/httpx/plugins/oauth.rb +175 -0
  109. data/lib/httpx/plugins/persistent.rb +1 -1
  110. data/lib/httpx/plugins/proxy/http.rb +23 -13
  111. data/lib/httpx/plugins/proxy/socks4.rb +9 -7
  112. data/lib/httpx/plugins/proxy/socks5.rb +11 -9
  113. data/lib/httpx/plugins/proxy.rb +80 -61
  114. data/lib/httpx/plugins/push_promise.rb +1 -1
  115. data/lib/httpx/plugins/rate_limiter.rb +5 -1
  116. data/lib/httpx/plugins/response_cache/file_store.rb +40 -0
  117. data/lib/httpx/plugins/response_cache/store.rb +62 -25
  118. data/lib/httpx/plugins/response_cache.rb +105 -12
  119. data/lib/httpx/plugins/retries.rb +87 -17
  120. data/lib/httpx/plugins/ssrf_filter.rb +145 -0
  121. data/lib/httpx/plugins/stream.rb +27 -23
  122. data/lib/httpx/plugins/upgrade/h2.rb +4 -4
  123. data/lib/httpx/plugins/upgrade.rb +8 -10
  124. data/lib/httpx/plugins/webdav.rb +80 -0
  125. data/lib/httpx/pool/synch_pool.rb +93 -0
  126. data/lib/httpx/pool.rb +102 -27
  127. data/lib/httpx/punycode.rb +9 -291
  128. data/lib/httpx/request/body.rb +154 -0
  129. data/lib/httpx/request.rb +130 -146
  130. data/lib/httpx/resolver/https.rb +62 -27
  131. data/lib/httpx/resolver/multi.rb +9 -13
  132. data/lib/httpx/resolver/native.rb +192 -76
  133. data/lib/httpx/resolver/resolver.rb +34 -9
  134. data/lib/httpx/resolver/system.rb +16 -11
  135. data/lib/httpx/resolver.rb +38 -16
  136. data/lib/httpx/response/body.rb +242 -0
  137. data/lib/httpx/response/buffer.rb +96 -0
  138. data/lib/httpx/response.rb +159 -217
  139. data/lib/httpx/selector.rb +9 -4
  140. data/lib/httpx/session.rb +137 -89
  141. data/lib/httpx/session_extensions.rb +4 -1
  142. data/lib/httpx/timers.rb +34 -8
  143. data/lib/httpx/transcoder/body.rb +0 -2
  144. data/lib/httpx/transcoder/chunker.rb +0 -1
  145. data/lib/httpx/transcoder/deflate.rb +37 -0
  146. data/lib/httpx/transcoder/form.rb +52 -33
  147. data/lib/httpx/transcoder/gzip.rb +74 -0
  148. data/lib/httpx/transcoder/json.rb +21 -8
  149. data/lib/httpx/transcoder/multipart/decoder.rb +139 -0
  150. data/lib/httpx/{plugins → transcoder}/multipart/encoder.rb +4 -4
  151. data/lib/httpx/{plugins → transcoder}/multipart/mime_type_detector.rb +1 -1
  152. data/lib/httpx/{plugins → transcoder}/multipart/part.rb +3 -2
  153. data/lib/httpx/transcoder/multipart.rb +17 -0
  154. data/lib/httpx/transcoder/utils/body_reader.rb +46 -0
  155. data/lib/httpx/transcoder/utils/deflater.rb +72 -0
  156. data/lib/httpx/transcoder/utils/inflater.rb +19 -0
  157. data/lib/httpx/transcoder/xml.rb +52 -0
  158. data/lib/httpx/transcoder.rb +5 -6
  159. data/lib/httpx/utils.rb +36 -16
  160. data/lib/httpx/version.rb +1 -1
  161. data/lib/httpx.rb +12 -14
  162. data/sig/altsvc.rbs +33 -0
  163. data/sig/buffer.rbs +2 -1
  164. data/sig/callbacks.rbs +3 -3
  165. data/sig/chainable.rbs +11 -9
  166. data/sig/connection/http1.rbs +8 -7
  167. data/sig/connection/http2.rbs +19 -19
  168. data/sig/connection.rbs +64 -24
  169. data/sig/errors.rbs +22 -3
  170. data/sig/httpx.rbs +5 -4
  171. data/sig/io/ssl.rbs +27 -0
  172. data/sig/io/tcp.rbs +60 -0
  173. data/sig/io/udp.rbs +20 -0
  174. data/sig/io/unix.rbs +27 -0
  175. data/sig/io.rbs +6 -0
  176. data/sig/options.rbs +32 -22
  177. data/sig/parser/http1.rbs +1 -1
  178. data/sig/plugins/{authentication → auth}/basic.rbs +0 -2
  179. data/sig/plugins/{authentication → auth}/digest.rbs +2 -1
  180. data/sig/plugins/auth.rbs +13 -0
  181. data/sig/plugins/{basic_authentication.rbs → basic_auth.rbs} +2 -2
  182. data/sig/plugins/brotli.rbs +22 -0
  183. data/sig/plugins/callbacks.rbs +38 -0
  184. data/sig/plugins/circuit_breaker.rbs +71 -0
  185. data/sig/plugins/compression.rbs +7 -5
  186. data/sig/plugins/cookies/jar.rbs +2 -2
  187. data/sig/plugins/cookies.rbs +2 -0
  188. data/sig/plugins/{digest_authentication.rbs → digest_auth.rbs} +2 -2
  189. data/sig/plugins/follow_redirects.rbs +18 -4
  190. data/sig/plugins/grpc/call.rbs +19 -0
  191. data/sig/plugins/grpc/grpc_encoding.rbs +37 -0
  192. data/sig/plugins/grpc/message.rbs +17 -0
  193. data/sig/plugins/grpc.rbs +7 -32
  194. data/sig/plugins/h2c.rbs +1 -1
  195. data/sig/plugins/{ntlm_authentication.rbs → ntlm_auth.rbs} +2 -2
  196. data/sig/plugins/oauth.rbs +54 -0
  197. data/sig/plugins/proxy/http.rbs +3 -0
  198. data/sig/plugins/proxy/socks4.rbs +9 -6
  199. data/sig/plugins/proxy/socks5.rbs +10 -6
  200. data/sig/plugins/proxy/ssh.rbs +1 -1
  201. data/sig/plugins/proxy.rbs +13 -5
  202. data/sig/plugins/push_promise.rbs +3 -3
  203. data/sig/plugins/rate_limiter.rbs +1 -1
  204. data/sig/plugins/response_cache.rbs +36 -7
  205. data/sig/plugins/retries.rbs +30 -8
  206. data/sig/plugins/stream.rbs +24 -17
  207. data/sig/plugins/upgrade.rbs +5 -3
  208. data/sig/pool.rbs +10 -7
  209. data/sig/request/body.rbs +38 -0
  210. data/sig/request.rbs +15 -24
  211. data/sig/resolver/https.rbs +8 -3
  212. data/sig/resolver/native.rbs +17 -4
  213. data/sig/resolver/resolver.rbs +8 -6
  214. data/sig/resolver/system.rbs +2 -0
  215. data/sig/resolver.rbs +9 -5
  216. data/sig/response/body.rbs +53 -0
  217. data/sig/response/buffer.rbs +24 -0
  218. data/sig/response.rbs +24 -39
  219. data/sig/selector.rbs +1 -1
  220. data/sig/session.rbs +29 -18
  221. data/sig/timers.rbs +18 -8
  222. data/sig/transcoder/body.rbs +4 -3
  223. data/sig/transcoder/deflate.rbs +11 -0
  224. data/sig/transcoder/form.rbs +5 -3
  225. data/sig/transcoder/gzip.rbs +24 -0
  226. data/sig/transcoder/json.rbs +8 -3
  227. data/sig/{plugins → transcoder}/multipart.rbs +15 -19
  228. data/sig/transcoder/utils/body_reader.rbs +15 -0
  229. data/sig/transcoder/utils/deflater.rbs +29 -0
  230. data/sig/transcoder/utils/inflater.rbs +12 -0
  231. data/sig/transcoder/xml.rbs +22 -0
  232. data/sig/transcoder.rbs +24 -9
  233. data/sig/utils.rbs +8 -2
  234. metadata +163 -41
  235. data/lib/httpx/plugins/authentication.rb +0 -20
  236. data/lib/httpx/plugins/basic_authentication.rb +0 -30
  237. data/lib/httpx/plugins/compression/brotli.rb +0 -54
  238. data/lib/httpx/plugins/compression/deflate.rb +0 -49
  239. data/lib/httpx/plugins/compression/gzip.rb +0 -88
  240. data/lib/httpx/plugins/compression.rb +0 -164
  241. data/lib/httpx/plugins/multipart/decoder.rb +0 -187
  242. data/lib/httpx/plugins/multipart.rb +0 -84
  243. data/lib/httpx/registry.rb +0 -85
  244. data/sig/plugins/authentication.rbs +0 -11
  245. data/sig/plugins/compression/brotli.rbs +0 -21
  246. data/sig/plugins/compression/deflate.rbs +0 -17
  247. data/sig/plugins/compression/gzip.rbs +0 -29
  248. data/sig/registry.rbs +0 -12
  249. /data/sig/plugins/{authentication → auth}/ntlm.rbs +0 -0
  250. /data/sig/plugins/{authentication → auth}/socks5.rbs +0 -0
@@ -16,7 +16,7 @@ module HTTPX
16
16
  #
17
17
  # This plugin adds DSL to build GRPC interfaces.
18
18
  #
19
- # https://gitlab.com/honeyryderchuck/httpx/wikis/GRPC
19
+ # https://gitlab.com/os85/httpx/wikis/GRPC
20
20
  #
21
21
  module GRPC
22
22
  unless String.method_defined?(:underscore)
@@ -49,20 +49,19 @@ module HTTPX
49
49
  class << self
50
50
  def load_dependencies(*)
51
51
  require "stringio"
52
+ require "httpx/plugins/grpc/grpc_encoding"
52
53
  require "httpx/plugins/grpc/message"
53
54
  require "httpx/plugins/grpc/call"
54
55
  end
55
56
 
56
57
  def configure(klass)
57
58
  klass.plugin(:persistent)
58
- klass.plugin(:compression)
59
59
  klass.plugin(:stream)
60
60
  end
61
61
 
62
62
  def extra_options(options)
63
63
  options.merge(
64
64
  fallback_protocol: "h2",
65
- http2_settings: { wait_for_handshake: false },
66
65
  grpc_rpcs: {}.freeze,
67
66
  grpc_compression: false,
68
67
  grpc_deadline: DEADLINE
@@ -108,14 +107,24 @@ module HTTPX
108
107
  @trailing_metadata = Hash[trailers]
109
108
  super
110
109
  end
110
+ end
111
+
112
+ module RequestBodyMethods
113
+ def initialize(*, **)
114
+ super
111
115
 
112
- def encoders
113
- @options.encodings
116
+ if (compression = @headers["grpc-encoding"])
117
+ deflater_body = self.class.initialize_deflater_body(@body, compression)
118
+ @body = Transcoder::GRPCEncoding.encode(deflater_body || @body, compressed: !deflater_body.nil?)
119
+ else
120
+ @body = Transcoder::GRPCEncoding.encode(@body, compressed: false)
121
+ end
114
122
  end
115
123
  end
116
124
 
117
125
  module InstanceMethods
118
126
  def with_channel_credentials(ca_path, key = nil, cert = nil, **ssl_opts)
127
+ # @type var ssl_params: ::Hash[::Symbol, untyped]
119
128
  ssl_params = {
120
129
  **ssl_opts,
121
130
  ca_file: ca_path,
@@ -141,17 +150,29 @@ module HTTPX
141
150
  deadline: @options.grpc_deadline,
142
151
  }.merge(opts)
143
152
 
153
+ local_rpc_name = rpc_name.underscore
154
+
144
155
  session_class = Class.new(self.class) do
156
+ # define rpc method with ruby style name
145
157
  class_eval(<<-OUT, __FILE__, __LINE__ + 1)
146
- def #{rpc_name}(input, **opts) # def grpc_action(input, **opts)
147
- rpc_execute("#{rpc_name}", input, **opts) # rpc_execute("grpc_action", input, **opts)
148
- end # end
158
+ def #{local_rpc_name}(input, **opts) # def grpc_action(input, **opts)
159
+ rpc_execute("#{local_rpc_name}", input, **opts) # rpc_execute("grpc_action", input, **opts)
160
+ end # end
149
161
  OUT
162
+
163
+ # define rpc method with original name
164
+ unless local_rpc_name == rpc_name
165
+ class_eval(<<-OUT, __FILE__, __LINE__ + 1)
166
+ def #{rpc_name}(input, **opts) # def grpcAction(input, **opts)
167
+ rpc_execute("#{local_rpc_name}", input, **opts) # rpc_execute("grpc_action", input, **opts)
168
+ end # end
169
+ OUT
170
+ end
150
171
  end
151
172
 
152
173
  session_class.new(@options.merge(
153
174
  grpc_rpcs: @options.grpc_rpcs.merge(
154
- rpc_name.underscore => [rpc_name, input, output, rpc_opts]
175
+ local_rpc_name => [rpc_name, input, output, rpc_opts]
155
176
  ).freeze
156
177
  ))
157
178
  end
@@ -195,7 +216,7 @@ module HTTPX
195
216
  **opts)
196
217
  grpc_request = build_grpc_request(rpc_method, input, deadline: deadline, metadata: metadata, **opts)
197
218
  response = request(grpc_request, **opts)
198
- response.raise_for_status
219
+ response.raise_for_status unless opts[:stream]
199
220
  GRPC::Call.new(response)
200
221
  end
201
222
 
@@ -233,7 +254,7 @@ module HTTPX
233
254
  uri.path = rpc_method
234
255
 
235
256
  headers = HEADERS.merge(
236
- "grpc-accept-encoding" => ["identity", *@options.encodings.registry.keys]
257
+ "grpc-accept-encoding" => ["identity", *@options.supported_compression_formats]
237
258
  )
238
259
  unless deadline == Float::INFINITY
239
260
  # convert to milliseconds
@@ -241,30 +262,16 @@ module HTTPX
241
262
  headers["grpc-timeout"] = "#{deadline}m"
242
263
  end
243
264
 
244
- headers = headers.merge(metadata) if metadata
265
+ headers = headers.merge(metadata.transform_keys(&:to_s)) if metadata
245
266
 
246
267
  # prepare compressor
247
- deflater = nil
248
268
  compression = @options.grpc_compression == true ? "gzip" : @options.grpc_compression
249
269
 
250
- if compression
251
- headers["grpc-encoding"] = compression
252
- deflater = @options.encodings.registry(compression).deflater
253
- end
254
-
255
- headers.merge!(@options.call_credentials.call) if @options.call_credentials
270
+ headers["grpc-encoding"] = compression if compression
256
271
 
257
- body = if input.respond_to?(:each)
258
- Enumerator.new do |y|
259
- input.each do |message|
260
- y << Message.encode(message, deflater: deflater)
261
- end
262
- end
263
- else
264
- Message.encode(input, deflater: deflater)
265
- end
272
+ headers.merge!(@options.call_credentials.call.transform_keys(&:to_s)) if @options.call_credentials
266
273
 
267
- build_request(:post, uri, headers: headers, body: body)
274
+ build_request("POST", uri, headers: headers, body: input)
268
275
  end
269
276
  end
270
277
  end
@@ -4,21 +4,16 @@ module HTTPX
4
4
  module Plugins
5
5
  #
6
6
  # This plugin adds support for upgrading a plaintext HTTP/1.1 connection to HTTP/2
7
- # (https://tools.ietf.org/html/rfc7540#section-3.2)
7
+ # (https://datatracker.ietf.org/doc/html/rfc7540#section-3.2)
8
8
  #
9
- # https://gitlab.com/honeyryderchuck/httpx/wikis/Upgrade#h2c
9
+ # https://gitlab.com/os85/httpx/wikis/Connection-Upgrade#h2c
10
10
  #
11
11
  module H2C
12
- VALID_H2C_VERBS = %i[get options head].freeze
12
+ VALID_H2C_VERBS = %w[GET OPTIONS HEAD].freeze
13
13
 
14
14
  class << self
15
- def load_dependencies(*)
16
- require "base64"
17
- end
18
-
19
- def configure(klass)
15
+ def load_dependencies(klass)
20
16
  klass.plugin(:upgrade)
21
- klass.default_options.upgrade_handlers.register "h2c", self
22
17
  end
23
18
 
24
19
  def call(connection, request, response)
@@ -26,7 +21,7 @@ module HTTPX
26
21
  end
27
22
 
28
23
  def extra_options(options)
29
- options.merge(max_concurrent_requests: 1)
24
+ options.merge(max_concurrent_requests: 1, upgrade_handlers: options.upgrade_handlers.merge("h2c" => self))
30
25
  end
31
26
  end
32
27
 
@@ -38,13 +33,13 @@ module HTTPX
38
33
 
39
34
  connection = pool.find_connection(upgrade_request.uri, upgrade_request.options)
40
35
 
41
- return super if connection && connection.upgrade_protocol == :h2c
36
+ return super if connection && connection.upgrade_protocol == "h2c"
42
37
 
43
38
  # build upgrade request
44
39
  upgrade_request.headers.add("connection", "upgrade")
45
40
  upgrade_request.headers.add("connection", "http2-settings")
46
41
  upgrade_request.headers["upgrade"] = "h2c"
47
- upgrade_request.headers["http2-settings"] = HTTP2Next::Client.settings_header(upgrade_request.options.http2_settings)
42
+ upgrade_request.headers["http2-settings"] = ::HTTP2::Client.settings_header(upgrade_request.options.http2_settings)
48
43
 
49
44
  super(upgrade_request, *remainder)
50
45
  end
@@ -78,22 +73,34 @@ module HTTPX
78
73
  @inflight -= prev_parser.requests.size
79
74
  end
80
75
 
81
- parser_options = @options.merge(max_concurrent_requests: request.options.max_concurrent_requests)
82
- @parser = H2CParser.new(@write_buffer, parser_options)
76
+ @parser = H2CParser.new(@write_buffer, @options)
83
77
  set_parser_callbacks(@parser)
84
78
  @inflight += 1
85
79
  @parser.upgrade(request, response)
86
- @upgrade_protocol = :h2c
87
-
88
- if request.options.max_concurrent_requests != @options.max_concurrent_requests
89
- @options = @options.merge(max_concurrent_requests: nil)
90
- end
80
+ @upgrade_protocol = "h2c"
91
81
 
92
82
  prev_parser.requests.each do |req|
93
83
  req.transition(:idle)
94
84
  send(req)
95
85
  end
96
86
  end
87
+
88
+ private
89
+
90
+ def send_request_to_parser(request)
91
+ super
92
+
93
+ return unless request.headers["upgrade"] == "h2c" && parser.is_a?(Connection::HTTP1)
94
+
95
+ max_concurrent_requests = parser.max_concurrent_requests
96
+
97
+ return if max_concurrent_requests == 1
98
+
99
+ parser.max_concurrent_requests = 1
100
+ request.once(:response) do
101
+ parser.max_concurrent_requests = max_concurrent_requests
102
+ end
103
+ end
97
104
  end
98
105
  end
99
106
  register_plugin(:h2c, H2C)
@@ -32,6 +32,15 @@ module HTTPX
32
32
  end
33
33
  end
34
34
 
35
+ module NativeResolverMethods
36
+ def transition(nextstate)
37
+ state = @state
38
+ val = super
39
+ meter_elapsed_time("Resolver::Native: #{state} -> #{nextstate}")
40
+ val
41
+ end
42
+ end
43
+
35
44
  module InstanceMethods
36
45
  def self.included(klass)
37
46
  klass.prepend TrackTimeMethods
@@ -42,6 +51,13 @@ module HTTPX
42
51
  meter_elapsed_time("Session: initializing...")
43
52
  super
44
53
  meter_elapsed_time("Session: initialized!!!")
54
+ resolver_type = @options.resolver_class
55
+ resolver_type = Resolver.resolver_for(resolver_type)
56
+ return unless resolver_type <= Resolver::Native
57
+
58
+ resolver_type.prepend TrackTimeMethods
59
+ resolver_type.prepend NativeResolverMethods
60
+ @options = @options.merge(resolver_class: resolver_type)
45
61
  end
46
62
 
47
63
  def close(*)
@@ -3,12 +3,12 @@
3
3
  module HTTPX
4
4
  module Plugins
5
5
  #
6
- # https://gitlab.com/honeyryderchuck/httpx/wikis/Authentication#ntlm-authentication
6
+ # https://gitlab.com/os85/httpx/wikis/Auth#ntlm-auth
7
7
  #
8
8
  module NTLMAuth
9
9
  class << self
10
10
  def load_dependencies(_klass)
11
- require_relative "authentication/ntlm"
11
+ require_relative "auth/ntlm"
12
12
  end
13
13
 
14
14
  def extra_options(options)
@@ -25,11 +25,11 @@ module HTTPX
25
25
  end
26
26
 
27
27
  module InstanceMethods
28
- def ntlm_authentication(user, password, domain = nil)
28
+ def ntlm_auth(user, password, domain = nil)
29
29
  with(ntlm: Authentication::Ntlm.new(user, password, domain: domain))
30
30
  end
31
31
 
32
- alias_method :ntlm_auth, :ntlm_authentication
32
+ private
33
33
 
34
34
  def send_requests(*requests)
35
35
  requests.flat_map do |request|
@@ -39,6 +39,8 @@ module HTTPX
39
39
  request.headers["authorization"] = ntlm.negotiate
40
40
  probe_response = wrap { super(request).first }
41
41
 
42
+ return probe_response unless probe_response.is_a?(Response)
43
+
42
44
  if probe_response.status == 401 && ntlm.can_authenticate?(probe_response.headers["www-authenticate"])
43
45
  request.transition(:idle)
44
46
  request.headers["authorization"] = ntlm.authenticate(request, probe_response.headers["www-authenticate"])
@@ -53,6 +55,6 @@ module HTTPX
53
55
  end
54
56
  end
55
57
  end
56
- register_plugin :ntlm_authentication, NTLMAuth
58
+ register_plugin :ntlm_auth, NTLMAuth
57
59
  end
58
60
  end
@@ -0,0 +1,175 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ #
6
+ # https://gitlab.com/os85/httpx/wikis/OAuth
7
+ #
8
+ module OAuth
9
+ class << self
10
+ def load_dependencies(_klass)
11
+ require_relative "auth/basic"
12
+ end
13
+ end
14
+
15
+ SUPPORTED_GRANT_TYPES = %w[client_credentials refresh_token].freeze
16
+ SUPPORTED_AUTH_METHODS = %w[client_secret_basic client_secret_post].freeze
17
+
18
+ class OAuthSession
19
+ attr_reader :grant_type, :client_id, :client_secret, :access_token, :refresh_token, :scope
20
+
21
+ def initialize(
22
+ issuer:,
23
+ client_id:,
24
+ client_secret:,
25
+ access_token: nil,
26
+ refresh_token: nil,
27
+ scope: nil,
28
+ token_endpoint: nil,
29
+ response_type: nil,
30
+ grant_type: nil,
31
+ token_endpoint_auth_method: nil
32
+ )
33
+ @issuer = URI(issuer)
34
+ @client_id = client_id
35
+ @client_secret = client_secret
36
+ @token_endpoint = URI(token_endpoint) if token_endpoint
37
+ @response_type = response_type
38
+ @scope = case scope
39
+ when String
40
+ scope.split
41
+ when Array
42
+ scope
43
+ end
44
+ @access_token = access_token
45
+ @refresh_token = refresh_token
46
+ @token_endpoint_auth_method = String(token_endpoint_auth_method) if token_endpoint_auth_method
47
+ @grant_type = grant_type || (@refresh_token ? "refresh_token" : "client_credentials")
48
+
49
+ unless @token_endpoint_auth_method.nil? || SUPPORTED_AUTH_METHODS.include?(@token_endpoint_auth_method)
50
+ raise Error, "#{@token_endpoint_auth_method} is not a supported auth method"
51
+ end
52
+
53
+ return if SUPPORTED_GRANT_TYPES.include?(@grant_type)
54
+
55
+ raise Error, "#{@grant_type} is not a supported grant type"
56
+ end
57
+
58
+ def token_endpoint
59
+ @token_endpoint || "#{@issuer}/token"
60
+ end
61
+
62
+ def token_endpoint_auth_method
63
+ @token_endpoint_auth_method || "client_secret_basic"
64
+ end
65
+
66
+ def load(http)
67
+ return if @grant_type && @scope
68
+
69
+ metadata = http.get("#{@issuer}/.well-known/oauth-authorization-server").raise_for_status.json
70
+
71
+ @token_endpoint = metadata["token_endpoint"]
72
+ @scope = metadata["scopes_supported"]
73
+ @grant_type = Array(metadata["grant_types_supported"]).find { |gr| SUPPORTED_GRANT_TYPES.include?(gr) }
74
+ @token_endpoint_auth_method = Array(metadata["token_endpoint_auth_methods_supported"]).find do |am|
75
+ SUPPORTED_AUTH_METHODS.include?(am)
76
+ end
77
+ nil
78
+ end
79
+
80
+ def merge(other)
81
+ obj = dup
82
+
83
+ case other
84
+ when OAuthSession
85
+ other.instance_variables.each do |ivar|
86
+ val = other.instance_variable_get(ivar)
87
+ next unless val
88
+
89
+ obj.instance_variable_set(ivar, val)
90
+ end
91
+ when Hash
92
+ other.each do |k, v|
93
+ obj.instance_variable_set(:"@#{k}", v) if obj.instance_variable_defined?(:"@#{k}")
94
+ end
95
+ end
96
+ obj
97
+ end
98
+ end
99
+
100
+ module OptionsMethods
101
+ def option_oauth_session(value)
102
+ case value
103
+ when Hash
104
+ OAuthSession.new(**value)
105
+ when OAuthSession
106
+ value
107
+ else
108
+ raise TypeError, ":oauth_session must be a #{OAuthSession}"
109
+ end
110
+ end
111
+ end
112
+
113
+ module InstanceMethods
114
+ def oauth_auth(**args)
115
+ with(oauth_session: OAuthSession.new(**args))
116
+ end
117
+
118
+ def with_access_token
119
+ oauth_session = @options.oauth_session
120
+
121
+ oauth_session.load(self)
122
+
123
+ grant_type = oauth_session.grant_type
124
+
125
+ headers = {}
126
+ form_post = { "grant_type" => grant_type, "scope" => Array(oauth_session.scope).join(" ") }.compact
127
+
128
+ # auth
129
+ case oauth_session.token_endpoint_auth_method
130
+ when "client_secret_post"
131
+ form_post["client_id"] = oauth_session.client_id
132
+ form_post["client_secret"] = oauth_session.client_secret
133
+ when "client_secret_basic"
134
+ headers["authorization"] = Authentication::Basic.new(oauth_session.client_id, oauth_session.client_secret).authenticate
135
+ end
136
+
137
+ case grant_type
138
+ when "client_credentials"
139
+ # do nothing
140
+ when "refresh_token"
141
+ form_post["refresh_token"] = oauth_session.refresh_token
142
+ end
143
+
144
+ token_request = build_request("POST", oauth_session.token_endpoint, headers: headers, form: form_post)
145
+ token_request.headers.delete("authorization") unless oauth_session.token_endpoint_auth_method == "client_secret_basic"
146
+
147
+ token_response = request(token_request)
148
+ token_response.raise_for_status
149
+
150
+ payload = token_response.json
151
+
152
+ access_token = payload["access_token"]
153
+ refresh_token = payload["refresh_token"]
154
+
155
+ with(oauth_session: oauth_session.merge(access_token: access_token, refresh_token: refresh_token))
156
+ end
157
+
158
+ def build_request(*)
159
+ request = super
160
+
161
+ return request if request.headers.key?("authorization")
162
+
163
+ oauth_session = @options.oauth_session
164
+
165
+ return request unless oauth_session && oauth_session.access_token
166
+
167
+ request.headers["authorization"] = "Bearer #{oauth_session.access_token}"
168
+
169
+ request
170
+ end
171
+ end
172
+ end
173
+ register_plugin :oauth, OAuth
174
+ end
175
+ end
@@ -15,7 +15,7 @@ module HTTPX
15
15
  # This plugin is also not recommendable when connecting to >9000 (like, a lot) different origins.
16
16
  # So when you use this, make sure that you don't fall into this trap.
17
17
  #
18
- # https://gitlab.com/honeyryderchuck/httpx/wikis/Persistent
18
+ # https://gitlab.com/os85/httpx/wikis/Persistent
19
19
  #
20
20
  module Persistent
21
21
  def self.load_dependencies(klass)
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "base64"
4
-
5
3
  module HTTPX
6
4
  module Plugins
7
5
  module Proxy
8
6
  module HTTP
7
+ class << self
8
+ def extra_options(options)
9
+ options.merge(supported_proxy_protocols: options.supported_proxy_protocols + %w[http])
10
+ end
11
+ end
12
+
9
13
  module InstanceMethods
10
14
  def with_proxy_basic_auth(opts)
11
15
  with(proxy: opts.merge(scheme: "basic"))
@@ -23,17 +27,23 @@ module HTTPX
23
27
  response = super
24
28
 
25
29
  if response &&
30
+ response.is_a?(Response) &&
26
31
  response.status == 407 &&
27
32
  !request.headers.key?("proxy-authorization") &&
28
33
  response.headers.key?("proxy-authenticate")
29
34
 
30
- connection = find_connection(request, connections, options)
35
+ uri = request.uri
36
+
37
+ proxy_options = proxy_options(uri, options)
38
+ connection = connections.find do |conn|
39
+ conn.match?(uri, proxy_options)
40
+ end
31
41
 
32
- if connection.options.proxy.can_authenticate?(response.headers["proxy-authenticate"])
42
+ if connection && connection.options.proxy.can_authenticate?(response.headers["proxy-authenticate"])
33
43
  request.transition(:idle)
34
44
  request.headers["proxy-authorization"] =
35
45
  connection.options.proxy.authenticate(request, response.headers["proxy-authenticate"])
36
- connection.send(request)
46
+ send_request(request, connections)
37
47
  return
38
48
  end
39
49
  end
@@ -60,7 +70,7 @@ module HTTPX
60
70
  return unless @io.connected?
61
71
 
62
72
  @parser || begin
63
- @parser = registry(@io.protocol).new(@write_buffer, @options.merge(max_concurrent_requests: 1))
73
+ @parser = self.class.parser_type(@io.protocol).new(@write_buffer, @options.merge(max_concurrent_requests: 1))
64
74
  parser = @parser
65
75
  parser.extend(ProxyParser)
66
76
  parser.on(:response, &method(:__http_on_connect))
@@ -71,7 +81,6 @@ module HTTPX
71
81
  else
72
82
  transition(:closing)
73
83
  transition(:closed)
74
- emit(:reset)
75
84
 
76
85
  parser.reset if @parser
77
86
  transition(:idle)
@@ -113,13 +122,14 @@ module HTTPX
113
122
 
114
123
  def __http_on_connect(request, response)
115
124
  @inflight -= 1
116
- if response.status == 200
125
+ if response.is_a?(Response) && response.status == 200
117
126
  req = @pending.first
118
127
  request_uri = req.uri
119
128
  @io = ProxySSL.new(@io, request_uri, @options)
120
129
  transition(:connected)
121
130
  throw(:called)
122
- elsif response.status == 407 &&
131
+ elsif response.is_a?(Response) &&
132
+ response.status == 407 &&
123
133
  !request.headers.key?("proxy-authorization") &&
124
134
  @options.proxy.can_authenticate?(response.headers["proxy-authenticate"])
125
135
 
@@ -139,9 +149,9 @@ module HTTPX
139
149
 
140
150
  module ProxyParser
141
151
  def join_headline(request)
142
- return super if request.verb == :connect
152
+ return super if request.verb == "CONNECT"
143
153
 
144
- "#{request.verb.to_s.upcase} #{request.uri} HTTP/#{@version.join(".")}"
154
+ "#{request.verb} #{request.uri} HTTP/#{@version.join(".")}"
145
155
  end
146
156
 
147
157
  def set_protocol_headers(request)
@@ -158,8 +168,8 @@ module HTTPX
158
168
  end
159
169
 
160
170
  class ConnectRequest < Request
161
- def initialize(uri, _options)
162
- super(:connect, uri, {})
171
+ def initialize(uri, options)
172
+ super("CONNECT", uri, options)
163
173
  @headers.delete("accept")
164
174
  end
165
175
 
@@ -4,7 +4,7 @@ require "resolv"
4
4
  require "ipaddr"
5
5
 
6
6
  module HTTPX
7
- class Socks4Error < Error; end
7
+ class Socks4Error < HTTPProxyError; end
8
8
 
9
9
  module Plugins
10
10
  module Proxy
@@ -16,6 +16,12 @@ module HTTPX
16
16
 
17
17
  Error = Socks4Error
18
18
 
19
+ class << self
20
+ def extra_options(options)
21
+ options.merge(supported_proxy_protocols: options.supported_proxy_protocols + PROTOCOLS)
22
+ end
23
+ end
24
+
19
25
  module ConnectionMethods
20
26
  def interests
21
27
  if @state == :connecting
@@ -79,15 +85,11 @@ module HTTPX
79
85
  end
80
86
 
81
87
  class SocksParser
82
- include Callbacks
88
+ include HTTPX::Callbacks
83
89
 
84
90
  def initialize(buffer, options)
85
91
  @buffer = buffer
86
- @options = Options.new(options)
87
- end
88
-
89
- def timeout
90
- @options.timeout[:operation_timeout]
92
+ @options = options
91
93
  end
92
94
 
93
95
  def close; end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
- class Socks5Error < Error; end
4
+ class Socks5Error < HTTPProxyError; end
5
5
 
6
6
  module Plugins
7
7
  module Proxy
@@ -18,8 +18,14 @@ module HTTPX
18
18
 
19
19
  Error = Socks5Error
20
20
 
21
- def self.load_dependencies(*)
22
- require_relative "../authentication/socks5"
21
+ class << self
22
+ def load_dependencies(*)
23
+ require_relative "../auth/socks5"
24
+ end
25
+
26
+ def extra_options(options)
27
+ options.merge(supported_proxy_protocols: options.supported_proxy_protocols + %w[socks5])
28
+ end
23
29
  end
24
30
 
25
31
  module ConnectionMethods
@@ -131,15 +137,11 @@ module HTTPX
131
137
  end
132
138
 
133
139
  class SocksParser
134
- include Callbacks
140
+ include HTTPX::Callbacks
135
141
 
136
142
  def initialize(buffer, options)
137
143
  @buffer = buffer
138
- @options = Options.new(options)
139
- end
140
-
141
- def timeout
142
- @options.timeout[:operation_timeout]
144
+ @options = options
143
145
  end
144
146
 
145
147
  def close; end