httpx 0.11.3 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/doc/release_notes/0_11_1.md +5 -1
  4. data/doc/release_notes/0_12_0.md +55 -0
  5. data/lib/httpx.rb +2 -1
  6. data/lib/httpx/adapters/faraday.rb +4 -6
  7. data/lib/httpx/altsvc.rb +1 -0
  8. data/lib/httpx/connection.rb +63 -15
  9. data/lib/httpx/connection/http1.rb +8 -7
  10. data/lib/httpx/connection/http2.rb +32 -25
  11. data/lib/httpx/io.rb +16 -3
  12. data/lib/httpx/io/ssl.rb +7 -9
  13. data/lib/httpx/io/tcp.rb +9 -8
  14. data/lib/httpx/io/tls.rb +218 -0
  15. data/lib/httpx/io/tls/box.rb +365 -0
  16. data/lib/httpx/io/tls/context.rb +199 -0
  17. data/lib/httpx/io/tls/ffi.rb +390 -0
  18. data/lib/httpx/parser/http1.rb +4 -4
  19. data/lib/httpx/plugins/aws_sdk_authentication.rb +81 -0
  20. data/lib/httpx/plugins/aws_sigv4.rb +218 -0
  21. data/lib/httpx/plugins/compression/deflate.rb +2 -5
  22. data/lib/httpx/plugins/internal_telemetry.rb +93 -0
  23. data/lib/httpx/plugins/multipart.rb +2 -0
  24. data/lib/httpx/plugins/multipart/encoder.rb +4 -9
  25. data/lib/httpx/plugins/proxy.rb +1 -1
  26. data/lib/httpx/plugins/proxy/http.rb +1 -1
  27. data/lib/httpx/plugins/proxy/socks4.rb +8 -0
  28. data/lib/httpx/plugins/proxy/socks5.rb +8 -0
  29. data/lib/httpx/plugins/push_promise.rb +3 -2
  30. data/lib/httpx/plugins/retries.rb +1 -1
  31. data/lib/httpx/plugins/stream.rb +3 -5
  32. data/lib/httpx/pool.rb +0 -1
  33. data/lib/httpx/registry.rb +1 -7
  34. data/lib/httpx/request.rb +11 -1
  35. data/lib/httpx/resolver/https.rb +3 -11
  36. data/lib/httpx/response.rb +9 -2
  37. data/lib/httpx/selector.rb +5 -0
  38. data/lib/httpx/session.rb +25 -2
  39. data/lib/httpx/transcoder/body.rb +3 -5
  40. data/lib/httpx/version.rb +1 -1
  41. data/sig/connection/http1.rbs +2 -2
  42. data/sig/connection/http2.rbs +5 -3
  43. data/sig/plugins/aws_sdk_authentication.rbs +17 -0
  44. data/sig/plugins/aws_sigv4.rbs +65 -0
  45. data/sig/plugins/push_promise.rbs +1 -1
  46. metadata +13 -2
@@ -29,18 +29,13 @@ module HTTPX::Plugins
29
29
 
30
30
  def rewind
31
31
  form = @form.each_with_object([]) do |(key, val), aux|
32
- v = case val
33
- when File
34
- val = val.reopen(val.path, File::RDONLY) if val.closed?
35
- val.rewind
36
- val
37
- else
38
- v
39
- end
40
- aux << [key, v]
32
+ val = val.reopen(val.path, File::RDONLY) if val.is_a?(File) && val.closed?
33
+ val.rewind if val.respond_to?(:rewind)
34
+ aux << [key, val]
41
35
  end
42
36
  @form = form
43
37
  @parts = to_parts(form)
38
+ @part_index = 0
44
39
  end
45
40
 
46
41
  private
@@ -242,7 +242,7 @@ module HTTPX
242
242
  register_plugin :proxy, Proxy
243
243
  end
244
244
 
245
- class ProxySSL < SSL
245
+ class ProxySSL < IO.registry["ssl"]
246
246
  def initialize(tcp, request_uri, options)
247
247
  @io = tcp.to_io
248
248
  super(request_uri, tcp.addresses, options)
@@ -81,7 +81,7 @@ module HTTPX
81
81
  request.uri.to_s
82
82
  end
83
83
 
84
- def set_request_headers(request)
84
+ def set_protocol_headers(request)
85
85
  super
86
86
  proxy_params = @options.proxy
87
87
  request.headers["proxy-authorization"] = "Basic #{proxy_params.token_authentication}" if proxy_params.authenticated?
