nxt_http_client 2.1.0 → 2.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0c6ed0e9821cb3c06245006c1c79a99788e4f46d2fc3ab2fbc4ab6e5009a48f3
4
- data.tar.gz: 30462fe5cfa99f8b5f3dbd6c5dacbcab75841dc93a155a41afb4f66ebcbb58fd
3
+ metadata.gz: b2c6e9588a07ff6820f7988990ce130bbe7ad8b0cc3467b83b5222e7a7061ed3
4
+ data.tar.gz: 6bf74b2f2c1788cc1140fa63444238a04a55130fe1bdd2ac6d1621d941aad22f
5
5
  SHA512:
6
- metadata.gz: 0dcf0bd755f9ee72b3961ac8cf57c3b6cc32c712fe2f4e4ff357ff98a0074a13436d84e395e35da1ea02e9ef173a7177f57487fa8b6207e98582c72b43800c41
7
- data.tar.gz: d22767005133cd7bf1a2b6fd7c0804a37e7bae777af7e93493cc0678207037dbcafa05493c0dab7ebf4d23b31c8bedfc1d3178de15953514fe8980efc4f70897
6
+ metadata.gz: 479a2c6036211c446ccab839f750aba66b3d10df80676eca0134f23be474798f65b1524e38e8e85c834b2e353c92f4561140b7190d41849227a9f175a9915381
7
+ data.tar.gz: a7694a6c37e29420f042b0bc65588586783fee0e4509771accb72e4f3cd2dc204992b05d98604288546a4351c45cb361d711bebdeffef514c52fb47e21c20615
@@ -0,0 +1,30 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ tests:
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ ruby-version: ["3.3", "3.4", "4.0"]
18
+ services:
19
+ redis:
20
+ image: redis:7
21
+ ports:
22
+ - 6379:6379
23
+ options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
24
+ steps:
25
+ - uses: actions/checkout@v6
26
+ - uses: ruby/setup-ruby@v1
27
+ with:
28
+ ruby-version: ${{ matrix.ruby-version }}
29
+ bundler-cache: true
30
+ - run: bundle exec rspec
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.3.2
1
+ 4.0.5
data/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ # v2.2.0 2026-06-15
2
+ - Report HTTP error details to Sentry via the structured `set_context('http_error', …)` instead of the
3
+ deprecated `set_extras(http_error_details: …)`. (Typhoeus is not auto-instrumented by Sentry, so the gem
4
+ attaches this itself.)
5
+ - `Error#to_h` now redacts the `Authorization` header and basic-auth `userpwd` (it is sent to Sentry).
6
+ - `json_response` now returns `nil` for an empty/204 body instead of raising `JSON::ParserError`.
7
+ - Add an opt-in error taxonomy under `NxtHttpClient::Error`. With `config.raise_error_taxonomy = true` the client
8
+ raises a typed subclass for an unhandled 4xx/5xx/code-0 response instead of returning it:
9
+ - HTTP status: `ClientError` with `BadRequest` (400), `Unauthorized` (401), `Forbidden` (403),
10
+ `NotFound` (404), `UnprocessableEntity` (422), `TooManyRequests` (429); `ServerError` (5xx).
11
+ - Network (`return_code`-mapped code-0): `NetworkError` with `Timeout`, `ConnectionFailed`,
12
+ `NameResolutionError`, `TlsError`; `CertificateError` (cert verification — a sibling, not a child).
13
+ - Retryable errors share base classes — `retry_on NxtHttpClient::Error::NetworkError, NxtHttpClient::Error::ServerError`.
14
+ 4xx, `CertificateError` and 429 are excluded (429 retry policy is left to consumers).
15
+ - `map_error(status, klass)` DSL to override the mapping per client (e.g. a domain `ValidationFailed` that
16
+ parses the body); inherited by subclasses.
17
+ - `config.raise_error_taxonomy` defaults to `false`, so the upgrade is backwards compatible — existing behavior
18
+ (and `raise_response_errors`) is unchanged until you opt in. A consumer's own `on(<code>)`/`on(:error)`/
19
+ `on(:timed_out)` callback always takes precedence over the taxonomy.
20
+
1
21
  # v2.1.0 2024-06-05
2
22
  - Bump dependencies
3
23
 
