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.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +399 -336
  3. data/CLA.md +1 -1
  4. data/CONTRIBUTING.md +2 -8
  5. data/LICENSE.md +7 -17
  6. data/README.md +1 -1
  7. data/ext/extconf.rb +45 -56
  8. data/ext/libskylight.yml +10 -6
  9. data/ext/skylight_native.c +22 -99
  10. data/lib/skylight.rb +201 -14
  11. data/lib/skylight/api.rb +32 -21
  12. data/lib/skylight/cli.rb +48 -46
  13. data/lib/skylight/cli/doctor.rb +62 -63
  14. data/lib/skylight/cli/helpers.rb +19 -19
  15. data/lib/skylight/cli/merger.rb +142 -138
  16. data/lib/skylight/config.rb +634 -199
  17. data/lib/skylight/deprecation.rb +17 -0
  18. data/lib/skylight/errors.rb +23 -9
  19. data/lib/skylight/extensions.rb +95 -0
  20. data/lib/skylight/extensions/source_location.rb +291 -0
  21. data/lib/skylight/formatters/http.rb +18 -0
  22. data/lib/skylight/gc.rb +99 -0
  23. data/lib/skylight/helpers.rb +81 -36
  24. data/lib/skylight/instrumenter.rb +336 -18
  25. data/lib/skylight/middleware.rb +134 -1
  26. data/lib/skylight/native.rb +60 -12
  27. data/lib/skylight/native_ext_fetcher.rb +13 -14
  28. data/lib/skylight/normalizers.rb +157 -0
  29. data/lib/skylight/normalizers/action_controller/process_action.rb +68 -0
  30. data/lib/skylight/normalizers/action_controller/send_file.rb +51 -0
  31. data/lib/skylight/normalizers/action_dispatch/process_middleware.rb +22 -0
  32. data/lib/skylight/normalizers/action_dispatch/route_set.rb +27 -0
  33. data/lib/skylight/normalizers/action_view/render_collection.rb +24 -0
  34. data/lib/skylight/normalizers/action_view/render_layout.rb +25 -0
  35. data/lib/skylight/normalizers/action_view/render_partial.rb +23 -0
  36. data/lib/skylight/normalizers/action_view/render_template.rb +23 -0
  37. data/lib/skylight/normalizers/active_job/perform.rb +90 -0
  38. data/lib/skylight/normalizers/active_model_serializers/render.rb +32 -0
  39. data/lib/skylight/normalizers/active_record/instantiation.rb +16 -0
  40. data/lib/skylight/normalizers/active_record/sql.rb +12 -0
  41. data/lib/skylight/normalizers/active_storage.rb +28 -0
  42. data/lib/skylight/normalizers/active_support/cache.rb +11 -0
  43. data/lib/skylight/normalizers/active_support/cache_clear.rb +16 -0
  44. data/lib/skylight/normalizers/active_support/cache_decrement.rb +16 -0
  45. data/lib/skylight/normalizers/active_support/cache_delete.rb +16 -0
  46. data/lib/skylight/normalizers/active_support/cache_exist.rb +16 -0
  47. data/lib/skylight/normalizers/active_support/cache_fetch_hit.rb +16 -0
  48. data/lib/skylight/normalizers/active_support/cache_generate.rb +16 -0
  49. data/lib/skylight/normalizers/active_support/cache_increment.rb +16 -0
  50. data/lib/skylight/normalizers/active_support/cache_read.rb +16 -0
  51. data/lib/skylight/normalizers/active_support/cache_read_multi.rb +16 -0
  52. data/lib/skylight/normalizers/active_support/cache_write.rb +16 -0
  53. data/lib/skylight/normalizers/coach/handler_finish.rb +44 -0
  54. data/lib/skylight/normalizers/coach/middleware_finish.rb +33 -0
  55. data/lib/skylight/normalizers/couch_potato/query.rb +20 -0
  56. data/lib/skylight/normalizers/data_mapper/sql.rb +12 -0
  57. data/lib/skylight/normalizers/default.rb +24 -0
  58. data/lib/skylight/normalizers/elasticsearch/request.rb +20 -0
  59. data/lib/skylight/normalizers/faraday/request.rb +38 -0
  60. data/lib/skylight/normalizers/grape/endpoint.rb +28 -0
  61. data/lib/skylight/normalizers/grape/endpoint_render.rb +25 -0
  62. data/lib/skylight/normalizers/grape/endpoint_run.rb +39 -0
  63. data/lib/skylight/normalizers/grape/endpoint_run_filters.rb +20 -0
  64. data/lib/skylight/normalizers/grape/format_response.rb +20 -0
  65. data/lib/skylight/normalizers/graphiti/render.rb +22 -0
  66. data/lib/skylight/normalizers/graphiti/resolve.rb +31 -0
  67. data/lib/skylight/normalizers/graphql/base.rb +127 -0
  68. data/lib/skylight/normalizers/render.rb +79 -0
  69. data/lib/skylight/normalizers/sequel/sql.rb +12 -0
  70. data/lib/skylight/normalizers/shrine.rb +32 -0
  71. data/lib/skylight/normalizers/sql.rb +45 -0
  72. data/lib/skylight/probes.rb +173 -0
  73. data/lib/skylight/probes/action_controller.rb +52 -0
  74. data/lib/skylight/probes/action_dispatch.rb +2 -0
  75. data/lib/skylight/probes/action_dispatch/request_id.rb +33 -0
  76. data/lib/skylight/probes/action_dispatch/routing/route_set.rb +30 -0
  77. data/lib/skylight/probes/action_view.rb +42 -0
  78. data/lib/skylight/probes/active_job.rb +27 -0
  79. data/lib/skylight/probes/active_job_enqueue.rb +35 -0
  80. data/lib/skylight/probes/active_model_serializers.rb +50 -0
  81. data/lib/skylight/probes/delayed_job.rb +144 -0
  82. data/lib/skylight/probes/elasticsearch.rb +36 -0
  83. data/lib/skylight/probes/excon.rb +25 -0
  84. data/lib/skylight/probes/excon/middleware.rb +65 -0
  85. data/lib/skylight/probes/faraday.rb +23 -0
  86. data/lib/skylight/probes/graphql.rb +38 -0
  87. data/lib/skylight/probes/httpclient.rb +44 -0
  88. data/lib/skylight/probes/middleware.rb +135 -0
  89. data/lib/skylight/probes/mongo.rb +156 -0
  90. data/lib/skylight/probes/mongoid.rb +13 -0
  91. data/lib/skylight/probes/net_http.rb +54 -0
  92. data/lib/skylight/probes/redis.rb +51 -0
  93. data/lib/skylight/probes/sequel.rb +29 -0
  94. data/lib/skylight/probes/sinatra.rb +66 -0
  95. data/lib/skylight/probes/sinatra_add_middleware.rb +10 -10
  96. data/lib/skylight/probes/tilt.rb +25 -0
  97. data/lib/skylight/railtie.rb +157 -27
  98. data/lib/skylight/sidekiq.rb +47 -0
  99. data/lib/skylight/subscriber.rb +108 -0
  100. data/lib/skylight/test.rb +151 -0
  101. data/lib/skylight/trace.rb +325 -22
  102. data/lib/skylight/user_config.rb +58 -0
  103. data/lib/skylight/util.rb +12 -0
  104. data/lib/skylight/util/allocation_free.rb +26 -0
  105. data/lib/skylight/util/clock.rb +57 -0
  106. data/lib/skylight/util/component.rb +22 -22
  107. data/lib/skylight/util/deploy.rb +16 -21
  108. data/lib/skylight/util/gzip.rb +20 -0
  109. data/lib/skylight/util/http.rb +106 -113
  110. data/lib/skylight/util/instrumenter_method.rb +26 -0
  111. data/lib/skylight/util/logging.rb +136 -0
  112. data/lib/skylight/util/lru_cache.rb +36 -0
  113. data/lib/skylight/util/platform.rb +1 -5
  114. data/lib/skylight/util/ssl.rb +1 -25
  115. data/lib/skylight/vendor/cli/thor/rake_compat.rb +1 -1
  116. data/lib/skylight/version.rb +5 -1
  117. data/lib/skylight/vm/gc.rb +60 -0
  118. 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 = resolve_name(name, force_worker)
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
- def program_name
50
- $PROGRAM_NAME
51
- end
48
+ def program_name
49
+ $PROGRAM_NAME
50
+ end
52
51
 
