httpx 0.11.3 → 0.14.0

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 (99) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/doc/release_notes/0_10_1.md +1 -1
  4. data/doc/release_notes/0_11_1.md +5 -1
  5. data/doc/release_notes/0_12_0.md +55 -0
  6. data/doc/release_notes/0_13_0.md +58 -0
  7. data/doc/release_notes/0_13_1.md +5 -0
  8. data/doc/release_notes/0_13_2.md +9 -0
  9. data/doc/release_notes/0_14_0.md +79 -0
  10. data/lib/httpx.rb +3 -3
  11. data/lib/httpx/adapters/faraday.rb +4 -6
  12. data/lib/httpx/altsvc.rb +1 -0
  13. data/lib/httpx/callbacks.rb +12 -3
  14. data/lib/httpx/chainable.rb +2 -2
  15. data/lib/httpx/connection.rb +92 -37
  16. data/lib/httpx/connection/http1.rb +37 -19
  17. data/lib/httpx/connection/http2.rb +82 -31
  18. data/lib/httpx/headers.rb +1 -1
  19. data/lib/httpx/io.rb +16 -3
  20. data/lib/httpx/io/ssl.rb +35 -24
  21. data/lib/httpx/io/tcp.rb +50 -28
  22. data/lib/httpx/io/tls.rb +218 -0
  23. data/lib/httpx/io/tls/box.rb +365 -0
  24. data/lib/httpx/io/tls/context.rb +199 -0
  25. data/lib/httpx/io/tls/ffi.rb +390 -0
  26. data/lib/httpx/io/udp.rb +31 -7
  27. data/lib/httpx/io/unix.rb +27 -12
  28. data/lib/httpx/options.rb +97 -74
  29. data/lib/httpx/parser/http1.rb +4 -4
  30. data/lib/httpx/plugins/aws_sdk_authentication.rb +84 -0
  31. data/lib/httpx/plugins/aws_sigv4.rb +219 -0
  32. data/lib/httpx/plugins/basic_authentication.rb +8 -3
  33. data/lib/httpx/plugins/compression.rb +24 -12
  34. data/lib/httpx/plugins/compression/brotli.rb +10 -7
  35. data/lib/httpx/plugins/compression/deflate.rb +8 -10
  36. data/lib/httpx/plugins/compression/gzip.rb +4 -3
  37. data/lib/httpx/plugins/cookies.rb +3 -7
  38. data/lib/httpx/plugins/digest_authentication.rb +5 -5
  39. data/lib/httpx/plugins/expect.rb +6 -6
  40. data/lib/httpx/plugins/follow_redirects.rb +4 -4
  41. data/lib/httpx/plugins/grpc.rb +247 -0
  42. data/lib/httpx/plugins/grpc/call.rb +62 -0
  43. data/lib/httpx/plugins/grpc/message.rb +85 -0
  44. data/lib/httpx/plugins/h2c.rb +43 -58
  45. data/lib/httpx/plugins/internal_telemetry.rb +93 -0
  46. data/lib/httpx/plugins/multipart.rb +2 -0
  47. data/lib/httpx/plugins/multipart/encoder.rb +4 -9
  48. data/lib/httpx/plugins/multipart/part.rb +1 -1
  49. data/lib/httpx/plugins/proxy.rb +4 -8
  50. data/lib/httpx/plugins/proxy/http.rb +1 -1
  51. data/lib/httpx/plugins/proxy/socks4.rb +8 -0
  52. data/lib/httpx/plugins/proxy/socks5.rb +8 -0
  53. data/lib/httpx/plugins/proxy/ssh.rb +3 -3
  54. data/lib/httpx/plugins/push_promise.rb +3 -2
  55. data/lib/httpx/plugins/rate_limiter.rb +1 -1
  56. data/lib/httpx/plugins/retries.rb +15 -16
  57. data/lib/httpx/plugins/stream.rb +99 -77
  58. data/lib/httpx/plugins/upgrade.rb +84 -0
  59. data/lib/httpx/plugins/upgrade/h2.rb +54 -0
  60. data/lib/httpx/pool.rb +14 -6
  61. data/lib/httpx/registry.rb +1 -7
  62. data/lib/httpx/request.rb +36 -3
  63. data/lib/httpx/resolver/https.rb +3 -11
  64. data/lib/httpx/resolver/native.rb +7 -3
  65. data/lib/httpx/response.rb +18 -7
  66. data/lib/httpx/selector.rb +5 -0
  67. data/lib/httpx/session.rb +41 -8
  68. data/lib/httpx/transcoder/body.rb +3 -5
  69. data/lib/httpx/transcoder/chunker.rb +1 -1
  70. data/lib/httpx/version.rb +1 -1
  71. data/sig/callbacks.rbs +2 -0
  72. data/sig/chainable.rbs +2 -1
  73. data/sig/connection/http1.rbs +7 -2
  74. data/sig/connection/http2.rbs +10 -4
  75. data/sig/options.rbs +16 -22
  76. data/sig/plugins/aws_sdk_authentication.rbs +19 -0
  77. data/sig/plugins/aws_sigv4.rbs +64 -0
  78. data/sig/plugins/basic_authentication.rbs +2 -0
  79. data/sig/plugins/compression.rbs +7 -5
  80. data/sig/plugins/compression/brotli.rbs +1 -1
  81. data/sig/plugins/compression/deflate.rbs +1 -1
  82. data/sig/plugins/compression/gzip.rbs +1 -1
  83. data/sig/plugins/cookies.rbs +0 -1
  84. data/sig/plugins/digest_authentication.rbs +0 -1
  85. data/sig/plugins/expect.rbs +0 -2
  86. data/sig/plugins/follow_redirects.rbs +0 -2
  87. data/sig/plugins/h2c.rbs +5 -10
  88. data/sig/plugins/persistent.rbs +0 -1
  89. data/sig/plugins/proxy.rbs +0 -1
  90. data/sig/plugins/push_promise.rbs +1 -1
  91. data/sig/plugins/retries.rbs +0 -4
  92. data/sig/plugins/stream.rbs +17 -16
  93. data/sig/plugins/upgrade.rbs +23 -0
  94. data/sig/request.rbs +7 -2
  95. data/sig/response.rbs +4 -1
  96. data/sig/session.rbs +4 -0
  97. metadata +56 -33
  98. data/lib/httpx/timeout.rb +0 -67
  99. data/sig/timeout.rbs +0 -29