data/Gemfile.lock CHANGED
@@ -1,86 +1,98 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nxt_http_client (2.1.0)
5
- activesupport (< 8.0)
4
+ nxt_http_client (2.2.0)
5
+ activesupport
6
6
  nxt_registry
7
7
  typhoeus
8
8
 
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- activesupport (7.1.3.4)
12
+ activesupport (8.1.3)
13
13
  base64
14
14
  bigdecimal
15
- concurrent-ruby (~> 1.0, >= 1.0.2)
15
+ concurrent-ruby (~> 1.0, >= 1.3.1)
16
16
  connection_pool (>= 2.2.5)
17
17
  drb
18
18
  i18n (>= 1.6, < 2)
19
+ json
20
+ logger (>= 1.4.2)
19
21
  minitest (>= 5.1)
20
- mutex_m
21
- tzinfo (~> 2.0)
22
- addressable (2.8.6)
23
- public_suffix (>= 2.0.2, < 6.0)
24
- base64 (0.2.0)
25
- bigdecimal (3.1.8)
22
+ securerandom (>= 0.3)
23
+ tzinfo (~> 2.0, >= 2.0.5)
24
+ uri (>= 0.13.1)
25
+ addressable (2.9.0)
26
+ public_suffix (>= 2.0.2, < 8.0)
27
+ base64 (0.3.0)
28
+ bigdecimal (4.1.2)
26
29
  coderay (1.1.3)
27
- concurrent-ruby (1.3.1)
28
- connection_pool (2.4.1)
29
- crack (1.0.0)
30
+ concurrent-ruby (1.3.6)
31
+ connection_pool (3.0.2)
32
+ crack (1.0.1)
30
33
  bigdecimal
31
34
  rexml
32
- diff-lcs (1.5.1)
33
- drb (2.2.1)
34
- ethon (0.16.0)
35
+ diff-lcs (1.6.2)
36
+ drb (2.2.3)
37
+ ethon (0.18.0)
35
38
  ffi (>= 1.15.0)
36
- ffi (1.17.0)
37
- ffi (1.17.0-x86_64-darwin)
38
- ffi (1.17.0-x86_64-linux-gnu)
39
- hashdiff (1.1.0)
40
- i18n (1.14.5)
39
+ logger
40
+ ffi (1.17.4)
41
+ ffi (1.17.4-x86_64-darwin)
42
+ ffi (1.17.4-x86_64-linux-gnu)
43
+ hashdiff (1.2.1)
44
+ i18n (1.14.8)
41
45
  concurrent-ruby (~> 1.0)
46
+ io-console (0.8.2)
47
+ json (2.19.8)
48
+ logger (1.7.0)
42
49
  method_source (1.1.0)
43
- minitest (5.23.1)
44
- mutex_m (0.2.0)
50
+ minitest (6.0.6)
51
+ drb (~> 2.0)
52
+ prism (~> 1.5)
45
53
  nxt_registry (0.3.10)
46
54
  activesupport
47
55
  nxt_vcr_harness (0.2.0)
48
56
  rspec (~> 3.0)
49
57
  vcr (~> 6.0)
50
- pry (0.14.2)
58
+ prism (1.9.0)
59
+ pry (0.16.0)
51
60
  coderay (~> 1.1)
52
61
  method_source (~> 1.0)
53
- public_suffix (5.0.5)
54
- rake (13.2.1)
55
- redis (5.2.0)
62
+ reline (>= 0.6.0)
63
+ public_suffix (7.0.5)
64
+ rake (13.4.2)
65
+ redis (5.4.1)
56
66
  redis-client (>= 0.22.0)
57
- redis-client (0.22.2)
67
+ redis-client (0.29.0)
58
68
  connection_pool
59
- rexml (3.2.8)
60
- strscan (>= 3.0.9)
61
- rspec (3.13.0)
69
+ reline (0.6.3)
70
+ io-console (~> 0.5)
71
+ rexml (3.4.4)
72
+ rspec (3.13.2)
62
73
  rspec-core (~> 3.13.0)
63
74
  rspec-expectations (~> 3.13.0)
64
75
  rspec-mocks (~> 3.13.0)
65
- rspec-core (3.13.0)
76
+ rspec-core (3.13.6)
66
77
  rspec-support (~> 3.13.0)