@@ -16,6 +16,14 @@ module HTTPX
16
16
  Error = Socks4Error
17
17
 
18
18
  module ConnectionMethods
19
+ def interests
20
+ if @state == :connecting
21
+ return @write_buffer.empty? ? :r : :w
22
+ end
23
+
24
+ super
25
+ end
26
+
19
27
  private
20
28
 
21
29
  def transition(nextstate)
@@ -35,6 +35,14 @@ module HTTPX
35
35
  super || @state == :authenticating || @state == :negotiating
36
36
  end
37
37
 
38
+ def interests
39
+ if @state == :connecting || @state == :authenticating || @state == :negotiating
40
+ return @write_buffer.empty? ? :r : :w
41
+ end
42
+
43
+ super
44
+ end
45
+
38
46
  private
39
47
 
40
48
  def transition(nextstate)
@@ -43,7 +43,7 @@ module HTTPX
43
43
  end
44
44
 
45
45
  def __on_promise_request(parser, stream, h)
46
- log(level: 1) do
46
+ log(level: 1, color: :yellow) do
47
47
  # :nocov:
48
48
  h.map { |k, v| "#{stream.id}: -> PROMISE HEADER: #{k}: #{v}" }.join("\n")
49
49
  # :nocov:
@@ -57,6 +57,8 @@ module HTTPX
57
57
  request.merge_headers(headers)
58
58
  promise_headers[stream] = request
59
59
  parser.pending.delete(request)
60
+ parser.streams[request] = stream
61
+ request.transition(:done)
60
62
  else
61
63
  stream.refuse
62
64
  end
@@ -67,7 +69,6 @@ module HTTPX
67
69
  return unless request
68
70
 
69
71
  parser.__send__(:on_stream_headers, stream, request, h)
70
- request.transition(:done)
71
72
  response = request.response
72
73
  response.mark_as_pushed!
73
74
  stream.on(:data, &parser.method(:on_stream_data).curry(3)[stream, request])
@@ -17,7 +17,7 @@ module HTTPX
17
17
  Errno::ECONNRESET,
18
18
  Errno::ECONNABORTED,
19
19
  Errno::EPIPE,
20
- (OpenSSL::SSL::SSLError if defined?(OpenSSL)),
20
+ (TLSError if defined?(TLSError)),
21
21
  TimeoutError,
22
22
  Parser::Error,
23
23
  Errno::EINVAL,
@@ -119,11 +119,9 @@ module HTTPX
119
119
  end
120
120
 
121
121
  def method_missing(meth, *args, &block)
122
- if @options.response_class.public_method_defined?(meth)
123
- response.__send__(meth, *args, &block)
124
- else
125
- super
126
- end
122
+ return super unless @options.response_class.public_method_defined?(meth)
123
+
124
+ response.__send__(meth, *args, &block)
127
125
  end
128
126
  end
129
127
  end
data/lib/httpx/pool.rb CHANGED
@@ -135,7 +135,6 @@ module HTTPX
135
135
  connection.on(:close) do
136
136
  unregister_connection(connection)
137
137
  end
138
- return if connection.state == :open
139
138
  end
140
139
 
141
140
  def unregister_connection(connection)
@@ -62,13 +62,7 @@ module HTTPX
62
62
  handler = @registry.fetch(tag)
63
63
  raise(Error, "#{tag} is not registered in #{self}") unless handler
64
64
 
65
- case handler
66
- when Symbol, String
67
- obj = const_get(handler)
68
- @registry[tag] = obj
69
- else
70
- handler
71
- end
65
+ handler
72
66
  end
73
67
 
74
68
  # @param [Object] tag the identifier for the handler in the registry
data/lib/httpx/request.rb CHANGED
@@ -217,6 +217,16 @@ module HTTPX
217
217
  "#{unbounded_body? ? "stream" : "@bytesize=#{bytesize}"}>"
218
218
  end
219
219
  # :nocov:
220
+
221
+ def respond_to_missing?(meth, *args)
222
+ @body.respond_to?(meth, *args) || super
223
+ end
224
+
225
+ def method_missing(meth, *args, &block)
226
+ return super unless @body.respond_to?(meth)
227
+
228
+ @body.__send__(meth, *args, &block)
229
+ end
220
230
  end
221
231
 
222
232
  def transition(nextstate)
@@ -247,7 +257,7 @@ module HTTPX
247
257
  return if @state == :expect
248
258
  end
249
259
  @state = nextstate
