Skip to content

Commit

Permalink
extract link checker from audit process
Browse files Browse the repository at this point in the history
did not duplicate content lentgh check in preparation for the removal of
content_length as discussed in Homebrew#131
  • Loading branch information
Paul Hinze committed Mar 13, 2013
1 parent b23c92d commit 78834bf
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 92 deletions.
63 changes: 0 additions & 63 deletions lib/cask/audit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ def initialize(cask, fetcher=Cask::Fetcher)
def run!
_check_required_fields
return if errors?
_get_data_from_request
return if errors?
_check_response_status
return if errors?
_check_content_length
end

def add_error(message)
Expand Down Expand Up @@ -65,62 +60,4 @@ def _check_required_fields
add_error "homepage is required" unless cask.homepage
end

http_responses = [
'HTTP/1.0 200 OK',
'HTTP/1.1 200 OK'
]

OK_RESPONSES = {
'http' => http_responses,
'https' => http_responses,
'ftp' => [ 'OK' ]
}

def _check_response_status
ok = OK_RESPONSES[cask.url.scheme]
unless ok.include?(@response_status)
add_error "unexpected http response, expecting #{ok.map(&:inspect).join(' or ')}, got #{@response_status.inspect}"
end
end

def _check_content_length
remote_content_length = @headers['Content-Length']
if cask.content_length.nil?
add_warning "specify content_length so we can check against URL, currently: content_length '#{remote_content_length}'"
else
unless cask.content_length == remote_content_length
add_warning "unexpected content_length for #{cask}; specified #{cask.content_length.inspect}, but got #{remote_content_length.inspect}"
end
end
end

def _get_data_from_request
response = @fetcher.head(cask.url)

if response.empty?
add_error "timeout while requesting #{cask.url}"
return
end

response_lines = response.split("\n").map(&:chomp)

case cask.url.scheme
when 'http', 'https' then
@response_status = response_lines.grep(/^HTTP/).last
http_headers = response_lines[(response_lines.index(@response_status)+1)..-1]
http_headers.each { |line|
header_name, header_value = line.split(': ')
@headers[header_name] = header_value
}
when 'ftp' then
@response_status = 'OK'
response_lines.each { |line|
header_name, header_value = line.split(': ')
@headers[header_name] = header_value
}
else
add_error "unknown scheme for #{cask.url}"
end
end

end
14 changes: 14 additions & 0 deletions lib/cask/cli/checklinks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class Cask::CLI::Checklinks
def self.run(*args)
casks_to_check = args.empty? ? Cask.all : args.map { |arg| Cask.load(arg) }
casks_to_check.each do |cask|
checker = Cask::LinkChecker.new(cask)
checker.run
puts checker.summary
end
end

def self.help
"checks for bad cask links"
end
end
104 changes: 104 additions & 0 deletions lib/cask/link_checker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
class Cask::LinkChecker
attr_accessor :cask, :errors, :response_status, :headers

def initialize(cask, fetcher=Cask::Fetcher)
@cask = cask
@errors = []
@warnings = []
@headers = {}
@fetcher = fetcher
end

def run
_get_data_from_request
return if errors?
_check_response_status
end

def add_error(message)
@errors << message
end

def add_warning(message)
@warnings << message
end

def errors?
!@errors.empty?
end

def warnings?
!@warnings.empty?
end

def result
if errors?
"#{Tty.red}failed#{Tty.reset}"
elsif warnings?
"#{Tty.yellow}warning#{Tty.reset}"
else
"#{Tty.green}passed#{Tty.reset}"
end
end

def summary
summary = ["audit for #{cask}: #{result}"]

@errors.each do |error|
summary << " #{Tty.red}-#{Tty.reset} #{error}"
end

@warnings.each do |warning|
summary << " #{Tty.yellow}-#{Tty.reset} #{warning}"
end

summary.join("\n")
end

HTTP_RESPONSES = [
'HTTP/1.0 200 OK',
'HTTP/1.1 200 OK'
]