@@ -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
@@ -101,7 +101,7 @@ module HTTPX
101
101
  transition(:open)
102
102
  end
103
103
 
104
- !@write_buffer.empty? || @queries.empty? ? :w : :r
104
+ calculate_interests
105
105
  end
106
106
 
107
107
  def <<(connection)
@@ -127,10 +127,14 @@ module HTTPX
127
127
 
128
128
  private
129
129
 
130
+ def calculate_interests
131
+ !@write_buffer.empty? || @queries.empty? ? :w : :r
132
+ end
133
+
130
134
  def consume
131
- dread
135
+ dread if calculate_interests == :r
132
136
  do_retry
133
- dwrite
137
+ dwrite if calculate_interests == :w
134
138
  end
135
139
 
136
140
  def do_retry
@@ -27,8 +27,7 @@ module HTTPX
27
27
  @version = version
28
28
  @status = Integer(status)
29
29
  @headers = @options.headers_class.new(headers)
30
- @body = @options.response_body_class.new(self, threshold_size: @options.body_threshold_size,
31
- window_size: @options.window_size)
30
+ @body = @options.response_body_class.new(self, @options)
32
31
  end
33
32
 
34
33
  def merge_headers(h)
@@ -83,17 +82,22 @@ module HTTPX
83
82
  end
84
83
 
85
84
  class Body
86
- def initialize(response, threshold_size:, window_size: 1 << 14)
85
+ def initialize(response, options)
87
86
  @response = response
88
87
  @headers = response.headers
89
- @threshold_size = threshold_size
90
- @window_size = window_size
88
+ @options = options
89
+ @threshold_size = options.body_threshold_size
90
+ @window_size = options.window_size
91
91
  @encoding = response.content_type.charset || Encoding::BINARY
92
92
  @length = 0
93
93
  @buffer = nil
94
94
  @state = :idle
95
95
  end
96
96
 
97
+ def closed?
98
+ @state == :closed
99
+ end
100
+
97
101
  def write(chunk)
98
102
  return if @state == :closed
99
103
 
@@ -268,8 +272,15 @@ module HTTPX
268
272
  @error.message
269
273
  end
270
274
 
271
- def to_s
272
- @error.backtrace.join("\n")
275
+ if Exception.method_defined?(:full_message)
276
+ def to_s
277
+ @error.full_message
278
+ end
279
+ else
280
+ def to_s
281
+ "#{@error.message} (#{@error.class})\n" \
282
+ "#{@error.backtrace.join("\n") if @error.backtrace}"
283
+ end
273
284
  end
