httpx 1.6.3 → 1.7.1

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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/doc/release_notes/0_11_0.md +3 -3
  3. data/doc/release_notes/1_6_3.md +2 -2
  4. data/doc/release_notes/1_7_0.md +149 -0
  5. data/doc/release_notes/1_7_1.md +21 -0
  6. data/lib/httpx/adapters/datadog.rb +1 -1
  7. data/lib/httpx/adapters/faraday.rb +1 -1
  8. data/lib/httpx/adapters/webmock.rb +18 -9
  9. data/lib/httpx/altsvc.rb +4 -2
  10. data/lib/httpx/connection/http1.rb +9 -9
  11. data/lib/httpx/connection/http2.rb +2 -0
  12. data/lib/httpx/connection.rb +7 -9
  13. data/lib/httpx/domain_name.rb +1 -1
  14. data/lib/httpx/headers.rb +2 -2
  15. data/lib/httpx/io/tcp.rb +1 -1
  16. data/lib/httpx/loggable.rb +2 -0
  17. data/lib/httpx/options.rb +118 -22
  18. data/lib/httpx/parser/http1.rb +1 -0
  19. data/lib/httpx/plugins/auth/digest.rb +44 -4
  20. data/lib/httpx/plugins/auth.rb +113 -4
  21. data/lib/httpx/plugins/aws_sdk_authentication.rb +0 -1
  22. data/lib/httpx/plugins/cookies/cookie.rb +1 -0
  23. data/lib/httpx/plugins/digest_auth.rb +4 -5
  24. data/lib/httpx/plugins/fiber_concurrency.rb +16 -1
  25. data/lib/httpx/plugins/grpc/grpc_encoding.rb +1 -1
  26. data/lib/httpx/plugins/grpc.rb +2 -2
  27. data/lib/httpx/plugins/internal_telemetry.rb +1 -1
  28. data/lib/httpx/plugins/ntlm_auth.rb +5 -3
  29. data/lib/httpx/plugins/oauth.rb +156 -57
  30. data/lib/httpx/plugins/persistent.rb +3 -5
  31. data/lib/httpx/plugins/proxy/http.rb +0 -4
  32. data/lib/httpx/plugins/proxy.rb +3 -1
  33. data/lib/httpx/plugins/query.rb +1 -1
  34. data/lib/httpx/plugins/rate_limiter.rb +20 -15
  35. data/lib/httpx/plugins/response_cache.rb +3 -7
  36. data/lib/httpx/plugins/retries.rb +60 -24
  37. data/lib/httpx/plugins/ssrf_filter.rb +1 -1
  38. data/lib/httpx/plugins/stream.rb +60 -9
  39. data/lib/httpx/plugins/stream_bidi.rb +84 -16
  40. data/lib/httpx/pool.rb +12 -3
  41. data/lib/httpx/request/body.rb +1 -1
  42. data/lib/httpx/request.rb +10 -1
  43. data/lib/httpx/resolver/cache/base.rb +136 -0
  44. data/lib/httpx/resolver/cache/memory.rb +42 -0
  45. data/lib/httpx/resolver/cache.rb +18 -0
  46. data/lib/httpx/resolver/https.rb +74 -20
  47. data/lib/httpx/resolver/multi.rb +10 -2
  48. data/lib/httpx/resolver/native.rb +32 -6
  49. data/lib/httpx/resolver/resolver.rb +3 -3
  50. data/lib/httpx/resolver.rb +36 -114
  51. data/lib/httpx/response/body.rb +5 -3
  52. data/lib/httpx/response.rb +22 -6
  53. data/lib/httpx/selector.rb +14 -3
  54. data/lib/httpx/session.rb +6 -6
  55. data/lib/httpx/timers.rb +6 -12
  56. data/lib/httpx/transcoder/body.rb +1 -1
  57. data/lib/httpx/transcoder/gzip.rb +7 -2
  58. data/lib/httpx/transcoder/json.rb +1 -1
  59. data/lib/httpx/transcoder/multipart/decoder.rb +5 -5
  60. data/lib/httpx/transcoder/multipart/encoder.rb +1 -1
  61. data/lib/httpx/transcoder/multipart.rb +17 -9
  62. data/lib/httpx/transcoder.rb +4 -6
  63. data/lib/httpx/utils.rb +13 -0
  64. data/lib/httpx/version.rb +1 -1
  65. data/sig/altsvc.rbs +9 -3
  66. data/sig/chainable.rbs +3 -3
  67. data/sig/connection.rbs +1 -3
  68. data/sig/loggable.rbs +1 -1
  69. data/sig/options.rbs +12 -4
  70. data/sig/plugins/auth/digest.rbs +6 -0
  71. data/sig/plugins/auth.rbs +37 -4
  72. data/sig/plugins/basic_auth.rbs +3 -3
  73. data/sig/plugins/digest_auth.rbs +2 -4
  74. data/sig/plugins/fiber_concurrency.rbs +6 -0
  75. data/sig/plugins/ntlm_auth.rbs +2 -2
  76. data/sig/plugins/oauth.rbs +44 -15
  77. data/sig/plugins/rate_limiter.rbs +4 -2
  78. data/sig/plugins/response_cache/file_store.rbs +2 -0
  79. data/sig/plugins/response_cache.rbs +4 -0
  80. data/sig/plugins/retries.rbs +12 -4
  81. data/sig/plugins/stream.rbs +13 -3
  82. data/sig/plugins/stream_bidi.rbs +2 -2
  83. data/sig/pool.rbs +1 -1
  84. data/sig/resolver/cache/base.rbs +28 -0
  85. data/sig/resolver/cache/memory.rbs +13 -0
  86. data/sig/resolver/cache.rbs +16 -0
  87. data/sig/resolver/https.rbs +24 -0
  88. data/sig/resolver/multi.rbs +8 -0
  89. data/sig/resolver/native.rbs +2 -0
  90. data/sig/resolver.rbs +5 -20
  91. data/sig/response.rbs +3 -0
  92. data/sig/session.rbs +3 -5
  93. data/sig/timers.rbs +1 -1
  94. data/sig/transcoder/multipart.rbs +4 -2
  95. data/sig/transcoder.rbs +5 -1
  96. data/sig/utils.rbs +2 -0
  97. metadata +11 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f49b29ea3703f6f40abe3cd82d455235b0a0b50a694bd8fa55839ac471d32bbb
