Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

file 154 lines (127 sloc) 4.621 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
require File.join(File.dirname(__FILE__), 'ping')
require 'net/http'
require 'net/https'
require 'uri'
require 'open-uri'

# Force non-blocking Socket.getaddrinfo on Unix systems. Do not use on
# Windows because it (ironically) causes blocking problems.
unless File::ALT_SEPARATOR or RUBY_VERSION >= "1.9.3"
  require 'resolv-replace'
end

# The Net module serves as a namespace only.
module Net

  # The Ping::HTTP class encapsulates methods for HTTP pings.
  class Ping::HTTP < Ping

    # By default an http ping will follow a redirect and give you the result
    # of the final URI. If this value is set to false, then it will not
    # follow a redirect and will return false immediately on a redirect.
    #
    attr_accessor :follow_redirect

    # The maximum number of redirects allowed. The default is 5.
    attr_accessor :redirect_limit

    # The user agent used for the HTTP request. The default is nil.
    attr_accessor :user_agent

    # OpenSSL certificate verification mode. The default is VERIFY_NONE.
    attr_accessor :ssl_verify_mode

    # Use GET request instead HEAD. The default is false.
    attr_accessor :get_request

    # was this ping proxied?
    attr_accessor :proxied

    # Creates and returns a new Ping::HTTP object. The default port is the
    # port associated with the URI. The default timeout is 5 seconds.
    #
    def initialize(uri=nil, port=nil, timeout=5)
      @follow_redirect = true
      @redirect_limit = 5
      @ssl_verify_mode = OpenSSL::SSL::VERIFY_NONE
      @get_request = false

      port ||= URI.parse(uri).port if uri

      super(uri, port, timeout)
    end

    # Looks for an HTTP response from the URI passed to the constructor.
    # If the result is a kind of Net::HTTPSuccess then the ping was
    # successful and true is returned. Otherwise, false is returned
    # and the Ping::HTTP#exception method should contain a string
    # indicating what went wrong.
    #
    # If the HTTP#follow_redirect accessor is set to true (which it is
    # by default) and a redirect occurs during the ping, then the
    # HTTP#warning attribute is set to the redirect message, but the
    # return result is still true. If it's set to false then a redirect
    # response is considered a failed ping.
    #
    # If no file or path is specified in the URI, then '/' is assumed.
    #
    def ping(host = @host)
      super(host)
      bool = false
      uri = URI.parse(host)

      start_time = Time.now

      response = do_ping(uri)

      if response.is_a?(Net::HTTPSuccess)
        bool = true
      elsif redirect?(response) # Check code, HTTPRedirection does not always work
        if @follow_redirect
          @warning = response.message
          rlimit = 0

          while redirect?(response)
            if rlimit >= redirect_limit
              @exception = "Redirect limit exceeded"
              break
            end
            redirect = URI.parse(response['location'])
            redirect = uri + redirect if redirect.relative?
            response = do_ping(redirect)
            rlimit += 1
          end

          if response.is_a?(Net::HTTPSuccess)
            bool = true
          else
            @warning = nil
            @exception ||= response.message
          end

        else
          @exception = response.message
        end
      end

      # There is no duration if the ping failed
      @duration = Time.now - start_time if bool

      bool
    end

    alias ping? ping
    alias pingecho ping
    alias follow_redirect? follow_redirect
    alias uri host
    alias uri= host=

    private

    def redirect?(response)
      response && response.code.to_i >= 300 && response.code.to_i < 400
    end

    def do_ping(uri)
      response = nil
      proxy = uri.find_proxy || URI.parse("")
      begin
        uri_path = uri.path.empty? ? '/' : uri.path
        headers = { }
        headers["User-Agent"] = user_agent unless user_agent.nil?
        Timeout.timeout(@timeout) do
          http = Net::HTTP::Proxy(proxy.host, proxy.port, proxy.user, proxy.password).new(uri.host, uri.port)
          @proxied = http.proxy?
          if @get_request == true
            request = Net::HTTP::Get.new(uri_path)
          else
            request = Net::HTTP::Head.new(uri_path)
          end

          if uri.scheme == 'https'
            http.use_ssl = true
            http.verify_mode = @ssl_verify_mode
          end

          response = http.start { |h| h.request(request) }
        end
      rescue Exception => err
        @exception = err.message
      end
      response
    end
  end
end
Something went wrong with that request. Please try again.