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
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Restify
|
|
4
|
+
module Adapter
|
|
5
|
+
class Ethon < Base
|
|
6
|
+
#
|
|
7
|
+
# Idle libcurl easy handles, kept for reuse.
|
|
8
|
+
#
|
|
9
|
+
# Handles can be taken and released from any thread. A handle must
|
|
10
|
+
# only be released once libcurl is done with it.
|
|
11
|
+
#
|
|
12
|
+
# @api private
|
|
13
|
+
#
|
|
14
|
+
class Pool
|
|
15
|
+
def initialize(size:, &factory)
|
|
16
|
+
@size = size
|
|
17
|
+
@factory = factory
|
|
18
|
+
@mutex = Mutex.new
|
|
19
|
+
@idle = []
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def checkout
|
|
23
|
+
@mutex.synchronize { @idle.pop } || @factory.call
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def release(easy)
|
|
27
|
+
easy.reset
|
|
28
|
+
|
|
29
|
+
@mutex.synchronize { @idle << easy if @idle.size < @size }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Forget all handles without releasing them as they belong to
|
|
33
|
+
# the parent process.
|
|
34
|
+
def forked!
|
|
35
|
+
@mutex.synchronize do
|
|
36
|
+
@idle.each {|easy| easy.handle.autorelease = false }
|
|
37
|
+
@idle.clear
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ethon'
|
|
4
|
+
require 'nio'
|
|
5
|
+
|
|
6
|
+
require 'restify/adapter/ethon/options'
|
|
7
|
+
require 'restify/adapter/ethon/easy'
|
|
8
|
+
require 'restify/adapter/ethon/event_loop'
|
|
9
|
+
require 'restify/adapter/ethon/pool'
|
|
10
|
+
|
|
11
|
+
module Restify
|
|
12
|
+
module Adapter
|
|
13
|
+
#
|
|
14
|
+
# An adapter using libcurl's multi interface via `Ethon` in
|
|
15
|
+
# `socket_action` mode, driven by a nio4r-based event loop.
|
|
16
|
+
#
|
|
17
|
+
# Requests are handed over to the event loop using a queue and the
|
|
18
|
+
# loop is woken up via `NIO::Selector#wakeup`. Both are safe to call
|
|
19
|
+
# from any thread.
|
|
20
|
+
#
|
|
21
|
+
# This is important because the event loop is run from either any
|
|
22
|
+
# waiting thread or a dedicated background thread.
|
|
23
|
+
#
|
|
24
|
+
# The background thread that is spawned on demand and restarted if
|
|
25
|
+
# it ever dies. A thread waiting on a request's promise takes over
|
|
26
|
+
# the loop and runs it itself until the promise is complete, see
|
|
27
|
+
# `#drive`. This avoids handing each request and response between
|
|
28
|
+
# threads, which is expensive (thread wakeup). Only one thread runs
|
|
29
|
+
# the loop at a time, therefore all libcurl and selector interaction
|
|
30
|
+
# is still serialized.
|
|
31
|
+
#
|
|
32
|
+
class Ethon < Base
|
|
33
|
+
include Logging
|
|
34
|
+
|
|
35
|
+
# Restify follows relations from URLs in server responses, and
|
|
36
|
+
# libcurl supports far more than HTTP, e.g. `file://` or `scp://`.
|
|
37
|
+
# Therefore only HTTP-like protocols must ever be used, both for
|
|
38
|
+
# the request itself and when following redirects.
|
|
39
|
+
#
|
|
40
|
+
# This is intentionally not configurable.
|
|
41
|
+
PROTOCOLS = %i[http https].freeze
|
|
42
|
+
|
|
43
|
+
# Set headers bypassing the wait for 100-continue responses many
|
|
44
|
+
# servers do not send correctly.
|
|
45
|
+
DEFAULT_HEADERS = {
|
|
46
|
+
'Expect' => '',
|
|
47
|
+
'Transfer-Encoding' => '',
|
|
48
|
+
}.freeze
|
|
49
|
+
|
|
50
|
+
DEFAULT_OPTIONS = {
|
|
51
|
+
followlocation: true,
|
|
52
|
+
tcp_keepalive: true,
|
|
53
|
+
tcp_keepidle: 5,
|
|
54
|
+
tcp_keepintvl: 5,
|
|
55
|
+
}.freeze
|
|
56
|
+
|
|
57
|
+
ENFORCED_OPTIONS = {
|
|
58
|
+
# Do not use signal handlers in libcurl, as it would interfere
|
|
59
|
+
# with the threads.
|
|
60
|
+
nosignal: true,
|
|
61
|
+
|
|
62
|
+
# Do not allow using and redirecting to unexpected (e.g. local)
|
|
63
|
+
# protocols.
|
|
64
|
+
protocols: PROTOCOLS,
|
|
65
|
+
redir_protocols: PROTOCOLS,
|
|
66
|
+
}.freeze
|
|
67
|
+
|
|
68
|
+
# Maximum number of idle easy handles kept for reuse.
|
|
69
|
+
POOL_SIZE = 64
|
|
70
|
+
|
|
71
|
+
def initialize(options: {}, **multi)
|
|
72
|
+
@options = Options.new({
|
|
73
|
+
**DEFAULT_OPTIONS,
|
|
74
|
+
**options,
|
|
75
|
+
**ENFORCED_OPTIONS,
|
|
76
|
+
})
|
|
77
|
+
@multi_options = multi
|
|
78
|
+
|
|
79
|
+
@mutex = Mutex.new
|
|
80
|
+
@thread = nil
|
|
81
|
+
|
|
82
|
+
setup
|
|
83
|
+
|
|
84
|
+
super()
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def call_native(request, writer)
|
|
88
|
+
check_fork!
|
|
89
|
+
|
|
90
|
+
easy = convert(request, writer)
|
|
91
|
+
|
|
92
|
+
debug 'request:add',
|
|
93
|
+
tag: request.object_id,
|
|
94
|
+
method: request.method.upcase,
|
|
95
|
+
url: request.uri,
|
|
96
|
+
timeout: request.timeout
|
|
97
|
+
|
|
98
|
+
# Ensure the event loop is running and let it pick up the new
|
|
99
|
+
# request.
|
|
100
|
+
thread
|
|
101
|
+
|
|
102
|
+
@events.enqueue(easy)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Run the event loop in the calling thread until the promise is
|
|
106
|
+
# complete or the timeout expires, see `Promise#wait` and
|
|
107
|
+
# `EventLoop#drive`.
|
|
108
|
+
#
|
|
109
|
+
# This only processes transfers, i.e. fulfills or rejects the
|
|
110
|
+
# adapter's promises. Callbacks chained with `Promise#then` still
|
|
111
|
+
# only run in the thread waiting on them.
|
|
112
|
+
#
|
|
113
|
+
# Returns false when the loop cannot be run in the calling thread,
|
|
114
|
+
# i.e. when waiting from within the loop itself.
|
|
115
|
+
#
|
|
116
|
+
def drive(promise, timeout)
|
|
117
|
+
check_fork!
|
|
118
|
+
|
|
119
|
+
@events.drive(promise, timeout)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
def driver
|
|
125
|
+
self
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def setup
|
|
129
|
+
@pid = Process.pid
|
|
130
|
+
@pool = Pool.new(size: POOL_SIZE) { Easy.new }
|
|
131
|
+
@events = EventLoop.new(**@multi_options)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def check_fork!
|
|
135
|
+
return if @pid == Process.pid
|
|
136
|
+
|
|
137
|
+
@mutex.synchronize { forked! if @pid != Process.pid }
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# A forked child process inherits the parent's event loop,
|
|
141
|
+
# connections and sockets. They must neither be used nor cleaned
|
|
142
|
+
# up here, as the parent still uses them.
|
|
143
|
+
#
|
|
144
|
+
# Therefore, abandon the state without releasing it, and set up
|
|
145
|
+
# everything again.
|
|
146
|
+
def forked!
|
|
147
|
+
@events.forked!
|
|
148
|
+
@pool.forked!
|
|
149
|
+
|
|
150
|
+
@thread = nil
|
|
151
|
+
setup
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def convert(request, writer)
|
|
155
|
+
@pool.checkout.tap do |easy|
|
|
156
|
+
easy._otel_span = OpenTelemetry::Trace.current_span
|
|
157
|
+
easy._restify_request = request
|
|
158
|
+
easy._restify_writer = writer
|
|
159
|
+
|
|
160
|
+
easy.prepare(request, @options)
|
|
161
|
+
|
|
162
|
+
easy.on_complete do |completed|
|
|
163
|
+
complete(completed, request, writer)
|
|
164
|
+
|
|
165
|
+
@pool.release(completed)
|
|
166
|
+
|
|
167
|
+
# Wake up threads waiting on the loop to check if their
|
|
168
|
+
# result is available now.
|
|
169
|
+
@events.notify
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def complete(easy, request, writer)
|
|
175
|
+
writer.set do
|
|
176
|
+
if logger&.debug?
|
|
177
|
+
debug 'request:complete',
|
|
178
|
+
tag: request.object_id,
|
|
179
|
+
status: easy.response_code,
|
|
180
|
+
message: easy.return_code
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
easy.response(request)
|
|
184
|
+
end
|
|
185
|
+
rescue StandardError => e
|
|
186
|
+
# This runs inside a libcurl callback, therefore no exception
|
|
187
|
+
# must ever escape from here. Anything reaching this point could
|
|
188
|
+
# not be handed to the promise anymore.
|
|
189
|
+
error(e)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def thread
|
|
193
|
+
@mutex.synchronize do
|
|
194
|
+
# Spawn thread if not yet started, or recreate it if it died.
|
|
195
|
+
if @thread.nil? || !@thread.status
|
|
196
|
+
debug 'loop:spawn'
|
|
197
|
+
|
|
198
|
+
events = @events
|
|
199
|
+
@thread = Thread.new { events.run }
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
@thread
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def _log_prefix
|
|
207
|
+
"[#{object_id}/#{Thread.current.object_id}]"
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Restify
|
|
4
|
+
module Adapter
|
|
5
|
+
#
|
|
6
|
+
# Manages coordination for exclusive ownership of an event loop,
|
|
7
|
+
# shared between a background thread and threads waiting on results.
|
|
8
|
+
# This way, the event loop can run on waiting threads directly,
|
|
9
|
+
# avoiding costly context switches to the background thread.
|
|
10
|
+
#
|
|
11
|
+
# Waiting threads have priority: the background thread only acquires
|
|
12
|
+
# the loop while no thread is waiting for it, and is interrupted to
|
|
13
|
+
# release it as soon as one is.
|
|
14
|
+
#
|
|
15
|
+
# Waiting threads can give up waiting once their result is
|
|
16
|
+
# available, e.g. because another thread running the loop produced
|
|
17
|
+
# it. Whoever runs the loop must call `#notify` after producing a
|
|
18
|
+
# result.
|
|
19
|
+
#
|
|
20
|
+
class LoopLock
|
|
21
|
+
# @yield Called when a waiting thread needs the background thread to
|
|
22
|
+
# release the loop, e.g. to wake it up from `select`.
|
|
23
|
+
#
|
|
24
|
+
def initialize(&interrupt)
|
|
25
|
+
@interrupt = interrupt
|
|
26
|
+
|
|
27
|
+
reset!
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Acquire the loop for a thread waiting on a result.
|
|
31
|
+
#
|
|
32
|
+
# @param timeout [Restify::Timeout, Numeric] Maximum time to wait,
|
|
33
|
+
# derived from the promise timeout of the waiting thread.
|
|
34
|
+
#
|
|
35
|
+
# @yieldreturn [Boolean] Whether the result is available, and
|
|
36
|
+
# the loop is not needed anymore.
|
|
37
|
+
#
|
|
38
|
+
# @return [Boolean] true when the calling thread holds the loop,
|
|
39
|
+
# false when the result is available or the timeout expired
|
|
40
|
+
# first.
|
|
41
|
+
#
|
|
42
|
+
def acquire(timeout, &done)
|
|
43
|
+
timeout = Timeout.new(timeout)
|
|
44
|
+
|
|
45
|
+
background = @mutex.synchronize do
|
|
46
|
+
@waiters += 1
|
|
47
|
+
@background
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
@interrupt&.call if background
|
|
51
|
+
|
|
52
|
+
@mutex.synchronize do
|
|
53
|
+
loop do
|
|
54
|
+
return false if done&.call
|
|
55
|
+
|
|
56
|
+
remaining = timeout.remaining
|
|
57
|
+
return false unless remaining.positive?
|
|
58
|
+
|
|
59
|
+
if free?
|
|
60
|
+
@owner = Thread.current
|
|
61
|
+
return true
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
@changed.wait(@mutex, remaining)
|
|
65
|
+
end
|
|
66
|
+
ensure
|
|
67
|
+
@waiters -= 1
|
|
68
|
+
|
|
69
|
+
# Let the background thread continue.
|
|
70
|
+
@changed.broadcast if @waiters.zero?
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Acquire the loop for the background thread. Blocks while any
|
|
75
|
+
# other thread holds the loop or waits for it.
|
|
76
|
+
def acquire_background
|
|
77
|
+
@mutex.synchronize do
|
|
78
|
+
@changed.wait(@mutex) until free? && @waiters.zero?
|
|
79
|
+
|
|
80
|
+
@owner = Thread.current
|
|
81
|
+
@background = true
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def release
|
|
86
|
+
@mutex.synchronize do
|
|
87
|
+
@owner = nil
|
|
88
|
+
@background = false
|
|
89
|
+
@changed.broadcast
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def owned?
|
|
94
|
+
@owner.equal?(Thread.current)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def notify
|
|
98
|
+
return unless @waiters.positive?
|
|
99
|
+
|
|
100
|
+
@mutex.synchronize { @changed.broadcast }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Forget all threads holding or waiting for the loop, e.g. in a
|
|
104
|
+
# forked child process, where these threads do not exist.
|
|
105
|
+
#
|
|
106
|
+
# Must not be called while other threads use the lock.
|
|
107
|
+
def reset!
|
|
108
|
+
@mutex = Mutex.new
|
|
109
|
+
@changed = ConditionVariable.new
|
|
110
|
+
@owner = nil
|
|
111
|
+
@background = false
|
|
112
|
+
@waiters = 0
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
|
|
117
|
+
# A thread holding the loop can be gone without releasing it, e.g.
|
|
118
|
+
# when killed.
|
|
119
|
+
def free?
|
|
120
|
+
@owner.nil? || !@owner.alive?
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -8,23 +8,25 @@ module Restify
|
|
|
8
8
|
module Telemetry
|
|
9
9
|
def call(request)
|
|
10
10
|
method = request.method.to_s.upcase
|
|
11
|
-
uri =
|
|
12
|
-
|
|
11
|
+
uri = request.uri
|
|
12
|
+
port = uri.inferred_port
|
|
13
|
+
name = "#{method} #{uri.scheme}://#{uri.host}:#{port}"
|
|
13
14
|
|
|
15
|
+
# Remove `nil` as OTEL spans must not contain any nil attributes
|
|
14
16
|
attributes = {
|
|
15
17
|
'http.request.method' => method,
|
|
16
18
|
'server.address' => uri.host,
|
|
17
|
-
'server.port' =>
|
|
19
|
+
'server.port' => port,
|
|
18
20
|
'url.full' => uri.to_s,
|
|
19
21
|
'url.scheme' => uri.scheme,
|
|
20
|
-
}
|
|
22
|
+
}.compact
|
|
21
23
|
|
|
22
24
|
span = tracer.start_span(name, attributes:, kind: :client)
|
|
23
25
|
OpenTelemetry::Trace.with_span(span) do
|
|
24
26
|
OpenTelemetry.propagation.inject(request.headers)
|
|
25
27
|
|
|
26
28
|
super.tap do |x|
|
|
27
|
-
x.add_observer do |
|
|
29
|
+
x.add_observer do |response, err|
|
|
28
30
|
if response
|
|
29
31
|
span.set_attribute('http.response.status_code', response&.code)
|
|
30
32
|
span.status = OpenTelemetry::Trace::Status.error unless (100..399).cover?(response&.code)
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
begin
|
|
4
|
+
require 'typhoeus'
|
|
5
|
+
rescue LoadError => e
|
|
6
|
+
raise LoadError.new(
|
|
7
|
+
"Restify::Adapter::Typhoeus requires the `typhoeus` gem, add it to your Gemfile (#{e.message})",
|
|
8
|
+
)
|
|
9
|
+
end
|
|
6
10
|
|
|
7
11
|
module Restify
|
|
8
12
|
module Adapter
|
|
@@ -23,6 +27,14 @@ module Restify
|
|
|
23
27
|
tcp_keepintvl: 5,
|
|
24
28
|
}.freeze
|
|
25
29
|
|
|
30
|
+
# Restify follows relations from URLs in server responses, and
|
|
31
|
+
# libcurl supports far more than HTTP, e.g. `file://` or `scp://`.
|
|
32
|
+
# Therefore only HTTP-like protocols must ever be used, both for
|
|
33
|
+
# the request itself and when following redirects.
|
|
34
|
+
#
|
|
35
|
+
# This is intentionally not configurable.
|
|
36
|
+
PROTOCOLS = %i[http https].freeze
|
|
37
|
+
|
|
26
38
|
# Patch Hydra to restore the correct OpenTelemetry span when
|
|
27
39
|
# adding the request, so that the Ethon instrumentation can
|
|
28
40
|
# properly pick up the context where the Restify request
|
|
@@ -41,8 +53,8 @@ module Restify
|
|
|
41
53
|
end
|
|
42
54
|
end
|
|
43
55
|
|
|
44
|
-
def initialize(sync: false, options: {}, **
|
|
45
|
-
@hydra = ::Typhoeus::Hydra.new(**
|
|
56
|
+
def initialize(sync: false, options: {}, **)
|
|
57
|
+
@hydra = ::Typhoeus::Hydra.new(**)
|
|
46
58
|
@hydra.extend(EasyOverride)
|
|
47
59
|
|
|
48
60
|
@mutex = Mutex.new
|
|
@@ -62,8 +74,11 @@ module Restify
|
|
|
62
74
|
req = convert(request, writer)
|
|
63
75
|
|
|
64
76
|
if sync?
|
|
65
|
-
|
|
66
|
-
|
|
77
|
+
begin
|
|
78
|
+
req.run
|
|
79
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
80
|
+
writer.reject(e)
|
|
81
|
+
end
|
|
67
82
|
else
|
|
68
83
|
debug 'request:add',
|
|
69
84
|
tag: request.object_id,
|
|
@@ -71,7 +86,7 @@ module Restify
|
|
|
71
86
|
url: request.uri,
|
|
72
87
|
timeout: request.timeout
|
|
73
88
|
|
|
74
|
-
@queue <<
|
|
89
|
+
@queue << req
|
|
75
90
|
|
|
76
91
|
thread.run unless thread.status
|
|
77
92
|
end
|
|
@@ -88,33 +103,38 @@ module Restify
|
|
|
88
103
|
body: request.body,
|
|
89
104
|
timeout: request.timeout,
|
|
90
105
|
connecttimeout: request.timeout,
|
|
106
|
+
|
|
107
|
+
# Block accessing and redirecting to non-HTTP protocols:
|
|
108
|
+
protocols: PROTOCOLS,
|
|
109
|
+
redir_protocols: PROTOCOLS,
|
|
91
110
|
).tap do |req|
|
|
92
111
|
req._otel_span = OpenTelemetry::Trace.current_span
|
|
93
112
|
req._restify_writer = writer
|
|
94
113
|
|
|
95
114
|
req.on_complete do |response|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
Restify::NetworkError.new(request, response.return_message)
|
|
105
|
-
|
|
106
|
-
|
|
115
|
+
writer.set do
|
|
116
|
+
debug 'request:complete',
|
|
117
|
+
tag: request.object_id,
|
|
118
|
+
status: response.code,
|
|
119
|
+
message: response.return_message,
|
|
120
|
+
timeout: response.timed_out?
|
|
121
|
+
|
|
122
|
+
if response.timed_out? || response.code.zero?
|
|
123
|
+
raise Restify::NetworkError.new(request, response.return_message)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
convert_back(response, request)
|
|
107
127
|
end
|
|
108
128
|
|
|
109
|
-
# Add all newly queued requests to active hydra, e.g.
|
|
110
|
-
# queued in a completion callback.
|
|
129
|
+
# Add all newly queued requests to active hydra, e.g.
|
|
130
|
+
# requests queued in a completion callback.
|
|
111
131
|
dequeue_all
|
|
112
132
|
end
|
|
113
133
|
end
|
|
114
134
|
end
|
|
115
135
|
|
|
116
136
|
def convert_back(response, request)
|
|
117
|
-
uri = request
|
|
137
|
+
uri = effective_uri(response, request)
|
|
118
138
|
status = response.code
|
|
119
139
|
body = response.body
|
|
120
140
|
headers = convert_headers(response.headers)
|
|
@@ -122,6 +142,15 @@ module Restify
|
|
|
122
142
|
::Restify::Response.new(request, uri, status, headers, body)
|
|
123
143
|
end
|
|
124
144
|
|
|
145
|
+
def effective_uri(response, request)
|
|
146
|
+
url = response.effective_url
|
|
147
|
+
|
|
148
|
+
# Only parse the URL after redirects, as parsing is expensive.
|
|
149
|
+
return request.uri if url.nil? || url == request.uri.to_s
|
|
150
|
+
|
|
151
|
+
Addressable::URI.parse(url)
|
|
152
|
+
end
|
|
153
|
+
|
|
125
154
|
def convert_headers(headers)
|
|
126
155
|
return {} unless headers.respond_to?(:each_pair)
|
|
127
156
|
|
|
@@ -165,7 +194,7 @@ module Restify
|
|
|
165
194
|
runs += 1
|
|
166
195
|
debug 'hydra:completed'
|
|
167
196
|
rescue StandardError => e
|
|
168
|
-
|
|
197
|
+
error(e)
|
|
169
198
|
end
|
|
170
199
|
ensure
|
|
171
200
|
debug 'hydra:exit'
|
data/lib/restify/context.rb
CHANGED
|
@@ -30,13 +30,23 @@ module Restify
|
|
|
30
30
|
@options = kwargs
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
# Resolve a URI against the context URI. Absolute URIs are returned
|
|
34
|
+
# as they are, e.g. when already joined by a relation.
|
|
35
|
+
#
|
|
36
|
+
# @param uri [Addressable::URI, String] URI to resolve.
|
|
37
|
+
# @return [Addressable::URI] Absolute URI.
|
|
38
|
+
#
|
|
33
39
|
def join(uri)
|
|
34
|
-
|
|
40
|
+
if uri.is_a?(Addressable::URI) && uri.absolute?
|
|
41
|
+
uri
|
|
42
|
+
else
|
|
43
|
+
self.uri.join(uri)
|
|
44
|
+
end
|
|
35
45
|
end
|
|
36
46
|
|
|
37
|
-
def inherit(uri, **
|
|
47
|
+
def inherit(uri, **)
|
|
38
48
|
uri ||= self.uri
|
|
39
|
-
Context.new(uri,
|
|
49
|
+
Context.new(uri, **, **options)
|
|
40
50
|
end
|
|
41
51
|
|
|
42
52
|
def process(response)
|
|
@@ -47,16 +57,16 @@ module Restify
|
|
|
47
57
|
processor.new(context, response).resource
|
|
48
58
|
end
|
|
49
59
|
|
|
50
|
-
def request(method, uri, data: nil, headers: {}, **
|
|
60
|
+
def request(method, uri, data: nil, headers: {}, **)
|
|
51
61
|
request = Request.new(
|
|
52
62
|
headers: default_headers.merge(headers),
|
|
53
|
-
|
|
63
|
+
**,
|
|
54
64
|
method:,
|
|
55
65
|
uri: join(uri),
|
|
56
66
|
data:,
|
|
57
67
|
)
|
|
58
68
|
|
|
59
|
-
ret = cache.call(request) {|req| adapter.call(req) }
|
|
69
|
+
ret = cache ? cache.call(request) {|req| adapter.call(req) } : adapter.call(request)
|
|
60
70
|
ret.then do |response|
|
|
61
71
|
raise ResponseError.from_code(response) if response.errored?
|
|
62
72
|
|
data/lib/restify/error.rb
CHANGED
|
@@ -13,6 +13,19 @@ module Restify
|
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
# A {ParseError} is raised when accessing the data of a resource,
|
|
17
|
+
# whose response body could not be parsed, e.g. invalid JSON. The
|
|
18
|
+
# original error is available as {#cause}.
|
|
19
|
+
#
|
|
20
|
+
class ParseError < StandardError
|
|
21
|
+
attr_reader :response
|
|
22
|
+
|
|
23
|
+
def initialize(response, message)
|
|
24
|
+
@response = response
|
|
25
|
+
super("Could not parse response body for `#{response&.uri}': #{message}")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
16
29
|
# A {ResponseError} is returned on a non-successful
|
|
17
30
|
# response from server. Usually it will either be a
|
|
18
31
|
# {ClientError} or a {ServerError}.
|
data/lib/restify/global.rb
CHANGED
|
@@ -2,47 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
module Restify
|
|
4
4
|
module Global
|
|
5
|
-
def new(uri, **
|
|
6
|
-
context = resolve_context
|
|
5
|
+
def new(uri, **)
|
|
6
|
+
context = resolve_context(uri, **)
|
|
7
7
|
|
|
8
8
|
Relation.new context, context.uri
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Restify::Adapter::Typhoeus.new
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def adapter=(adapter)
|
|
18
|
-
@adapter = adapter
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def cache
|
|
22
|
-
@cache ||= begin
|
|
23
|
-
require 'active_support'
|
|
24
|
-
require 'active_support/cache'
|
|
25
|
-
Restify::Cache.new store: ActiveSupport::Cache::MemoryStore.new
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def cache=(cache)
|
|
30
|
-
@cache = cache
|
|
31
|
-
end
|
|
11
|
+
attr_writer :adapter
|
|
12
|
+
attr_accessor :cache, :logger
|
|
32
13
|
|
|
33
|
-
def
|
|
34
|
-
::
|
|
14
|
+
def adapter
|
|
15
|
+
@adapter ||= Restify::Adapter::Ethon.new
|
|
35
16
|
end
|
|
36
17
|
|
|
37
|
-
def logger=(logger); end
|
|
38
|
-
|
|
39
18
|
private
|
|
40
19
|
|
|
41
|
-
def resolve_context(uri, **
|
|
20
|
+
def resolve_context(uri, **)
|
|
42
21
|
if uri.is_a? Symbol
|
|
43
|
-
Restify::Registry.fetch(uri).inherit(nil, **
|
|
22
|
+
Restify::Registry.fetch(uri).inherit(nil, **)
|
|
44
23
|
else
|
|
45
|
-
Context.new(uri, **
|
|
24
|
+
Context.new(uri, **)
|
|
46
25
|
end
|
|
47
26
|
end
|
|
48
27
|
end
|