site-inspector 3.1.0 → 3.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +34 -0
  3. data/.ruby-version +1 -1
  4. data/Gemfile +1 -1
  5. data/Guardfile +1 -1
  6. data/README.md +6 -1
  7. data/Rakefile +2 -2
  8. data/bin/site-inspector +15 -15
  9. data/lib/cliver/dependency_ext.rb +21 -0
  10. data/lib/site-inspector.rb +13 -11
  11. data/lib/site-inspector/checks/accessibility.rb +27 -17
  12. data/lib/site-inspector/checks/check.rb +1 -3
  13. data/lib/site-inspector/checks/content.rb +6 -6
  14. data/lib/site-inspector/checks/cookies.rb +6 -8
  15. data/lib/site-inspector/checks/dns.rb +21 -20
  16. data/lib/site-inspector/checks/headers.rb +12 -13
  17. data/lib/site-inspector/checks/hsts.rb +8 -9
  18. data/lib/site-inspector/checks/https.rb +3 -5
  19. data/lib/site-inspector/checks/sniffer.rb +8 -9
  20. data/lib/site-inspector/domain.rb +28 -32
  21. data/lib/site-inspector/endpoint.rb +31 -32
  22. data/lib/site-inspector/version.rb +1 -1
  23. data/script/cibuild +3 -1
  24. data/script/pa11y-version +9 -0
  25. data/site-inspector.gemspec +25 -25
  26. data/spec/checks/site_inspector_endpoint_accessibility_spec.rb +31 -30
  27. data/spec/checks/site_inspector_endpoint_check_spec.rb +10 -11
  28. data/spec/checks/site_inspector_endpoint_content_spec.rb +43 -44
  29. data/spec/checks/site_inspector_endpoint_cookies_spec.rb +30 -31
  30. data/spec/checks/site_inspector_endpoint_dns_spec.rb +72 -77
  31. data/spec/checks/site_inspector_endpoint_headers_spec.rb +26 -27
  32. data/spec/checks/site_inspector_endpoint_hsts_spec.rb +26 -27
  33. data/spec/checks/site_inspector_endpoint_https_spec.rb +11 -12
  34. data/spec/checks/site_inspector_endpoint_sniffer_spec.rb +56 -57
  35. data/spec/site_inspector_cache_spec.rb +6 -6
  36. data/spec/site_inspector_disk_cache_spec.rb +9 -9
  37. data/spec/site_inspector_domain_spec.rb +132 -136
  38. data/spec/site_inspector_endpoint_spec.rb +108 -108
  39. data/spec/site_inspector_spec.rb +17 -18
  40. data/spec/spec_helper.rb +3 -3
  41. metadata +21 -3
@@ -4,15 +4,15 @@ class SiteInspector
4
4
  class LocalhostError < StandardError; end
5
5
 
6
6
  def self.resolver
7
- require "dnsruby"
7
+ require 'dnsruby'
8
8
  @resolver ||= begin
9
9
  resolver = Dnsruby::Resolver.new
10
- resolver.config.nameserver = ["8.8.8.8", "8.8.4.4"]
10
+ resolver.config.nameserver = ['8.8.8.8', '8.8.4.4']
11
11
  resolver
12
12
  end
13
13
  end
14
14
 
15
- def query(type="ANY")
15
+ def query(type = 'ANY')
16
16
  SiteInspector::Endpoint::Dns.resolver.query(host.to_s, type).answer
17
17
  rescue Dnsruby::ResolvTimeout, Dnsruby::ServFail, Dnsruby::NXDomain
18
18
  []
@@ -22,20 +22,21 @@ class SiteInspector
22
22
  @records ||= query
23
23
  end
24
24
 
25
- def has_record?(type)
25
+ def record?(type)
26
26
  records.any? { |record| record.type == type } || query(type).count != 0
27
27
  end
28
+ alias_method :has_record?, :record?
28
29
 
29
30
  def dnssec?
30
- @dnssec ||= has_record? "DNSKEY"
31
+ @dnssec ||= has_record? 'DNSKEY'
31
32
  end
32
33
 
33
34
  def ipv6?
34
- @ipv6 ||= has_record? "AAAA"
35
+ @ipv6 ||= has_record? 'AAAA'
35
36
  end
36
37
 
37
38
  def cdn
