vpndetection 5.6.0 → 5.6.2

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: af30c794b9bdfb02657775e5e1fd768fa77855d97b1c4f8c2ea3ee4b9319380f
4
- data.tar.gz: dab0d13a90c8f25adb6d6e2ecbafa524d62f1629bcb65e1e4cc55101fe81321d
3
+ metadata.gz: 12d734b2b201e4ec11c2d0562d1c188bc26417064a2230c120f197c50d81f975
4
+ data.tar.gz: a04054e2f2786070bef386c3abb2e63ca06975cfa6429df62cdb0cfb4f6e91b4
5
5
  SHA512:
6
- metadata.gz: 3432eaf4c3bed7d1659898e61a2ce8d9918035e84a39c721e31958cafc97703cb3222c87ab0ed083f2ab02c422b0c737931da0a81b13950877fcb070e5c7df0f
7
- data.tar.gz: 8152326e9e5b8e8e20d170be7dbd950b8ff5bd077eb6327857e4697898a4a87ebc642a5e104bdcd4d78d579ff785315bb7501f8001ecc3ccf91d042c80781f91
6
+ metadata.gz: 642c2248cf17028bcaee8b70501bb7acbfde2cdb2141c31b6f2e87702ee8abe450e645677ee62a0e3e8f0239d61752da26dc0c77707bedab39b9d792ccaf85b1
7
+ data.tar.gz: 252d9c789ae4c2c354cbcc52dea4ecf55ce3e3ecc582f085c1a5b5486e26e8e1fc9fe5b439ed049295586a4bce725371bd9a472330eccddad59f255db4404701
@@ -340,15 +340,17 @@ module VPNDetection
340
340
  end
341
341
 
342
342
  # Every address lands in exactly one of `results` and `errors`; an address in
343
- # neither is the server breaking its own contract, and is reported as such
344
- # rather than lost.
343
+ # neither, or with an entry that is not an answer, is the server breaking its
344
+ # own contract, and is reported as that address's error rather than lost.
345
345
  def batch_answer(ip, body)
346
- if (served = body['results'][ip])
346
+ served = body['results'][ip]
347
+ failed = body['errors'][ip]
348
+ if Transport.lookup_answer?(served)
347
349
  Result.new(served)
348
- elsif (failed = body['errors'][ip])
350
+ elsif failed.is_a?(Hash)
349
351
  Error.from_entry(failed['status'], failed['error'])
350
352
  else
351
- Error.new(:server_error, "the batch answer did not include #{ip}", status: 200)
353
+ Error.new(:server_error, "the batch answer carried no readable entry for #{ip}", status: 200)
352
354
  end
353
355
  end
354
356
  end
@@ -181,8 +181,8 @@ module VPNDetection
181
181
  # every non-2xx as a failure, so it arrives as an ApiError carrying the
182
182
  # Location header.
183
183
  def redirect_location(id, format, timeout)
184
- @api.download_database(id, format, timeout: timeout)
185
- raise Error.new(:server_error, 'expected a redirect to object storage')
184
+ _, status, = @api.download_database_with_http_info(id, format, timeout: timeout)
185
+ raise Error.new(:server_error, 'expected a redirect to object storage', status: status)
186
186
  rescue ApiError => e
187
187
  raise unless e.code == 302
188
188
 
@@ -97,11 +97,12 @@ module VPNDetection
97
97
  message.is_a?(String) ? message : nil
98
98
  end
99
99
 
100
+ # Seconds as digits, or an HTTP date, and nothing else: `Float()` would also
101
+ # read `0x10`, `1_0` and `1e3` as a wait.
100
102
  def self.parse_retry_after(value)
101
- return nil if value.nil? || value.to_s.strip.empty?
102
-
103
- seconds = Float(value, exception: false)
104
- return seconds if seconds && seconds >= 0
103
+ value = value.to_s.strip
104
+ return nil if value.empty?
105
+ return Integer(value, 10) if value.match?(/\A\d+\z/)
105
106
 
