require 'socket' require 'open4' module RackCheck class ServerManager include RackCheck::Helpers attr_reader :context, :ru_file def initialize(context, ru_file) @ru_file = ru_file @stop_var = Concurrent::MVar.new @context = context end def run Thread.new do begin with_environment(ru_file) do |dir| Dir.chdir(dir) do @pid, @in, @out, @err = Open4.popen4 context.app_server_command @stop_var.take Process.kill 'TERM', @pid Process.wait @pid @pid = nil end end rescue => e $stderr.puts "App server thread exited: #{e.message}\n#{e.backtrace.join("\n")}" end end port = wait_for_port "http://localhost:#{port}" end def stop @stop_var.put true while check_port sleep 0.1 end end def errors @err&.read.strip end def output @out&.read.strip end private def wait_for_port time = Time.now while Time.now - time < context.startup_timeout && !check_port sleep 0.1 end if !check_port Process.kill 'TERM', @pid if @pid raise "Application server did not start up in time" end context.app_server_port end def check_port TCPSocket.new('localhost', context.app_server_port).close true rescue false end end end