OK_RESPONSES = {
'http' => HTTP_RESPONSES,
'https' => HTTP_RESPONSES,
'ftp' => [ 'OK' ]
}

def _check_response_status
ok = OK_RESPONSES[cask.url.scheme]
unless ok.include?(@response_status)
add_error "unexpected http response, expecting #{ok.map(&:inspect).join(' or ')}, got #{@response_status.inspect}"
end
end

def _get_data_from_request
response = @fetcher.head(cask.url)

if response.empty?
add_error "timeout while requesting #{cask.url}"
return
end

response_lines = response.split("\n").map(&:chomp)

case cask.url.scheme
when 'http', 'https' then
@response_status = response_lines.grep(/^HTTP/).last
http_headers = response_lines[(response_lines.index(@response_status)+1)..-1]
http_headers.each { |line|
header_name, header_value = line.split(': ')
@headers[header_name] = header_value
}
when 'ftp' then
@response_status = 'OK'
response_lines.each { |line|
header_name, header_value = line.split(': ')
@headers[header_name] = header_value
}
else
add_error "unknown scheme for #{cask.url}"
end
end
end
29 changes: 0 additions & 29 deletions test/cask/audit_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,34 +57,5 @@ class CaskMissingHomepage < Cask
end
end

describe "request processing" do
it "adds an error if response is empty" do
cask = TestHelper.test_cask
TestHelper.fake_response_for(cask.url, "")
audit = Cask::Audit.new(cask, TestHelper.fake_fetcher)
audit.run!
audit.errors.must_include "timeout while requesting #{cask.url}"
end

it "properly populates the response code and headers from an http response" do
TestHelper.fake_response_for(TestHelper.test_cask.url, <<-RESPONSE.gsub(/^ /, ''))
HTTP/1.1 200 OK
Content-Type: application/x-apple-diskimage
ETag: "b4208f3e84967be4b078ecaa03fba941"
Content-Length: 23726161
Last-Modified: Sun, 12 Aug 2012 21:17:21 GMT
RESPONSE

audit = Cask::Audit.new(TestHelper.test_cask, TestHelper.fake_fetcher)
audit.run!
audit.response_status.must_equal 'HTTP/1.1 200 OK'
audit.headers.must_equal({
'Content-Type' => 'application/x-apple-diskimage',
'ETag' => '"b4208f3e84967be4b078ecaa03fba941"',
'Content-Length' => '23726161',
'Last-Modified' => 'Sun, 12 Aug 2012 21:17:21 GMT'
})
end
end
end
end
33 changes: 33 additions & 0 deletions test/cask/link_checker_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require 'test_helper'

describe Cask::Installer do
describe "request processing" do
it "adds an error if response is empty" do
cask = TestHelper.test_cask
TestHelper.fake_response_for(cask.url, "")
checker = Cask::LinkChecker.new(cask, TestHelper.fake_fetcher)
checker.run
checker.errors.must_include "timeout while requesting #{cask.url}"
end

it "properly populates the response code and headers from an http response" do
TestHelper.fake_response_for(TestHelper.test_cask.url, <<-RESPONSE.gsub(/^ */, ''))
HTTP/1.1 200 OK
Content-Type: application/x-apple-diskimage
ETag: "b4208f3e84967be4b078ecaa03fba941"
Content-Length: 23726161
Last-Modified: Sun, 12 Aug 2012 21:17:21 GMT
RESPONSE

checker = Cask::LinkChecker.new(TestHelper.test_cask, TestHelper.fake_fetcher)
checker.run
checker.response_status.must_equal 'HTTP/1.1 200 OK'
checker.headers.must_equal({
'Content-Type' => 'application/x-apple-diskimage',
'ETag' => '"b4208f3e84967be4b078ecaa03fba941"',
'Content-Length' => '23726161',
'Last-Modified' => 'Sun, 12 Aug 2012 21:17:21 GMT'
})
end
end
end

0 comments on commit 78834bf

Please sign in to comment.