httpx 0.11.3 → 0.14.0

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