Skip to content

Instantly share code, notes, and snippets.

@xaviershay
Created November 18, 2009 08:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xaviershay/237652 to your computer and use it in GitHub Desktop.
Save xaviershay/237652 to your computer and use it in GitHub Desktop.
module Support
class ApplicationServer
attr_accessor :options
attr_accessor :started
def initialize(options = {})
self.options = options
end
def option(key, default = nil)
options.has_key?(key) ? options[key] : default
end
def port
option(:port, 9293)
end
def daemonize?
option(:daemonize, true)
end
def start
if File.exists?(pid_file)
# Server has already been started, so we don't have to do it
self.started = false
else
$stderr.puts "==> Starting rack application server on port #{port}... "
fork do
args = options[:config], '-p', port.to_s
if daemonize?
args += ['-D', '--pid', pid_file]
else
File.open(pid_file, 'w') { |fp| fp.write Process.pid }
end
ENV["RACK_ENV"] = "test"
exec 'rackup', *args
end
self.started = true
end
end
def stop
silence_stream(STDOUT) do
if File.exists?(pid_file)
pid = File.read(pid_file)
system("kill -9 #{pid}")
FileUtils.rm_f pid_file
end
end
end
def fail
$stderr.puts
$stderr.puts
$stderr.puts "==> Failed to boot the Rack application server... exiting!"
exit
end
def pid_file
prepare_pid_file(Dir.pwd + "/tmp", "rack.#{port}.pid")
end
def silence_stream(stream)
old_stream = stream.dup
stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
stream.sync = true
yield
ensure
stream.reopen(old_stream)
end
def boot
if start
wait
stop_at_exit
end
end
def stop_at_exit
at_exit do
stop
end
end
def wait
wait_for_socket
$stderr.puts "==> Server ready on port #{port}\n"
end
def wait_for_socket
silence_stream(STDOUT) do
TCPSocket.wait_for_service_with_timeout \
:host => 'localhost',
:port => port,
:timeout => 5 # seconds
end
rescue SocketError
fail
end
def prepare_pid_file(file_path, pid_file_name)
FileUtils.mkdir_p File.expand_path(file_path)
File.expand_path("#{file_path}/#{pid_file_name}")
end
# Load more than one server in parallel
def self.multi_boot(*servers)
servers.collect {|config|
ApplicationServer.new(config)
}.each {|server|
server.start
}.each {|server|
server.wait
server.stop_at_exit if server.started
}
end
end
end
class TCPSocket
def self.wait_for_service_with_timeout(options)
start_time = Time.now
until listening_service?(options)
verbose_wait
if options[:timeout] && (Time.now > start_time + options[:timeout])
raise SocketError.new("Socket did not open within #{options[:timeout]} seconds")
end
end
end
def self.wait_for_service_termination_with_timeout(options)
start_time = Time.now
while listening_service?(options)
verbose_wait
if options[:timeout] && (Time.now > start_time + options[:timeout])
raise SocketError.new("Socket did not terminate within #{options[:timeout]} seconds")
end
end
end
def self.wait_for_service(options)
verbose_wait until listening_service?(options)
end
def self.wait_for_service_termination(options)
verbose_wait while listening_service?(options)
end
def self.listening_service?(options)
Timeout::timeout(options[:timeout] || 20) do
begin
socket = TCPSocket.new(options[:host], options[:port])
socket.close unless socket.nil?
true
rescue Errno::ECONNREFUSED,
Errno::EBADF # Windows
false
end
end
end
def self.verbose_wait
puts ".\n"
sleep 2
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment