skylight 4.3.2 → 5.1.1

Sign up to get free protection for your applications and to get access to all the features.
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