250
- emit(@state)
260
+ emit(@state, self)
251
261
  nil
252
262
  end
253
263
 
@@ -55,20 +55,12 @@ module HTTPX
55
55
  early_resolve(connection) || resolve(connection)
56
56
  end
57
57
 
58
- def timeout
59
- @connections.map(&:timeout).min
60
- end
61
-
62
58
  def closed?
63
- return true unless @resolver_connection
64
-
65
- resolver_connection.closed?
59
+ true
66
60
  end
67
61
 
68
- def interests
69
- return if @queries.empty?
70
-
71
- resolver_connection.__send__(__method__)
62
+ def empty?
63
+ true
72
64
  end
73
65
 
74
66
  private
@@ -268,8 +268,15 @@ module HTTPX
268
268
  @error.message
269
269
  end
270
270
 
271
- def to_s
272
- @error.backtrace.join("\n")
271
+ if Exception.method_defined?(:full_message)
272
+ def to_s
273
+ @error.full_message
274
+ end
275
+ else
276
+ def to_s
277
+ "#{@error.message} (#{@error.class})\n" \
278
+ "#{@error.backtrace.join("\n") if @error.backtrace}"
279
+ end
273
280
  end
274
281
 
275
282
  def raise_for_status
@@ -69,6 +69,11 @@ class HTTPX::Selector
69
69
 
70
70
  if @selectables.empty?
71
71
  @selectables = selectables
72
+
73
+ # do not run event loop if there's nothing to wait on.
74
+ # this might happen if connect failed and connection was unregistered.
75
+ return if (!r || r.empty?) && (!w || w.empty?)
76
+
72
77
  break
73
78
  else
74
79
  @selectables = [*selectables, @selectables]
data/lib/httpx/session.rb CHANGED
@@ -199,7 +199,18 @@ module HTTPX
199
199
  responses << response
200
200
  requests.shift
201
201
 
202
- break if requests.empty? || pool.empty?
202
+ break if requests.empty?
203
+
204
+ next unless pool.empty?
205
+
206
+ # in some cases, the pool of connections might have been drained because there was some
207
+ # handshake error, and the error responses have already been emitted, but there was no
208
+ # opportunity to traverse the requests, hence we're returning only a fraction of the errors
209
+ # we were supposed to. This effectively fetches the existing responses and return them.
210
+ while (request = requests.shift)
211
+ responses << fetch_response(request, connections, request_options)
212
+ end
213
+ break
203
214
  end
204
215
  responses
205
216
  ensure
@@ -269,7 +280,19 @@ module HTTPX
269
280
  end
270
281
  # :nocov:
271
282
  end
283
+ end
284
+
285
+ unless ENV.grep(/https?_proxy$/i).empty?
286
+ proxy_session = plugin(:proxy)
287
+ ::HTTPX.send(:remove_const, :Session)
288
+ ::HTTPX.send(:const_set, :Session, proxy_session.class)
289
+ end
272
290
 
273
- plugin(:proxy) unless ENV.grep(/https?_proxy$/i).empty?
291
+ # :nocov:
292
+ if Session.default_options.debug_level > 2
293
+ proxy_session = plugin(:internal_telemetry)
294
+ ::HTTPX.send(:remove_const, :Session)
295
+ ::HTTPX.send(:const_set, :Session, proxy_session.class)
274
296
  end
297
+ # :nocov:
275
298
  end
@@ -44,11 +44,9 @@ module HTTPX::Transcoder
44
44
  end
45
45
 
46
46
  def method_missing(meth, *args, &block)
47
- if @raw.respond_to?(meth)
48
- @raw.__send__(meth, *args, &block)
49
- else
50
- super
51
- end
47
+ return super unless @raw.respond_to?(meth)
48
+
49
+ @raw.__send__(meth, *args, &block)
52
50
  end
53
51
  end
54
52
 
data/lib/httpx/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
- VERSION = "0.11.3"
4
+ VERSION = "0.12.0"
5
5
  end
@@ -31,7 +31,7 @@ module HTTPX
31
31
 
32
32
  def on_headers: (Hash[String, Array[String]] headers) -> void
33
33
 
34
- def on_trailers: (Array[String, String] headers) -> void
34
+ def on_trailers: (Hash[String, Array[String]] headers) -> void
35
35
 
36
36
  def on_data: (string chunk) -> void
37
37
 
@@ -51,7 +51,7 @@ module HTTPX
51
51
 
52
52
  def disable_pipelining: () -> void
53
53
 
