Class: GitHubPages::HealthCheck

Inherits:
Object
  • Object
show all
Defined in:
lib/github-pages-health-check.rb,
lib/github-pages-health-check/error.rb,
lib/github-pages-health-check/version.rb,
lib/github-pages-health-check/cloudflare.rb,
lib/github-pages-health-check/errors/deprecated_ip.rb,
lib/github-pages-health-check/errors/invalid_cname.rb,
lib/github-pages-health-check/errors/invalid_a_record.rb,
lib/github-pages-health-check/errors/not_served_by_pages.rb

Defined Under Namespace

Classes: CloudFlare, DeprecatedIP, Error, InvalidARecord, InvalidCNAME, NotServedByPages

Constant Summary collapse

LEGACY_IP_ADDRESSES =
%w[
  207.97.227.245
  204.232.175.78
  199.27.73.133
]
CURRENT_IP_ADDRESSES =
%w[
  192.30.252.153
  192.30.252.154
]
TIMEOUT =

DNS and HTTP timeout, in seconds

10
TYPHOEUS_OPTIONS =
{
  :followlocation  => true,
  :timeout         => TIMEOUT,
  :accept_encoding => "gzip",
  :method          => :head,
  :headers         => {
    "User-Agent"   => "Mozilla/5.0 (compatible; GitHub Pages Health Check/#{VERSION}; +https://github.com/github/pages-health-check)"
  }
}
VERSION =
"0.5.0"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(domain) ⇒ HealthCheck

Returns a new instance of HealthCheck.



48
49
50
# File 'lib/github-pages-health-check.rb', line 48

def initialize(domain)
  @domain = domain
end

Instance Attribute Details

#domainObject

Returns the value of attribute domain.



22
23
24
# File 'lib/github-pages-health-check.rb', line 22

def domain
  @domain
end

Instance Method Details

#a_record?Boolean

Is this domain’s first response an A record?

Returns:

  • (Boolean)


96
97
98
# File 'lib/github-pages-health-check.rb', line 96

def a_record?
  dns.first.class == Net::DNS::RR::A if dns?
end

#apex_domain?Boolean

Is this domain an SLD, meaning a CNAME would be innapropriate

Returns:

  • (Boolean)


112
113
114
115
116
# File 'lib/github-pages-health-check.rb', line 112

def apex_domain?
  PublicSuffix.parse(domain).trd == nil
rescue
  false
end

#check!Object Also known as: valid!

Runs all checks, raises an error if invalid

Raises:

  • (InvalidDNS)


185
186
187
188
189
190
191
192
193
# File 'lib/github-pages-health-check.rb', line 185

def check!
  raise InvalidDNS unless dns?
  return if proxied?
  raise DeprecatedIP if a_record? && old_ip_address?
  raise InvalidARecord if valid_domain? && a_record? && !should_be_a_record?
  raise InvalidCNAME if valid_domain? && !github_domain? && !apex_domain? && !pointed_to_github_user_domain?
  raise NotServedByPages unless served_by_pages?
  true
end

#cloudflare_ip?Boolean

Returns:

  • (Boolean)


52
53
54
55
56
# File 'lib/github-pages-health-check.rb', line 52

def cloudflare_ip?
  dns.all? do |answer|
    answer.class == Net::DNS::RR::A && CloudFlare.controls_ip?(answer.address)
  end if dns?
end

#cname_record?Boolean

Is this domain’s first response a CNAME record?

Returns:

  • (Boolean)


101
102
103
# File 'lib/github-pages-health-check.rb', line 101

def cname_record?
  dns.first.class == Net::DNS::RR::CNAME if dns?
end

#dnsObject Also known as: dns_resolves?

Returns an array of DNS answers



72
73
74
75
76
77
78
79
80
# File 'lib/github-pages-health-check.rb', line 72

def dns
  @dns ||= Timeout::timeout(TIMEOUT) do
    without_warnings do
      Net::DNS::Resolver.start(absolute_domain).answer if domain
    end
  end
rescue Exception
  nil
end

#dns?Boolean

Are we even able to get the DNS record?

Returns:

  • (Boolean)


83
84
85
# File 'lib/github-pages-health-check.rb', line 83

def dns?
  !dns.nil?
end

#github_domain?Boolean

Is this domain owned by GitHub?

Returns:

  • (Boolean)


141
142
143
# File 'lib/github-pages-health-check.rb', line 141

def github_domain?
  !!domain.match(/\.github\.com$/)
end

#inspectObject



212
213
214
# File 'lib/github-pages-health-check.rb', line 212

def inspect
  "#<GitHubPages::HealthCheck @domain=\"#{domain}\" valid?=#{valid?}>"
end

#old_ip_address?Boolean

Does this domain have any A record that points to the legacy IPs?

Returns:

  • (Boolean)


89
90
91
92
93
# File 'lib/github-pages-health-check.rb', line 89

