skylight 4.3.2 → 5.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +399 -336
- data/CLA.md +1 -1
- data/CONTRIBUTING.md +2 -8
- data/LICENSE.md +7 -17
- data/README.md +1 -1
- data/ext/extconf.rb +45 -56
- data/ext/libskylight.yml +10 -6
- data/ext/skylight_native.c +22 -99
- data/lib/skylight.rb +201 -14
- data/lib/skylight/api.rb +32 -21
- data/lib/skylight/cli.rb +48 -46
- data/lib/skylight/cli/doctor.rb +62 -63
- data/lib/skylight/cli/helpers.rb +19 -19
- data/lib/skylight/cli/merger.rb +142 -138
- data/lib/skylight/config.rb +634 -199
- data/lib/skylight/deprecation.rb +17 -0
- data/lib/skylight/errors.rb +23 -9
- data/lib/skylight/extensions.rb +95 -0
- data/lib/skylight/extensions/source_location.rb +291 -0
- data/lib/skylight/formatters/http.rb +18 -0
- data/lib/skylight/gc.rb +99 -0
- data/lib/skylight/helpers.rb +81 -36
- data/lib/skylight/instrumenter.rb +336 -18
- data/lib/skylight/middleware.rb +134 -1
- data/lib/skylight/native.rb +60 -12
- data/lib/skylight/native_ext_fetcher.rb +13 -14
- data/lib/skylight/normalizers.rb +157 -0
- data/lib/skylight/normalizers/action_controller/process_action.rb +68 -0
- data/lib/skylight/normalizers/action_controller/send_file.rb +51 -0
- data/lib/skylight/normalizers/action_dispatch/process_middleware.rb +22 -0
- data/lib/skylight/normalizers/action_dispatch/route_set.rb +27 -0
- data/lib/skylight/normalizers/action_view/render_collection.rb +24 -0
- data/lib/skylight/normalizers/action_view/render_layout.rb +25 -0
- data/lib/skylight/normalizers/action_view/render_partial.rb +23 -0
- data/lib/skylight/normalizers/action_view/render_template.rb +23 -0
- data/lib/skylight/normalizers/active_job/perform.rb +90 -0
- data/lib/skylight/normalizers/active_model_serializers/render.rb +32 -0
- data/lib/skylight/normalizers/active_record/instantiation.rb +16 -0
- data/lib/skylight/normalizers/active_record/sql.rb +12 -0
- data/lib/skylight/normalizers/active_storage.rb +28 -0
- data/lib/skylight/normalizers/active_support/cache.rb +11 -0
- data/lib/skylight/normalizers/active_support/cache_clear.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_decrement.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_delete.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_exist.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_fetch_hit.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_generate.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_increment.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_read.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_read_multi.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_write.rb +16 -0
- data/lib/skylight/normalizers/coach/handler_finish.rb +44 -0
- data/lib/skylight/normalizers/coach/middleware_finish.rb +33 -0
- data/lib/skylight/normalizers/couch_potato/query.rb +20 -0
- data/lib/skylight/normalizers/data_mapper/sql.rb +12 -0
- data/lib/skylight/normalizers/default.rb +24 -0
- data/lib/skylight/normalizers/elasticsearch/request.rb +20 -0
- data/lib/skylight/normalizers/faraday/request.rb +38 -0
- data/lib/skylight/normalizers/grape/endpoint.rb +28 -0
- data/lib/skylight/normalizers/grape/endpoint_render.rb +25 -0
- data/lib/skylight/normalizers/grape/endpoint_run.rb +39 -0
- data/lib/skylight/normalizers/grape/endpoint_run_filters.rb +20 -0
- data/lib/skylight/normalizers/grape/format_response.rb +20 -0
- data/lib/skylight/normalizers/graphiti/render.rb +22 -0
- data/lib/skylight/normalizers/graphiti/resolve.rb +31 -0
- data/lib/skylight/normalizers/graphql/base.rb +127 -0
- data/lib/skylight/normalizers/render.rb +79 -0
- data/lib/skylight/normalizers/sequel/sql.rb +12 -0
- data/lib/skylight/normalizers/shrine.rb +32 -0
- data/lib/skylight/normalizers/sql.rb +45 -0
- data/lib/skylight/probes.rb +173 -0
- data/lib/skylight/probes/action_controller.rb +52 -0
- data/lib/skylight/probes/action_dispatch.rb +2 -0
- data/lib/skylight/probes/action_dispatch/request_id.rb +33 -0
- data/lib/skylight/probes/action_dispatch/routing/route_set.rb +30 -0
- data/lib/skylight/probes/action_view.rb +42 -0
- data/lib/skylight/probes/active_job.rb +27 -0
- data/lib/skylight/probes/active_job_enqueue.rb +35 -0
- data/lib/skylight/probes/active_model_serializers.rb +50 -0
- data/lib/skylight/probes/delayed_job.rb +144 -0
- data/lib/skylight/probes/elasticsearch.rb +36 -0
- data/lib/skylight/probes/excon.rb +25 -0
- data/lib/skylight/probes/excon/middleware.rb +65 -0
- data/lib/skylight/probes/faraday.rb +23 -0
- data/lib/skylight/probes/graphql.rb +38 -0
- data/lib/skylight/probes/httpclient.rb +44 -0
- data/lib/skylight/probes/middleware.rb +135 -0
- data/lib/skylight/probes/mongo.rb +156 -0
- data/lib/skylight/probes/mongoid.rb +13 -0
- data/lib/skylight/probes/net_http.rb +54 -0
- data/lib/skylight/probes/redis.rb +51 -0
- data/lib/skylight/probes/sequel.rb +29 -0
- data/lib/skylight/probes/sinatra.rb +66 -0
- data/lib/skylight/probes/sinatra_add_middleware.rb +10 -10
- data/lib/skylight/probes/tilt.rb +25 -0
- data/lib/skylight/railtie.rb +157 -27
- data/lib/skylight/sidekiq.rb +47 -0
- data/lib/skylight/subscriber.rb +108 -0
- data/lib/skylight/test.rb +151 -0
- data/lib/skylight/trace.rb +325 -22
- data/lib/skylight/user_config.rb +58 -0
- data/lib/skylight/util.rb +12 -0
- data/lib/skylight/util/allocation_free.rb +26 -0
- data/lib/skylight/util/clock.rb +57 -0
- data/lib/skylight/util/component.rb +22 -22
- data/lib/skylight/util/deploy.rb +16 -21
- data/lib/skylight/util/gzip.rb +20 -0
- data/lib/skylight/util/http.rb +106 -113
- data/lib/skylight/util/instrumenter_method.rb +26 -0
- data/lib/skylight/util/logging.rb +136 -0
- data/lib/skylight/util/lru_cache.rb +36 -0
- data/lib/skylight/util/platform.rb +1 -5
- data/lib/skylight/util/ssl.rb +1 -25
- data/lib/skylight/vendor/cli/thor/rake_compat.rb +1 -1
- data/lib/skylight/version.rb +5 -1
- data/lib/skylight/vm/gc.rb +60 -0
- metadata +126 -13
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require "yaml"
|
|
2
|
+
require "skylight/errors"
|
|
3
|
+
|
|
4
|
+
module Skylight
|
|
5
|
+
class UserConfig
|
|
6
|
+
attr_accessor :disable_dev_warning, :disable_env_warning
|
|
7
|
+
|
|
8
|
+
def initialize(config)
|
|
9
|
+
@config = config
|
|
10
|
+
@file_path = nil
|
|
11
|
+
reload
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def file_path
|
|
15
|
+
return @file_path if @file_path
|
|
16
|
+
|
|
17
|
+
config_path =
|
|
18
|
+
@config[:user_config_path] ||
|
|
19
|
+
begin
|
|
20
|
+
require "etc"
|
|
21
|
+
home_dir = ENV["HOME"] || Etc.getpwuid.dir || (ENV["USER"] && File.expand_path("~#{ENV["USER"]}"))
|
|
22
|
+
if home_dir
|
|
23
|
+
File.join(home_dir, ".skylight")
|
|
24
|
+
else
|
|
25
|
+
raise ConfigError,
|
|
26
|
+
"The Skylight `user_config_path` must be defined since the home directory cannot be inferred"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
@file_path = File.expand_path(config_path)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def disable_dev_warning?
|
|
34
|
+
disable_dev_warning || ENV["SKYLIGHT_DISABLE_DEV_WARNING"] =~ /^true$/i
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def disable_env_warning?
|
|
38
|
+
disable_env_warning
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def reload
|
|
42
|
+
config = File.exist?(file_path) ? YAML.load_file(file_path) : false
|
|
43
|
+
return unless config
|
|
44
|
+
|
|
45
|
+
self.disable_dev_warning = !!config["disable_dev_warning"]
|
|
46
|
+
self.disable_env_warning = !!config["disable_env_warning"]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def save
|
|
50
|
+
FileUtils.mkdir_p(File.dirname(file_path))
|
|
51
|
+
File.open(file_path, "w") { |f| f.puts YAML.dump(to_hash) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def to_hash
|
|
55
|
+
{ "disable_dev_warning" => disable_dev_warning, "disable_env_warning" => disable_env_warning }
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
# @api private
|
|
3
|
+
module Util
|
|
4
|
+
# Used from the main lib
|
|
5
|
+
require "skylight/util/allocation_free"
|
|
6
|
+
require "skylight/util/clock"
|
|
7
|
+
require "skylight/util/instrumenter_method"
|
|
8
|
+
|
|
9
|
+
# Used from the CLI
|
|
10
|
+
autoload :Gzip, "skylight/util/gzip"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Util
|
|
3
|
+
# Helpers to reduce memory allocation
|
|
4
|
+
module AllocationFree
|
|
5
|
+
# Find an item in an array without allocation.
|
|
6
|
+
#
|
|
7
|
+
# @param array [Array] the array to search
|
|
8
|
+
# @yield a block called against each item until a match is found
|
|
9
|
+
# @yieldparam item an item from the array
|
|
10
|
+
# @yieldreturn [Boolean] whether `item` matches the criteria
|
|
11
|
+
# return the found item or nil, if nothing found
|
|
12
|
+
def array_find(array)
|
|
13
|
+
i = 0
|
|
14
|
+
|
|
15
|
+
while i < array.size
|
|
16
|
+
item = array[i]
|
|
17
|
+
return item if yield item
|
|
18
|
+
|
|
19
|
+
i += 1
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
nil
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Util
|
|
3
|
+
# A more precise clock
|
|
4
|
+
class Clock
|
|
5
|
+
def self.use_native!
|
|
6
|
+
class_eval do
|
|
7
|
+
def tick
|
|
8
|
+
native_hrtime
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# rubocop:disable Lint/DuplicateMethods
|
|
14
|
+
def tick
|
|
15
|
+
now = Time.now
|
|
16
|
+
now.to_i * 1_000_000_000 + now.usec * 1_000
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# rubocop:enable Lint/DuplicateMethods
|
|
20
|
+
|
|
21
|
+
# TODO: rename to secs
|
|
22
|
+
def absolute_secs
|
|
23
|
+
Time.now.to_i
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# TODO: remove
|
|
27
|
+
def nanos
|
|
28
|
+
tick
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# TODO: remove
|
|
32
|
+
def secs
|
|
33
|
+
nanos / 1_000_000_000
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class << self
|
|
37
|
+
def absolute_secs
|
|
38
|
+
default.absolute_secs
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def nanos
|
|
42
|
+
default.nanos
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def secs
|
|
46
|
+
default.secs
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def default
|
|
50
|
+
@default ||= Clock.new
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
attr_writer :default
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require "uri"
|
|
3
4
|
|
|
4
5
|
module Skylight
|
|
@@ -6,16 +7,17 @@ module Skylight
|
|
|
6
7
|
class Component
|
|
7
8
|
attr_accessor :environment, :name
|
|
8
9
|
|
|
9
|
-
NAME_FORMAT = /\A[a-zA-Z0-9_-]+\z
|
|
10
|
+
NAME_FORMAT = /\A[a-zA-Z0-9_-]+\z/.freeze
|
|
10
11
|
DEFAULT_NAME = "web"
|
|
11
12
|
WORKER_NAME = "worker"
|
|
12
13
|
DEFAULT_ENVIRONMENT = "production"
|
|
13
14
|
|
|
14
15
|
def initialize(environment, name, force_worker: false)
|
|
15
16
|
@environment = environment || DEFAULT_ENVIRONMENT
|
|
16
|
-
@name
|
|
17
|
+
@name = resolve_name(name, force_worker)
|
|
17
18
|
|
|
18
19
|
raise ArgumentError, "environment can't be blank" if @environment.empty?
|
|
20
|
+
|
|
19
21
|
validate_string!(@environment, "environment")
|
|
20
22
|
validate_string!(@name, "name")
|
|
21
23
|
end
|
|
@@ -38,34 +40,32 @@ module Skylight
|
|
|
38
40
|
|
|
39
41
|
# keys here should match those from the main config
|
|
40
42
|
def as_json(*)
|
|
41
|
-
{
|
|
42
|
-
component: name,
|
|
43
|
-
env: environment
|
|
44
|
-
}
|
|
43
|
+
{ component: name, env: environment }
|
|
45
44
|
end
|
|
46
45
|
|
|
47
46
|
private
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
def program_name
|
|
49
|
+
$PROGRAM_NAME
|
|
50
|
+
end
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
def argv
|
|
53
|
+
ARGV
|
|
54
|
+
end
|
|
56
55
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
def resolve_name(given_name, force_worker)
|
|
57
|
+
# don't allow workers to be called 'web'
|
|
58
|
+
return WORKER_NAME if force_worker && (given_name.nil? || given_name == DEFAULT_NAME)
|
|
59
|
+
return DEFAULT_NAME if given_name.nil?
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
given_name
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def validate_string!(string, kind)
|
|
65
|
+
return true if string =~ NAME_FORMAT
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
raise ArgumentError, "#{kind} can only contain lowercase letters, numbers, and dashes"
|
|
68
|
-
end
|
|
67
|
+
raise ArgumentError, "#{kind} can only contain lowercase letters, numbers, and dashes"
|
|
68
|
+
end
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
71
|
end
|
data/lib/skylight/util/deploy.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require "json"
|
|
2
|
-
require "skylight/
|
|
2
|
+
require "skylight/util/logging"
|
|
3
3
|
|
|
4
4
|
module Skylight
|
|
5
5
|
module Util
|
|
@@ -13,8 +13,7 @@ module Skylight
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
class EmptyDeploy
|
|
16
|
-
attr_reader :config
|
|
17
|
-
attr_reader :timestamp
|
|
16
|
+
attr_reader :config, :timestamp
|
|
18
17
|
|
|
19
18
|
def initialize(config)
|
|
20
19
|
@config = config
|
|
@@ -35,8 +34,8 @@ module Skylight
|
|
|
35
34
|
|
|
36
35
|
def to_query_hash
|
|
37
36
|
hash = {
|
|
38
|
-
timestamp:
|
|
39
|
-
deploy_id:
|
|
37
|
+
timestamp: timestamp,
|
|
38
|
+
deploy_id: id.to_s[0..100] # Keep this sane
|
|
40
39
|
}
|
|
41
40
|
hash[:git_sha] = git_sha.to_s[0..40] if git_sha # A valid SHA will never exceed 40
|
|
42
41
|
hash[:description] = description[0..255] if description # Avoid massive descriptions
|
|
@@ -45,13 +44,11 @@ module Skylight
|
|
|
45
44
|
end
|
|
46
45
|
|
|
47
46
|
class DefaultDeploy < EmptyDeploy
|
|
48
|
-
include
|
|
47
|
+
include Util::Logging
|
|
49
48
|
|
|
50
49
|
def initialize(*)
|
|
51
50
|
super
|
|
52
|
-
if description && !id
|
|
53
|
-
warn "The configured deploy will be ignored as an id or git_sha must be provided."
|
|
54
|
-
end
|
|
51
|
+
warn "The configured deploy will be ignored as an id or git_sha must be provided." if description && !id
|
|
55
52
|
end
|
|
56
53
|
|
|
57
54
|
def id
|
|
@@ -87,15 +84,13 @@ module Skylight
|
|
|
87
84
|
|
|
88
85
|
private
|
|
89
86
|
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
def get_info
|
|
88
|
+
info_path = config[:'heroku.dyno_info_path']
|
|
92
89
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
info["release"]
|
|
96
|
-
end
|
|
97
|
-
end
|
|
90
|
+
if File.exist?(info_path) && (info = JSON.parse(File.read(info_path)))
|
|
91
|
+
info["release"]
|
|
98
92
|
end
|
|
93
|
+
end
|
|
99
94
|
end
|
|
100
95
|
|
|
101
96
|
class GitDeploy < EmptyDeploy
|
|
@@ -108,12 +103,12 @@ module Skylight
|
|
|
108
103
|
|
|
109
104
|
private
|
|
110
105
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
end
|
|
106
|
+
def get_info
|
|
107
|
+
Dir.chdir(config.root) do
|
|
108
|
+
info = `git log -1 --pretty="%H %s" 2>&1`
|
|
109
|
+
info.split(" ", 2).map(&:strip) if $CHILD_STATUS.success?
|
|
116
110
|
end
|
|
111
|
+
end
|
|
117
112
|
end
|
|
118
113
|
|
|
119
114
|
DEPLOY_TYPES = [DefaultDeploy, HerokuDeploy, GitDeploy].freeze
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require "zlib"
|
|
2
|
+
|
|
3
|
+
module Skylight
|
|
4
|
+
module Util
|
|
5
|
+
# Provides Gzip compressing support
|
|
6
|
+
module Gzip
|
|
7
|
+
# Compress a string with Gzip
|
|
8
|
+
#
|
|
9
|
+
# @param str [String] uncompressed string
|
|
10
|
+
# @return [String] compressed string
|
|
11
|
+
def self.compress(str)
|
|
12
|
+
output = StringIO.new
|
|
13
|
+
gz = Zlib::GzipWriter.new(output)
|
|
14
|
+
gz.write(str)
|
|
15
|
+
gz.close
|
|
16
|
+
output.string
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/skylight/util/http.rb
CHANGED
|
@@ -3,23 +3,23 @@ require "json"
|
|
|
3
3
|
require "openssl"
|
|
4
4
|
require "net/http"
|
|
5
5
|
require "net/https"
|
|
6
|
-
require "skylight/
|
|
6
|
+
require "skylight/util/gzip"
|
|
7
7
|
require "skylight/util/ssl"
|
|
8
8
|
|
|
9
9
|
module Skylight
|
|
10
10
|
module Util
|
|
11
11
|
class HTTP
|
|
12
12
|
CONTENT_ENCODING = "content-encoding".freeze
|
|
13
|
-
CONTENT_LENGTH
|
|
14
|
-
CONTENT_TYPE
|
|
15
|
-
ACCEPT
|
|
16
|
-
X_VERSION_HDR
|
|
13
|
+
CONTENT_LENGTH = "content-length".freeze
|
|
14
|
+
CONTENT_TYPE = "content-type".freeze
|
|
15
|
+
ACCEPT = "Accept".freeze
|
|
16
|
+
X_VERSION_HDR = "x-skylight-agent-version".freeze
|
|
17
17
|
APPLICATION_JSON = "application/json".freeze
|
|
18
|
-
AUTHORIZATION
|
|
19
|
-
DEFLATE
|
|
20
|
-
GZIP
|
|
18
|
+
AUTHORIZATION = "authorization".freeze
|
|
19
|
+
DEFLATE = "deflate".freeze
|
|
20
|
+
GZIP = "gzip".freeze
|
|
21
21
|
|
|
22
|
-
include
|
|
22
|
+
include Logging
|
|
23
23
|
|
|
24
24
|
attr_accessor :authentication
|
|
25
25
|
attr_reader :host, :port, :config
|
|
@@ -35,7 +35,8 @@ module Skylight
|
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
class ReadResponseError < StandardError
|
|
38
|
+
class ReadResponseError < StandardError
|
|
39
|
+
end
|
|
39
40
|
|
|
40
41
|
def initialize(config, service = :auth, opts = {})
|
|
41
42
|
@config = config
|
|
@@ -46,7 +47,7 @@ module Skylight
|
|
|
46
47
|
|
|
47
48
|
url = URI.parse(url)
|
|
48
49
|
|
|
49
|
-
@ssl
|
|
50
|
+
@ssl = url.scheme == "https"
|
|
50
51
|
@host = url.host
|
|
51
52
|
@port = url.port
|
|
52
53
|
|
|
@@ -81,137 +82,129 @@ module Skylight
|
|
|
81
82
|
|
|
82
83
|
private
|
|
83
84
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
end
|
|
85
|
+
def get_timeout(type, config, service, opts)
|
|
86
|
+
config.duration_ms("#{service}_http_#{type}_timeout") || opts[:timeout] || 15
|
|
87
|
+
end
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
def build_request(type, endpoint, hdrs, length = nil)
|
|
90
|
+
headers = {}
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
headers[CONTENT_LENGTH] = length.to_s if length
|
|
93
|
+
headers[AUTHORIZATION] = authentication if authentication
|
|
94
|
+
headers[ACCEPT] = APPLICATION_JSON
|
|
95
|
+
headers[X_VERSION_HDR] = VERSION
|
|
96
|
+
headers[CONTENT_ENCODING] = GZIP if length && @deflate
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
headers[k] = v
|
|
100
|
-
end
|
|
98
|
+
hdrs.each { |k, v| headers[k] = v }
|
|
101
99
|
|
|
102
|
-
|
|
100
|
+
type.new(endpoint, headers)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def do_request(http, req)
|
|
104
|
+
begin
|
|
105
|
+
client = http.start
|
|
106
|
+
rescue StandardError => e
|
|
107
|
+
# TODO: Retry here
|
|
108
|
+
raise StartError, e
|
|
103
109
|
end
|
|
104
110
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
raise StartError, e
|
|
111
|
-
end
|
|
111
|
+
begin
|
|
112
|
+
res = client.request(req)
|
|
113
|
+
rescue *READ_EXCEPTIONS => e
|
|
114
|
+
raise ReadResponseError, e.inspect
|
|
115
|
+
end
|
|
112
116
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
end
|
|
117
|
+
yield res
|
|
118
|
+
ensure
|
|
119
|
+
client&.finish
|
|
120
|
+
end
|
|
118
121
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
def execute(req, body = nil)
|
|
123
|
+
t do
|
|
124
|
+
fmt "executing HTTP request; host=%s; port=%s; path=%s, body=%s",
|
|
125
|
+
@host,
|
|
126
|
+
@port,
|
|
127
|
+
req.path,
|
|
128
|
+
body && body.bytesize
|
|
122
129
|
end
|
|
123
130
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
end
|
|
131
|
+
if body
|
|
132
|
+
body = Gzip.compress(body) if @deflate
|
|
133
|
+
req.body = body
|
|
134
|
+
end
|
|
129
135
|
|
|
130
|
-
|
|
131
|
-
body = Gzip.compress(body) if @deflate
|
|
132
|
-
req.body = body
|
|
133
|
-
end
|
|
136
|
+
http = Net::HTTP.new(@host, @port, @proxy_addr, @proxy_port, @proxy_user, @proxy_pass)
|
|
134
137
|
|
|
135
|
-
|
|
136
|
-
|
|
138
|
+
http.open_timeout = @open_timeout
|
|
139
|
+
http.read_timeout = @read_timeout
|
|
137
140
|
|
|
138
|
-
|
|
139
|
-
http.
|
|
141
|
+
if @ssl
|
|
142
|
+
http.use_ssl = true
|
|
140
143
|
|
|
141
|
-
|
|
142
|
-
http.use_ssl = true
|
|
144
|
+
http.ca_file = SSL.ca_cert_file_or_default unless SSL.ca_cert_file?
|
|
143
145
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
end
|
|
146
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
147
|
+
end
|
|
147
148
|
|
|
148
|
-
|
|
149
|
+
do_request(http, req) do |res|
|
|
150
|
+
unless res.code =~ /2\d\d/
|
|
151
|
+
debug "server responded with #{res.code}"
|
|
152
|
+
t { fmt "body=%s", res.body }
|
|
149
153
|
end
|
|
150
154
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
155
|
+
Response.new(res.code.to_i, res, res.body)
|
|
156
|
+
end
|
|
157
|
+
rescue Exception => e
|
|
158
|
+
error "http %s %s failed; error=%s; msg=%s", req.method, req.path, e.class, e.message
|
|
159
|
+
t { e.backtrace.join("\n") }
|
|
160
|
+
ErrorResponse.new(req, e)
|
|
161
|
+
end
|
|
156
162
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
rescue Exception => e
|
|
160
|
-
error "http %s %s failed; error=%s; msg=%s", req.method, req.path, e.class, e.message
|
|
161
|
-
t { e.backtrace.join("\n") }
|
|
162
|
-
ErrorResponse.new(req, e)
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
class Response
|
|
166
|
-
attr_reader :status, :headers, :body, :exception
|
|
167
|
-
|
|
168
|
-
def initialize(status, headers, body)
|
|
169
|
-
@status = status
|
|
170
|
-
@headers = headers
|
|
171
|
-
|
|
172
|
-
if (headers[CONTENT_TYPE] || "").include?(APPLICATION_JSON)
|
|
173
|
-
begin
|
|
174
|
-
@body = JSON.parse(body)
|
|
175
|
-
rescue JSON::ParserError
|
|
176
|
-
@body = body # not really JSON I guess
|
|
177
|
-
end
|
|
178
|
-
else
|
|
179
|
-
@body = body
|
|
180
|
-
end
|
|
181
|
-
end
|
|
163
|
+
class Response
|
|
164
|
+
attr_reader :status, :headers, :body, :exception
|
|
182
165
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
166
|
+
def initialize(status, headers, body)
|
|
167
|
+
@status = status
|
|
168
|
+
@headers = headers
|
|
186
169
|
|
|
187
|
-
|
|
188
|
-
|
|
170
|
+
if (headers[CONTENT_TYPE] || "").include?(APPLICATION_JSON)
|
|
171
|
+
begin
|
|
172
|
+
@body = JSON.parse(body)
|
|
173
|
+
rescue JSON::ParserError
|
|
174
|
+
@body = body # not really JSON I guess
|
|
175
|
+
end
|
|
176
|
+
else
|
|
177
|
+
@body = body
|
|
189
178
|
end
|
|
179
|
+
end
|
|
190
180
|
|
|
191
|
-
|
|
192
|
-
|
|
181
|
+
def success?
|
|
182
|
+
status >= 200 && status < 300
|
|
183
|
+
end
|
|
193
184
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
end
|
|
198
|
-
res
|
|
199
|
-
end
|
|
185
|
+
def to_s
|
|
186
|
+
body.to_s
|
|
187
|
+
end
|
|
200
188
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
189
|
+
def get(key)
|
|
190
|
+
body.dig(*key.split(".")) if body.is_a?(Hash)
|
|
191
|
+
end
|
|
204
192
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
193
|
+
def respond_to_missing?(name, include_all = false)
|
|
194
|
+
super || body.respond_to?(name, include_all)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def method_missing(name, *args, &blk)
|
|
198
|
+
if respond_to_missing?(name)
|
|
199
|
+
body.send(name, *args, &blk)
|
|
200
|
+
else
|
|
201
|
+
super
|
|
211
202
|
end
|
|
212
203
|
end
|
|
204
|
+
end
|
|
213
205
|
|
|
214
|
-
|
|
206
|
+
ErrorResponse =
|
|
207
|
+
Struct.new(:request, :exception) do
|
|
215
208
|
def status
|
|
216
209
|
nil
|
|
217
210
|
end
|