106
107
  # The header also permits an HTTP date.
107
108
  when_at = Time.httpdate(value) rescue nil
@@ -80,6 +80,24 @@ module VPNDetection
80
80
  request
81
81
  end
82
82
 
83
+ # A 2xx the generated client cannot read as the answer it promises is the
84
+ # server's fault, and a retryable :server_error, as it is on the OAuth calls:
85
+ # a proxy's HTML page, a cut-off or empty body, or an object missing a member
86
+ # the spec requires. The generated reader raised a RuntimeError, a
87
+ # JSON::ParserError or an ArgumentError for those, none of them retried, and
88
+ # handed an empty or non-object body back as nil, as if it were the answer.
89
+ def deserialize(response, return_type)
90
+ data = super
91
+ return data unless data.nil?
92
+
93
+ raise Error.new(:server_error, "the API answered with no #{return_type}", status: response.code)
94
+ rescue Error
95
+ raise
96
+ rescue StandardError => e
97
+ raise Error.new(:server_error, "could not read the answer as #{return_type}: #{e.message}",
98
+ status: response.code)
99
+ end
100
+
83
101
  # A GET for the presigned link the download endpoint hands out.
84
102
  #
85
103
  # Built here rather than through {#build_request} so it carries NO
@@ -162,22 +180,43 @@ module VPNDetection
162
180
  raise Error.from_transport(response) if transport_failure?(response)
163
181
  raise Error.from_status(response.code, response.headers, response.body) unless response.success?
164
182
 
165
- Entitlement.build_from_hash(parse_object(response))
183
+ body = parse_object(response)
184
+ begin
185
+ Entitlement.build_from_hash(body)
186
+ rescue StandardError => e
187
+ raise Error.new(:server_error, "could not read the answer as Entitlement: #{e.message}",
188
+ status: response.code)
189
+ end
166
190
  end
167
191
 
168
192
  def self.lookup_result(response)
169
193
  raise Error.from_transport(response) if transport_failure?(response)
170
194
  raise Error.from_status(response.code, response.headers, response.body) unless response.success?
171
195
 
172
- Result.new(parse_object(response))
196
+ body = parse_object(response)
197
+ return Result.new(body) if lookup_answer?(body)
198
+
199
+ raise Error.new(:server_error, 'the answer carried no ip and is_vpn', status: response.code)
200
+ end
201
+
202
+ # Whether a parsed body is a lookup's answer: every plan's carries `ip` and
203
+ # `is_vpn`, so one without them is a 2xx the call cannot read.
204
+ def self.lookup_answer?(body)
205
+ body.is_a?(Hash) && body['ip'].is_a?(String) && [true, false].include?(body['is_vpn'])
173
206
  end
174
207
 
175
- # The two maps of a batch answer, each present even when empty.
208
+ # The two maps of a batch answer, each present even when empty. An answer
209
+ # carrying neither is unreadable as a whole, and is retried as one.
176
210
  def self.batch_body(response)
177
211
  raise Error.from_transport(response) if transport_failure?(response)
178
212
  raise Error.from_status(response.code, response.headers, response.body) unless response.success?
179
213
 
180
214
  body = parse_object(response)
215
+ unless body['results'].is_a?(Hash) || body['errors'].is_a?(Hash)
216
+ raise Error.new(:server_error, 'the batch answer carried neither results nor errors',
217
+ status: response.code)
218
+ end
219
+
181
220
  {
182
221
  'results' => body['results'].is_a?(Hash) ? body['results'] : {},
183
222
  'errors' => body['errors'].is_a?(Hash) ? body['errors'] : {},
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module VPNDetection
4
- VERSION = '5.6.0'
4
+ VERSION = '5.6.2'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vpndetection
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.6.0
4
+ version: 5.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mslm Dev