httpx 0.7.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +48 -0
  3. data/README.md +9 -5
  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_1_0.md +9 -0
  11. data/doc/release_notes/0_2_0.md +5 -0
  12. data/doc/release_notes/0_2_1.md +16 -0
  13. data/doc/release_notes/0_3_0.md +12 -0
  14. data/doc/release_notes/0_3_1.md +6 -0
  15. data/doc/release_notes/0_4_0.md +51 -0
  16. data/doc/release_notes/0_4_1.md +3 -0
  17. data/doc/release_notes/0_5_0.md +15 -0
  18. data/doc/release_notes/0_5_1.md +14 -0
  19. data/doc/release_notes/0_6_0.md +5 -0
  20. data/doc/release_notes/0_6_1.md +6 -0
  21. data/doc/release_notes/0_6_2.md +6 -0
  22. data/doc/release_notes/0_6_3.md +13 -0
  23. data/doc/release_notes/0_6_4.md +21 -0
  24. data/doc/release_notes/0_6_5.md +22 -0
  25. data/doc/release_notes/0_6_6.md +19 -0
  26. data/doc/release_notes/0_6_7.md +5 -0
  27. data/doc/release_notes/0_7_0.md +46 -0
  28. data/doc/release_notes/0_8_0.md +27 -0
  29. data/doc/release_notes/0_8_1.md +8 -0
  30. data/doc/release_notes/0_8_2.md +7 -0
  31. data/doc/release_notes/0_9_0.md +38 -0
  32. data/lib/httpx.rb +2 -0
  33. data/lib/httpx/adapters/faraday.rb +1 -1
  34. data/lib/httpx/altsvc.rb +18 -2
  35. data/lib/httpx/chainable.rb +9 -8
  36. data/lib/httpx/connection.rb +177 -72
  37. data/lib/httpx/connection/http1.rb +44 -13
  38. data/lib/httpx/connection/http2.rb +77 -34
  39. data/lib/httpx/domain_name.rb +440 -0
  40. data/lib/httpx/errors.rb +1 -0
  41. data/lib/httpx/extensions.rb +23 -3
  42. data/lib/httpx/headers.rb +2 -2
  43. data/lib/httpx/io/ssl.rb +11 -4
  44. data/lib/httpx/io/tcp.rb +16 -5
  45. data/lib/httpx/io/udp.rb +4 -1
  46. data/lib/httpx/loggable.rb +6 -6
  47. data/lib/httpx/options.rb +22 -15
  48. data/lib/httpx/parser/http1.rb +14 -17
  49. data/lib/httpx/plugins/compression.rb +49 -64
  50. data/lib/httpx/plugins/compression/brotli.rb +10 -14
  51. data/lib/httpx/plugins/compression/deflate.rb +7 -6
  52. data/lib/httpx/plugins/compression/gzip.rb +45 -17
  53. data/lib/httpx/plugins/cookies.rb +21 -60
  54. data/lib/httpx/plugins/cookies/cookie.rb +173 -0
  55. data/lib/httpx/plugins/cookies/jar.rb +74 -0
  56. data/lib/httpx/plugins/cookies/set_cookie_parser.rb +142 -0
  57. data/lib/httpx/plugins/digest_authentication.rb +2 -0
  58. data/lib/httpx/plugins/expect.rb +12 -1
  59. data/lib/httpx/plugins/follow_redirects.rb +20 -2
  60. data/lib/httpx/plugins/h2c.rb +1 -1
  61. data/lib/httpx/plugins/multipart.rb +0 -8
  62. data/lib/httpx/plugins/persistent.rb +6 -1
  63. data/lib/httpx/plugins/proxy.rb +16 -12
  64. data/lib/httpx/plugins/proxy/http.rb +7 -2
  65. data/lib/httpx/plugins/proxy/socks4.rb +4 -2
  66. data/lib/httpx/plugins/proxy/socks5.rb +5 -1
  67. data/lib/httpx/plugins/push_promise.rb +2 -2
  68. data/lib/httpx/plugins/rate_limiter.rb +51 -0
  69. data/lib/httpx/plugins/retries.rb +13 -6
  70. data/lib/httpx/plugins/stream.rb +109 -13
  71. data/lib/httpx/pool.rb +13 -15
  72. data/lib/httpx/registry.rb +2 -1
  73. data/lib/httpx/request.rb +14 -19
  74. data/lib/httpx/resolver.rb +7 -8
  75. data/lib/httpx/resolver/https.rb +22 -5
  76. data/lib/httpx/resolver/native.rb +27 -33
  77. data/lib/httpx/resolver/options.rb +2 -2
  78. data/lib/httpx/resolver/resolver_mixin.rb +1 -1
  79. data/lib/httpx/response.rb +22 -17
  80. data/lib/httpx/selector.rb +96 -97
  81. data/lib/httpx/session.rb +32 -24
  82. data/lib/httpx/timeout.rb +7 -1
  83. data/lib/httpx/transcoder/chunker.rb +0 -2
  84. data/lib/httpx/transcoder/form.rb +0 -6
  85. data/lib/httpx/transcoder/json.rb +0 -4
  86. data/lib/httpx/utils.rb +45 -0
  87. data/lib/httpx/version.rb +1 -1
  88. data/sig/buffer.rbs +24 -0
  89. data/sig/callbacks.rbs +14 -0
  90. data/sig/chainable.rbs +37 -0
  91. data/sig/connection.rbs +2 -0
  92. data/sig/connection/http2.rbs +4 -0
  93. data/sig/domain_name.rbs +17 -0
  94. data/sig/errors.rbs +3 -0
  95. data/sig/headers.rbs +42 -0
  96. data/sig/httpx.rbs +14 -0
  97. data/sig/loggable.rbs +11 -0
  98. data/sig/missing.rbs +12 -0
  99. data/sig/options.rbs +118 -0
  100. data/sig/parser/http1.rbs +50 -0
  101. data/sig/plugins/authentication.rbs +11 -0
  102. data/sig/plugins/basic_authentication.rbs +13 -0
  103. data/sig/plugins/compression.rbs +55 -0
  104. data/sig/plugins/compression/brotli.rbs +21 -0
  105. data/sig/plugins/compression/deflate.rbs +17 -0
  106. data/sig/plugins/compression/gzip.rbs +29 -0
  107. data/sig/plugins/cookies.rbs +26 -0
  108. data/sig/plugins/cookies/cookie.rbs +50 -0
  109. data/sig/plugins/cookies/jar.rbs +27 -0
  110. data/sig/plugins/digest_authentication.rbs +33 -0
  111. data/sig/plugins/expect.rbs +19 -0
  112. data/sig/plugins/follow_redirects.rbs +37 -0
  113. data/sig/plugins/h2c.rbs +26 -0
  114. data/sig/plugins/multipart.rbs +19 -0
  115. data/sig/plugins/persistent.rbs +17 -0
  116. data/sig/plugins/proxy.rbs +47 -0
  117. data/sig/plugins/proxy/http.rbs +14 -0
  118. data/sig/plugins/proxy/socks4.rbs +33 -0
  119. data/sig/plugins/proxy/socks5.rbs +36 -0
  120. data/sig/plugins/proxy/ssh.rbs +18 -0
  121. data/sig/plugins/push_promise.rbs +22 -0
  122. data/sig/plugins/rate_limiter.rbs +11 -0
  123. data/sig/plugins/retries.rbs +48 -0
  124. data/sig/plugins/stream.rbs +39 -0
  125. data/sig/pool.rbs +2 -0
  126. data/sig/registry.rbs +9 -0
  127. data/sig/request.rbs +61 -0
  128. data/sig/response.rbs +87 -0
  129. data/sig/session.rbs +49 -0
  130. data/sig/test.rbs +9 -0
  131. data/sig/timeout.rbs +29 -0
  132. data/sig/transcoder.rbs +16 -0
  133. data/sig/transcoder/body.rbs +18 -0
  134. data/sig/transcoder/chunker.rbs +32 -0
  135. data/sig/transcoder/form.rbs +16 -0
  136. data/sig/transcoder/json.rbs +14 -0
  137. metadata +120 -21
