Path: blob/trunk/rb/lib/selenium/webdriver/common/service_manager.rb
1865 views
# frozen_string_literal: true12# Licensed to the Software Freedom Conservancy (SFC) under one3# or more contributor license agreements. See the NOTICE file4# distributed with this work for additional information5# regarding copyright ownership. The SFC licenses this file6# to you under the Apache License, Version 2.0 (the7# "License"); you may not use this file except in compliance8# with the License. You may obtain a copy of the License at9#10# http://www.apache.org/licenses/LICENSE-2.011#12# Unless required by applicable law or agreed to in writing,13# software distributed under the License is distributed on an14# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY15# KIND, either express or implied. See the License for the16# specific language governing permissions and limitations17# under the License.1819module Selenium20module WebDriver21#22# Base class implementing default behavior of service_manager object,23# responsible for starting and stopping driver implementations.24#25# @api private26#27class ServiceManager28START_TIMEOUT = 2029SOCKET_LOCK_TIMEOUT = 4530STOP_TIMEOUT = 203132#33# End users should use a class method for the desired driver, rather than using this directly.34#35# @api private36#3738def initialize(config)39@executable_path = config.executable_path40@host = Platform.localhost41@port = config.port42@io = config.log43@extra_args = config.args44@shutdown_supported = config.shutdown_supported4546raise Error::WebDriverError, "invalid port: #{@port}" if @port < 147end4849def start50raise "already started: #{uri.inspect} #{@executable_path.inspect}" if process_running?5152Platform.exit_hook { stop } # make sure we don't leave the server running5354socket_lock.locked do55find_free_port56start_process57connect_until_stable58end59end6061def stop62return unless @shutdown_supported63return if process_exited?6465stop_server66@process.poll_for_exit STOP_TIMEOUT67rescue ChildProcess::TimeoutError, Errno::ECONNREFUSED68nil # noop69ensure70stop_process71end7273def uri74@uri ||= URI.parse("http://#{@host}:#{@port}")75end7677private7879def build_process(*command)80WebDriver.logger.debug("Executing Process #{command}", id: :driver_service)81@process = ChildProcess.build(*command)82@io ||= WebDriver.logger.io if WebDriver.logger.debug?83@process.io = @io if @io8485@process86end8788def connect_to_server89Net::HTTP.start(@host, @port) do |http|90http.open_timeout = STOP_TIMEOUT / 291http.read_timeout = STOP_TIMEOUT / 29293yield http94end95end9697def find_free_port98@port = PortProber.above(@port)99end100101def start_process102@process = build_process(@executable_path, "--port=#{@port}", *@extra_args)103@process.start104end105106def stop_process107return if process_exited?108109@process.stop STOP_TIMEOUT110end111112def stop_server113connect_to_server do |http|114headers = WebDriver::Remote::Http::Common::DEFAULT_HEADERS.dup115WebDriver.logger.debug('Sending shutdown request to server', id: :driver_service)116http.get('/shutdown', headers)117end118end119120def process_running?121defined?(@process) && @process&.alive?122end123124def process_exited?125@process.nil? || @process.exited?126end127128def connect_until_stable129socket_poller = SocketPoller.new @host, @port, START_TIMEOUT130return if socket_poller.connected?131132raise Error::WebDriverError, cannot_connect_error_text133end134135def cannot_connect_error_text136"unable to connect to #{@executable_path} #{@host}:#{@port}"137end138139def socket_lock140@socket_lock ||= SocketLock.new(@port - 1, SOCKET_LOCK_TIMEOUT)141end142end # Service143end # WebDriver144end # Selenium145146147