38
- detect_by_hostname "cdn"
39
+ detect_by_hostname 'cdn'
39
40
  end
40
41
 
41
42
  def cdn?
@@ -43,7 +44,7 @@ class SiteInspector
43
44
  end
44
45
 
45
46
  def cloud_provider
46
- detect_by_hostname "cloud"
47
+ detect_by_hostname 'cloud'
47
48
  end
48
49
 
49
50
  def cloud?
@@ -52,7 +53,7 @@ class SiteInspector
52
53
 
53
54
  def google_apps?
54
55
  @google ||= records.any? do |record|
55
- record.type == "MX" && record.exchange.to_s =~ /google(mail)?\.com\.?$/
56
+ record.type == 'MX' && record.exchange.to_s =~ /google(mail)?\.com\.?\z/i
56
57
  end
57
58
  end
58
59
 
@@ -75,7 +76,7 @@ class SiteInspector
75
76
  end
76
77
 
77
78
  def cnames
78
- @cnames ||= records.select { |record| record.type == "CNAME" }.map do |record|
79
+ @cnames ||= records.select { |record| record.type == 'CNAME' }.map do |record|
79
80
  PublicSuffix.parse(record.cname.to_s)
80
81
  end
81
82
  end
@@ -85,15 +86,15 @@ class SiteInspector
85
86
  end
86
87
 
87
88
  def to_h
88
- return { :error => LocalhostError } if localhost?
89
+ return { error: LocalhostError } if localhost?
89
90
  {
90
- :dnssec => dnssec?,
91
- :ipv6 => ipv6?,
92
- :cdn => cdn,
93
- :cloud_provider => cloud_provider,
94
- :google_apps => google_apps?,
95
- :hostname => hostname,
96
- :ip => ip
91
+ dnssec: dnssec?,
92
+ ipv6: ipv6?,
93
+ cdn: cdn,
94
+ cloud_provider: cloud_provider,
95
+ google_apps: google_apps?,
96
+ hostname: hostname,
97
+ ip: ip
97
98
  }
98
99
  end
99
100
 
@@ -115,7 +116,7 @@ class SiteInspector
115
116
 
116
117
  def detect_by_hostname(type)
117
118
  haystack = load_data(type)
118
- needle = haystack.find do |name, domain|
119
+ needle = haystack.find do |_name, domain|
119
120
  cnames.any? do |cname|
120
121
  domain == cname.tld || domain == "#{cname.sld}.#{cname.tld}"
121
122
  end
@@ -124,7 +125,7 @@ class SiteInspector
124
125
  return needle[0].to_sym if needle
125
126
  return nil unless hostname
126
127
 
127
- needle = haystack.find do |name, domain|
128
+ needle = haystack.find do |_name, domain|
128
129
  domain == hostname.tld || domain == "#{hostname.sld}.#{hostname.tld}"
129
130
  end
130
131
 
@@ -1,7 +1,6 @@
1
1
  class SiteInspector
2
2
  class Endpoint
3
3
  class Headers < Check
4
-
5
4
  # TODO: kill this
6
5
  def strict_transport_security?
7
6
  !!strict_transport_security
@@ -19,33 +18,33 @@ class SiteInspector
19
18
 
20
19
  # TODO: kill this
21
20
  def strict_transport_security
22
- headers["strict-transport-security"]
21
+ headers['strict-transport-security']
23
22
  end
24
23
 
25
24
  def content_security_policy
26
- headers["content-security-policy"]
25
+ headers['content-security-policy']
27
26
  end
28
27
 
29
28
  def click_jacking_protection
30
- headers["x-frame-options"]
29
+ headers['x-frame-options']
31
30
  end
32
31
 
33
32
  def server
34
- headers["server"]
33
+ headers['server']
35
34
  end
36
35
 
37
36
  def xss_protection
38
- headers["x-xss-protection"]
37
+ headers['x-xss-protection']
39
38
  end
40
39
 
41
40
  # more specific checks than presence of headers
42
41
  def xss_protection?
43
- xss_protection == "1; mode=block"
42
+ xss_protection == '1; mode=block'
44
43
  end
45
44
 
46
45
  # Returns an array of hashes of downcased key/value header pairs (or an empty hash)
47
46
  def all
48
- @all ||= (response && response.headers) ? Hash[response.headers.map{ |k,v| [k.downcase,v] }] : {}
47
+ @all ||= (response && response.headers) ? Hash[response.headers.map { |k, v| [k.downcase, v] }] : {}
49
48
  end