@@ -5,7 +5,9 @@ module HTTPX
5
5
  include Loggable
6
6
  include Chainable
7
7
 
8
- def initialize(options = {}, &blk)
8
+ EMPTY_HASH = {}.freeze
9
+
10
+ def initialize(options = EMPTY_HASH, &blk)
9
11
  @options = self.class.default_options.merge(options)
10
12
  @responses = {}
11
13
  @persistent = @options.persistent
@@ -29,13 +31,21 @@ module HTTPX
29
31
  end
30
32
 
31
33
  def request(*args, **options)
32
- requests = build_requests(*args, options)
34
+ requests = args.first.is_a?(Request) ? args : build_requests(*args, options)
33
35
  responses = send_requests(*requests, options)
34
36
  return responses.first if responses.size == 1
35
37
 
36
38
  responses
37
39
  end
38
40
 
41
+ def build_request(verb, uri, options = EMPTY_HASH)
42
+ rklass = @options.request_class
43
+ request = rklass.new(verb, uri, @options.merge(options).merge(persistent: @persistent))
44
+ request.on(:response, &method(:on_response).curry[request])
45
+ request.on(:promise, &method(:on_promise))
46
+ request
47
+ end
48
+
39
49
  private
40
50
 
41
51
  def pool
@@ -47,7 +57,7 @@ module HTTPX
47
57
  end