53
- def argv
54
- ARGV
55
- end
52
+ def argv
53
+ ARGV
54
+ end
56
55
 
57
- def resolve_name(given_name, force_worker)
58
- # don't allow workers to be called 'web'
59
- return WORKER_NAME if force_worker && (given_name.nil? || given_name == DEFAULT_NAME)
60
- return DEFAULT_NAME if given_name.nil?
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
- given_name
63
- end
61
+ given_name
62
+ end
63
+
64
+ def validate_string!(string, kind)
65
+ return true if string =~ NAME_FORMAT
64
66
 
65
- def validate_string!(string, kind)
66
- return true if string =~ NAME_FORMAT
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
@@ -1,5 +1,5 @@
1
1
  require "json"
2
- require "skylight/core/util/logging"
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: timestamp,
39
- deploy_id: id.to_s[0..100] # Keep this sane
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 Core::Util::Logging
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
- def get_info
91
- info_path = config[:'heroku.dyno_info_path']
87
+ def get_info
88
+ info_path = config[:'heroku.dyno_info_path']
92
89
 
93
- if File.exist?(info_path)
94
- if (info = JSON.parse(File.read(info_path)))
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
- def get_info
112
- Dir.chdir(config.root) do
113
- info = `git log -1 --pretty="%H %s" 2>&1`
114
- info.split(" ", 2).map(&:strip) if $CHILD_STATUS.success?
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
@@ -3,23 +3,23 @@ require "json"
3
3
  require "openssl"
