From d08a65e7f75cd0e8538a4139f26009d705197219 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 23 Mar 2012 10:26:29 -0400 Subject: [PATCH] IsPortOpen utility --- lib/vagrant/util/is_port_open.rb | 38 +++++++++++++++ test/unit/vagrant/util/is_port_open_test.rb | 53 +++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 lib/vagrant/util/is_port_open.rb create mode 100644 test/unit/vagrant/util/is_port_open_test.rb diff --git a/lib/vagrant/util/is_port_open.rb b/lib/vagrant/util/is_port_open.rb new file mode 100644 index 000000000..f30c3a180 --- /dev/null +++ b/lib/vagrant/util/is_port_open.rb @@ -0,0 +1,38 @@ +require "socket" +require "timeout" + +module Vagrant + module Util + # Contains the method {#is_ruby_open?} to check if a port is open + # (listening) or closed (not in use). This method isn't completely + # fool-proof, but it works enough of the time to be useful. + module IsPortOpen + # Checks if a port is open (listening) on a given host and port. + # + # @param [String] host Hostname or IP address. + # @param [Integer] port Port to check. + # @return [Boolean] `true` if the port is open (listening), `false` + # otherwise. + def is_port_open?(host, port) + # We wrap this in a timeout because once in awhile the TCPSocket + # _will_ hang, but this signals that the port is closed. + Timeout.timeout(1) do + # Attempt to make a connection + s = TCPSocket.new(host, port) + + # A connection was made! Properly clean up the socket, not caring + # at all if any exception is raised, because we already know the + # result. + s.close rescue nil + + # The port is open if we reached this point, since we were able + # to connect. + return true + end + rescue Timeout::Error, Errno::ECONNREFUSED, Errno::EHOSTUNREACH + # Any of the above exceptions signal that the port is closed. + return false + end + end + end +end diff --git a/test/unit/vagrant/util/is_port_open_test.rb b/test/unit/vagrant/util/is_port_open_test.rb new file mode 100644 index 000000000..113294b60 --- /dev/null +++ b/test/unit/vagrant/util/is_port_open_test.rb @@ -0,0 +1,53 @@ +require File.expand_path("../../../base", __FILE__) + +require "socket" + +require "vagrant/util/is_port_open" + +describe Vagrant::Util::IsPortOpen do + let(:klass) do + Class.new do + extend Vagrant::Util::IsPortOpen + end + end + + let(:open_port) { 52811 } + let(:closed_port) { 52811 } + + it "should report open ports" do + # Start a thread which listens on a port + thr = Thread.new do + server = TCPServer.new(open_port) + Thread.current[:running] = true + + # Wait until we're told to die + Thread.current[:die] = false + while !Thread.current[:die] + Thread.pass + end + + # Die! + server.close + end + + # Wait until the server is running + while !thr[:running] + Thread.pass + end + + # Verify that we report the port is open + klass.is_port_open?("localhost", open_port).should be + + # Kill the thread + thr[:die] = true + thr.join + end + + it "should report closed ports" do + # This CAN fail, since port 52811 might actually be in use, but I'm + # not sure what to do except choose some random port and hope for the + # best, really. + klass.is_port_open?("localhost", closed_port).should_not be + end +end +