httpx 0.20.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
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