4
4
  require "net/http"
5
5
  require "net/https"
6
- require "skylight/core/util/gzip"
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 = "content-length".freeze
14
- CONTENT_TYPE = "content-type".freeze
15
- ACCEPT = "Accept".freeze
16
- X_VERSION_HDR = "x-skylight-agent-version".freeze
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 = "authorization".freeze
19
- DEFLATE = "deflate".freeze
20
- GZIP = "gzip".freeze
18
+ AUTHORIZATION = "authorization".freeze
19
+ DEFLATE = "deflate".freeze
20
+ GZIP = "gzip".freeze
21
21
 
22
- include Core::Util::Logging
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; end
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 = url.scheme == "https"
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
- def get_timeout(type, config, service, opts)
85
- config.duration_ms("#{service}_http_#{type}_timeout") ||
86
- opts[:timeout] || 15
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
- def build_request(type, endpoint, hdrs, length = nil)
90
- headers = {}
89
+ def build_request(type, endpoint, hdrs, length = nil)
90
+ headers = {}
91
91
 
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
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
- hdrs.each do |k, v|
99
- headers[k] = v
100
- end
98
+ hdrs.each { |k, v| headers[k] = v }
101
99
 
102
- type.new(endpoint, headers)
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
- def do_request(http, req)
106
- begin
107
- client = http.start
108
- rescue => e
109
- # TODO: Retry here
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
- begin
114
- res = client.request(req)
115
- rescue *READ_EXCEPTIONS => e
116
- raise ReadResponseError, e.inspect
117
- end
117
+ yield res
118
+ ensure
119
+ client&.finish
120
+ end
118
121
 
119
- yield res
120
- ensure
121
- client.finish if client
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
- def execute(req, body = nil)
125
- t do
126
- fmt "executing HTTP request; host=%s; port=%s; path=%s, body=%s",
127
- @host, @port, req.path, body && body.bytesize
128
- end
131
+ if body
132
+ body = Gzip.compress(body) if @deflate
133
+ req.body = body
134
+ end
129
135
 
130
- if body
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
- http = Net::HTTP.new(@host, @port,
136
- @proxy_addr, @proxy_port, @proxy_user, @proxy_pass)
138
+ http.open_timeout = @open_timeout
139
+ http.read_timeout = @read_timeout
137
140
 
138
- http.open_timeout = @open_timeout
139
- http.read_timeout = @read_timeout
141
+ if @ssl
142
+ http.use_ssl = true
140
143
 
141
- if @ssl
142
- http.use_ssl = true
144
+ http.ca_file = SSL.ca_cert_file_or_default unless SSL.ca_cert_file?
143
145
 
144
- unless SSL.ca_cert_file?
145
- http.ca_file = SSL.ca_cert_file_or_default
146
- end
146
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
147
+ end
147
148
 
148
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
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
- do_request(http, req) do |res|
152
- unless res.code =~ /2\d\d/
153
- debug "server responded with #{res.code}"
154
- t { fmt "body=%s", res.body }
155
- end
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
- Response.new(res.code.to_i, res, res.body)
158
- end
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
- def success?
184
- status >= 200 && status < 300
185
- end
166
+ def initialize(status, headers, body)
167
+ @status = status
168
+ @headers = headers
186
169
 
187
- def to_s
188
- body.to_s
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
- def get(key)
192
- return nil unless body.is_a?(Hash)
181
+ def success?
182
+ status >= 200 && status < 300
183
+ end
193
184
 
194
- res = body
195
- key.split(".").each do |part|
196
- return unless (res = res[part])
197
- end
198
- res
199
- end
185
+ def to_s
186
+ body.to_s
187
+ end
200
188
 
201
- def respond_to_missing?(name, include_all = false)
202
- super || body.respond_to?(name, include_all)
203
- end
189
+ def get(key)
190
+ body.dig(*key.split(".")) if body.is_a?(Hash)
191
+ end
204
192
 
205
- def method_missing(name, *args, &blk)
206
- if respond_to_missing?(name)
207
- body.send(name, *args, &blk)
208
- else
209
- super
210
- end
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
- ErrorResponse = Struct.new(:request, :exception) do
206
+ ErrorResponse =
207
+ Struct.new(:request, :exception) do
215
208
  def status
216
209
  nil
217
210
  end