48
58
 
49
59
  def on_promise(_, stream)
50
- log(level: 2, label: "#{stream.id}: ") { "refusing stream!" }
60
+ log(level: 2) { "#{stream.id}: refusing stream!" }
51
61
  stream.refuse
52
62
  end
53
63
 
@@ -56,7 +66,8 @@ module HTTPX
56
66
  end
57
67
 
58
68
  def find_connection(request, connections, options)
59
- uri = URI(request.uri)
69
+ uri = request.uri
70
+
60
71
  connection = pool.find_connection(uri, options) || build_connection(uri, options)
61
72
  unless connections.nil? || connections.include?(connection)
62
73
  connections << connection
@@ -122,13 +133,13 @@ module HTTPX
122
133
  requests = case args.size
123
134
  when 1
124
135
  reqs = args.first
125
- reqs.map do |verb, uri|
126
- build_request(verb, uri, request_options)
136
+ reqs.map do |verb, uri, opts = EMPTY_HASH|
137
+ build_request(verb, uri, request_options.merge(opts))
127
138
  end
128
- when 2, 3
139
+ when 2
129
140
  verb, uris = args
130
141
  if uris.respond_to?(:each)
131
- uris.map do |uri, **opts|
142
+ uris.enum_for(:each).map do |uri, opts = EMPTY_HASH|
132
143
  build_request(verb, uri, request_options.merge(opts))
133
144
  end
134
145
  else
@@ -179,15 +190,13 @@ module HTTPX
179
190
  begin
180
191
  # guarantee ordered responses
181
192
  loop do
182
- begin
183
- request = requests.first
184
- pool.next_tick until (response = fetch_response(request, connections, request_options))
193
+ request = requests.first
194
+ pool.next_tick until (response = fetch_response(request, connections, request_options))
185
195
 
186
- responses << response
187
- requests.shift
196
+ responses << response
197
+ requests.shift
188
198
 
189
- break if requests.empty? || pool.empty?
190
- end
199
+ break if requests.empty? || pool.empty?
191
200
  end
192
201
  responses
193
202
  ensure
@@ -195,14 +204,6 @@ module HTTPX
195
204
  end
196
205
  end
197
206
 
198
- def build_request(verb, uri, options)
199
- rklass = @options.request_class
200
- request = rklass.new(verb, uri, @options.merge(options))
201
- request.on(:response, &method(:on_response).curry[request])
202
- request.on(:promise, &method(:on_promise))
203
- request
204
- end
205
-
206
207
  @default_options = Options.new
207
208
  @default_options.freeze
208
209
  @plugins = []
@@ -219,7 +220,7 @@ module HTTPX
219
220
  def plugin(pl, options = nil, &block)
220
221
  # raise Error, "Cannot add a plugin to a frozen config" if frozen?
221
222
  pl = Plugins.load_plugin(pl) if pl.is_a?(Symbol)