54
- def set_request_headers: (Request) -> void
54
+ def set_protocol_headers: (Request) -> void
55
55
 
56
56
  def headline_uri: (Request) -> String
57
57
 
@@ -3,7 +3,7 @@ module HTTPX
3
3
  include Callbacks
4
4
  include Loggable
5
5
 
6
- attr_reader streams: Hash[HTTP2Next::Stream, Response]
6
+ attr_reader streams: Hash[Request, HTTP2Next::Stream]
7
7
  attr_reader pending: Array[Request]
8
8
 
9
9
  @options: Options
@@ -13,7 +13,7 @@ module HTTPX
13
13
  @pings: Array[String]
14
14
  @buffer: Buffer
15
15
 
16
- def interests: () -> io_interests
16
+ def interests: () -> io_interests?
17
17
 
18
18
  def close: () -> void
19
19
 
@@ -23,6 +23,8 @@ module HTTPX
23
23
 
24
24
  def <<: (String) -> void
25
25
 
26
+ def can_buffer_more_requests: () -> bool
27
+
26
28
  def send: (Request) -> void
27
29
 
28
30
  def consume: () -> void
@@ -41,7 +43,7 @@ module HTTPX
41
43
 
42
44
  def headline_uri: (Request) -> String
43
45
 
44
- def set_request_headers: (Request) -> void
46
+ def set_protocol_headers: (Request) -> void
45
47
 
46
48
  def handle: (Request request, HTTP2Next::Stream stream) -> void
47
49
 
@@ -0,0 +1,17 @@
1
+ module HTTPX
2
+ module Plugins
3
+ module AwsSdkAuthentication
4
+ class Credentials
5
+ include _SigV4Credentials
6
+ end
7
+
8
+ def self.load_dependencies: (singleton(Session)) -> void
9
+
10
+ def self.extra_options: (Options) -> (Options)
11
+
12
+ module InstanceMethods
13
+ def aws_sdk_authentication: (**untyped) -> instance
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,65 @@
1
+ module HTTPX
2
+ module Plugins
3
+
4
+ interface _SigV4Credentials
5
+ def username: () -> String
6
+ def password: () -> String
7
+ def security_token: () -> String?
8
+ end
9
+
10
+ module AWSSigV4
11
+
12
+ Credentials: _SigV4Credentials
13
+
14
+
15
+ class Signer
16
+
17
+ def sign!: (Request) -> void
18
+
19
+ private
20
+
21
+ def initialize: (
22
+ service: String,
23
+ region: String,
24
+ ?credentials: _SigV4Credentials,
25
+ ?username: String,
26
+ ?password: String,
27
+ ?security_token: String,
28
+ ?provider_prefix: String,
29
+ ?header_provider_field: String,
30
+ ?unsigned_headers: Array[String],
31
+ ?apply_checksum_header: bool,
32
+ ?algorithm: String
33
+ ) -> untyped
34
+
35
+
36
+ def sha256_hexdigest: (bodyIO value) -> String
37
+
38
+ def hmac: (String key, String value) -> String
39
+ def hexhmac: (String key, String value) -> String
40
+ end
41
+
42
+
43
+ interface _SigV4Options
44
+ def sigv4_signer: () -> Signer?
45
+ def sigv4_signer=: (Signer) -> Signer
46
+ def with_sigv4_signer: (Signer) -> instance
47
+ end
48
+
49
+ def self.extra_options: (Options) -> (Options & _SigV4Options)
50
+ def self.load_dependencies: (singleton(Session)) -> void
51
+
52
+ module InstanceMethods
53
+ def aws_sigv4_authentication: (**untyped) -> instance
54
+ end
55
+
56
+ module RequestMethods
57
+ def canonical_path: () -> String
58
+
59
+ def canonical_query: () -> String
60
+ end
61
+ end
62
+
63
+ type awsSigV4Session = Session & Plugins::AWSSigV4::InstanceMethods
64
+ end
65
+ end
@@ -11,7 +11,7 @@ module HTTPX
11
11
  module InstanceMethods
12
12
  private
13
13
 
14
- def promise_headers: () -> Hash[HTTP2Next::Stream, Request]
14
+ def promise_headers: () -> Hash[HTTP2Next::Stream, Request]
15
15
  def __on_promise_request: (Connection::HTTP2, HTTP2Next::Stream, headers_input) -> void
16
16
  def __on_promise_response: (Connection::HTTP2, HTTP2Next::Stream, headers_input) -> void
17
17
  end