require 'timeout' # # The HTTP-protocol test. # # This object is instantiated if the parser sees a line such as: # ### ### http://foo.vm.bytemark.co.uk/ must run http with content 'foo' otherwise 'ftp fail'. ### # # module Custodian module ProtocolTest class HTTPTest < TestFactory # # The line from which we were constructed. # attr_reader :line # # The URL to poll # attr_reader :url # # The expected status + content # attr_reader :expected_status, :expected_content # # The actual status & content received. # attr_reader :status, :content # # Constructor # def initialize( line ) # # Save the line # @line = line # # Save the URL # @url = line.split( /\s+/)[0] @host = @url if ( @url !~ /^https?:/ ) raise ArgumentError, "The target wasn't an URL" end # # Is this test inverted? # if ( line =~ /must\s+not\s+run\s+/ ) @inverted = true else @inverted = false end # # Expected status # if ( line =~ /with status ([0-9]+)/ ) @expected_status = $1.dup else @expected_status = "200" end # # The content we expect to find # if ( line =~ /with content '([^']+)'/ ) @expected_content = $1.dup else @expected_content = nil end end # # Allow this test to be serialized. # def to_s @line end # # Run the test. # def run_test # Reset state, in case we've previously run. @error = nil @status = nil @content = nil begin require 'rubygems' require 'curb' rescue LoadError @error = "The required rubygem 'curb' was not found." return false end begin timeout( 20 ) do begin c = Curl::Easy.new(@url) c.follow_location = true c.max_redirects = 10 c.timeout = 20 c.perform @status = c.response_code @content = c.body_str rescue Curl::Err::SSLCACertificateError => x @error = "SSL-Validation error" return false rescue Curl::Err::TimeoutError @error = "Timed out fetching page." return false rescue Curl::Err::TooManyRedirectsError @error = "Too many redirections (more than 10)" return false rescue => x @error = "Exception: #{x}" return false end end rescue Timeout::Error => e @error = "Timed out during fetch." return false end # # A this point we've either had an exception, or we've # got a result # if ( @expected_status.to_i != @status.to_i ) @error = "Status code was #{@status} not the expected #{@expected_status}" return false end if ( !@expected_content.nil? ) if ( @content && (! @content.match(/#{@expected_content}/i) ) ) @error = "

The response did not contain our expected text '#{@expected_content}'

" end end # # All done. # true end # # If the test fails then report the error../ # def error @error end register_test_type "http" register_test_type "https" end end end