67
- rspec-expectations (3.13.0)
78
+ rspec-expectations (3.13.5)
68
79
  diff-lcs (>= 1.2.0, < 2.0)
69
80
  rspec-support (~> 3.13.0)
70
- rspec-mocks (3.13.1)
81
+ rspec-mocks (3.13.8)
71
82
  diff-lcs (>= 1.2.0, < 2.0)
72
83
  rspec-support (~> 3.13.0)
73
- rspec-support (3.13.1)
84
+ rspec-support (3.13.7)
74
85
  rspec_junit_formatter (0.6.0)
75
86
  rspec-core (>= 2, < 4, != 2.12.0)
76
- strscan (3.1.0)
77
- timecop (0.9.9)
78
- typhoeus (1.4.1)
79
- ethon (>= 0.9.0)
87
+ securerandom (0.4.1)
88
+ timecop (0.9.11)
89
+ typhoeus (1.6.0)
90
+ ethon (>= 0.18.0)
80
91
  tzinfo (2.0.6)
81
92
  concurrent-ruby (~> 1.0)
82
- vcr (6.2.0)
83
- webmock (3.23.1)
93
+ uri (1.1.1)
94
+ vcr (6.4.0)
95
+ webmock (3.26.2)
84
96
  addressable (>= 2.8.0)
85
97
  crack (>= 0.3.2)
86
98
  hashdiff (>= 0.4.0, < 2.0.0)
data/README.md CHANGED
@@ -48,7 +48,7 @@ class UserServiceClient < NxtHttpClient::Client
48
48
  # Note: This error handler is set by default when you use
49
49
  # config.raise_response_errors = true
50
50
  handler.on(:error) do |response|
51
- Sentry.set_extras(http_error_details: error.to_h)
51
+ Sentry.set_context('http_error', error.to_h)
52
52
  raise StandardError, "I can't handle this: #{response.code}"
53
53
  end
54
54
  end
@@ -118,12 +118,16 @@ Register your default request options on the class level. Available options are:
118
118
  - `base_url=`
119
119
  - `x_request_id_proc=`
120
120
  - `json_request=`: Shorthand to set the Content-Type request header to JSON and automatically convert request bodies to JSON