222
- unless @plugins.include?(pl)
223
+ if !@plugins.include?(pl)
223
224
  @plugins << pl
224
225
  pl.load_dependencies(self, &block) if pl.respond_to?(:load_dependencies)
225
226
  @default_options = @default_options.dup
@@ -243,6 +244,13 @@ module HTTPX
243
244
  opts.connection_class.__send__(:include, pl::ConnectionMethods) if defined?(pl::ConnectionMethods)
244
245
  pl.configure(self, &block) if pl.respond_to?(:configure)
245
246
 
247
+ @default_options.freeze
248
+ elsif options
249
+ # this can happen when two plugins are loaded, an one of them calls the other under the hood,
250
+ # albeit changing some default.
251
+ @default_options = @default_options.dup
252
+ @default_options = @default_options.merge(options)
253
+
246
254
  @default_options.freeze
247
255
  end
248
256
  self
@@ -6,6 +6,7 @@ module HTTPX
6
6
  class Timeout
7
7
  CONNECT_TIMEOUT = 60
8
8
  OPERATION_TIMEOUT = 60
9
+ KEEP_ALIVE_TIMEOUT = 20
9
10
 
10
11
  def self.new(opts = {})
11
12
  return opts if opts.is_a?(Timeout)
@@ -13,14 +14,16 @@ module HTTPX
13
14
  super(**opts)
14
15
  end
15
16
 
16
- attr_reader :connect_timeout, :operation_timeout, :total_timeout
17
+ attr_reader :connect_timeout, :operation_timeout, :keep_alive_timeout, :total_timeout
17
18
 
18
19
  def initialize(connect_timeout: CONNECT_TIMEOUT,
19
20
  operation_timeout: OPERATION_TIMEOUT,
21
+ keep_alive_timeout: KEEP_ALIVE_TIMEOUT,
20
22
  total_timeout: nil,
21
23
  loop_timeout: nil)
22
24
  @connect_timeout = connect_timeout
23
25
  @operation_timeout = operation_timeout
26
+ @keep_alive_timeout = keep_alive_timeout
24
27
  @total_timeout = total_timeout
25
28
 
26
29
  return unless loop_timeout
@@ -35,6 +38,7 @@ module HTTPX
35
38
  if other.is_a?(Timeout)
36
39
  @connect_timeout == other.instance_variable_get(:@connect_timeout) &&
37
40
  @operation_timeout == other.instance_variable_get(:@operation_timeout) &&
41
+ @keep_alive_timeout == other.instance_variable_get(:@keep_alive_timeout) &&
38
42
  @total_timeout == other.instance_variable_get(:@total_timeout)
39
43
  else
40
44
  super
@@ -49,9 +53,11 @@ module HTTPX
49
53
  when Timeout
50
54
  connect_timeout = other.instance_variable_get(:@connect_timeout) || @connect_timeout
51
55
  operation_timeout = other.instance_variable_get(:@operation_timeout) || @operation_timeout
56
+ keep_alive_timeout = other.instance_variable_get(:@keep_alive_timeout) || @keep_alive_timeout
52
57
  total_timeout = other.instance_variable_get(:@total_timeout) || @total_timeout
53
58
  Timeout.new(connect_timeout: connect_timeout,
54
59
  operation_timeout: operation_timeout,
60
+ keep_alive_timeout: keep_alive_timeout,
55
61
  total_timeout: total_timeout)
56
62
  else
57
63
  raise ArgumentError, "can't merge with #{other.class}"
@@ -10,8 +10,6 @@ module HTTPX::Transcoder
10
10
  class Encoder
11
11
  extend Forwardable
12
12
 
13
- def_delegator :@raw, :readpartial
14
-
15
13
  def initialize(body)
16
14
  @raw = body
17
15
  end
@@ -14,8 +14,6 @@ module HTTPX::Transcoder
14
14
 
15
15
  def_delegator :@raw, :bytesize
16
16
 
17
- def_delegator :@raw, :force_encoding
18
-
19
17
  def initialize(form)
20
18
  @raw = URI.encode_www_form(form)
21
19
  end
@@ -23,10 +21,6 @@ module HTTPX::Transcoder
23
21
  def content_type
