module ElbPing::HttpPinger

Responsible for all HTTP ping-like functionality

Public Class Methods

cert_matches?(cert, host) click to toggle source

Check if a given host matches a cert’s pattern

Arguments:

  • cert: (object) of X.509 certificate

  • host: (string) of a hostname to compare

# File lib/elbping/pinger.rb, line 31
def self.cert_matches?(cert, host)
  File.fnmatch(cert_name(cert.subject).first, host)
end
cert_name(x509_subject) click to toggle source

Extract CNs from a X509 subject string

Arguments:

  • x509_subject: (string) of cert subject

# File lib/elbping/pinger.rb, line 15
def self.cert_name(x509_subject)
  cn_bucket = Array.new
  x509_subject.to_a.each do |entry|
    if entry.first == 'CN' and entry[1]
      cn_bucket << entry[1]
    end
  end
  cn_bucket
end
ping_node(node, host, port, path, use_ssl, verb_len, timeout) click to toggle source

Make HTTP request to given node using custom request method and measure response time

Arguments:

  • node: (string) of node IP

  • host: (string) of hostname, used for checking SSL cert match

  • port: (string || Fixnum) of positive integer [1, 65535]

  • path: (string) of path to request, e.g. “/”

  • use_ssl: (boolean) Whether or not this is HTTPS

  • verb_len: (Fixnum) of positive integer, how long the custom HTTP verb should be

  • timeout: (Fixnum) of positive integer, how many seconds for connect and read timeouts

# File lib/elbping/pinger.rb, line 46
def self.ping_node(node, host, port, path, use_ssl, verb_len, timeout)
  ##
  # Build request class
  ping_request = Class.new(Net::HTTPRequest) do
    const_set :METHOD, "A" * verb_len
    const_set :REQUEST_HAS_BODY, false
    const_set :RESPONSE_HAS_BODY, false
  end

  ##
  # Configure http object
  start = Time.now.getutc
  http = Net::HTTP.new(node, port.to_s)
  http.open_timeout     = timeout
  http.read_timeout     = timeout
  http.continue_timeout = timeout

  # Enable SSL if it's to be used
  if use_ssl
    http.use_ssl          = true
    http.verify_mode      = OpenSSL::SSL::VERIFY_NONE
    http.ssl_timeout      = timeout
  end

  ##
  # Make the HTTP request and handle any errors along the way
  error, exc = nil, nil
  req, response, cert = nil, nil, nil

  begin
    http.start do
      req = ping_request.new(path)
      cert = http.peer_cert
      response = http.request(req)
    end
  rescue OpenSSL::SSL::SSLError => e
    # This probably? won't happen with VERIFY_NONE
    error = :sslerror
  rescue Errno::ECONNREFUSED
    error = :econnrefused
  rescue Timeout::Error
    error = :timeout
  rescue Interrupt
    raise
  rescue SystemExit
    raise
  rescue StandardError => e
    exc = e # because I don't understand scope in ruby yet
    error = :exception
  end

  ssl_status = {}
  if use_ssl
    raise "No cert when SSL enabled?!" unless cert
    ssl_status = {
      :sslSubject => cert_name(cert.subject),
      :sslExpires => cert.not_after,
      :sslHostMatch => cert_matches?(cert, host)
    }
  end

  {:code => error || response.code,
    :exception => exc,
    :node => node,
    :duration => ((Time.now.getutc - start) * 1000).to_i, # returns in ms
  }.merge(ssl_status)
end