def old_ip_address?
  dns.any? do |answer|
    answer.class == Net::DNS::RR::A && LEGACY_IP_ADDRESSES.include?(answer.address.to_s)
  end if dns?
end

#pages_domain?(domain = domain()) ⇒ Boolean

Is the given cname a pages domain?

domain - the domain to check, generaly the target of a cname

Returns:

  • (Boolean)


136
137
138
# File 'lib/github-pages-health-check.rb', line 136

def pages_domain?(domain = domain())
  !!domain.match(/^[\w-]+\.github\.(io|com)\.?$/i)
end

#pointed_to_github_pages_ip?Boolean

Is the domain’s first response an A record to a valid GitHub Pages IP?

Returns:

  • (Boolean)


129
130
131
# File 'lib/github-pages-health-check.rb', line 129

def pointed_to_github_pages_ip?
  dns.first.class == Net::DNS::RR::A && CURRENT_IP_ADDRESSES.include?(dns.first.value) if dns?
end

#pointed_to_github_user_domain?Boolean

Is the domain’s first response a CNAME to a pages domain?

Returns:

  • (Boolean)


124
125
126
# File 'lib/github-pages-health-check.rb', line 124

def pointed_to_github_user_domain?
  dns.first.class == Net::DNS::RR::CNAME && pages_domain?(dns.first.cname.to_s) if dns?
end

#proxied?Boolean

Does this non-GitHub-pages domain proxy a GitHub Pages site?

This can be:

1. A Cloudflare-owned IP address
2. A site that returns GitHub.com server headers, but isn't CNAME'd to a GitHub domain
3. A site that returns GitHub.com server headers, but isn't CNAME'd to a GitHub IP

Returns:

  • (Boolean)


64
65
66
67
68
69
# File 'lib/github-pages-health-check.rb', line 64

def proxied?
  return unless dns?
  return true if cloudflare_ip?
  return false if pointed_to_github_pages_ip? || pointed_to_github_user_domain?
  served_by_pages?
end

#reasonObject

Return the error, if any



205
206
207
208
209
210
# File 'lib/github-pages-health-check.rb', line 205

def reason
  check!
  nil
rescue GitHubPages::HealthCheck::Error => e
  e
end

#served_by_pages?Boolean

Returns:

  • (Boolean)


167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/github-pages-health-check.rb', line 167

def served_by_pages?
  @served_by_pages ||= begin
    response = Typhoeus.head(uri, TYPHOEUS_OPTIONS)
    # Workaround for webmock not playing nicely with Typhoeus redirects
    # See https://github.com/bblimke/webmock/issues/237
    if response.mock? && response.headers["Location"]
      response = Typhoeus.head(response.headers["Location"], TYPHOEUS_OPTIONS)
    end

    (response.mock? || response.return_code == :ok) && response.headers["Server"] == "GitHub.com"
  end
end

#should_be_a_record?Boolean

Should the domain be an apex record?

Returns:

  • (Boolean)


119
120
121
# File 'lib/github-pages-health-check.rb', line 119

def should_be_a_record?
  !pages_domain? && apex_domain?
end

#to_hashObject Also known as: to_h



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/github-pages-health-check.rb', line 145

def to_hash
  {
    :uri                            => uri.to_s,
    :dns_resolves?                  => dns?,
    :proxied?                       => proxied?,
    :cloudflare_ip?                 => cloudflare_ip?,
    :old_ip_address?                => old_ip_address?,
    :a_record?                      => a_record?,
    :cname_record?                  => cname_record?,
    :valid_domain?                  => valid_domain?,
    :apex_domain?                   => apex_domain?,
    :should_be_a_record?            => should_be_a_record?,
    :pointed_to_github_user_domain? => pointed_to_github_user_domain?,
    :pointed_to_github_pages_ip?    => pointed_to_github_pages_ip?,
    :pages_domain?                  => pages_domain?,
    :served_by_pages?               => served_by_pages?,
    :valid?                         => valid?,
    :reason                         => reason
  }
end

#to_jsonObject



180
181
182
# File 'lib/github-pages-health-check.rb', line 180

def to_json
  to_hash.to_json
end

#to_sObject



216
217
218
219
220
# File 'lib/github-pages-health-check.rb', line 216

def to_s
  to_hash.inject(Array.new) do |all, pair|
    all.push pair.join(": ")
  end.join("\n")
end

#valid?Boolean

Runs all checks, returns true if valid, otherwise false

Returns:

  • (Boolean)


197
198
199
200
201
202
# File 'lib/github-pages-health-check.rb', line 197

def valid?
  check!
  true
rescue
  false
end

#valid_domain?Boolean

Is this a valid domain that PublicSuffix recognizes? Used as an escape hatch to prevent false positves on DNS checkes

Returns:

  • (Boolean)


107
108
109
# File 'lib/github-pages-health-check.rb', line 107

def valid_domain?
  PublicSuffix.valid? domain
end