24
22
  "application/x-www-form-urlencoded"
25
23
  end
26
-
27
- def to_str
28
- @raw.to_s
29
- end
30
24
  end
31
25
 
32
26
  def encode(form)
@@ -10,14 +10,10 @@ module HTTPX::Transcoder
10
10
  class Encoder
11
11
  extend Forwardable
12
12
 
13
- def_delegator :@raw, :to_str
14
-
15
13
  def_delegator :@raw, :to_s
16
14
 
17
15
  def_delegator :@raw, :bytesize
18
16
 
19
- def_delegator :@raw, :force_encoding
20
-
21
17
  def initialize(json)
22
18
  @raw = ::JSON.dump(json)
23
19
  @charset = @raw.encoding.name.downcase
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Utils
5
+ using URIExtensions
6
+
7
+ module_function
8
+
9
+ # The value of this field can be either an HTTP-date or a number of
10
+ # seconds to delay after the response is received.
11
+ def parse_retry_after(retry_after)
12
+ # first: bet on it being an integer
13
+ Integer(retry_after)
14
+ rescue ArgumentError
15
+ # Then it's a datetime
16
+ time = Time.httpdate(retry_after)
17
+ time - Time.now
18
+ end
19
+
20
+ if RUBY_VERSION < "2.3"
21
+ def uri(*args)
22
+ URI(*args)
23
+ end
24
+ else
25
+
26
+ URIParser = URI::RFC2396_Parser.new
27
+
28
+ def uri(uri)
29
+ return Kernel.URI(uri) unless uri.is_a?(String) && !uri.ascii_only?
30
+
31
+ uri = Kernel.URI(URIParser.escape(uri))
32
+
33
+ non_ascii_hostname = URIParser.unescape(uri.host)
34
+
35
+ non_ascii_hostname.force_encoding(Encoding::UTF_8)
36
+
37
+ idna_hostname = DomainName.new(non_ascii_hostname).hostname
38
+
39
+ uri.host = idna_hostname
40
+ uri.non_ascii_hostname = non_ascii_hostname
41
+ uri
42
+ end
43
+ end
44
+ end
45
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
- VERSION = "0.7.0"
4
+ VERSION = "0.10.0"
5
5
  end
