|
| 1 | +# Usage: ruby doctor.rb [HOST=status.github.com[:PORT=443]] |
| 2 | +require 'rbconfig' |
| 3 | +require 'net/https' |
| 4 | + |
| 5 | +if ARGV[0] =~ /^[^-]/ |
| 6 | + host, port = ARGV[0].split(':', 2) |
| 7 | +else |
| 8 | + host = 'status.github.com' |
| 9 | +end |
| 10 | +port ||= 443 |
| 11 | + |
| 12 | +ruby = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name']) |
| 13 | +ruby_version = RUBY_VERSION |
| 14 | +if patch = RbConfig::CONFIG['PATCHLEVEL'] |
| 15 | + ruby_version += "-p#{patch}" |
| 16 | +end |
| 17 | +puts "%s (%s)" % [ruby, ruby_version] |
| 18 | + |
| 19 | +openssl_dir = OpenSSL::X509::DEFAULT_CERT_AREA |
| 20 | +mac_openssl = '/System/Library/OpenSSL' == openssl_dir |
| 21 | +puts "%s: %s" % [OpenSSL::OPENSSL_VERSION, openssl_dir] |
| 22 | +[OpenSSL::X509::DEFAULT_CERT_DIR_ENV, OpenSSL::X509::DEFAULT_CERT_FILE_ENV].each do |key| |
| 23 | + puts "%s=%s" % [key, ENV[key].to_s.inspect] |
| 24 | +end |
| 25 | + |
| 26 | +ca_file = ENV[OpenSSL::X509::DEFAULT_CERT_FILE_ENV] || OpenSSL::X509::DEFAULT_CERT_FILE |
| 27 | +ca_path = (ENV[OpenSSL::X509::DEFAULT_CERT_DIR_ENV] || OpenSSL::X509::DEFAULT_CERT_DIR).chomp('/') |
| 28 | + |
| 29 | +puts "\nHEAD https://#{host}:#{port}" |
| 30 | +http = Net::HTTP.new(host, port) |
| 31 | +http.use_ssl = true |
| 32 | + |
| 33 | +# Explicitly setting cert_store like this is not needed in most cases but it |
| 34 | +# seems necessary in edge cases such as when using `verify_callback` in some |
| 35 | +# combination of Ruby + OpenSSL versions. |
| 36 | +http.cert_store = OpenSSL::X509::Store.new |
| 37 | +http.cert_store.set_default_paths |
| 38 | + |
| 39 | +http.verify_mode = OpenSSL::SSL::VERIFY_PEER |
| 40 | +failed_cert = failed_cert_reason = nil |
| 41 | + |
| 42 | +if mac_openssl |
| 43 | + warn "warning: unable show failed certificate info on OS X's OpenSSL" |
| 44 | + # This drives me absolutely nuts. It seems that on Rubies compiled against OS X's |
| 45 | + # system OpenSSL, the mere fact of defining a `verify_callback` makes the |
| 46 | + # cert verification fail for requests that would otherwise be successful. |
| 47 | +else |
| 48 | + http.verify_callback = lambda { |verify_ok, store_context| |
| 49 | + if !verify_ok |
| 50 | + failed_cert = store_context.current_cert |
| 51 | + failed_cert_reason = "%d: %s" % [ store_context.error, store_context.error_string ] |
| 52 | + end |
| 53 | + verify_ok |
| 54 | + } |
| 55 | +end |
| 56 | + |
| 57 | +user_agent = "net/http #{ruby_version}" |
| 58 | +req = Net::HTTP::Head.new('/', 'user-agent' => user_agent) |
| 59 | + |
| 60 | +begin |
| 61 | + res = http.start { http.request(req) } |
| 62 | + abort res.inspect if res.code.to_i >= 500 |
| 63 | + puts "OK" |
| 64 | +rescue Errno::ECONNREFUSED |
| 65 | + puts "Error: connection refused" |
| 66 | + exit 1 |
| 67 | +rescue OpenSSL::SSL::SSLError => e |
| 68 | + puts "#{e.class}: #{e.message}" |
| 69 | + |
| 70 | + if failed_cert |
| 71 | + puts "\nThe server presented a certificate that could not be verified:" |
| 72 | + puts "subject: #{failed_cert.subject}" |
| 73 | + puts "issuer: #{failed_cert.issuer}" |
| 74 | + puts "error code %s" % failed_cert_reason |
| 75 | + end |
| 76 | + |
| 77 | + ca_file_missing = !File.exist?(ca_file) && !mac_openssl |
| 78 | + ca_path_empty = Dir["#{ca_path}/*"].empty? |
| 79 | + |
| 80 | + if ca_file_missing || ca_path_empty |
| 81 | + puts "\nPossible causes:" |
| 82 | + puts "- `%s' does not exist" % ca_file if ca_file_missing |
| 83 | + puts "- `%s/' is empty" % ca_path if ca_path_empty |
| 84 | + end |
| 85 | + |
| 86 | + exit 1 |
| 87 | +end |
0 commit comments