274
285
 
275
286
  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
@@ -77,7 +77,7 @@ module HTTPX
77
77
  end
78
78
 
79
79
  def set_connection_callbacks(connection, connections, options)
80
- connection.on(:misdirected) do |misdirected_request|
80
+ connection.only(:misdirected) do |misdirected_request|
81
81
  other_connection = connection.create_idle(ssl: { alpn_protocols: %w[http/1.1] })
82
82
  other_connection.merge(connection)
83
83
  catch(:coalesced) do
@@ -88,11 +88,11 @@ module HTTPX
88
88
  misdirected_request.transition(:idle)
89
89
  other_connection.send(misdirected_request)
90
90
  end
91
- connection.on(:altsvc) do |alt_origin, origin, alt_params|
91
+ connection.only(:altsvc) do |alt_origin, origin, alt_params|
92
92
  other_connection = build_altsvc_connection(connection, connections, alt_origin, origin, alt_params, options)
93
93
  connections << other_connection if other_connection
94
94
  end
95
- connection.on(:exhausted) do
95
+ connection.only(:exhausted) do
96
96
  other_connection = connection.create_idle
97
97
  other_connection.merge(connection)
98
98
  catch(:coalesced) do
@@ -175,12 +175,18 @@ module HTTPX
175
175
  end
176
176
 
177
177
  def send_requests(*requests, options)
178
- connections = []
179
178
  request_options = @options.merge(options)
180
179
 
180
+ connections = _send_requests(requests, request_options)
181
+ receive_requests(requests, connections, request_options)
182
+ end
183
+
184
+ def _send_requests(requests, options)
185
+ connections = []
186
+
181
187
  requests.each do |request|
182
188
  error = catch(:resolve_error) do
183
- connection = find_connection(request, connections, request_options)
189
+ connection = find_connection(request, connections, options)
184
190
  connection.send(request)
185
191
  end
186
192
  next unless error.is_a?(ResolveError)
@@ -188,18 +194,33 @@ module HTTPX
188
194
  request.emit(:response, ErrorResponse.new(request, error, options))
189
195
  end
190
196
 
197
+ connections
198
+ end
199
+
200
+ def receive_requests(requests, connections, options)
191
201
  responses = []
192
202
 
193
203
  begin
194
204
  # guarantee ordered responses
195
205
  loop do
196
206
  request = requests.first
197
- pool.next_tick until (response = fetch_response(request, connections, request_options))
207
+ pool.next_tick until (response = fetch_response(request, connections, options))
198
208
 
199
209
  responses << response
200
210
  requests.shift
201
211
 
202
- break if requests.empty? || pool.empty?
212
+ break if requests.empty?
213
+
214
+ next unless pool.empty?
215
+
216
+ # in some cases, the pool of connections might have been drained because there was some
217
+ # handshake error, and the error responses have already been emitted, but there was no
218
+ # opportunity to traverse the requests, hence we're returning only a fraction of the errors
219
+ # we were supposed to. This effectively fetches the existing responses and return them.
220
+ while (request = requests.shift)
221
+ responses << fetch_response(request, connections, options)
222
+ end
223
+ break
203
224
  end
204
225
  responses
205
226
  ensure
@@ -269,7 +290,19 @@ module HTTPX
269
290
  end
270
291
  # :nocov:
271
292
  end
293
+ end
294
+
295
+ unless ENV.grep(/https?_proxy$/i).empty?
296
+ proxy_session = plugin(:proxy)
297
+ ::HTTPX.send(:remove_const, :Session)
298
+ ::HTTPX.send(:const_set, :Session, proxy_session.class)
299
+ end
272
300
 
273
- plugin(:proxy) unless ENV.grep(/https?_proxy$/i).empty?
301
+ # :nocov:
302
+ if Session.default_options.debug_level > 2
303
+ proxy_session = plugin(:internal_telemetry)
304
+ ::HTTPX.send(:remove_const, :Session)
305
+ ::HTTPX.send(:const_set, :Session, proxy_session.class)
274
306
  end
307
+ # :nocov:
275
308
  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
 
@@ -20,7 +20,7 @@ module HTTPX::Transcoder
20
20
  @raw.each do |chunk|
21
21
  yield "#{chunk.bytesize.to_s(16)}#{CRLF}#{chunk}#{CRLF}"
22
22
  end