@@ -0,0 +1,24 @@
1
+ module HTTPX
2
+ class Buffer
3
+ include _ToS
4
+ include _ToStr
5
+
6
+ @buffer: String
7
+
8
+ attr_reader limit: Integer
9
+
10
+ def full?: () -> bool
11
+ def shift!: (Integer) -> void
12
+
13
+ # delegated
14
+ def <<: (string data) -> void
15
+ def empty?: () -> bool
16
+ def bytesize: () -> Integer
17
+ def clear: () -> void
18
+ def replace: (string) -> void
19
+
20
+ private
21
+
22
+ def initialize: (Integer limit) -> untyped
23
+ end
24
+ end
@@ -0,0 +1,14 @@
1
+ module HTTPX
2
+ interface _Callable
3
+ def call: (*untyped) -> void
4
+ end
5
+
6
+ module Callbacks
7
+ def on: (Symbol) { (*untyped) -> void } -> void
8
+ def once: (Symbol) { (*untyped) -> void } -> void
9
+ def emit: (Symbol, *untyped) -> void
10
+
11
+ def callbacks: () -> Hash[Symbol, Array[_Callable]]
12
+ | (Symbol) -> Array[_Callable]
13
+ end
14
+ end
@@ -0,0 +1,37 @@
1
+ module HTTPX
2
+ module Chainable
3
+ def request: (*untyped, **untyped) -> (response | Array[response])
4
+ def accept: (String) -> Session
5
+ def wrap: () { (Session) -> void } -> void
6
+ | () -> void
7
+
8
+ def with: (options) -> Session
9
+ | (options) { (Session) -> Session} -> Session
10
+
11
+
12
+
13
+
14
+ def plugin: (:authentication) -> Plugins::sessionAuthentication
15
+ | (:basic_authentication) -> Plugins::sessionBasicAuthentication
16
+ | (:digest_authentication) -> Plugins::sessionDigestAuthentication
17
+ | (:compression) -> Session
18
+ | (:cookies) -> Plugins::sessionCookies
19
+ | (:expect) -> Session
20
+ | (:follow_redirects) -> Plugins::sessionFollowRedirects
21
+ | (:h2c) -> Plugins::sessionH2C
22
+ | (:multipart) -> Session
23
+ | (:persistent) -> Plugins::sessionPersistent
24
+ | (:proxy) -> Plugins::sessionProxy
25
+ | (:push_promise) -> Plugins::sessionPushPromise
26
+ | (:retries) -> Plugins::sessionRetries
27
+ | (:stream) -> Plugins::sessionStream
28
+ | (Symbol | Module, ?options?) { (Class) -> void } -> Session
29
+ | (Symbol | Module, ?options?) -> Session
30
+
31
+ private
32
+
33
+ def default_options: () -> Options
34
+ def branch: (options) -> Session
35
+ | (options) { (Session) -> Session } -> Session
36
+ end
37
+ end
@@ -0,0 +1,2 @@
1
+ class HTTPX::Connection
2
+ end
@@ -0,0 +1,4 @@
1
+ module HTTPX
2
+ class Connection::HTTP2
3
+ end
4
+ end
@@ -0,0 +1,17 @@
1
+ module HTTPX
2
+ class DomainName
3
+ type domain = string | DomainName
4
+
5
+ include Comparable
6
+
7
+ def normalize: (String) -> String
8
+
9
+ def cookie_domain?: (domain, ?bool?) -> bool
10
+
11
+ def self.new: (domain) -> untyped
12
+
13
+ private
14
+
15
+ def initialize: (string) -> untyped
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module HTTPX
2
+ Error: singleton(StandardError)
3
+ end
@@ -0,0 +1,42 @@
1
+ module HTTPX
2
+ class Headers
3
+ include _ToS
4
+
5
+ @headers: headers_hash
6
+
7
+ def self.new: (?untyped headers) -> Headers
8
+
9
+ def ==: (untyped other) -> bool
10
+
11
+ def []: (headers_key field) -> _ToS?
12
+ def []=: (headers_key field, headers_value value) -> void
13
+
14
+ def add: (headers_key field, string value) -> void
15
+ def delete: (headers_key field) -> void
16
+
17
+ def each: () { (headers_key, String) -> void } -> void
18
+ | () -> Enumerable[[headers_key, String], void]
19
+
20
+ def get: (headers_key field) -> Array[String]
21
+ def key?: (headers_key downcased_key) -> bool
22
+
23
+ def merge: (_Each[headers_key, headers_value] other) -> Headers
24
+
25
+ def same_headers?: (untyped headers) -> bool
26
+
27
+ def to_a: () -> Array[[headers_key, String]]
28
+ def inspect: () -> String
29
+
30
+ private
31
+
32
+ def initialize: (headers?) -> untyped
33
+ def array_value: (headers_value) -> Array[String]
34
+ def downcased: (headers_key) -> String
35
+ end
36
+
37
+ type headers_key = String | Symbol
38
+ type headers_value = _ToS | Array[_ToS]
39
+ type headers_hash = Hash[headers_key, headers_value]
40
+ type headers_input = headers_hash | Array[[headers_key, string]]
41
+ type headers = Headers | headers_input
42
+ end
@@ -0,0 +1,14 @@
1
+ module HTTPX
2
+ extend Chainable
3
+
4
+ VERSION: String
5
+
6
+ type uri = URI::HTTP | URI::HTTPS | string
7
+
8
+ type verb = :options | :get | :head | :post | :put | :delete | :trace | :connect |
9
+ :propfind | :proppatch | :mkcol | :copy | :move | :lock | :unlock | :orderpatch |
10
+ :acl | :report | :patch | :search
11
+
12
+ module Plugins
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ module HTTPX
2
+ interface _IOLogger
3
+ def <<: (string?) -> void
4
+ end
5
+
6
+ module Loggable
7
+ def log: (?level: Integer?, ?color: Symbol?) { () -> String } -> void
8
+
9
+ def log_exception: (StandardError, **untyped) -> void
10
+ end
11
+ end