restify 2.1.0 → 3.0.0.rc3
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 +4 -4
- data/CHANGELOG.md +75 -0
- data/README.md +30 -5
- data/lib/restify/adapter/base.rb +10 -1
- data/lib/restify/adapter/ethon/easy.rb +147 -0
- data/lib/restify/adapter/ethon/event_loop.rb +255 -0
- data/lib/restify/adapter/ethon/options.rb +81 -0
- data/lib/restify/adapter/ethon/pool.rb +43 -0
- data/lib/restify/adapter/ethon.rb +211 -0
- data/lib/restify/adapter/loop_lock.rb +124 -0
- data/lib/restify/adapter/telemetry.rb +7 -5
- data/lib/restify/adapter/typhoeus.rb +52 -23
- data/lib/restify/context.rb +16 -6
- data/lib/restify/error.rb +13 -0
- data/lib/restify/global.rb +9 -30
- data/lib/restify/logging.rb +10 -4
- data/lib/restify/processors/base/parsing.rb +43 -26
- data/lib/restify/promise.rb +194 -42
- data/lib/restify/registry.rb +2 -2
- data/lib/restify/relation.rb +27 -13
- data/lib/restify/request.rb +2 -2
- data/lib/restify/resource.rb +20 -2
- data/lib/restify/response.rb +39 -3
- data/lib/restify/timeout.rb +23 -34
- data/lib/restify/version.rb +3 -3
- data/lib/restify/webmock.rb +126 -0
- data/lib/restify.rb +5 -2
- data/spec/restify/adapter/ethon/easy_spec.rb +111 -0
- data/spec/restify/adapter/ethon/event_loop_spec.rb +96 -0
- data/spec/restify/adapter/ethon/options_spec.rb +104 -0
- data/spec/restify/adapter/ethon/pool_spec.rb +81 -0
- data/spec/restify/adapter/ethon_spec.rb +307 -0
- data/spec/restify/adapter/loop_lock_spec.rb +144 -0
- data/spec/restify/adapter/telemetry_spec.rb +61 -0
- data/spec/restify/adapter/typhoeus_spec.rb +26 -0
- data/spec/restify/cache_spec.rb +1 -3
- data/spec/restify/context_spec.rb +69 -0
- data/spec/restify/features/opentelemetry_spec.rb +2 -0
- data/spec/restify/features/protocols_spec.rb +28 -0
- data/spec/restify/features/redirects_spec.rb +45 -0
- data/spec/restify/features/request_errors_spec.rb +1 -1
- data/spec/restify/features/response_bodies_spec.rb +45 -0
- data/spec/restify/features/response_encoding_spec.rb +82 -0
- data/spec/restify/features/response_errors_spec.rb +22 -1
- data/spec/restify/features/webmock_spec.rb +92 -1
- data/spec/restify/global_spec.rb +78 -10
- data/spec/restify/processors/json_spec.rb +103 -0
- data/spec/restify/processors/msgpack_spec.rb +36 -0
- data/spec/restify/promise_spec.rb +288 -9
- data/spec/restify/relation_spec.rb +25 -6
- data/spec/restify/request_spec.rb +26 -0
- data/spec/restify/timeout_spec.rb +43 -33
- data/spec/spec_helper.rb +10 -6
- data/spec/support/stub_server.rb +4 -4
- metadata +32 -221
- data/vendor/bundle/ruby/4.0.0/bundler/gems/my-rubocop-2b861962124a/LICENSE +0 -21
- data/vendor/bundle/ruby/4.0.0/bundler/gems/my-rubocop-2b861962124a/README.md +0 -23
- data/vendor/bundle/ruby/4.0.0/gems/activesupport-8.1.3/CHANGELOG.md +0 -561
- data/vendor/bundle/ruby/4.0.0/gems/activesupport-8.1.3/MIT-LICENSE +0 -20
- data/vendor/bundle/ruby/4.0.0/gems/activesupport-8.1.3/README.rdoc +0 -40
- data/vendor/bundle/ruby/4.0.0/gems/addressable-2.9.0/CHANGELOG.md +0 -326
- data/vendor/bundle/ruby/4.0.0/gems/addressable-2.9.0/LICENSE.txt +0 -202
- data/vendor/bundle/ruby/4.0.0/gems/addressable-2.9.0/README.md +0 -121
- data/vendor/bundle/ruby/4.0.0/gems/ast-2.4.3/LICENSE.MIT +0 -20
- data/vendor/bundle/ruby/4.0.0/gems/ast-2.4.3/README.YARD.md +0 -12
- data/vendor/bundle/ruby/4.0.0/gems/base64-0.3.0/README.md +0 -48
- data/vendor/bundle/ruby/4.0.0/gems/bigdecimal-4.1.2/LICENSE +0 -56
- data/vendor/bundle/ruby/4.0.0/gems/concurrent-ruby-1.3.7/CHANGELOG.md +0 -615
- data/vendor/bundle/ruby/4.0.0/gems/concurrent-ruby-1.3.7/LICENSE.txt +0 -21
- data/vendor/bundle/ruby/4.0.0/gems/concurrent-ruby-1.3.7/README.md +0 -409
- data/vendor/bundle/ruby/4.0.0/gems/connection_pool-3.0.2/LICENSE +0 -20
- data/vendor/bundle/ruby/4.0.0/gems/connection_pool-3.0.2/README.md +0 -182
- data/vendor/bundle/ruby/4.0.0/gems/crack-1.0.1/LICENSE +0 -20
- data/vendor/bundle/ruby/4.0.0/gems/crack-1.0.1/README.md +0 -43
- data/vendor/bundle/ruby/4.0.0/gems/diff-lcs-1.6.2/CHANGELOG.md +0 -518
- data/vendor/bundle/ruby/4.0.0/gems/diff-lcs-1.6.2/README.md +0 -92
- data/vendor/bundle/ruby/4.0.0/gems/docile-1.4.1/LICENSE +0 -21
- data/vendor/bundle/ruby/4.0.0/gems/docile-1.4.1/README.md +0 -409
- data/vendor/bundle/ruby/4.0.0/gems/drb-2.2.3/LICENSE.txt +0 -22
- data/vendor/bundle/ruby/4.0.0/gems/ethon-0.18.0/CHANGELOG.md +0 -417
- data/vendor/bundle/ruby/4.0.0/gems/ethon-0.18.0/LICENSE +0 -20
- data/vendor/bundle/ruby/4.0.0/gems/ethon-0.18.0/README.md +0 -118
- data/vendor/bundle/ruby/4.0.0/gems/ffi-1.17.4-x86_64-linux-gnu/CHANGELOG.md +0 -507
- data/vendor/bundle/ruby/4.0.0/gems/ffi-1.17.4-x86_64-linux-gnu/LICENSE +0 -24
- data/vendor/bundle/ruby/4.0.0/gems/ffi-1.17.4-x86_64-linux-gnu/LICENSE.SPECS +0 -22
- data/vendor/bundle/ruby/4.0.0/gems/ffi-1.17.4-x86_64-linux-gnu/README.md +0 -138
- data/vendor/bundle/ruby/4.0.0/gems/hashdiff-1.2.1/LICENSE +0 -19
- data/vendor/bundle/ruby/4.0.0/gems/hashdiff-1.2.1/README.md +0 -323
- data/vendor/bundle/ruby/4.0.0/gems/hashdiff-1.2.1/changelog.md +0 -127
- data/vendor/bundle/ruby/4.0.0/gems/hitimes-3.2.0/LICENSE.txt +0 -16
- data/vendor/bundle/ruby/4.0.0/gems/hitimes-3.2.0/README.md +0 -187
- data/vendor/bundle/ruby/4.0.0/gems/i18n-1.15.2/MIT-LICENSE +0 -20
- data/vendor/bundle/ruby/4.0.0/gems/i18n-1.15.2/README.md +0 -139
- data/vendor/bundle/ruby/4.0.0/gems/json-2.19.9/README.md +0 -310
- data/vendor/bundle/ruby/4.0.0/gems/language_server-protocol-3.17.0.5/LICENSE.txt +0 -21
- data/vendor/bundle/ruby/4.0.0/gems/language_server-protocol-3.17.0.5/README.md +0 -88
- data/vendor/bundle/ruby/4.0.0/gems/lint_roller-1.1.0/CHANGELOG.md +0 -15
- data/vendor/bundle/ruby/4.0.0/gems/lint_roller-1.1.0/LICENSE.txt +0 -21
- data/vendor/bundle/ruby/4.0.0/gems/lint_roller-1.1.0/README.md +0 -173
- data/vendor/bundle/ruby/4.0.0/gems/little-plugger-1.1.4/README.rdoc +0 -53
- data/vendor/bundle/ruby/4.0.0/gems/logger-1.7.0/README.md +0 -104
- data/vendor/bundle/ruby/4.0.0/gems/logging-2.4.0/LICENSE +0 -22
- data/vendor/bundle/ruby/4.0.0/gems/logging-2.4.0/README.md +0 -140
- data/vendor/bundle/ruby/4.0.0/gems/minitest-6.0.6/README.rdoc +0 -763
- data/vendor/bundle/ruby/4.0.0/gems/msgpack-1.8.3/ChangeLog +0 -383
- data/vendor/bundle/ruby/4.0.0/gems/msgpack-1.8.3/LICENSE +0 -177
- data/vendor/bundle/ruby/4.0.0/gems/msgpack-1.8.3/README.md +0 -302
- data/vendor/bundle/ruby/4.0.0/gems/multi_json-1.21.1/LICENSE.md +0 -20
- data/vendor/bundle/ruby/4.0.0/gems/multi_json-1.21.1/README.md +0 -281
- data/vendor/bundle/ruby/4.0.0/gems/nio4r-2.7.5/ext/libev/LICENSE +0 -37
- data/vendor/bundle/ruby/4.0.0/gems/nio4r-2.7.5/ext/libev/README +0 -59
- data/vendor/bundle/ruby/4.0.0/gems/nio4r-2.7.5/license.md +0 -80
- data/vendor/bundle/ruby/4.0.0/gems/nio4r-2.7.5/readme.md +0 -91
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-api-1.10.0/CHANGELOG.md +0 -219
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-api-1.10.0/LICENSE +0 -201
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-api-1.10.0/README.md +0 -68
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-common-0.25.0/CHANGELOG.md +0 -110
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-common-0.25.0/LICENSE +0 -201
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-common-0.25.0/README.md +0 -62
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-base-0.26.1/CHANGELOG.md +0 -97
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-base-0.26.1/LICENSE +0 -201
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-base-0.26.1/README.md +0 -155
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-ethon-0.29.0/CHANGELOG.md +0 -192
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-ethon-0.29.0/LICENSE +0 -201
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-instrumentation-ethon-0.29.0/README.md +0 -66
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-registry-0.6.0/CHANGELOG.md +0 -31
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-registry-0.6.0/LICENSE +0 -201
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-registry-0.6.0/README.md +0 -40
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-sdk-1.12.0/CHANGELOG.md +0 -302
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-sdk-1.12.0/LICENSE +0 -201
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-sdk-1.12.0/README.md +0 -101
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-semantic_conventions-1.41.0/CHANGELOG.md +0 -65
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-semantic_conventions-1.41.0/LICENSE +0 -201
- data/vendor/bundle/ruby/4.0.0/gems/opentelemetry-semantic_conventions-1.41.0/README.md +0 -115
- data/vendor/bundle/ruby/4.0.0/gems/parallel-2.1.0/MIT-LICENSE.txt +0 -20
- data/vendor/bundle/ruby/4.0.0/gems/parser-3.3.11.1/LICENSE.txt +0 -26
- data/vendor/bundle/ruby/4.0.0/gems/prism-1.9.0/CHANGELOG.md +0 -786
- data/vendor/bundle/ruby/4.0.0/gems/prism-1.9.0/LICENSE.md +0 -7
- data/vendor/bundle/ruby/4.0.0/gems/prism-1.9.0/README.md +0 -143
- data/vendor/bundle/ruby/4.0.0/gems/public_suffix-7.0.5/CHANGELOG.md +0 -649
- data/vendor/bundle/ruby/4.0.0/gems/public_suffix-7.0.5/LICENSE.txt +0 -22
- data/vendor/bundle/ruby/4.0.0/gems/public_suffix-7.0.5/README.md +0 -231
- data/vendor/bundle/ruby/4.0.0/gems/puma-8.0.2/LICENSE +0 -29
- data/vendor/bundle/ruby/4.0.0/gems/puma-8.0.2/README.md +0 -484
- data/vendor/bundle/ruby/4.0.0/gems/puma-8.0.2/docs/jungle/README.md +0 -9
- data/vendor/bundle/ruby/4.0.0/gems/puma-8.0.2/docs/jungle/rc.d/README.md +0 -74
- data/vendor/bundle/ruby/4.0.0/gems/racc-1.8.1/ChangeLog +0 -846
- data/vendor/bundle/ruby/4.0.0/gems/racc-1.8.1/README.ja.rdoc +0 -58
- data/vendor/bundle/ruby/4.0.0/gems/racc-1.8.1/README.rdoc +0 -60
- data/vendor/bundle/ruby/4.0.0/gems/rack-3.2.6/CHANGELOG.md +0 -1346
- data/vendor/bundle/ruby/4.0.0/gems/rack-3.2.6/MIT-LICENSE +0 -20
- data/vendor/bundle/ruby/4.0.0/gems/rack-3.2.6/README.md +0 -384
- data/vendor/bundle/ruby/4.0.0/gems/rainbow-3.1.1/Changelog.md +0 -101
- data/vendor/bundle/ruby/4.0.0/gems/rainbow-3.1.1/LICENSE +0 -20
- data/vendor/bundle/ruby/4.0.0/gems/rainbow-3.1.1/README.markdown +0 -227
- data/vendor/bundle/ruby/4.0.0/gems/rake-13.4.2/MIT-LICENSE +0 -21
- data/vendor/bundle/ruby/4.0.0/gems/rake-13.4.2/README.rdoc +0 -155
- data/vendor/bundle/ruby/4.0.0/gems/rake-release-1.4.0/LICENSE +0 -21
- data/vendor/bundle/ruby/4.0.0/gems/rake-release-1.4.0/README.md +0 -107
- data/vendor/bundle/ruby/4.0.0/gems/regexp_parser-2.12.0/LICENSE +0 -22
- data/vendor/bundle/ruby/4.0.0/gems/rexml-3.4.4/LICENSE.txt +0 -22
- data/vendor/bundle/ruby/4.0.0/gems/rexml-3.4.4/README.md +0 -57
- data/vendor/bundle/ruby/4.0.0/gems/rspec-3.13.2/LICENSE.md +0 -27
- data/vendor/bundle/ruby/4.0.0/gems/rspec-3.13.2/README.md +0 -47
- data/vendor/bundle/ruby/4.0.0/gems/rspec-core-3.13.6/Changelog.md +0 -2447
- data/vendor/bundle/ruby/4.0.0/gems/rspec-core-3.13.6/LICENSE.md +0 -26
- data/vendor/bundle/ruby/4.0.0/gems/rspec-core-3.13.6/README.md +0 -389
- data/vendor/bundle/ruby/4.0.0/gems/rspec-expectations-3.13.5/Changelog.md +0 -1366
- data/vendor/bundle/ruby/4.0.0/gems/rspec-expectations-3.13.5/LICENSE.md +0 -25
- data/vendor/bundle/ruby/4.0.0/gems/rspec-expectations-3.13.5/README.md +0 -326
- data/vendor/bundle/ruby/4.0.0/gems/rspec-mocks-3.13.8/Changelog.md +0 -1351
- data/vendor/bundle/ruby/4.0.0/gems/rspec-mocks-3.13.8/LICENSE.md +0 -25
- data/vendor/bundle/ruby/4.0.0/gems/rspec-mocks-3.13.8/README.md +0 -465
- data/vendor/bundle/ruby/4.0.0/gems/rspec-support-3.13.7/Changelog.md +0 -444
- data/vendor/bundle/ruby/4.0.0/gems/rspec-support-3.13.7/LICENSE.md +0 -23
- data/vendor/bundle/ruby/4.0.0/gems/rspec-support-3.13.7/README.md +0 -40
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-1.88.0/LICENSE.txt +0 -20
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-1.88.0/README.md +0 -255
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-ast-1.49.1/LICENSE.txt +0 -20
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-ast-1.49.1/README.md +0 -54
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-capybara-2.23.0/CHANGELOG.md +0 -116
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-capybara-2.23.0/MIT-LICENSE.md +0 -21
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-capybara-2.23.0/README.md +0 -91
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-factory_bot-2.28.0/CHANGELOG.md +0 -135
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-factory_bot-2.28.0/MIT-LICENSE.md +0 -21
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-factory_bot-2.28.0/README.md +0 -91
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-performance-1.26.1/LICENSE.txt +0 -20
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-performance-1.26.1/README.md +0 -100
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec-3.10.2/CHANGELOG.md +0 -1136
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec-3.10.2/MIT-LICENSE.md +0 -21
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec-3.10.2/README.md +0 -112
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec_rails-2.32.0/CHANGELOG.md +0 -98
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec_rails-2.32.0/MIT-LICENSE.md +0 -21
- data/vendor/bundle/ruby/4.0.0/gems/rubocop-rspec_rails-2.32.0/README.md +0 -93
- data/vendor/bundle/ruby/4.0.0/gems/ruby-progressbar-1.13.0/LICENSE.txt +0 -19
- data/vendor/bundle/ruby/4.0.0/gems/ruby-progressbar-1.13.0/README.md +0 -131
- data/vendor/bundle/ruby/4.0.0/gems/securerandom-0.4.1/README.md +0 -72
- data/vendor/bundle/ruby/4.0.0/gems/simplecov-0.22.0/CHANGELOG.md +0 -191
- data/vendor/bundle/ruby/4.0.0/gems/simplecov-0.22.0/LICENSE +0 -20
- data/vendor/bundle/ruby/4.0.0/gems/simplecov-0.22.0/README.md +0 -974
- data/vendor/bundle/ruby/4.0.0/gems/simplecov-cobertura-3.2.0/LICENSE +0 -202
- data/vendor/bundle/ruby/4.0.0/gems/simplecov-cobertura-3.2.0/README.md +0 -65
- data/vendor/bundle/ruby/4.0.0/gems/simplecov-html-0.13.2/CHANGELOG.md +0 -114
- data/vendor/bundle/ruby/4.0.0/gems/simplecov-html-0.13.2/LICENSE +0 -20
- data/vendor/bundle/ruby/4.0.0/gems/simplecov-html-0.13.2/README.md +0 -30
- data/vendor/bundle/ruby/4.0.0/gems/simplecov_json_formatter-0.1.4/CHANGELOG.md +0 -13
- data/vendor/bundle/ruby/4.0.0/gems/simplecov_json_formatter-0.1.4/README.md +0 -29
- data/vendor/bundle/ruby/4.0.0/gems/typhoeus-1.6.0/CHANGELOG.md +0 -465
- data/vendor/bundle/ruby/4.0.0/gems/typhoeus-1.6.0/LICENSE +0 -22
- data/vendor/bundle/ruby/4.0.0/gems/typhoeus-1.6.0/README.md +0 -588
- data/vendor/bundle/ruby/4.0.0/gems/tzinfo-2.0.6/LICENSE +0 -19
- data/vendor/bundle/ruby/4.0.0/gems/tzinfo-2.0.6/README.md +0 -406
- data/vendor/bundle/ruby/4.0.0/gems/unicode-display_width-3.2.0/CHANGELOG.md +0 -299
- data/vendor/bundle/ruby/4.0.0/gems/unicode-display_width-3.2.0/MIT-LICENSE.txt +0 -22
- data/vendor/bundle/ruby/4.0.0/gems/unicode-display_width-3.2.0/README.md +0 -194
- data/vendor/bundle/ruby/4.0.0/gems/unicode-emoji-4.2.0/CHANGELOG.md +0 -202
- data/vendor/bundle/ruby/4.0.0/gems/unicode-emoji-4.2.0/MIT-LICENSE.txt +0 -20
- data/vendor/bundle/ruby/4.0.0/gems/unicode-emoji-4.2.0/README.md +0 -205
- data/vendor/bundle/ruby/4.0.0/gems/uri-1.1.1/README.md +0 -55
- data/vendor/bundle/ruby/4.0.0/gems/webmock-3.26.2/CHANGELOG.md +0 -2148
- data/vendor/bundle/ruby/4.0.0/gems/webmock-3.26.2/LICENSE +0 -20
- data/vendor/bundle/ruby/4.0.0/gems/webmock-3.26.2/README.md +0 -1229
data/lib/restify/timeout.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require '
|
|
3
|
+
require 'timeout'
|
|
4
4
|
|
|
5
5
|
module Restify
|
|
6
6
|
class Timeout
|
|
@@ -8,56 +8,44 @@ module Restify
|
|
|
8
8
|
attr_accessor :default_timeout
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
# Default wait timeout of 300 seconds.
|
|
12
11
|
self.default_timeout = 300
|
|
13
12
|
|
|
13
|
+
# @return [Float] Seconds to wait in total.
|
|
14
|
+
attr_reader :duration
|
|
15
|
+
|
|
14
16
|
def initialize(timeout, target = nil)
|
|
15
|
-
@timeout = parse_timeout(timeout)
|
|
16
17
|
@target = target
|
|
17
|
-
|
|
18
|
-
@
|
|
19
|
-
@interval.start
|
|
18
|
+
@duration = parse_timeout(timeout)
|
|
19
|
+
@deadline = now + @duration
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def
|
|
23
|
-
|
|
24
|
-
raise self unless ivar.complete?
|
|
25
|
-
end
|
|
26
|
-
rescue ::Timeout::Error
|
|
27
|
-
raise self
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def timeout!
|
|
31
|
-
raise self if wait_interval <= 0
|
|
22
|
+
def remaining
|
|
23
|
+
@deadline - now
|
|
32
24
|
end
|
|
33
25
|
|
|
34
26
|
def exception
|
|
35
|
-
Error.new(@target)
|
|
27
|
+
Error.new(@target, @duration)
|
|
36
28
|
end
|
|
37
29
|
|
|
38
30
|
private
|
|
39
31
|
|
|
40
|
-
def
|
|
41
|
-
|
|
32
|
+
def now
|
|
33
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
42
34
|
end
|
|
43
35
|
|
|
44
36
|
def parse_timeout(value)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
begin
|
|
48
|
-
value = Float(value)
|
|
49
|
-
rescue ArgumentError
|
|
50
|
-
raise ArgumentError.new \
|
|
51
|
-
"Timeout must be an number but is #{value}"
|
|
52
|
-
end
|
|
37
|
+
value = self.class.default_timeout if value.nil?
|
|
38
|
+
timeout = Float(value, exception: false)
|
|
53
39
|
|
|
54
|
-
raise ArgumentError.new
|
|
40
|
+
raise ArgumentError.new("Timeout must be a number but is #{value.inspect}") unless timeout
|
|
41
|
+
raise ArgumentError.new("Timeout must be > 0 but is #{value.inspect}.") unless timeout.positive?
|
|
42
|
+
raise ArgumentError.new("Timeout must be finite but is #{value.inspect}.") unless timeout.finite?
|
|
55
43
|
|
|
56
|
-
|
|
44
|
+
timeout
|
|
57
45
|
end
|
|
58
46
|
|
|
59
47
|
class << self
|
|
60
|
-
def new(timeout,
|
|
48
|
+
def new(timeout, target = nil)
|
|
61
49
|
return timeout if timeout.is_a?(self)
|
|
62
50
|
|
|
63
51
|
super
|
|
@@ -65,15 +53,16 @@ module Restify
|
|
|
65
53
|
end
|
|
66
54
|
|
|
67
55
|
class Error < ::Timeout::Error
|
|
68
|
-
attr_reader :target
|
|
56
|
+
attr_reader :target, :duration
|
|
69
57
|
|
|
70
|
-
def initialize(target)
|
|
58
|
+
def initialize(target, duration)
|
|
71
59
|
@target = target
|
|
60
|
+
@duration = duration
|
|
72
61
|
|
|
73
62
|
if @target
|
|
74
|
-
super("Operation on #{@target} timed out")
|
|
63
|
+
super("Operation on #{@target} timed out after #{duration}s")
|
|
75
64
|
else
|
|
76
|
-
super(
|
|
65
|
+
super("Operation timed out after #{duration}s")
|
|
77
66
|
end
|
|
78
67
|
end
|
|
79
68
|
end
|
data/lib/restify/version.rb
CHANGED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'restify'
|
|
4
|
+
require 'webmock'
|
|
5
|
+
|
|
6
|
+
module WebMock
|
|
7
|
+
module HttpLibAdapters
|
|
8
|
+
#
|
|
9
|
+
# WebMock support for Restify (independent of the adapter)
|
|
10
|
+
#
|
|
11
|
+
# Requests are intercepted before they are handed over to an
|
|
12
|
+
# adapter. Stubbed responses are returned as they are, including
|
|
13
|
+
# redirects, like WebMock does for other libcurl-based libraries.
|
|
14
|
+
# Requests not stubbed are passed on to the adapter, if allowed.
|
|
15
|
+
#
|
|
16
|
+
# Must be loaded before WebMock is enabled, e.g. in
|
|
17
|
+
# `spec_helper.rb`:
|
|
18
|
+
#
|
|
19
|
+
# require 'webmock/rspec'
|
|
20
|
+
# require 'restify/webmock'
|
|
21
|
+
#
|
|
22
|
+
class RestifyAdapter < HttpLibAdapter
|
|
23
|
+
adapter_for :restify
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
def enable!
|
|
27
|
+
@enabled = true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def disable!
|
|
31
|
+
@enabled = false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def enabled?
|
|
35
|
+
@enabled == true
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Respond to a request with a stubbed response, or pass it on to
|
|
39
|
+
# the given block if allowed.
|
|
40
|
+
#
|
|
41
|
+
# @api private
|
|
42
|
+
def call(request, &)
|
|
43
|
+
signature = signature(request)
|
|
44
|
+
RequestRegistry.instance.requested_signatures.put(signature)
|
|
45
|
+
|
|
46
|
+
stub = StubRegistry.instance.response_for_request(signature)
|
|
47
|
+
return passthrough(signature, &) unless stub
|
|
48
|
+
|
|
49
|
+
CallbackRegistry.invoke_callbacks({lib: :restify}, signature, stub)
|
|
50
|
+
|
|
51
|
+
Restify::Promise.fulfilled(respond(request, stub))
|
|
52
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
53
|
+
# e.g. `NetConnectNotAllowedError`
|
|
54
|
+
Restify::Promise.rejected(e)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def signature(request)
|
|
60
|
+
RequestSignature.new(
|
|
61
|
+
request.method.to_sym,
|
|
62
|
+
request.uri.to_s,
|
|
63
|
+
body: request.body,
|
|
64
|
+
headers: request.headers,
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def passthrough(signature)
|
|
69
|
+
unless ::WebMock.net_connect_allowed?(signature.uri)
|
|
70
|
+
raise NetConnectNotAllowedError.new(signature)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
yield.tap do |promise|
|
|
74
|
+
# Observers run when the request is complete, even when
|
|
75
|
+
# nobody waits on the promise.
|
|
76
|
+
promise.add_observer do |response, _|
|
|
77
|
+
next unless response
|
|
78
|
+
|
|
79
|
+
CallbackRegistry.invoke_callbacks(
|
|
80
|
+
{lib: :restify, real_request: true},
|
|
81
|
+
signature,
|
|
82
|
+
webmock_response(response),
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def respond(request, stub)
|
|
89
|
+
raise Restify::NetworkError.new(request, 'Timeout was reached') if stub.should_timeout
|
|
90
|
+
|
|
91
|
+
stub.raise_error_if_any
|
|
92
|
+
|
|
93
|
+
headers = (stub.headers || {}).to_h do |name, value|
|
|
94
|
+
[name.upcase.tr('-', '_'), value]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
Restify::Response.new(request, request.uri, stub.status[0], headers, stub.body)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def webmock_response(response)
|
|
101
|
+
Response.new.tap do |webmock|
|
|
102
|
+
webmock.status = [response.code, response.message.to_s]
|
|
103
|
+
webmock.headers = response.headers.to_h do |name, value|
|
|
104
|
+
[name.split('_').map(&:capitalize).join('-'), value]
|
|
105
|
+
end
|
|
106
|
+
webmock.body = response.body.to_s
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Intercept requests after `Adapter::Telemetry` but before
|
|
112
|
+
# `Adapter::Base#call` so that telemetry is included.
|
|
113
|
+
#
|
|
114
|
+
# @api private
|
|
115
|
+
module Intercept
|
|
116
|
+
def call(request)
|
|
117
|
+
return super unless RestifyAdapter.enabled?
|
|
118
|
+
|
|
119
|
+
RestifyAdapter.call(request) { super }
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
::Restify::Adapter::Telemetry.include(Intercept)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
data/lib/restify.rb
CHANGED
|
@@ -4,7 +4,6 @@ require 'forwardable'
|
|
|
4
4
|
|
|
5
5
|
require 'restify/version'
|
|
6
6
|
|
|
7
|
-
require 'concurrent'
|
|
8
7
|
require 'addressable/uri'
|
|
9
8
|
require 'addressable/template'
|
|
10
9
|
|
|
@@ -28,7 +27,11 @@ module Restify
|
|
|
28
27
|
|
|
29
28
|
module Adapter
|
|
30
29
|
require 'restify/adapter/base'
|
|
31
|
-
require 'restify/adapter/
|
|
30
|
+
require 'restify/adapter/loop_lock'
|
|
31
|
+
require 'restify/adapter/ethon'
|
|
32
|
+
|
|
33
|
+
# Optional, requires the `typhoeus` gem.
|
|
34
|
+
autoload :Typhoeus, 'restify/adapter/typhoeus'
|
|
32
35
|
end
|
|
33
36
|
|
|
34
37
|
module Processors
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Restify::Adapter::Ethon::Easy do
|
|
6
|
+
let(:easy) { described_class.new }
|
|
7
|
+
|
|
8
|
+
describe '#response' do
|
|
9
|
+
subject(:response) { easy.response(request) }
|
|
10
|
+
|
|
11
|
+
let(:request) { Restify::Request.new(uri: 'http://example.org/base') }
|
|
12
|
+
|
|
13
|
+
# A transfer is handed back from libcurl with its return code and, for
|
|
14
|
+
# HTTP, the status code of the response.
|
|
15
|
+
let(:info) do
|
|
16
|
+
{
|
|
17
|
+
return_code: :ok,
|
|
18
|
+
response_code: 200,
|
|
19
|
+
effective_url: 'http://example.org/base',
|
|
20
|
+
response_headers: "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n",
|
|
21
|
+
response_body: '{}',
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
before { allow(easy).to receive_messages(info) }
|
|
26
|
+
|
|
27
|
+
it 'returns the response' do
|
|
28
|
+
expect(response).to be_a Restify::Response
|
|
29
|
+
expect(response).to have_attributes(code: 200, body: '{}', request:)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'reuses the request URI without redirects' do
|
|
33
|
+
expect(response.uri).to be request.uri
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context 'when redirected' do
|
|
37
|
+
let(:info) { super().merge(effective_url: 'http://example.org/other/base') }
|
|
38
|
+
|
|
39
|
+
it 'uses the effective URL as the response URI' do
|
|
40
|
+
expect(response.uri.to_s).to eq 'http://example.org/other/base'
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
context 'when the transfer failed' do
|
|
45
|
+
let(:info) { super().merge(return_code: :couldnt_connect, response_code: 0) }
|
|
46
|
+
|
|
47
|
+
it { expect { response }.to raise_error Restify::NetworkError, /connect/i }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Non-HTTP protocols are refused before a transfer is started, but
|
|
51
|
+
# libcurl still reports success with a zero status code whenever no
|
|
52
|
+
# HTTP status was received, and no status code at all when it cannot
|
|
53
|
+
# be read back. Neither has a response to process.
|
|
54
|
+
context 'without an HTTP status' do
|
|
55
|
+
let(:info) { super().merge(response_code: 0) }
|
|
56
|
+
|
|
57
|
+
it { expect { response }.to raise_error Restify::NetworkError, /without HTTP status/ }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
context 'with an unavailable HTTP status' do
|
|
61
|
+
let(:info) { super().merge(response_code: nil) }
|
|
62
|
+
|
|
63
|
+
it { expect { response }.to raise_error Restify::NetworkError, /without HTTP status/ }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
describe 'headers' do
|
|
67
|
+
subject(:headers) { response.headers }
|
|
68
|
+
|
|
69
|
+
context 'with multiple values' do
|
|
70
|
+
let(:info) { super().merge(response_headers: "HTTP/1.1 200 OK\r\nLink: <a>\r\nLink: <b>\r\n\r\n") }
|
|
71
|
+
|
|
72
|
+
it { is_expected.to eq('LINK' => ['<a>', '<b>']) }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
context 'with a folded value' do
|
|
76
|
+
let(:info) { super().merge(response_headers: "HTTP/1.1 200 OK\r\nX-Long: first\r\n\tsecond\r\n\r\n") }
|
|
77
|
+
|
|
78
|
+
it { is_expected.to eq('X_LONG' => 'first second') }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
context 'with multiple responses, e.g. after a redirect' do
|
|
82
|
+
let(:info) do
|
|
83
|
+
super().merge(
|
|
84
|
+
response_headers: "HTTP/1.1 302 Found\r\nLocation: /b\r\n\r\n" \
|
|
85
|
+
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n",
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it { is_expected.to eq('CONTENT_TYPE' => 'text/plain') }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
context 'without headers' do
|
|
93
|
+
let(:info) { super().merge(response_headers: nil) }
|
|
94
|
+
|
|
95
|
+
it { is_expected.to eq({}) }
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe '#reset' do
|
|
101
|
+
it 'forgets the previous request' do
|
|
102
|
+
easy._otel_span = Object.new
|
|
103
|
+
easy._restify_request = Object.new
|
|
104
|
+
easy._restify_writer = Object.new
|
|
105
|
+
|
|
106
|
+
easy.reset
|
|
107
|
+
|
|
108
|
+
expect([easy._otel_span, easy._restify_request, easy._restify_writer]).to all be_nil
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Restify::Adapter::Ethon::EventLoop do
|
|
6
|
+
subject(:events) { described_class.new }
|
|
7
|
+
|
|
8
|
+
let(:threads) { [] }
|
|
9
|
+
|
|
10
|
+
let(:options) do
|
|
11
|
+
Restify::Adapter::Ethon::Options.new(Restify::Adapter::Ethon::ENFORCED_OPTIONS)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
before do
|
|
15
|
+
stub_request(:get, 'http://stubserver/base').to_return(status: 200, body: '{}')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
after { threads.each(&:kill).each(&:join) }
|
|
19
|
+
|
|
20
|
+
# A prepared easy handle with its promise, and a queue recording the
|
|
21
|
+
# thread completing it.
|
|
22
|
+
def transfer
|
|
23
|
+
request = Restify::Request.new(uri: 'http://localhost:9292/base')
|
|
24
|
+
writer = nil
|
|
25
|
+
promise = Restify::Promise.create {|w| writer = w }
|
|
26
|
+
completed_by = Queue.new
|
|
27
|
+
|
|
28
|
+
easy = Restify::Adapter::Ethon::Easy.new
|
|
29
|
+
easy._restify_request = request
|
|
30
|
+
easy._restify_writer = writer
|
|
31
|
+
easy.prepare(request, options)
|
|
32
|
+
easy.on_complete do |completed|
|
|
33
|
+
completed_by << Thread.current
|
|
34
|
+
writer.set { completed.response(request) }
|
|
35
|
+
events.notify
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
[easy, promise, completed_by]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe '#drive' do
|
|
42
|
+
it 'runs transfers in the calling thread' do
|
|
43
|
+
easy, promise, completed_by = transfer
|
|
44
|
+
events.enqueue(easy)
|
|
45
|
+
|
|
46
|
+
expect(events.drive(promise, Restify::Timeout.new(1))).to be true
|
|
47
|
+
expect(promise.value.code).to eq 200
|
|
48
|
+
expect(completed_by.pop(timeout: 1)).to eq Thread.current
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'gives up when the timeout expires' do
|
|
52
|
+
expect(events.drive(Restify::Promise.new, Restify::Timeout.new(0.1))).to be true
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'does not run the loop again from within the loop' do
|
|
56
|
+
events.instance_variable_get(:@lock).acquire(0.1)
|
|
57
|
+
|
|
58
|
+
expect(events.drive(Restify::Promise.new, Restify::Timeout.new(0.1))).to be false
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe '#run' do
|
|
63
|
+
it 'completes transfers enqueued from other threads' do
|
|
64
|
+
background = Thread.new { events.run }
|
|
65
|
+
threads << background
|
|
66
|
+
|
|
67
|
+
easy, promise, completed_by = transfer
|
|
68
|
+
events.enqueue(easy)
|
|
69
|
+
|
|
70
|
+
expect(completed_by.pop(timeout: 1)).to eq background
|
|
71
|
+
expect(promise).to be_fulfilled
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
describe '#forked!' do
|
|
76
|
+
it 'rejects pending transfers' do
|
|
77
|
+
easy, promise, = transfer
|
|
78
|
+
events.enqueue(easy)
|
|
79
|
+
|
|
80
|
+
events.forked!
|
|
81
|
+
|
|
82
|
+
expect(promise).to be_rejected
|
|
83
|
+
expect(promise.reason).to be_a Restify::NetworkError
|
|
84
|
+
expect(promise.reason.message).to include 'Request started before fork'
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it 'does not release pending handles' do
|
|
88
|
+
easy, = transfer
|
|
89
|
+
events.enqueue(easy)
|
|
90
|
+
|
|
91
|
+
events.forked!
|
|
92
|
+
|
|
93
|
+
expect(easy.handle.autorelease?).to be false
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Restify::Adapter::Ethon::Options do
|
|
6
|
+
subject(:options) { described_class.new(values) }
|
|
7
|
+
|
|
8
|
+
def number(name)
|
|
9
|
+
described_class.number(name)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe '#to_a' do
|
|
13
|
+
subject(:compiled) { options.to_a }
|
|
14
|
+
|
|
15
|
+
context 'with bool options' do
|
|
16
|
+
let(:values) { {followlocation: true, verbose: false, tcp_keepalive: 0} }
|
|
17
|
+
|
|
18
|
+
it 'converts to 1 or 0' do
|
|
19
|
+
expect(compiled).to eq [
|
|
20
|
+
[number(:followlocation), :long, 1],
|
|
21
|
+
[number(:verbose), :long, 0],
|
|
22
|
+
[number(:tcp_keepalive), :long, 0],
|
|
23
|
+
]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context 'with int options' do
|
|
28
|
+
let(:values) { {timeout_ms: 1500, maxredirs: '5'} }
|
|
29
|
+
|
|
30
|
+
it { expect(compiled).to eq [[number(:timeout_ms), :long, 1500], [number(:maxredirs), :long, 5]] }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
context 'with enum options' do
|
|
34
|
+
let(:values) { {sslversion: :tlsv1, proxytype: 'https', timecondition: 1} }
|
|
35
|
+
|
|
36
|
+
it 'converts symbols, strings, and numbers' do
|
|
37
|
+
expect(compiled).to eq [
|
|
38
|
+
[number(:sslversion), :long, 1],
|
|
39
|
+
[number(:proxytype), :long, 2],
|
|
40
|
+
[number(:timecondition), :long, 1],
|
|
41
|
+
]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
context 'with bitmask options' do
|
|
46
|
+
let(:values) { {protocols: %i[http https], redir_protocols: :https} }
|
|
47
|
+
|
|
48
|
+
it { expect(compiled).to eq [[number(:protocols), :long, 3], [number(:redir_protocols), :long, 2]] }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
context 'with string options' do
|
|
52
|
+
let(:values) { {useragent: :restify} }
|
|
53
|
+
|
|
54
|
+
it { expect(compiled).to eq [[number(:useragent), :string, 'restify']] }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
context 'with nil values' do
|
|
58
|
+
let(:values) { {useragent: nil, timeout_ms: 5} }
|
|
59
|
+
|
|
60
|
+
it { expect(compiled).to eq [[number(:timeout_ms), :long, 5]] }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
context 'with other option types' do
|
|
64
|
+
let(:values) { {postquote: []} }
|
|
65
|
+
|
|
66
|
+
it 'uses the generic handling' do
|
|
67
|
+
expect(compiled).to eq [[:postquote, nil, []]]
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe '#initialize' do
|
|
73
|
+
it 'raises on unknown options' do
|
|
74
|
+
expect { described_class.new(unknown: 1) }.to raise_error ArgumentError, /Unknown libcurl option: unknown/
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'raises on unknown values' do
|
|
78
|
+
expect { described_class.new(sslversion: :tlsv9) }.to raise_error ArgumentError, /sslversion: :tlsv9/
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe '#apply' do
|
|
83
|
+
let(:values) { {followlocation: true, useragent: 'restify', postquote: []} }
|
|
84
|
+
let(:easy) { instance_double(Restify::Adapter::Ethon::Easy, handle: FFI::Pointer.new(1)) }
|
|
85
|
+
|
|
86
|
+
it 'sets converted options directly, and others with the generic handling' do
|
|
87
|
+
expect(Ethon::Curl).to receive(:easy_setopt).with(easy.handle, number(:followlocation), :long, 1)
|
|
88
|
+
expect(Ethon::Curl).to receive(:easy_setopt).with(easy.handle, number(:useragent), :string, 'restify')
|
|
89
|
+
expect(Ethon::Curl).to receive(:set_option).with(:postquote, [], easy.handle)
|
|
90
|
+
|
|
91
|
+
options.apply(easy)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
describe '.number' do
|
|
96
|
+
it 'returns the number of the libcurl option' do
|
|
97
|
+
expect(number(:timeout_ms)).to eq 155
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it 'raises on unknown options' do
|
|
101
|
+
expect { number(:unknown) }.to raise_error ArgumentError, /Unknown libcurl option/
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Restify::Adapter::Ethon::Pool do
|
|
6
|
+
subject(:pool) do
|
|
7
|
+
described_class.new(size: 2) do
|
|
8
|
+
created << Handle.new(0, Pointer.new(true))
|
|
9
|
+
created.last
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
let(:created) { [] }
|
|
14
|
+
|
|
15
|
+
before do
|
|
16
|
+
stub_const('Pointer', Struct.new(:autorelease))
|
|
17
|
+
stub_const('Handle', Struct.new(:resets, :handle) do
|
|
18
|
+
def reset
|
|
19
|
+
self.resets += 1
|
|
20
|
+
end
|
|
21
|
+
end,)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def release(*handles)
|
|
25
|
+
handles.each {|handle| pool.release(handle) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe '#checkout' do
|
|
29
|
+
it 'creates a handle when none is idle' do
|
|
30
|
+
expect(pool.checkout).to be created.first
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'reuses released handles' do
|
|
34
|
+
handle = pool.checkout
|
|
35
|
+
release(handle)
|
|
36
|
+
|
|
37
|
+
expect(pool.checkout).to be handle
|
|
38
|
+
expect(created.size).to eq 1
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'returns distinct handles to concurrent threads' do
|
|
42
|
+
release(*Array.new(2) { pool.checkout })
|
|
43
|
+
|
|
44
|
+
handles = Array.new(4) { Thread.new { pool.checkout } }.map(&:value)
|
|
45
|
+
|
|
46
|
+
expect(handles.map(&:object_id).uniq.size).to eq 4
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe '#release' do
|
|
51
|
+
it 'resets handles' do
|
|
52
|
+
handle = pool.checkout
|
|
53
|
+
release(handle)
|
|
54
|
+
|
|
55
|
+
expect(handle.resets).to eq 1
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'keeps a limited number of idle handles' do
|
|
59
|
+
released = Array.new(3) { pool.checkout }
|
|
60
|
+
release(*released)
|
|
61
|
+
|
|
62
|
+
# Two of them are reused, the third one is created anew.
|
|
63
|
+
reused = Array.new(3) { pool.checkout }
|
|
64
|
+
|
|
65
|
+
expect(reused.count {|handle| released.any? {|r| r.equal?(handle) } }).to eq 2
|
|
66
|
+
expect(created.size).to eq 4
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
describe '#forked!' do
|
|
71
|
+
it 'does not release idle handles' do
|
|
72
|
+
idle = pool.checkout
|
|
73
|
+
release(idle)
|
|
74
|
+
|
|
75
|
+
pool.forked!
|
|
76
|
+
|
|
77
|
+
expect(idle.handle.autorelease).to be false
|
|
78
|
+
expect(pool.checkout).not_to be idle
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|