lib/capybara/webkit/server.rb
require "open3"
require "thread"
module Capybara
module Webkit
class Server
SERVER_PATH = File.expand_path("../../../../bin/webkit_server", __FILE__)
WEBKIT_SERVER_START_TIMEOUT = 15
attr_reader :port, :pid
def initialize(options = {})
if options.has_key?(:stderr)
@output_target = options[:stderr]
elsif options.has_key?(:stdout)
warn "[DEPRECATION] The Capybara::Webkit::Connection `stdout` " \
"option is deprecated. Please use `stderr` instead."
@output_target = options[:stdout]
else
@output_target = $stderr
end
end
def start
open_pipe
discover_port
discover_pid
forward_output_in_background_thread
register_shutdown_hook
end
private
def open_pipe
if IO.respond_to?(:popen4)
@pid,
@pipe_stdin,
@pipe_stdout,
@pipe_stderr = IO.popen4(SERVER_PATH)
else
@pipe_stdin,
@pipe_stdout,
@pipe_stderr,
@wait_thr = Open3.popen3(SERVER_PATH)
end
end
def discover_port
if IO.select([@pipe_stdout], nil, nil, WEBKIT_SERVER_START_TIMEOUT)
@port = parse_port(@pipe_stdout.first)
else
raise(
ConnectionError,
"#{SERVER_PATH} failed to start after " \
"#{WEBKIT_SERVER_START_TIMEOUT} seconds.",
)
end
end
def parse_port(line)
if match = line.to_s.match(/listening on port: (\d+)/)
match[1].to_i
else
raise ConnectionError, "#{SERVER_PATH} failed to start."
end
end
def discover_pid
@pid ||= @wait_thr[:pid]
end
def forward_output_in_background_thread
Thread.new do
Thread.current.abort_on_exception = true
IO.copy_stream(@pipe_stderr, @output_target) if @output_target
end
end
def register_shutdown_hook
@owner_pid = Process.pid
at_exit do
if Process.pid == @owner_pid
kill_process
end
end
end
def kill_process
if @pid
if RUBY_PLATFORM =~ /mingw32/
Process.kill(9, @pid)
else
Process.kill("INT", @pid)
end
Process.wait(@pid)
@wait_thr.join if @wait_thr
end
rescue Errno::ESRCH, Errno::ECHILD
# This just means that the webkit_server process has already ended
ensure
@pid = @wait_thr = nil
end
end
end
end