23
- yield "0#{CRLF}#{CRLF}"
23
+ yield "0#{CRLF}"
24
24
  end
25
25
 
26
26
  def respond_to_missing?(meth, *args)
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.14.0"
5
5
  end
data/sig/callbacks.rbs CHANGED
@@ -6,8 +6,10 @@ module HTTPX
6
6
  module Callbacks
7
7
  def on: (Symbol) { (*untyped) -> void } -> void
8
8
  def once: (Symbol) { (*untyped) -> void } -> void
9
+ def only: (Symbol) { (*untyped) -> void } -> void
9
10
  def emit: (Symbol, *untyped) -> void
10
11
 
12
+ def callbacks_for?: (Symbol) -> bool
11
13
  def callbacks: () -> Hash[Symbol, Array[_Callable]]
12
14
  | (Symbol) -> Array[_Callable]
13
15
  end
data/sig/chainable.rbs CHANGED
@@ -18,7 +18,8 @@ module HTTPX
18
18
  | (:cookies) -> Plugins::sessionCookies
19
19
  | (:expect) -> Session
20
20
  | (:follow_redirects) -> Plugins::sessionFollowRedirects
21
- | (:h2c) -> Plugins::sessionH2C
21
+ | (:upgrade) -> Session
22
+ | (:h2c) -> Session
22
23
  | (:multipart) -> Session
23
24
  | (:persistent) -> Plugins::sessionPersistent
24
25
  | (:proxy) -> Plugins::sessionProxy
@@ -4,6 +4,7 @@ module HTTPX
4
4
  include Loggable
5
5
 
6
6
  attr_reader pending: Array[Request]
7
+ attr_reader requests: Array[Request]
7
8
 
8
9
  @options: Options
9
10
  @max_concurrent_requests: Integer
@@ -31,7 +32,7 @@ module HTTPX
31
32
 
32
33
  def on_headers: (Hash[String, Array[String]] headers) -> void
33
34
 
34
- def on_trailers: (Array[String, String] headers) -> void
35
+ def on_trailers: (Hash[String, Array[String]] headers) -> void
35
36
 
36
37
  def on_data: (string chunk) -> void
37
38
 
@@ -51,7 +52,7 @@ module HTTPX
51
52
 
52
53
  def disable_pipelining: () -> void
53
54
 
54
- def set_request_headers: (Request) -> void
55
+ def set_protocol_headers: (Request) -> void
55
56
 
56
57
  def headline_uri: (Request) -> String
57
58
 
@@ -59,6 +60,10 @@ module HTTPX
59
60
 
60
61
  def join_headers: (Request request) -> void
61
62
 
63
+ def join_trailers: (Request request) -> void
64
+
65
+ def join_headers2: (Headers headers) -> void
66
+
62
67
  def join_body: (Request request) -> void
63
68
 
64
69
  def capitalized: (String field) -> String
@@ -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
 
@@ -51,13 +53,17 @@ module HTTPX
51
53
 
52
54
  def join_headers: (HTTP2Next::Stream stream, Request request) -> void
53
55
 
56
+ def join_trailers: (HTTP2Next::Stream stream, Request request) -> void
57
+
54
58
  def join_body: (HTTP2Next::Stream stream, Request request) -> void
55
59
 
56
60
  def on_stream_headers: (HTTP2Next::Stream stream, Request request, Array[[String, String]] headers) -> void
57
61
 
62
+ def on_stream_trailers: (HTTP2Next::Stream stream, Request request, Array[[String, String]] headers) -> void
63
+
58
64
  def on_stream_data: (HTTP2Next::Stream stream, Request request, string data) -> void
59
65
 
60
- def on_stream_close: (HTTP2Next::Stream stream, Request request, Symbol? error) -> void
66
+ def on_stream_close: (HTTP2Next::Stream stream, Request request, (Symbol | StandardError)? error) -> void
61
67
 
62
68
  def on_frame: (string bytes) -> void
63
69
 
data/sig/options.rbs CHANGED
@@ -5,68 +5,67 @@ module HTTPX
5
5
  WINDOW_SIZE: Integer
6
6
  MAX_BODY_THRESHOLD_SIZE: Integer
7
7
 
8
+ type timeout_type = :connect_timeout | :operation_timeout | :keep_alive_timeout | :total_timeout
9
+ type timeout = Hash[timeout_type, Numeric?]
10
+
8
11
  def self.new: (options) -> instance
9
12
  | () -> instance
10
13
 