50
49
  alias_method :headers, :all
51
50
 
@@ -55,11 +54,11 @@ class SiteInspector
55
54
 
56
55
  def to_h
57
56
  {
58
- :strict_transport_security => strict_transport_security || false,
59
- :content_security_policy => content_security_policy || false,
60
- :click_jacking_protection => click_jacking_protection || false,
61
- :server => server,
62
- :xss_protection => xss_protection || false,
57
+ strict_transport_security: strict_transport_security || false,
58
+ content_security_policy: content_security_policy || false,
59
+ click_jacking_protection: click_jacking_protection || false,
60
+ server: server,
61
+ xss_protection: xss_protection || false
63
62
  }
64
63
  end
65
64
  end
@@ -3,7 +3,6 @@ class SiteInspector
3
3
  # Utility parser for HSTS headers.
4
4
  # RFC: http://tools.ietf.org/html/rfc6797
5
5
  class Hsts < Check
6
-
7
6
  def valid?
8
7
  return false unless header
9
8
  pairs.none? { |key, value| "#{key}#{value}" =~ /[\s\'\"]/ }
@@ -28,17 +27,17 @@ class SiteInspector
28
27
 
29
28
  # Google's minimum max-age for automatic preloading
30
29
  def preload_ready?
31
- include_subdomains? and preload? and max_age >= 10886400
30
+ include_subdomains? && preload? && max_age >= 10_886_400
32
31
  end
33
32
 
34
33
  def to_h
35
34
  {
36
- valid: valid?,
37
- max_age: max_age,
35
+ valid: valid?,
36
+ max_age: max_age,
38
37
  include_subdomains: include_subdomains?,
39
- preload: preload?,
40
- enabled: enabled?,
41
- preload_ready: preload_ready?
38
+ preload: preload?,
39
+ enabled: enabled?,
40
+ preload_ready: preload_ready?
42
41
  }
43
42
  end
44
43
 
@@ -49,7 +48,7 @@ class SiteInspector
49
48
  end
50
49
 
51
50
  def header
52
- @header ||= headers["strict-transport-security"]
51
+ @header ||= headers['strict-transport-security']
53
52
  end
54
53
 
55
54
  def directives
@@ -60,7 +59,7 @@ class SiteInspector
60
59
  @pairs ||= begin
61
60
  pairs = {}
62
61
  directives.each do |directive|
63
- key, value = directive.downcase.split("=")
62
+ key, value = directive.downcase.split('=')
64
63
 
65
64
  if value =~ /\".*\"/
66
65
  value = value.sub(/^\"/, '')
@@ -1,9 +1,8 @@
1
1
  class SiteInspector
2
2
  class Endpoint
3
3
  class Https < Check
4
-
5
4
  def scheme?
6
- scheme == "https"
5
+ scheme == 'https'
7
6
  end
8
7
 
9
8
  def valid?
@@ -24,8 +23,8 @@ class SiteInspector
24
23
 
25
24
  def to_h
26
25
  {
27
- valid: valid?,
28
- return_code: response.return_code,
26
+ valid: valid?,
27
+ return_code: response.return_code
29
28
  }
30
29
  end
31
30
 
@@ -34,7 +33,6 @@ class SiteInspector
34
33
  def scheme
35
34
  @scheme ||= request.base_url.scheme
36
35
  end
37
-
38
36
  end
39
37
  end
40
38
  end
@@ -1,7 +1,6 @@
1
1
  class SiteInspector
2
2
  class Endpoint
3
3
  class Sniffer < Check
4
-
5
4
  OPEN_SOURCE_FRAMEWORKS = [
6
5
  # Sniffles
7
6
  :drupal,
@@ -20,9 +19,9 @@ class SiteInspector
20
19
  cms = sniff :cms
21
20
  return cms unless cms.nil?
22
21
  return :expression_engine if endpoint.cookies.any? { |c| c.keys.first =~ /^exp_/ }
23
- return :php if endpoint.cookies["PHPSESSID"]
24
- return :coldfusion if endpoint.cookies["CFID"] && endpoint.cookies["CFTOKEN"]
25
- return :cowboy if endpoint.headers.server.to_s.downcase == "cowboy"
22
+ return :php if endpoint.cookies['PHPSESSID']
23
+ return :coldfusion if endpoint.cookies['CFID'] && endpoint.cookies['CFTOKEN']
24
+ return :cowboy if endpoint.headers.server.to_s.downcase == 'cowboy'
26
25
  nil
27
26
  end
28
27
 
@@ -44,10 +43,10 @@ class SiteInspector
44
43
 
45
44
  def to_h
46
45
  {
47
- :framework => framework,
48
- :analytics => analytics,
49
- :javascript => javascript,
50
- :advertising => advertising
46
+ framework: framework,
47
+ analytics: analytics,
48
+ javascript: javascript,
49
+ advertising: advertising
51
50
  }
52
51
  end
53
52
 
@@ -55,7 +54,7 @@ class SiteInspector
55
54
 
56
55
  def sniff(type)
57
56
  require 'sniffles'
58
- results = Sniffles.sniff(endpoint.content.body, type).select { |name, meta| meta[:found] }
57
+ results = Sniffles.sniff(endpoint.content.body, type).select { |_name, meta| meta[:found] }
59
58
  results.keys.first if results
60
59
  rescue
61
60
  nil
@@ -1,23 +1,22 @@
1
1
  class SiteInspector
2
2
  class Domain
3
-
4
3
  attr_reader :host
5
4
 
6
5
  def initialize(host)
7
6
  host = host.downcase
8
- host = host.sub /^https?\:/, ""
9
- host = host.sub /^\/+/, ""
10
- host = host.sub /^www\./, ""
7
+ host = host.sub(/^https?\:/, '')
8
+ host = host.sub(%r{^/+}, '')
9
+ host = host.sub(/^www\./, '')
11
10
  uri = Addressable::URI.parse "//#{host}"
12
11
  @host = uri.host
13
12
  end
14
13
 
15
14
  def endpoints
16
15
  @endpoints ||= [
17
- Endpoint.new("https://#{host}", :domain => self),
18
- Endpoint.new("https://www.#{host}", :domain => self),
19
- Endpoint.new("http://#{host}", :domain => self),
20
- Endpoint.new("http://www.#{host}", :domain => self)
16
+ Endpoint.new("https://#{host}", domain: self),
17
+ Endpoint.new("https://www.#{host}", domain: self),
18
+ Endpoint.new("http://#{host}", domain: self),
19
+ Endpoint.new("http://www.#{host}", domain: self)
21
20
  ]
22
21
  end
23
22
 
@@ -37,16 +36,15 @@ class SiteInspector
37
36
 
38
37
  # Does *any* endpoint return a 200 or 300 response code?
39
38
  def up?
40
- endpoints.any? { |e| e.up? }
39
+ endpoints.any?(&:up?)
41
40
  end
42
41
 
43
42
  # Does *any* endpoint respond to HTTP?
44
43
  # TODO: needs to allow an invalid chain.
45
44
  def responds?
46
- endpoints.any? { |e| e.responds? }
45
+ endpoints.any?(&:responds?)
47
46
  end
48
47
 
49
-
50
48
  # TODO: These weren't present before, and may not be useful.
51
49
  # Can you connect to www?
52
50
  def www?
@@ -84,7 +82,7 @@ class SiteInspector
84
82
  # TODO: don't need to require that the HTTPS cert is valid for this purpose.
85
83
  def enforces_https?
86
84
  return false unless https?
87
- endpoints.select { |e| e.http? }.all? { |e| !e.up? || (e.redirect && e.redirect.https?) }
85
+ endpoints.select(&:http?).all? { |e| !e.up? || (e.redirect && e.redirect.https?) }
88
86
  end
89
87
 
90
88
  # we can say that a canonical HTTPS site "defaults" to HTTPS,
@@ -93,7 +91,7 @@ class SiteInspector
93
91
  #
94
92
  # TODO: not implemented.
95
93
  def defaults_https?
96
- raise "Not implemented. Halp?"
94
+ fail 'Not implemented. Halp?'
97
95
  end
98
96
 
99
97
  # HTTPS is "downgraded" if both:
@@ -128,10 +126,10 @@ class SiteInspector
128
126
  return false unless www?
129
127
 
130
128
  # Are both root endpoints down?
131
- return true if endpoints.select { |e| e.root? }.all? { |e| !e.up? }
129
+ return true if endpoints.select(&:root?).all? { |e| !e.up? }
132
130
 
133
131
  # Does either root endpoint redirect to a www endpoint?
134
- endpoints.select { |e| e.root? }.any? { |e| e.redirect && e.redirect.www? }
132
+ endpoints.select(&:root?).any? { |e| e.redirect && e.redirect.www? }
135
133
  end
136
134
 
137
135
  # A domain is "canonically" at https if:
@@ -159,10 +157,10 @@ class SiteInspector
159
157
  return false unless https?
160
158
 
161
159
  # Both http endpoints are down
162
- return true if endpoints.select { |e| e.http? }.all? { |e| !e.up? }
160
+ return true if endpoints.select(&:http?).all? { |e| !e.up? }
163
161
 
164
162
  # at least one http endpoint redirects immediately to https
165
- endpoints.select { |e| e.http? }.any? { |e| e.redirect && e.redirect.https? }
163
+ endpoints.select(&:http?).any? { |e| e.redirect && e.redirect.https? }
166
164
  end
167
165
 
168
166
  # A domain redirects if
@@ -175,7 +173,7 @@ class SiteInspector
175
173
 
176
174
  # The first endpoint to respond with a redirect
177
175
  def redirect
178
- endpoints.find { |e| e.external_redirect? }
176
+ endpoints.find(&:external_redirect?)
179
177
  end
180
178
 
181
179
  # HSTS on the canonical domain?
@@ -223,7 +221,7 @@ class SiteInspector
223
221
  # :all - return information about all endpoints
224
222
  #
225
223
  # Returns a complete hash of the domain's information
226
- def to_h(options={})
224
+ def to_h(options = {})
227
225
  prefetch
228
226
 
229
227
  hash = {
@@ -244,19 +242,17 @@ class SiteInspector
244
242
  canonical_endpoint: canonical_endpoint.to_h(options)
245
243
  }
246
244
 
247
- if options["all"]
248
- hash.merge!({
249
- endpoints: {
250
- https: {
251
- root: endpoints[0].to_h(options),
252
- www: endpoints[1].to_h(options)
253
- },
254
- http: {
255
- root: endpoints[2].to_h(options),
256
- www: endpoints[3].to_h(options)
257
- }
258
- }
259
- })
245
+ if options['all']
246
+ hash.merge!(endpoints: {
247
+ https: {
248
+ root: endpoints[0].to_h(options),
249
+ www: endpoints[1].to_h(options)
250
+ },
251
+ http: {
252
+ root: endpoints[2].to_h(options),
253
+ www: endpoints[3].to_h(options)
254
+ }
255
+ })
260
256
  end
261
257
 
262
258
  hash
@@ -17,12 +17,12 @@ class SiteInspector
17
17
  # endpoint - (string) the endpoint to query (e.g., `https://example.com`)
18
18
  # options - A hash of options
19
19
  # domain - the parent domain object, if passed, facilitates caching of redirects
20
- def initialize(host, options={})
20
+ def initialize(host, options = {})
21
21
  @uri = Addressable::URI.parse(host.downcase)
22
22
  # The root URL always has an implict path of "/", even if not requested
23
23
  # Make it explicit to facilitate caching and prevent a potential redirect
24
- @uri.path = "/"
25
- @host = uri.host.sub(/^www\./, "")
24
+ @uri.path = '/'
25
+ @host = uri.host.sub(/^www\./, '')
26
26
  @checks = {}
27
27
  @domain = options[:domain]
28
28
  end
@@ -72,24 +72,24 @@ class SiteInspector
72
72
 
73
73
  # Does the endpoint return a 2xx or 3xx response code?
74
74
  def up?
75
- response && response_code.start_with?("2") || response_code.start_with?("3")
75
+ response && response_code.start_with?('2') || response_code.start_with?('3')
76
76
  end
77
77
 
78
78
  # Does the server respond at all?
79
79
  def responds?
80
- response.code != 0 && !timed_out?
80
+ response.code != 0 && !timed_out?
81
81
  end
82
82
 
83
83
  # If the domain is a redirect, what's the first endpoint we're redirected to?
84
84
  def redirect
85
- return unless response && response_code.start_with?("3")
85
+ return unless response && response_code.start_with?('3')
86
86
 
87
87
  @redirect ||= begin
88
- redirect = Addressable::URI.parse(headers["location"])
88
+ redirect = Addressable::URI.parse(headers['location'])
89
89
 
90
90
  # This is a relative redirect, but we still need the absolute URI
91
91
  if redirect.relative?
92
- redirect.path = "/#{redirect.path}" unless redirect.path[0] == "/"
92
+ redirect.path = "/#{redirect.path}" unless redirect.path[0] == '/'
93
93
  redirect.host = host
94
94
  redirect.scheme = scheme
95
95
  end
@@ -116,12 +116,12 @@ class SiteInspector
116
116
  return redirect unless redirect.redirect?
117
117
 
118
118
  @resolves_to ||= begin
119
- response = request(:followlocation => true)
119
+ response = request(followlocation: true)
120
120
 
121
121
  # Workaround for Webmock not playing nicely with Typhoeus redirects
122
122
  if response.mock?
123
- if response.headers["Location"]
124
- url = response.headers["Location"]
123
+ if response.headers['Location']
124
+ url = response.headers['Location']
125
125
  else
126
126
  url = response.request.url
127
127
  end
@@ -142,7 +142,7 @@ class SiteInspector
142
142
  end
143
143
 
144
144
  def inspect
145
- "#<SiteInspector::Endpoint uri=\"#{uri.to_s}\">"
145
+ "#<SiteInspector::Endpoint uri=\"#{uri}\">"
146
146
  end
147
147
 
148
148
  # Returns information about the endpoint
@@ -154,37 +154,39 @@ class SiteInspector
154
154
  # a hash of check symbols and bools representing which checks should be run
155
155
  #
156
156
  # Returns the hash representing the endpoint and its checks
157
- def to_h(options={})
157
+ def to_h(options = {})
158
158
  hash = {
159
- uri: uri.to_s,
160
- host: host,
161
- www: www?,
162
- https: https?,
163
- scheme: scheme,
164
- up: up?,
165
- responds: responds?,
166
- timed_out: timed_out?,
167
- redirect: redirect?,
168
- external_redirect: external_redirect?,
159
+ uri: uri.to_s,
160
+ host: host,
161
+ www: www?,
162
+ https: https?,
163
+ scheme: scheme,
164
+ up: up?,
165
+ responds: responds?,
166
+ timed_out: timed_out?,
167
+ redirect: redirect?,
168
+ external_redirect: external_redirect?
169
169
  }
170
170
 
171
171
  # Either they've specifically asked for a check, or we throw everything at them
172
172
  checks = SiteInspector::Endpoint.checks.select { |c| options.keys.include?(c.name) }
173
173
  checks = SiteInspector::Endpoint.checks if checks.empty?
174
174
 
175
- Parallel.each(checks, :in_threads => 4) do |check|
176
- hash[check.name] = self.send(check.name).to_h
175
+ Parallel.each(checks, in_threads: 4) do |check|
176
+ hash[check.name] = send(check.name).to_h
177
177
  end
178
178
 
179
179
  hash
180
180
  end
181
181
 
182
182
  def self.checks
183
- ObjectSpace.each_object(Class).select { |klass| klass < Check }.select { |check| check.enabled? }
183
+ return @checks if defined? @checks
184
+ @checks = ObjectSpace.each_object(Class).select { |klass| klass < Check }.select(&:enabled?)
184
185
  end
185
186
 
186
187
  def method_missing(method_sym, *arguments, &block)
187
- if check = SiteInspector::Endpoint.checks.find { |c| c.name == method_sym }
188
+ check = SiteInspector::Endpoint.checks.find { |c| c.name == method_sym }
189
+ if check
188
190
  @checks[method_sym] ||= check.new(self)
189
191
  else
190
192
  super
@@ -209,11 +211,8 @@ class SiteInspector
209
211
  # Try to return the existing endpoint, rather than create a new one
210
212
  def find_or_create_by_uri(uri)
211
213
  uri = Addressable::URI.parse(uri.downcase)
212
- if domain && cached_endpoint = domain.endpoints.find { |e| e.uri.to_s == uri.to_s }
213
- cached_endpoint
214
- else
215
- Endpoint.new(uri.to_s)
216
- end
214
+ cached_endpoint = domain.endpoints.find { |e| e.uri.to_s == uri.to_s } if domain
215
+ cached_endpoint || Endpoint.new(uri.to_s)
217
216
  end
218
217
  end
219
218
  end