httpx 1.2.0 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3df88a59256b85aacf9d98578c965626b3bfa4611695ae21a707252df71cb0a
4
- data.tar.gz: f1616869724c217272d0dc165130548edc8ca35eadd97ab42d5a17012a0019d6
3
+ metadata.gz: 7aff2acc77577cb1243c30a63766f404755131c2f1b19162b9b6ba13c49d61f8
4
+ data.tar.gz: 6297b0857edce0f5d93a0272c2b5e891257969c36cbf0c34c6a7ca6e4a5ced67
5
5
  SHA512:
6
- metadata.gz: b4ca8efc0a1ffe3dec2d2d6ee345bcb58bc2eec9e4c5b4fe951a889041be8eeaeb4b005d9771f0e712f161dad69f8de25d07d520c3e0cd17ae1574f53fadace1
7
- data.tar.gz: 509c48d74aafb520e3142c5f9b2356e2f36bf9d649eb723ca07a2863077057ab12ae2e17dbf61f13149eac4f80201252dad9b46cb904f5664b8910b62a868c1d
6
+ metadata.gz: 94da91dd14a8318e8f0a0199214f97c008b927c7b8d9f602f045434bbd375b4446c767ed805e424d826c3dc48e90a8a0fded192858d32763c4c38033e0107cae
7
+ data.tar.gz: 657cefe9a977ca56dddda02bbdb07a94b4a8f158193e5f0d695c254bdbcafd95ffccd29641d38958d16468a81fd236f4d95ef4487c335cf7e079fb29b8d456ba
data/README.md CHANGED
@@ -136,7 +136,7 @@ The test suite runs against [httpbin proxied over nghttp2](https://nghttp2.org/h
136
136
 
137
137
  All Rubies greater or equal to 2.7, and always latest JRuby and Truffleruby.
138
138
 
139
- **Note**: This gem is tested against all latest patch versions, i.e. if you're using 3.2.0 and you experience some issue, please test it against 3.2.$latest before creating an issue.
139
+ **Note**: This gem is tested against all latest patch versions, i.e. if you're using 3.3.0 and you experience some issue, please test it against 3.3.$latest before creating an issue.
140
140
 
141
141
  ## Resources
142
142
  | | |
@@ -2,13 +2,13 @@
2
2
 
3
3
  ## improvements
4
4
 
5
- * (Re-)enabling default retries in DNS name queries; this had been disabled as a result of revamping timouts, and resulted in queries only being sent once, which is very little for UDP-related traffic, and breaks if using DNs rate-limiting software. Retries the query just once, for now.
5
+ * (Re-)enabling default retries in DNS name queries; this had been disabled as a result of revamping timeouts, and resulted in queries only being sent once, which is very little for UDP-related traffic, and breaks if using DNs rate-limiting software. Retries the query just once, for now.
6
6
 
7
7
  ## bugfixes
8
8
 
9
9
  * reset timers when adding new intervals, as these may be added as a result on after-select connection handling, and must wait for the next tick cycle (before the patch, they were triggering too soon).
10
10
  * fixed "on close" callback leak on connection reuse, which caused linear performance regression in benchmarks performing one request per connection.
11
- * fixed hanging connection whan an HTTP/1.1 emitted a "connection: close" header but the server would not emit one (it closes the connection now).
11
+ * fixed hanging connection when an HTTP/1.1 emitted a "connection: close" header but the server would not emit one (it closes the connection now).
12
12
  * fixed recursive dns cached lookups which may have already expired, and created nil entries in the returned address list.
13
13
  * dns system resolver is now able to retry on failure.
14
14
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  ### `:ssrf_filter` plugin
6
6
 
7
- The `:ssrf_filter` p plugin prevents server-side request forgery attacks, by blocking requests to the internal network. This is useful when the URLs used to perform requests aren’t under the developer control (such as when they are inserted via a web application form).
7
+ The `:ssrf_filter` plugin prevents server-side request forgery attacks, by blocking requests to the internal network. This is useful when the URLs used to perform requests aren’t under the developer control (such as when they are inserted via a web application form).
8
8
 
9
9
  ```ruby
10
10
  http = HTTPX.plugin(:ssrf_filter)
@@ -31,7 +31,7 @@ More info under https://honeyryderchuck.gitlab.io/httpx/wiki/Callbacks
31
31
  This option allows passing a callback which, when returning `false`, can interrupt the redirect loop.
32
32
 
33
33
  ```ruby
34
- http = HTTPX.plugin(:follow_redirects).with(redirect_on: ->(location_uri) { BLACKLIST_HOSTS.include?(location_uri.host) ]
34
+ http = HTTPX.plugin(:follow_redirects).with(redirect_on: ->(location_uri) { BLACKLIST_HOSTS.include?(location_uri.host) })
35
35
  ```
36
36
 
37
37
  ### `:close_on_handshake_timeout` timeout
@@ -0,0 +1,6 @@
1
+ # 1.2.1
2
+
3
+ ## Bugfixes
4
+
5
+ * DoH resolver: try resolving other candidates on "domain not found" error (same behaviour as with native resolver).
6
+ * Allow HTTP/2 connections to exit cleanly when TLS session gets corrupted and termination handshake can't be performed.
@@ -0,0 +1,10 @@
1
+ # 1.2.2
2
+
3
+ ## Bugfixes
4
+
5
+ * only raise "unknown option" error when option is not supported, not anymore when error happens in the setup of a support option.
6
+ * usage of `HTTPX::Session#wrap` within a thread with other sessions using the `:persistent` plugin won't inadvertedly terminate its open connections.
7
+ * terminate connections on `IOError` (`SocketError` does not cover them).
8
+ * terminate connections on HTTP/2 protocol and handshake errors, which happen during establishment or termination of a HTTP/2 connection (they were being previously kept around, although they'd irrecoverable).
9
+ * `:oauth` plugin: fixing check preventing the OAuth metadata server integration path to be exercised.
10
+ * fix instantiation of the options headers object with the wrong headers class.
data/lib/httpx/buffer.rb CHANGED
@@ -3,6 +3,14 @@
3
3
  require "forwardable"
4
4
 
5
5
  module HTTPX
6
+ # Internal class to abstract a string buffer, by wrapping a string and providing the
7
+ # minimum possible API and functionality required.
8
+ #
9
+ # buffer = Buffer.new(640)
10
+ # buffer.full? #=> false
11
+ # buffer << "aa"
12
+ # buffer.capacity #=> 638
13
+ #
6
14
  class Buffer
7
15
  extend Forwardable
8
16
 
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
+ # Session mixin, implements most of the APIs that the users call.
5
+ # delegates to a default session when extended.
4
6
  module Chainable
5
7
  %w[head get post put delete trace options connect patch].each do |meth|
6
8
  class_eval(<<-MOD, __FILE__, __LINE__ + 1)
@@ -10,6 +12,7 @@ module HTTPX
10
12
  MOD
11
13
  end
12
14
 
15
+ # delegates to the default session (see HTTPX::Session#request).
13
16
  def request(*args, **options)
14
17
  branch(default_options).request(*args, **options)
15
18
  end
@@ -18,10 +21,12 @@ module HTTPX
18
21
  with(headers: { "accept" => String(type) })
19
22
  end
20
23
 
24
+ # delegates to the default session (see HTTPX::Session#wrap).
21
25
  def wrap(&blk)
22
26
  branch(default_options).wrap(&blk)
23
27
  end
24
28
 
29
+ # returns a new instance loaded with the +pl+ plugin and +options+.
25
30
  def plugin(pl, options = nil, &blk)
26
31
  klass = is_a?(S) ? self.class : Session
27
32
  klass = Class.new(klass)
@@ -29,16 +34,19 @@ module HTTPX
29
34
  klass.plugin(pl, options, &blk).new
30
35
  end
31
36
 
37
+ # returns a new instance loaded with +options+.
32
38
  def with(options, &blk)
33
39
  branch(default_options.merge(options), &blk)
34
40
  end
35
41
 
36
42
  private
37
43
 
44
+ # returns default instance of HTTPX::Options.
38
45
  def default_options
39
46
  @options || Session.default_options
40
47
  end
41
48
 
49
+ # returns a default instance of HTTPX::Session.
42
50
  def branch(options, &blk)
43
51
  return self.class.new(options, &blk) if is_a?(S)
44
52
 
@@ -228,11 +228,7 @@ module HTTPX
228
228
  return if @state == :closing || @state == :closed
229
229
 
230
230
  transition(:closing)
231
- unless @write_buffer.empty?
232
- # handshakes, try sending
233
- consume
234
- @write_buffer.clear
235
- end
231
+
236
232
  transition(:closed)
237
233
  end
238
234
 
@@ -526,14 +522,15 @@ module HTTPX
526
522
  Errno::ENETUNREACH,
527
523
  Errno::EPIPE,
528
524
  Errno::ENOENT,
529
- SocketError => e
525
+ SocketError,
526
+ IOError => e
530
527
  # connect errors, exit gracefully
531
528
  error = ConnectionError.new(e.message)
532
529
  error.set_backtrace(e.backtrace)
533
530
  connecting? && callbacks_for?(:connect_error) ? emit(:connect_error, error) : handle_error(error)
534
531
  @state = :closed
535
532
  emit(:close)
536
- rescue TLSError => e
533
+ rescue TLSError, HTTP2Next::Error::ProtocolError, HTTP2Next::Error::HandshakeError => e
537
534
  # connect errors, exit gracefully
538
535
  handle_error(e)
539
536
  connecting? && callbacks_for?(:connect_error) ? emit(:connect_error, e) : handle_error(e)
@@ -566,6 +563,15 @@ module HTTPX
566
563
  when :closing
567
564
  return unless @state == :idle || @state == :open
568
565
 
566
+ unless @write_buffer.empty?
567
+ # preset state before handshake, as error callbacks
568
+ # may take it back here.
569
+ @state = nextstate
570
+ # handshakes, try sending
571
+ consume
572
+ @write_buffer.clear
573
+ return
574
+ end
569
575
  when :closed
570
576
  return unless @state == :closing
571
577
  return unless @write_buffer.empty?
@@ -700,7 +706,7 @@ module HTTPX
700
706
  interval = @timers.after(timeout, callback)
701
707
 
702
708
  Array(finish_events).each do |event|
703
- # clean up reques timeouts if the connection errors out
709
+ # clean up request timeouts if the connection errors out
704
710
  request.once(event) do
705
711
  if @intervals.include?(interval)
706
712
  interval.delete(callback)
data/lib/httpx/io/tcp.rb CHANGED
@@ -78,7 +78,8 @@ module HTTPX
78
78
  rescue Errno::ECONNREFUSED,
79
79
  Errno::EADDRNOTAVAIL,
80
80
  Errno::EHOSTUNREACH,
81
- SocketError => e
81
+ SocketError,
82
+ IOError => e
82
83
  raise e if @ip_index <= 0
83
84
 
84
85
  log { "failed connecting to #{@ip} (#{e.message}), trying next..." }
data/lib/httpx/options.rb CHANGED
@@ -47,13 +47,13 @@ module HTTPX
47
47
  write_timeout: WRITE_TIMEOUT,
48
48
  request_timeout: REQUEST_TIMEOUT,
49
49
  },
50
+ :headers_class => Class.new(Headers),
50
51
  :headers => {},
51
52
  :window_size => WINDOW_SIZE,
52
53
  :buffer_size => BUFFER_SIZE,
53
54
  :body_threshold_size => MAX_BODY_THRESHOLD_SIZE,
54
55
  :request_class => Class.new(Request),
55
56
  :response_class => Class.new(Response),
56
- :headers_class => Class.new(Headers),
57
57
  :request_body_class => Class.new(Request::Body),
58
58
  :response_body_class => Class.new(Response::Body),
59
59
  :connection_class => Class.new(Connection),
@@ -99,7 +99,7 @@ module HTTPX
99
99
  # :compress_request_body :: whether to auto-decompress response body (defaults to <tt>true</tt>)
100
100
  # :timeout :: hash of timeout configurations (supports <tt>:connect_timeout</tt>, <tt>:settings_timeout</tt>,
101
101
  # <tt>:operation_timeout</tt>, <tt>:keep_alive_timeout</tt>, <tt>:read_timeout</tt>, <tt>:write_timeout</tt>
102
- # and <tt>:request_timeout</tt>
102
+ # and <tt>:request_timeout</tt>
103
103
  # :headers :: hash of HTTP headers (ex: <tt>{ "x-custom-foo" => "bar" }</tt>)
104
104
  # :window_size :: number of bytes to read from a socket
105
105
  # :buffer_size :: internal read and write buffer size in bytes
@@ -154,7 +154,7 @@ module HTTPX
154
154
  end
155
155
 
156
156
  def option_headers(value)
157
- Headers.new(value)
157
+ headers_class.new(value)
158
158
  end
159
159
 
160
160
  def option_timeout(value)
@@ -342,12 +342,11 @@ module HTTPX
342
342
  defaults.each do |k, v|
343
343
  next if v.nil?
344
344
 
345
- begin
346
- value = __send__(:"option_#{k}", v)
347
- instance_variable_set(:"@#{k}", value)
348
- rescue NoMethodError
349
- raise Error, "unknown option: #{k}"
350
- end
345
+ option_method_name = :"option_#{k}"
346
+ raise Error, "unknown option: #{k}" unless respond_to?(option_method_name)
347
+
348
+ value = __send__(option_method_name, v)
349
+ instance_variable_set(:"@#{k}", value)
351
350
  end
352
351
  end
353
352
  end
@@ -60,9 +60,9 @@ module HTTPX
60
60
  end
61
61
 
62
62
  def load(http)
63
- return unless @token_endpoint && @token_endpoint_auth_method && @grant_type && @scope
63
+ return if @token_endpoint_auth_method && @grant_type && @scope
64
64
 
65
- metadata = http.get("#{issuer}/.well-known/oauth-authorization-server").raise_for_status.json
65
+ metadata = http.get("#{@issuer}/.well-known/oauth-authorization-server").raise_for_status.json
66
66
 
67
67
  @token_endpoint = metadata["token_endpoint"]
68
68
  @scope = metadata["scopes_supported"]
@@ -70,6 +70,7 @@ module HTTPX
70
70
  @token_endpoint_auth_method = Array(metadata["token_endpoint_auth_methods_supported"]).find do |am|
71
71
  SUPPORTED_AUTH_METHODS.include?(am)
72
72
  end
73
+ nil
73
74
  end
74
75
 
75
76
  def merge(other)
@@ -246,8 +246,8 @@ module HTTPX
246
246
  return super unless @options.proxy
247
247
 
248
248
  @state = :open
249
- transition(:closing)
250
- transition(:closed)
249
+
250
+ super
251
251
  emit(:close)
252
252
  end
253
253
 
data/lib/httpx/pool.rb CHANGED
@@ -19,6 +19,17 @@ module HTTPX
19
19
  @connections = []
20
20
  end
21
21
 
22
+ def wrap
23
+ connections = @connections
24
+ @connections = []
25
+
26
+ begin
27
+ yield self
28
+ ensure
29
+ @connections.unshift(*connections)
30
+ end
31
+ end
32
+
22
33
  def empty?
23
34
  @connections.empty?
24
35
  end
data/lib/httpx/request.rb CHANGED
@@ -61,7 +61,7 @@ module HTTPX
61
61
  @uri = origin.merge("#{base_path}#{@uri}")
62
62
  end
63
63
 
64
- @headers = @options.headers_class.new(@options.headers)
64
+ @headers = @options.headers.dup
65
65
  @headers["user-agent"] ||= USER_AGENT
66
66
  @headers["accept"] ||= "*/*"
67
67
 
@@ -138,9 +138,14 @@ module HTTPX
138
138
  # Indicates no such domain was found.
139
139
 
140
140
  host = @requests.delete(request)
141
- connection = reset_hostname(host)
141
+ connection = reset_hostname(host, reset_candidates: false)
142
142
 
143
- emit_resolve_error(connection)
143
+ unless @queries.value?(connection)
144
+ emit_resolve_error(connection)
145
+ return
146
+ end
147
+
148
+ resolve
144
149
  when :dns_error
145
150
  host = @requests.delete(request)
146
151
  connection = reset_hostname(host)
@@ -166,6 +166,7 @@ module HTTPX
166
166
 
167
167
  private
168
168
 
169
+ # prepares inflaters for the advertised encodings in "content-encoding" header.
169
170
  def initialize_inflaters
170
171
  @inflaters = nil
171
172
 
@@ -187,6 +188,7 @@ module HTTPX
187
188
  end
188
189
  end
189
190
 
191
+ # passes the +chunk+ through all inflaters to decode it.
190
192
  def decode_chunk(chunk)
191
193
  @inflaters.reverse_each do |inflater|
192
194
  chunk = inflater.call(chunk)
@@ -195,6 +197,7 @@ module HTTPX
195
197
  chunk
196
198
  end
197
199
 
200
+ # tries transitioning the body STM to the +nextstate+.
198
201
  def transition(nextstate)
199
202
  case nextstate
200
203
  when :open
@@ -70,9 +70,10 @@ module HTTPX
70
70
  @body.write(data)
71
71
  end
72
72
 
73
- # returns the response mime type, as per what's declared in the content-type header.
73
+ # returns the HTTPX::ContentType for the response, as per what's declared in the content-type header.
74
74
  #
75
- # response.content_type #=> "text/plain"
75
+ # response.content_type #=> #<HTTPX::ContentType:xxx @header_value="text/plain">
76
+ # response.content_type.mime_type #=> "text/plain"
76
77
  def content_type
77
78
  @content_type ||= ContentType.new(@headers["content-type"])
78
79
  end
data/lib/httpx/session.rb CHANGED
@@ -28,13 +28,15 @@ module HTTPX
28
28
  # http.get("https://wikipedia.com")
29
29
  # end # wikipedia connection closes here
30
30
  def wrap
31
- begin
32
- prev_persistent = @persistent
33
- @persistent = true
34
- yield self
35
- ensure
36
- @persistent = prev_persistent
37
- close unless @persistent
31
+ prev_persistent = @persistent
32
+ @persistent = true
33
+ pool.wrap do
34
+ begin
35
+ yield self
36
+ ensure
37
+ @persistent = prev_persistent
38
+ close unless @persistent
39
+ end
38
40
  end
39
41
  end
40
42
 
data/lib/httpx/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
- VERSION = "1.2.0"
4
+ VERSION = "1.2.2"
5
5
  end
data/sig/pool.rbs CHANGED
@@ -7,6 +7,8 @@ module HTTPX
7
7
  @selector: Selector
8
8
  @connections: Array[Connection]
9
9
 
10
+ def wrap: () { (instance) -> void } -> void
11
+
10
12
  def empty?: () -> void
11
13
 
12
14
  def next_tick: () -> void
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: httpx
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Cardoso
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-14 00:00:00.000000000 Z
11
+ date: 2024-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http-2-next
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 1.0.1
19
+ version: 1.0.3
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 1.0.1
26
+ version: 1.0.3
27
27
  description: A client library for making HTTP requests from Ruby.
28
28
  email:
29
29
  - cardoso_tiago@hotmail.com
@@ -138,6 +138,8 @@ extra_rdoc_files:
138
138
  - doc/release_notes/1_1_4.md
139
139
  - doc/release_notes/1_1_5.md
140
140
  - doc/release_notes/1_2_0.md
141
+ - doc/release_notes/1_2_1.md
142
+ - doc/release_notes/1_2_2.md
141
143
  files:
142
144
  - LICENSE.txt
143
145
  - README.md
@@ -247,6 +249,8 @@ files:
247
249
  - doc/release_notes/1_1_4.md
248
250
  - doc/release_notes/1_1_5.md
249
251
  - doc/release_notes/1_2_0.md
252
+ - doc/release_notes/1_2_1.md
253
+ - doc/release_notes/1_2_2.md
250
254
  - lib/httpx.rb
251
255
  - lib/httpx/adapters/datadog.rb
252
256
  - lib/httpx/adapters/faraday.rb