14
+ # headers
15
+ attr_reader uri: URI?
16
+ def uri=: (uri) -> void
17
+
11
18
  # headers
12
19
  attr_reader headers: Headers?
13
20
  def headers=: (headers) -> void
14
- def with_headers: (headers) -> instance
15
21
 
16
22
  # timeout
17
- attr_reader timeout: Timeout?
18
- def timeout=: (Hash[Symbol, untyped] | Timeout) -> void
19
- def with_timeout: (Hash[Symbol, untyped] | Timeout) -> instance
23
+ attr_reader timeout: timeout
24
+ def timeout=: (timeout) -> void
20
25
 
21
26
  # max_concurrent_requests
22
27
  attr_reader max_concurrent_requests: Integer?
23
28
  def max_concurrent_requests=: (Integer) -> void
24
- def with_max_concurrent_requests: (Integer) -> instance
25
29
 
26
30
  # max_requests
27
31
  attr_reader max_requests: Integer?
28
32
  def max_requests=: (Integer) -> void
29
- def with_max_requests: (Integer) -> instance
30
33
 
31
34
  # window_size
32
35
  attr_reader window_size: int?
33
36
  def window_size=: (int) -> void
34
- def with_window_size: (int) -> instance
35
37
 
36
38
  # body_threshold_size
37
39
  attr_reader body_threshold_size: int?
38
40
  def body_threshold_size=: (int) -> void
39
- def with_body_threshold_size: (int) -> instance
40
41
 
41
42
  # transport
42
43
  attr_reader transport: _ToS?
43
44
  def transport=: (_ToS) -> void
44
- def with_transport: (_ToS) -> instance
45
45
 
46
46
  # transport_options
47
47
  attr_reader transport_options: Hash[untyped, untyped]?
48
48
  def transport_options=: (Hash[untyped, untyped]) -> void
49
- def with_transport_options: (Hash[untyped, untyped]) -> instance
49
+
50
+ # addresses
51
+ attr_reader addresses: _ToAry[untyped]?
52
+ def addresses=: (_ToAry[untyped]) -> void
50
53
 
51
54
  # params
52
55
  attr_reader params: Transcoder::urlencoded_input?
53
56
  def params=: (Transcoder::urlencoded_input) -> void
54
- def with_params: (Transcoder::urlencoded_input) -> instance
55
57
 
56
58
  # form
57
59
  attr_reader form: Transcoder::urlencoded_input?
58
60
  def form=: (Transcoder::urlencoded_input) -> void
59
- def with_form: (Transcoder::urlencoded_input) -> instance
60
61
 
61
62
  # json
62
63
  attr_reader json: _ToJson?
63
64
  def json=: (_ToJson) -> void
64
- def with_json: (_ToJson) -> instance
65
65
 
66
66
  # body
67
67
  attr_reader body: bodyIO?
68
68
  def body=: (bodyIO) -> void
69
- def with_body: (bodyIO) -> instance
70
69
 
71
70
  # ssl
72
71
 
@@ -79,32 +78,27 @@ module HTTPX
79
78
  # request_class
80
79
  attr_reader request_class: singleton(Request)
81
80
  def request_class=: (singleton(Request)) -> void
82
- def with_request_class: (singleton(Request)) -> instance
83
81
 
84
82
  # io
85
- attr_reader io: _ToIO?
86
- def io=: (_ToIO) -> void
87
- def with_io: (_ToIO) -> instance
83
+ type io_option = _ToIO | Hash[String, _ToIO]
84
+ attr_reader io: io_option?
85
+ def io=: (io_option) -> void
88
86
 
89
87
  # fallback_protocol
90
88
  attr_reader fallback_protocol: String?
91
89
  def fallback_protocol=: (String) -> void
92
- def with_fallback_protocol: (String) -> instance
93
90
 
94
91
  # debug
95
92
  attr_reader debug: _IOLogger?
96
93
  def debug=: (_IOLogger) -> void
97
- def with_debug: (_IOLogger) -> instance
98
94
 
99
95
  # debug_level
100
96
  attr_reader debug_level: Integer?
101
97
  def debug_level=: (Integer) -> void
102
- def with_debug_level: (Integer) -> instance
103
98
 
104
99
  # persistent
105
100
  attr_reader persistent: bool?
106
101
  def persistent=: (bool) -> void
107
- def with_persistent: (bool) -> instance
108
102
 
109
103
  def ==: (untyped other) -> bool
110
104
  def merge: (_ToHash other) -> instance