121
- - `json_response=`: Shorthand to set the Accept request header and automatically convert success response bodies to JSON
122
- - `raise_response_errors=`: Makes the client raise a `NxtHttpClient::Error` for a non-success response.
123
- You can also do this manually by setting a response_handler.
121
+ - `json_response=`: Shorthand to set the Accept request header and automatically convert success response bodies to JSON (an empty/204 body becomes `nil`)
122
+ - `raise_response_errors=`: Makes the client raise a generic `NxtHttpClient::Error` for a non-success response.
123
+ Superseded by `raise_error_taxonomy` (which raises typed errors and takes precedence when both are set); kept for
124
+ backward compatibility.
125
+ - `raise_error_taxonomy=`: Defaults to `false`. Opt in to raise the mapped `NxtHttpClient::Error` taxonomy
126
+ (`ClientError`/`ServerError`/`NetworkError` subclasses) on an unhandled 4xx/5xx/code-0 response instead of
127
+ returning it. See [Error taxonomy](#error-taxonomy).
124
128
  - `bearer_auth=`: Set a bearer token to be sent in the Authorization header
125
129
  - `basic_auth=`: Pass a Hash containing `:username` and `:password`, to be sent as Basic credentials in the Authorization header
126
- - `timeouts(total:, connect: nil)`: Configure timeouts
130
+ - `timeout_seconds(total:, connect: nil)`: Configure timeouts
127
131
 
128
132
  ### response_handler
129
133
 
@@ -215,13 +219,84 @@ To set a timeout, use the `timeout_seconds` config method:
215
219
  configure do |config|
216
220
  config.timeout_seconds(total: 10)
217
221
  # You can also set a connect timeout
218
- config.timeout_seconds(total: 10, connecttimeout: 2)
222
+ config.timeout_seconds(total: 10, connect: 2)
219
223
  end
220
224
  ```
221
225
 
222
226
  NxtHttpClient::Error exposes the `timed_out?` method from `Typhoeus::Response`, so you can check if an error is raised due to a timeout.
223
227
  This is useful when setting a custom timeout value in your configuration.
224
228
 
229
+ ### Error taxonomy
230
+
231
+ Set `config.raise_error_taxonomy = true` and the client raises a typed subclass of `NxtHttpClient::Error` for an
232
+ unhandled 4xx, 5xx, or code-0 (network) response, so you no longer need to hand-roll per-status
233
+ `on(400)`/`on(422)`/`on(5xx)`/`on(0)` handlers just to get a usable taxonomy. (3xx and other codes are returned
234
+ as before.) It is **off by default** (the client returns the response); all classes inherit from
235
+ `NxtHttpClient::Error`, so existing `rescue NxtHttpClient::Error` handlers keep working.
236
+
237
+ **HTTP status:**
238
+
239
+ | status | class | retryable? |
240
+ |---------------|---------------------------------------------|------------|
241
+ | 400 | `NxtHttpClient::Error::BadRequest` | no |
242
+ | 401 | `NxtHttpClient::Error::Unauthorized` | no |
243
+ | 403 | `NxtHttpClient::Error::Forbidden` | no |
244
+ | 404 | `NxtHttpClient::Error::NotFound` | no |
245
+ | 422 | `NxtHttpClient::Error::UnprocessableEntity` | no |
246
+ | 429 | `NxtHttpClient::Error::TooManyRequests` | up to you |
247
+ | other 4xx | `NxtHttpClient::Error::ClientError` | no |
248
+ | 5xx | `NxtHttpClient::Error::ServerError` | yes |
249
+
250
+ **Network / code 0.** Typhoeus/libcurl surfaces network failures and timeouts as a response with HTTP **code 0**
251
+ (no response received); the real cause lives in libcurl's `return_code`:
252
+
253
+ | libcurl `return_code` | class | retryable? |
254
+ |----------------------------------------------------|---------------------------------------------|------------|
255
+ | `:operation_timedout` | `NxtHttpClient::Error::Timeout` | yes |
256
+ | `:couldnt_connect` | `NxtHttpClient::Error::ConnectionFailed` | yes |
257
+ | `:couldnt_resolve_host` / `:couldnt_resolve_proxy` | `NxtHttpClient::Error::NameResolutionError` | yes |
258
+ | `:ssl_connect_error` and other non-cert `:ssl_*` | `NxtHttpClient::Error::TlsError` | yes |
259
+ | any other code-0 | `NxtHttpClient::Error::NetworkError` | yes |
260
+ | cert verification (`:peer_failed_verification`, …) | `NxtHttpClient::Error::CertificateError` | no |
261
+
262
+ #### Retrying
263
+
264
+ The retryable errors share two base classes, so a job retries them in one place:
265
+
266
+ ```ruby
267
+ retry_on NxtHttpClient::Error::NetworkError, NxtHttpClient::Error::ServerError
268
+ ```
269
+
270
+ `ClientError` (4xx) is not retried — those are caller mistakes. `CertificateError` is a **sibling** of `NetworkError`
271
+ (not a child), so it is excluded from `retry_on NetworkError` — a failed cert/CA verification is permanent.
272
+ `TooManyRequests` (429) is left out of the retryable set; add your own `retry_on NxtHttpClient::Error::TooManyRequests`
273
+ (ideally honoring `Retry-After`) if you want it.
274
+
275
+ #### Precedence and opting out
276
+
277
+ A consumer's own `on(<code>)` / `on(:error)` / `on(:timed_out)` callback always takes precedence; the raise only
278
+ fires when nothing else handled the response. So you can enable the taxonomy for retries yet still handle, say, a
279
+ 404 inline with your own `on(404)`.
280
+
281
+ #### Domain-typed errors
282
+
283
+ Map a status to your own error class with `map_error` to get a domain error that parses the response body. It is
284
+ inherited by subclasses and overrides the default for that status. `map_error` only takes effect when
285
+ `raise_error_taxonomy` is enabled — it customizes the taxonomy, it does not enable raising on its own:
286
+
287
+ ```ruby
288
+ class MyService::Client < NxtHttpClient::Client
289
+ configure { |config| config.raise_error_taxonomy = true }
290
+ map_error 422, MyService::Error::ValidationFailed
291
+ end
292
+
293
+ class MyService::Error::ValidationFailed < NxtHttpClient::Error
294
+ def default_message
295
+ body.dig('errors', 0, 'detail') # `body` parses JSON response bodies for you
296
+ end
297
+ end
298
+ ```
299
+
225
300
  ### Logging
226
301
 
227
302
  NxtHttpClient also comes with a log method on the class level that you can pass a proc if you want to log your request.
@@ -144,7 +144,25 @@ module NxtHttpClient
144
144
 
145
145
  def callback_or_response(response, response_handler)
146
146
  callback = response_handler.callback_for_response(response)
147
- callback && instance_exec(response, &callback) || response
147
+ return instance_exec(response, &callback) || response if callback
148
+
149
+ return raise_mapped_error(response) if raise_mapped_error?(response)
150
+
151
+ response
152
+ end
153
+
154
+ # Reached only when no consumer callback matched, so a consumer on(<code>)/on(:error) keeps precedence.
155
+ def raise_mapped_error?(response)
156
+ return false unless config.raise_error_taxonomy
157
+
158
+ code = response.code.to_i
159
+ code.zero? || (400..599).cover?(code)
160
+ end
161
+
162
+ def raise_mapped_error(response)
163
+ error = self.class.error_class_for(response).new(response)
164
+ ::Sentry.set_context('http_error', error.to_h) if defined?(::Sentry)
165
+ raise error
148
166
  end
149
167
 
150
168
  def build_response_handler(handler, &block)
@@ -153,17 +171,20 @@ module NxtHttpClient
153
171
  if config.json_response
154
172
  response_handler.configure do |handler|
155
173
  handler.on(:success) do |response|
156
- response.define_singleton_method(:body) { JSON(response.response_body) }
174
+ # nil for a blank/204 body parsing "" would raise JSON::ParserError
175
+ response.define_singleton_method(:body) { response.response_body.presence && JSON(response.response_body) }
157
176
  response
158
177
  end
159
178
  end
160
179
  end
161
180
 
162
- if config.raise_response_errors
181
+ # Legacy generic-error raising. Superseded by raise_error_taxonomy (typed), which takes precedence here
182
+ # via the callback_or_response fallback — so don't also register this shadowing on(:error) handler.
183
+ if config.raise_response_errors && !config.raise_error_taxonomy
163
184
  response_handler.configure do |handler|
164
185
  handler.on(:error) do |response|
165
186
  error = NxtHttpClient::Error.new(response)
166
- ::Sentry.set_extras(http_error_details: error.to_h) if defined?(::Sentry)
187
+ ::Sentry.set_context('http_error', error.to_h) if defined?(::Sentry)
167
188
  raise error
168
189
  end
169
190
  end
@@ -52,6 +52,24 @@ module NxtHttpClient
52
52
  @response_handler
53
53
  end
54
54
 
55
+ # Override the taxonomy's error class for a status, e.g. `map_error 422, MyService::ValidationFailed`.
56
+ # Only takes effect when config.raise_error_taxonomy is enabled.
57
+ def map_error(status, error_class)
58
+ unless error_class.is_a?(Class) && error_class <= NxtHttpClient::Error
59
+ raise ArgumentError, "#{error_class.inspect} must be a subclass of NxtHttpClient::Error"
60
+ end
61
+
62
+ error_map[Integer(status)] = error_class
63
+ end
64
+
65
+ def error_map
66
+ @error_map ||= dup_option_from_ancestor(:@error_map) { {} }
67
+ end
68
+
69
+ def error_class_for(response)
70
+ error_map[response.code.to_i] || NxtHttpClient::Error.error_class_for(response)
71
+ end
72
+
55
73
  private
56
74
 
57
75
  def client_ancestors
@@ -8,7 +8,10 @@ module NxtHttpClient
8
8
  json_request: false,
9
9
  # Helper to set the Accept request header and automatically convert success response bodies to JSON
10
10
  json_response: false,
11
- raise_response_errors: false,
11
+ raise_response_errors: false, # Legacy: raises a generic NxtHttpClient::Error. Superseded by raise_error_taxonomy.
12
+ # Opt in (default false) to raise the mapped NxtHttpClient::Error taxonomy on an unhandled
13
+ # 4xx/5xx/code-0 response instead of returning it. See README "Error taxonomy".
14
+ raise_error_taxonomy: false,
12
15
 
13
16
  bearer_auth: nil,
14
17
  basic_auth: nil,
@@ -1,5 +1,49 @@
1
1
  module NxtHttpClient
2
2
  class Error < StandardError
3
+ # Cert/trust failures — mapped to non-retryable CertificateError, kept out of the generic TlsError.
4
+ CERTIFICATE_RETURN_CODES = %i[
5
+ peer_failed_verification ssl_certproblem ssl_cacert_badfile ssl_issuer_error ssl_crl_badfile
6
+ ].freeze
7
+
8
+ REDACTED = '[REDACTED]'
9
+ SENSITIVE_HEADERS = %w[Authorization Proxy-Authorization].freeze
10
+
11
+ def self.from_response(response, message = nil)
12
+ error_class_for(response).new(response, message)
13
+ end
14
+
15
+ def self.error_class_for(response)
16
+ code = response.respond_to?(:code) ? response.code.to_i : 0
17
+ return network_error_class(response.respond_to?(:return_code) ? response.return_code : nil) if code.zero?
18
+
19
+ status_error_class(code)
20
+ end
21
+
22
+ def self.status_error_class(code)
23
+ case code
24
+ when 400 then BadRequest
25
+ when 401 then Unauthorized
26
+ when 403 then Forbidden
27
+ when 404 then NotFound
28
+ when 422 then UnprocessableEntity
29
+ when 429 then TooManyRequests
30
+ when 400..499 then ClientError
31
+ when 500..599 then ServerError
32
+ else self # 3xx etc. → base Error
33
+ end
34
+ end
35
+
36
+ def self.network_error_class(return_code)
37
+ case return_code
38
+ when :operation_timedout then Timeout
39
+ when :couldnt_connect then ConnectionFailed
40
+ when :couldnt_resolve_host, :couldnt_resolve_proxy then NameResolutionError
41
+ when *CERTIFICATE_RETURN_CODES then CertificateError
42
+ else
43
+ return_code.to_s.include?('ssl') ? TlsError : NetworkError
44
+ end
45
+ end
46
+
3
47
  def initialize(response, message = nil)
4
48
  @response = response.blank? ? Typhoeus::Response.new : response
5
49
  @id = SecureRandom.uuid
@@ -22,9 +66,9 @@ module NxtHttpClient
22
66
  id: id,
23
67
  url: url,
24
68
  response_code: response_code,
25
- request_options: request_options,
69
+ request_options: redact_credentials(request_options),
26
70
  response_headers: response_headers,
27
- request_headers: request_headers,
71
+ request_headers: redact_authorization(request_headers),
28
72
  body: body,
29
73
  x_request_id: x_request_id
30
74
  }
@@ -77,5 +121,46 @@ module NxtHttpClient
77
121
  def response_content_type
78
122
  response_headers['Content-Type']
79
123
  end
124
+
125
+ private
126
+
127
+ # Keep Authorization tokens / basic-auth creds out of serialized output (to_h reaches Sentry).
128
+ def redact_credentials(options)
129
+ options = options.merge('userpwd' => REDACTED) if options.key?('userpwd')
130
+ return options unless options['headers'].respond_to?(:key?)
131
+
132
+ options.merge('headers' => redact_authorization(options['headers']))
133
+ end
134
+
135
+ def redact_authorization(headers)
136
+ return headers unless headers.respond_to?(:keys)
137
+
138
+ # HTTP header names are case-insensitive, and HashWithIndifferentAccess does not normalize case.
139
+ sensitive = headers.keys.select { |key| SENSITIVE_HEADERS.any? { |name| key.to_s.casecmp?(name) } }
140
+ return headers if sensitive.empty?
141
+
142
+ headers.merge(sensitive.index_with { REDACTED })
143
+ end
144
+
145
+ public
146
+
147
+ class ClientError < self; end
148
+ class BadRequest < ClientError; end # 400
149
+ class Unauthorized < ClientError; end # 401
150
+ class Forbidden < ClientError; end # 403
151
+ class NotFound < ClientError; end # 404
152
+ class UnprocessableEntity < ClientError; end # 422
153
+ class TooManyRequests < ClientError; end # 429
154
+
155
+ class ServerError < self; end
156
+
157
+ class NetworkError < self; end # code 0 (no HTTP response)
158
+ class Timeout < NetworkError; end # :operation_timedout
159
+ class ConnectionFailed < NetworkError; end # :couldnt_connect
160
+ class NameResolutionError < NetworkError; end # :couldnt_resolve_host / :couldnt_resolve_proxy
161
+ class TlsError < NetworkError; end # :ssl_connect_error and other non-cert :ssl_*
162
+
163
+ # Sibling of NetworkError, not a child, so it's excluded from `retry_on NetworkError`.
164
+ class CertificateError < self; end
80
165
  end
81
166
  end
@@ -1,3 +1,3 @@
1
1
  module NxtHttpClient
2
- VERSION = "2.1.0"
2
+ VERSION = "2.2.0"
3
3
  end
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.require_paths = ['lib']
24
24
 
25
25
  spec.add_dependency 'typhoeus'
26
- spec.add_dependency 'activesupport', '< 8.0'
26
+ spec.add_dependency 'activesupport'
27
27
  spec.add_dependency 'nxt_registry'
28
28
 
29
29
  spec.add_development_dependency 'bundler'
metadata CHANGED
@@ -1,17 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nxt_http_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Robecke
8
8
  - Nils Sommer
9
9
  - Raphael Kallensee
10
10
  - Luetfi Demirci
11
- autorequire:
12
11
  bindir: exe
13
12
  cert_chain: []
14
- date: 2024-06-05 00:00:00.000000000 Z
13
+ date: 1980-01-02 00:00:00.000000000 Z
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
17
16
  name: typhoeus
@@ -31,16 +30,16 @@ dependencies:
31
30
  name: activesupport
32
31
  requirement: !ruby/object:Gem::Requirement
33
32
  requirements:
34
- - - "<"
33
+ - - ">="
35
34
  - !ruby/object:Gem::Version
36
- version: '8.0'
35
+ version: '0'
37
36
  type: :runtime
38
37
  prerelease: false
39
38
  version_requirements: !ruby/object:Gem::Requirement
40
39
  requirements:
41
- - - "<"
40
+ - - ">="
42
41
  - !ruby/object:Gem::Version
43
- version: '8.0'
42
+ version: '0'
44
43
  - !ruby/object:Gem::Dependency
45
44
  name: nxt_registry
46
45
  requirement: !ruby/object:Gem::Requirement
@@ -202,11 +201,10 @@ executables: []
202
201
  extensions: []
203
202
  extra_rdoc_files: []
204
203
  files:
205
- - ".circleci/config.yml"
204
+ - ".github/workflows/ci.yml"
206
205
  - ".gitignore"
207
206
  - ".rspec"
208
207
  - ".ruby-version"
209
- - ".travis.yml"
210
208
  - CHANGELOG.md
211
209
  - Gemfile
212
210
  - Gemfile.lock
@@ -234,7 +232,6 @@ homepage: https://github.com/nxt-insurance/nxt_http_client
234
232
  licenses:
235
233
  - MIT
236
234
  metadata: {}
237
- post_install_message:
238
235
  rdoc_options: []
239
236
  require_paths:
240
237
  - lib
@@ -249,8 +246,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
249
246
  - !ruby/object:Gem::Version
250
247
  version: '0'
251
248
  requirements: []
252
- rubygems_version: 3.5.3
253
- signing_key:
249
+ rubygems_version: 4.0.10
254
250
  specification_version: 4
255
251
  summary: NxtHttpClient is a simple DSL on top the typhoeus http gem
256
252
  test_files: []
data/.circleci/config.yml DELETED
@@ -1,20 +0,0 @@
1
- version: 2.1
2
-
3
- orbs:
4
- ruby: circleci/ruby@2.0.1
5
-
6
- jobs:
7
- build:
8
- docker:
9
- - image: cimg/ruby:3.3.2-node
10
- - image: cimg/redis:6.0.16
11
-
12
- working_directory: ~/repo
13
-
14
- steps:
15
- - checkout
16
-
17
- - ruby/install-deps:
18
- key: gems-v2
19
- include-branch-in-cache-key: false
20
- - ruby/rspec-test
data/.travis.yml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- sudo: false
3
- language: ruby
4
- cache: bundler
5
- rvm:
6
- - 3.3.2
7
- before_install: gem install bundler -v 1.17.2