Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/rb/lib/selenium/webdriver/common/socket_poller.rb
1865 views
1
# frozen_string_literal: true
2
3
# Licensed to the Software Freedom Conservancy (SFC) under one
4
# or more contributor license agreements. See the NOTICE file
5
# distributed with this work for additional information
6
# regarding copyright ownership. The SFC licenses this file
7
# to you under the Apache License, Version 2.0 (the
8
# "License"); you may not use this file except in compliance
9
# with the License. You may obtain a copy of the License at
10
#
11
# http://www.apache.org/licenses/LICENSE-2.0
12
#
13
# Unless required by applicable law or agreed to in writing,
14
# software distributed under the License is distributed on an
15
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
# KIND, either express or implied. See the License for the
17
# specific language governing permissions and limitations
18
# under the License.
19
20
require 'selenium/webdriver/common/platform'
21
require 'socket'
22
23
module Selenium
24
module WebDriver
25
class SocketPoller
26
def initialize(host, port, timeout = 0, interval = 0.25)
27
@host = host
28
@port = Integer(port)
29
@timeout = Float(timeout)
30
@interval = interval
31
end
32
33
#
34
# Returns true if the server is listening within the given timeout,
35
# false otherwise.
36
#
37
# @return [Boolean]
38
#
39
40
def connected?
41
with_timeout { listening? }
42
end
43
44
#
45
# Returns true if the server has stopped listening within the given timeout,
46
# false otherwise.
47
#
48
# @return [Boolean]
49
#
50
51
def closed?
52
with_timeout { !listening? }
53
end
54
55
private
56
57
CONNECT_TIMEOUT = 5
58
59
NOT_CONNECTED_ERRORS = [Errno::ECONNREFUSED, Errno::ENOTCONN, SocketError].tap { |arr|
60
arr << Errno::EPERM if Platform.cygwin?
61
}.freeze
62
63
CONNECTED_ERRORS = [Errno::EISCONN].tap { |arr|
64
arr << Errno::EINVAL if Platform.windows?
65
arr << Errno::EALREADY if Platform.wsl?
66
}.freeze
67
68
if Platform.jruby?
69
# we use a plain TCPSocket here since JRuby has issues closing socket
70
# see https://github.com/jruby/jruby/issues/5709
71
def listening?
72
TCPSocket.new(@host, @port).close
73
true
74
rescue *NOT_CONNECTED_ERRORS
75
false
76
end
77
else
78
def listening?
79
addr = Socket.getaddrinfo(@host, @port, Socket::AF_INET, Socket::SOCK_STREAM)
80
sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
81
sockaddr = Socket.pack_sockaddr_in(@port, addr[0][3].to_s)
82
83
begin
84
sock.connect_nonblock sockaddr
85
rescue Errno::EINPROGRESS
86
retry if socket_writable?(sock) && conn_completed?(sock)
87
raise Errno::ECONNREFUSED
88
rescue *CONNECTED_ERRORS
89
# yay!
90
end
91
92
sock.close
93
true
94
rescue *NOT_CONNECTED_ERRORS
95
sock&.close
96
WebDriver.logger.debug("polling for socket on #{[@host, @port].inspect}", id: :driver_service)
97
false
98
end
99
end
100
101
def socket_writable?(sock)
102
sock.wait_writable(CONNECT_TIMEOUT)
103
end
104
105
def conn_completed?(sock)
106
sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).int.zero?
107
end
108
109
def with_timeout
110
max_time = current_time + @timeout
111
112
until current_time > max_time
113
return true if yield
114
115
sleep @interval
116
end
117
118
false
119
end
120
121
def current_time
122
Process.clock_gettime(Process::CLOCK_MONOTONIC)
123
end
124
end # SocketPoller
125
end # WebDriver
126
end # Selenium
127
128