4
- data.tar.gz: ce933bb3c35d9434f810d4fc83acb244a1ad0db187123d5f01dcbbbd83622a47
3
+ metadata.gz: a9c30e22a2d406a61ef87a58fddd607bb2b1fad7b50d837d8db062d4a538a2d2
4
+ data.tar.gz: 9c5e1997b9c03434071c59b2deb2b004f7b1a1077c2ccaed03b9e3a1db7aafe5
5
5
  SHA512:
6
- metadata.gz: 258cb32129840347a1a37633fb6273133187c98636b63ffcc0b8064f39e3b5642d693ee5da5d7776529625fbe4b68b5193b405c2e0658b2c9b1193b49e316bdc
7
- data.tar.gz: 387397ab1954b6abf6a8a54d8764fb25e786fb1bd293401e8afd57f823464f67210527fee0b9e94b34cc3d2931143a5d94598fb6241f5728df77a8f1e045a5bf
6
+ metadata.gz: 87d99c4971b99f086f7811be81a8453e71bd2535120b5e560a2fc837d50e8a040e70ab1b2b191beee4b481fcea5063576d8895d318a0089183c21cb59fdd0f24
7
+ data.tar.gz: 85469cf5b5822990367f1e5591798a1512de90a60226853da4b4e6b00dde8816d6218dd2aacc9b5d85f84f6cb1637dd4f7b37836863f44144f05329e4fbdf7f5
@@ -21,7 +21,7 @@ stub_http_request(:get, "https://www.google.com").and_return(status: 200, body:
21
21
 
22
22
  ```
23
23
 
24
- Read more about it in the [webmock integration documentation](https://os85.gitlab.io/httpx/wiki/Webmock-Adapter).
24
+ Read more about it in the [webmock integration documentation](https://honeyryderchuck.gitlab.io/httpx/wiki/Webmock-Adapter).
25
25
 
26
26
  ### Datadog Adapter
27
27
 
@@ -40,7 +40,7 @@ A trace will be emitted for every request, so this should be an interesting visu
40
40
 
41
41
  Customization options and traces are similar to what [the net-http adapter provides](https://docs.datadoghq.com/tracing/setup_overview/setup/ruby/#nethttp).
42
42
 
43
- Read more about it in the [datadog integration documentation](https://os85.gitlab.io/httpx/wiki/Datadog-Adapter).
43
+ Read more about it in the [datadog integration documentation](https://honeyryderchuck.gitlab.io/httpx/wiki/Datadog-Adapter).
44
44
 
45
45
  ## Improvements
46
46
 
@@ -52,7 +52,7 @@ Read more about it in the [datadog integration documentation](https://os85.gitla
52
52
  HTTPX.plugin(:multipart).post(uri, form: {file: File.new("path/to/file")})
53
53
  ```
54
54
 
55
- Read more about it in the [multipart plugin documentation](https://os85.gitlab.io/httpx/wiki/Multipart-Uploads), including also about why this was made.
55
+ Read more about it in the [multipart plugin documentation](https://honeyryderchuck.gitlab.io/httpx/wiki/Multipart-Uploads), including also about why this was made.
56
56
 
57
57
  ### Expect Plugin
58
58
 
@@ -7,8 +7,8 @@
7
7
  ## Improvements
8
8
 
9
9
  * `system` resolver now works in a non-blocking manner, initiating the dns query in a separate thread and waiting on the pipe after that (it was blocking the main thread during resolution before).
10
- * reduce allocation to a sinfle shared option object when headers are passed as a session-level option, like `HTTPX.with(headers: geaders).get(...)`
11
- * privilege using `String#replace` in buffer operations (instead of "clean-then-append").
10
+ * reduce allocation to a single shared option object when headers are passed as a session-level option, like `HTTPX.with(headers: headers).get(...)`
11
+ * favour using `String#replace` in buffer operations (instead of "clean-then-append").
12
12
  * using `Array#unshift` instead of `Array#concat` in order to ensure that request ordering is respected in the face of an in-between error which requires reconnect-and-resend.
13
13
  * replaced more internal callback indirection with plain method calls.
14
14
 
@@ -0,0 +1,149 @@
1
+ # 1.7.0
2
+
3
+ ## Features
4
+
5
+ ### All AUTH plugin improvements!!
6
+
7
+ #### `:auth`
8
+
9
+ The `:auth` plugin can now be used with a dynamic callable object (methods, procs...) to generate the token.
10
+
11
+ ```ruby
12
+ # static token, pre 1.7.0
13
+ HTTPX.plugin(:auth).authorization("API-TOKEN")
14
+ # dynamically generate token!
15
+ HTTPX.plugin(:auth).authorization { generate_new_ephemeral_token }
16
+ ```
17
+
18
+ The `.authorization` method is now syntactic sugar for a new option, `:auth_header_value`, which can be used directly, alongside a `:auth_header_type`:
19
+
20
+ ```ruby
21
+ HTTPX.plugin(:auth).authorization("API-TOKEN")
22
+ HTTPX.plugin(:auth).authorization { generate_new_ephemeral_token }
23
+ HTTPX.plugin(:auth).authorization("Bearer API-TOKEN")
24
+ # same as
25
+ HTTPX.plugin(:auth, auth_header_value: "API-TOKEN")
26
+ HTTPX.plugin(:auth, auth_header_value: -> { generate_new_ephemeral_token })
27
+ HTTPX.plugin(:auth, auth_header_type: "Bearer", auth_header_value: "API-TOKEN")
28
+ ```
29
+
30
+ A new option `:generate_auth_value_on_retry` (which can be passed a callable receiving a response object) is now available; when used alongside the `:retries` plugin, it'll use the callable passed to the `.authorization` method to generate a new token before retrying the request:
31
+
32
+ ```ruby
33
+ authed = HTTPX.plugin(:retries).plugin(:auth, generate_auth_value_on_retry: ->(res) {
34
+ res.status == 401
35
+ }).authorization { generate_new_ephemeral_token }
36
+ authed.get("https://example.com")
37
+ ```
38
+
39
+ Read more about it in the [auth plugin wiki](https://honeyryderchuck.gitlab.io/httpx/wiki/Auth).
40
+
41
+ #### `:oauth`
42
+
43
+ The `:oauth` plugin implementation was revamped to make use of the `:auth` plugin new functionality, in order to make managing an oauth session more seamless.
44
+
45
+ Take the following example:
46
+
47
+ ```ruby
48
+ session = HTTPX.plugin(:oauth).with_oauth_options(
49
+ issuer: server.origin,
50
+ client_id: "CLIENT_ID",
51
+ client_secret: "SECRET",
52
+ )
53
+ session.get("https://example.com") #=> will load server metadata, request an access token, and perform the request with the access token.
54
+ # 2 hours later...
55
+ session.get("https://example.com")
56
+ # it'll reuse the same acces token, and if the request fails with 401, it'll request a new
57
+ # access token using the refresh token grant (when supported by the token issuer), and
58
+ # reperform the original request with the new access token.
59
+ ```
60
+
61
+ A new option, `:oauth_options`, is now available. The same parameters previously supported by the `:oauth_session` options are supported.
62
+
63
+ The following components are therefore deprecated and scheduled for removal in a future major version:
64
+
65
+ * `:oauth_session` option
66
+ * `.oauth_auth` session method
67
+ * `.with_access_token` session method
68
+
69
+ #### `:bearer_auth`, `:digest_auth`; `:ntlm_auth`
70
+
71
+ The `:auth` plugin is now the foundation of each of these plugins, which haven't suffered major API changes.
72
+
73
+ Read more about it in the [auth plugin wiki](https://honeyryderchuck.gitlab.io/httpx/wiki/OAuth).
74
+
75
+ ### `:retries` plugin: `:retry_after` backoff algorithms
76
+
77
+ The `:retries` plugins supports two new possible values for the `:retry_after` option: `:exponential_backoff` and `:polynomial_backoff`. They'll implement the respective calculation per each retry of a given request.
78
+
79
+ ```ruby
80
+ # will wait 1, 2, 4, 8, 16 seconds... depending of how many retries it can wait for
81
+ session = HTTPX.plugin(:retries, retry_after: :exponential_backoff)
82
+ ```
83
+
84
+ Read more about it in the [retries plugin wiki](https://honeyryderchuck.gitlab.io/httpx/wiki/Retries).
85
+
86
+ ### Ractor compatibility
87
+
88
+ `httpx` can be used within a ractor:
89
+
90
+ ```ruby
91
+ # ruby 4.0 syntax
92
+ response = Ractor.new(uri) do |uri|
93
+ HTTPX.get(uri)
94
+ end.value
95
+ ```
96
+
97
+ Bear in mind that, if you're connection via HTTPS, you'll need make sure you're using version 4.0 or higher of the `openssl` gem.
98
+
99
+ The test suite isn't exhaustive for ractors yet, but most plugins should also be ractor-compatible. If they don't work, that's a bug, and you're recommended to report it.
100
+
101
+ ## Improvements
102
+
103
+ * When encoding the `:json` param to send it as `application/json` payload, (example: `HTTPX.post("https://example.com", json: { foo: "bar })`), and the method uses the `json` standard library, it'll use `JSON.generate` (instead of `JSON.dump`) to encode the JSON payload. The reason is that, unlike `JSON.dump`, it doesn't rely on access to a global mutable hash, and is therefore ractor-safe.
104
+ * `:stream` plugin: the stream response class (the object that is returned in request calls is a stream response) can be extended now. You can add a `StreamResponseMethods` method to your plugin. Read more about it in the documentation.
105
+ * The resolver name cache (used by the native and https resolvers) was remade into a LRU cache, and will therefore not keep on growing when `httpx` is used to connect to a huge number of hostnames in a process.
106
+ * the native and https DNS resolvers will ignore answers with SERVFAIL code while there are retries left (some resolvers use such error code for rate limiting).
107
+ * `:timeout` option values are now validated, and an error is raised when passing an unrecognized timeout option (which is a good layer of protection for typos).
108
+ * pool: try passing the scheduler to a thread waiting on a connection, to avoid the current case where a connection may be checked-in-then-immediately-out-after when doing multiple requests in a loop, never giving a chance to others and potentially making the pool time out.
109
+ * headers deep-freeze and dup.
110
+
111
+ ## Bugfixes
112
+
113
+ * recover and close connection when an `IOError` is raised while waiting for IO readiness (could cause busy loops during HTTP/2 termination handshake).
114
+ * `:stream_bidi` plugin: improve thread-safety of buffer operations when the session is used from multiple threads.
115
+ * `:stream_bidi` plugin: added missing methods to signal in order to comply with the Selectable API (it was reported as raising `NoMethodError` under certain conditions).
116
+ * `:stream_bidi` plugin: can support non-bidirectional stream requests using the same session.
117
+ * `:stream` plugin: is now compatible with fiber scheduler engines (via the `:fiber_concurrency` plugin).
118
+ * `:stream` plugin: make sure that stream long-running requests do not share the same connection as regular threads.
119
+ * `:digest_auth` plugin: can now support qop values wrapped inside parentheses in the `www-authenticate` header (i.e. `qop="('auth',)"`).
120
+ * https resolver: handle 3XX redirect responses in HTTP DNS queries.
121
+ * https resolver: do not close HTTP connections whhich are shared across AAAA and A resolution paths when its in use by one of them.
122
+ * fix access to private method from `http-2` which was made public in more recent versions, but not in older still-supported versions.
123
+ * fixed resolver log message using a "connection" label.
124
+ * `HTTPX::Response.copy_to` will explicitly close the response at the end; given that the body file can be moved as a result, there is no guarantee that the response is still usable, so might as well just close it altogether.
125
+ * selector: avoid skipping persistent connections in the selector to deactivate due to iterate-and-modify.
126
+
127
+ ## Breaking Changes
128
+
129
+ ### `:digest_auth` error
130
+
131
+ The main error class for the `:digest_auth` plugin has been moved to a different location. If you were rescuing the `HTTPX::Plugins::DigestAuth::DigestError` error, you should now point to the `HTTPX::Authentication::Digest::Error`.
132
+
133
+ ### `:stream` plugin: `build_request` should receive `stream: true` for stream requests
134
+
135
+ In case you're building request objects before passing them to the session, you're now forced to create them with the `:stream` option on:
136
+
137
+ ```ruby
138
+ session = HTTPX.plugin(:stream)
139
+
140
+ # before
141
+ req = session.build_request("GET", "https://example.com/stream")
142
+ session.request(req, stream: true)
143
+
144
+ # after
145
+ req = session.build_request("GET", "https://example.com/stream", stream: true)
146
+ session.request(req)
147
+ ```
148
+
149
+ Previous code may still work in a few cases, but it is not guaranteed to work on all cases.
@@ -0,0 +1,21 @@
1
+ # 1.7.1
2
+
3
+ ## Improvements
4
+
5
+ * fixed timers handling in the selector loop which caused them to be traversed-for-drop twice on each tick.
6
+ * connection: take proxy connecting states when transitioning to `:closing` state.
7
+ * refactored name resolution cache to a cache adapter API with a default memory cache which keeps the current behaviour and will allow to add others.
8
+ * a new option, `:resolver_cache`, was added, which is `:memory` by default.
9
+ * this fixes an issue introduced for multi-ractor support where the cache store would not be thread-safe when used in a non main ractor.
10
+ * `:auth` plugin: when loaded with the `:retries` plugin, and the auth value method is dynamic/callable, will recover out-of-the-box from 401 HTTP responses by retrying the request with a newly-generated token.
11
+ * the `:rate_limiter` plugin will use this work to retry rate-limited responses without having to set setting `:retry_change_requests` to true, thereby eliminating a potential issue with non-idempotent requests.
12
+
13
+ ## Bugfixes
14
+
15
+ * HTTP1 parser: clear buffer on reset.
16
+ * http1 fix: handle the case in `#handle_error` where the response is an error response
17
+ * `:stream_bidi` plugin: fix internal state preventing bidi requests from being retried.
18
+ * `:stream_bidi` plugin: will only allow initial request body being passed using `:body` param (others, like `:json`, will raise an exception)
19
+ * https resolver: return `:idle` on `#state` calls when no connection is available (sometimes called in internal log messages).
20
+ * selector loop fix: when there are no selectables and an interval is passed, sleep instead of returning (thereby avoiding potential busy loop).
21
+ *
@@ -80,7 +80,7 @@ module Datadog::Tracing
80
80
  else
81
81
  span.set_tag(TAG_STATUS_CODE, response.status.to_s)
82
82
 
83
- span.set_error(::HTTPX::HTTPError.new(response)) if response.status >= 400 && response.status <= 599
83
+ span.set_error(::HTTPX::HTTPError.new(response)) if response.status.between?(400, 599)
84
84
 
85
85
  span.set_tags(
86
86
  Datadog.configuration.tracing.header_tags.response_tags(response.headers.to_h)
@@ -9,7 +9,7 @@ module Faraday
9
9
  class HTTPX < Faraday::Adapter
10
10
  def initialize(app = nil, opts = {}, &block)
11
11
  @connection = @bind = nil
12
- super(app, opts, &block)
12
+ super
13
13
  end
14
14
 
15
15
  module RequestMixin
@@ -38,12 +38,15 @@ module WebMock
38
38
 
39
39
  return build_error_response(request, webmock_response.exception) if webmock_response.exception
40
40
 
41
- request.options.response_class.new(request,
42
- webmock_response.status[0],
43
- "2.0",
44
- webmock_response.headers).tap do |res|
45
- res.mocked = true
46
- end
41
+ request
42
+ .options
43
+ .response_class
44
+ .new(
45
+ request,
46
+ webmock_response.status[0],
47
+ "2.0",
48
+ webmock_response.headers
49
+ ).tap(&:mock!)
47
50
  end
48
51
 
49
52
  def build_error_response(request, exception)
@@ -72,17 +75,23 @@ module WebMock
72
75
  end
73
76
 
74
77
  module ResponseMethods
75
- attr_accessor :mocked
76
-
77
78
  def initialize(*)
78
79
  super
79
80
  @mocked = false
80
81
  end
82
+
83
+ def mock!
84
+ @mocked = true
85
+ end
86
+
87
+ def mocked?
88
+ @mocked
89
+ end
81
90
  end
82
91
 
83
92
  module ResponseBodyMethods
84
93
  def decode_chunk(chunk)
85
- return chunk if @response.mocked
94
+ return chunk if @response.mocked?
86
95
 
87
96
  super
88
97
  end
data/lib/httpx/altsvc.rb CHANGED
@@ -8,6 +8,8 @@ module HTTPX
8
8
  module ConnectionMixin
9
9
  using URIExtensions
10
10
 
11
+ H2_ALTSVC_SCHEMES = %w[https h2].freeze
12
+
11
13
  def send(request)
12
14
  request.headers["alt-used"] = @origin.authority if @parser && !@write_buffer.full? && match_altsvcs?(request.uri)
13
15
 
@@ -41,12 +43,12 @@ module HTTPX
41
43
  end
42
44
 
43
45
  def altsvc_match?(uri, other_uri)
44
- other_uri = URI(other_uri)
46
+ other_uri = URI(other_uri) #: http_uri
45
47
 
46
48
  uri.origin == other_uri.origin || begin
47
49
  case uri.scheme
48
50
  when "h2"
49
- (other_uri.scheme == "https" || other_uri.scheme == "h2") &&
51
+ H2_ALTSVC_SCHEMES.include?(other_uri.scheme) &&
50
52
  uri.host == other_uri.host &&
51
53
  uri.port == other_uri.port
52
54
  else
@@ -10,6 +10,11 @@ module HTTPX
10
10
  MAX_REQUESTS = 200
11
11
  CRLF = "\r\n"
12
12
 
13
+ UPCASED = {
14
+ "www-authenticate" => "WWW-Authenticate",
15
+ "http2-settings" => "HTTP2-Settings",
16
+ "content-md5" => "Content-MD5",
17
+ }.freeze
13
18
  attr_reader :pending, :requests
14
19
 
15
20
  attr_accessor :max_concurrent_requests
@@ -192,9 +197,10 @@ module HTTPX
192
197
  end
193
198
 
194
199
  def handle_error(ex, request = nil)
195
- if (ex.is_a?(EOFError) || ex.is_a?(TimeoutError)) && @request && @request.response &&
196
- !@request.response.headers.key?("content-length") &&
197
- !@request.response.headers.key?("transfer-encoding")
200
+ if (ex.is_a?(EOFError) || ex.is_a?(TimeoutError)) && @request &&
201
+ (response = @request.response) && response.is_a?(Response) &&
202
+ !response.headers.key?("content-length") &&
203
+ !response.headers.key?("transfer-encoding")
198
204
  # if the response does not contain a content-length header, the server closing the
199
205
  # connnection is the indicator of response consumed.
200
206
  # https://greenbytes.de/tech/webdav/rfc2616.html#rfc.section.4.4
@@ -386,12 +392,6 @@ module HTTPX
386
392
  end
387
393
  end
388
394
 
389
- UPCASED = {
390
- "www-authenticate" => "WWW-Authenticate",
391
- "http2-settings" => "HTTP2-Settings",
392
- "content-md5" => "Content-MD5",
393
- }.freeze
394
-
395
395
  def capitalized(field)
396
396
  UPCASED[field] || field.split("-").map(&:capitalize).join("-")
397
397
  end
@@ -3,6 +3,8 @@
3
3
  require "securerandom"
4
4
  require "http/2"
5
5
 
6
+ HTTP2::Connection.__send__(:public, :send_buffer) if HTTP2::VERSION < "1.1.1"
7
+
6
8
  module HTTPX
7
9
  class Connection::HTTP2
8
10
  include Callbacks
@@ -100,14 +100,13 @@ module HTTPX
100
100
  def match?(uri, options)
101
101
  return false if !used? && (@state == :closing || @state == :closed)
102
102
 
103
- (
104
- @origins.include?(uri.origin) &&
103
+ @origins.include?(uri.origin) &&
105
104
  # if there is more than one origin to match, it means that this connection
106
105
  # was the result of coalescing. To prevent blind trust in the case where the
107
106
  # origin came from an ORIGIN frame, we're going to verify the hostname with the
108
107
  # SSL certificate
109
- (@origins.size == 1 || @origin == uri.origin || (@io.is_a?(SSL) && @io.verify_hostname(uri.host)))
110
- ) && @options == options
108
+ (@origins.size == 1 || @origin == uri.origin || (@io.is_a?(SSL) && @io.verify_hostname(uri.host))) &&
109
+ @options == options
111
110
  end
112
111
 
113
112
  def mergeable?(connection)
@@ -146,10 +145,6 @@ module HTTPX
146
145
  end
147
146
  end
148
147
 
149
- def create_idle(options = {})
150
- self.class.new(@origin, @options.merge(options))
151
- end
152
-
153
148
  def merge(connection)
154
149
  @origins |= connection.instance_variable_get(:@origins)
155
150
  if @ssl_session.nil? && connection.ssl_session
@@ -729,7 +724,7 @@ module HTTPX
729
724
 
730
725
  disconnect
731
726
  when :closing
732
- return unless @state == :idle || @state == :open
727
+ return unless connecting? || @state == :open
733
728
 
734
729
  unless @write_buffer.empty?
735
730
  # preset state before handshake, as error callbacks
@@ -855,6 +850,9 @@ module HTTPX
855
850
  end
856
851
  end
857
852
 
853
+ # recover internal state and emit all relevant error responses when +error+ was raised.
854
+ # this takes an optiona +request+ which may have already been handled and can be opted out
855
+ # in the state recovery process.
858
856
  def handle_error(error, request = nil)
859
857
  parser.handle_error(error, request) if @parser && @parser.respond_to?(:handle_error)
860
858
  while (req = @pending.shift)
@@ -55,7 +55,7 @@ module HTTPX
55
55
  def new(domain)
56
56
  return domain if domain.is_a?(self)
57
57
 
58
- super(domain)
58
+ super
59
59
  end
60
60
 
61
61
  # Normalizes a _domain_ using the Punycode algorithm as necessary.
data/lib/httpx/headers.rb CHANGED
@@ -42,12 +42,12 @@ module HTTPX
42
42
  # dupped initialization
43
43
  def initialize_dup(orig)
44
44
  super
45
- @headers = orig.instance_variable_get(:@headers).dup
45
+ @headers = orig.instance_variable_get(:@headers).transform_values(&:dup)
46
46
  end
47
47
 
48
48
  # freezes the headers hash
49
49
  def freeze
50
- @headers.freeze
50
+ @headers.each_value(&:freeze).freeze
51
51
  super
52
52
  end
53
53
 
data/lib/httpx/io/tcp.rb CHANGED
@@ -111,7 +111,7 @@ module HTTPX
111
111
  raise e if @ip_index.negative?
112
112
 
113
113
  log { "failed connecting to #{@ip} (#{e.message}), evict from cache and trying next..." }
114
- Resolver.cached_lookup_evict(@hostname, @ip)
114
+ @options.resolver_cache.evict(@hostname, @ip)
115
115
 
116
116
  @io = build_socket
117
117
  retry
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "fiber" if RUBY_VERSION < "3.0.0"
4
+
3
5
  module HTTPX
4
6
  module Loggable
5
7
  COLORS = {