Path: blob/trunk/rb/lib/selenium/webdriver/common/socket_poller.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.1819require 'selenium/webdriver/common/platform'20require 'socket'2122module Selenium23module WebDriver24class SocketPoller25def initialize(host, port, timeout = 0, interval = 0.25)26@host = host27@port = Integer(port)28@timeout = Float(timeout)29@interval = interval30end3132#33# Returns true if the server is listening within the given timeout,34# false otherwise.35#36# @return [Boolean]37#3839def connected?40with_timeout { listening? }41end4243#44# Returns true if the server has stopped listening within the given timeout,45# false otherwise.46#47# @return [Boolean]48#4950def closed?51with_timeout { !listening? }52end5354private5556CONNECT_TIMEOUT = 55758NOT_CONNECTED_ERRORS = [Errno::ECONNREFUSED, Errno::ENOTCONN, SocketError].tap { |arr|59arr << Errno::EPERM if Platform.cygwin?60}.freeze6162CONNECTED_ERRORS = [Errno::EISCONN].tap { |arr|63arr << Errno::EINVAL if Platform.windows?64arr << Errno::EALREADY if Platform.wsl?65}.freeze6667if Platform.jruby?68# we use a plain TCPSocket here since JRuby has issues closing socket69# see https://github.com/jruby/jruby/issues/570970def listening?71TCPSocket.new(@host, @port).close72true73rescue *NOT_CONNECTED_ERRORS74false75end76else77def listening?78addr = Socket.getaddrinfo(@host, @port, Socket::AF_INET, Socket::SOCK_STREAM)79sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)80sockaddr = Socket.pack_sockaddr_in(@port, addr[0][3].to_s)8182begin83sock.connect_nonblock sockaddr84rescue Errno::EINPROGRESS85retry if socket_writable?(sock) && conn_completed?(sock)86raise Errno::ECONNREFUSED87rescue *CONNECTED_ERRORS88# yay!89end9091sock.close92true93rescue *NOT_CONNECTED_ERRORS94sock&.close95WebDriver.logger.debug("polling for socket on #{[@host, @port].inspect}", id: :driver_service)96false97end98end99100def socket_writable?(sock)101sock.wait_writable(CONNECT_TIMEOUT)102end103104def conn_completed?(sock)105sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).int.zero?106end107108def with_timeout109max_time = current_time + @timeout110111until current_time > max_time112return true if yield113114sleep @interval115end116117false118end119120def current_time121Process.clock_gettime(Process::CLOCK_MONOTONIC)122end123end # SocketPoller124end # WebDriver125end # Selenium126127128