Skip to content

Commit f35a9dc

Browse files
committed
rb: Use socket lock when starting PhantomJS driver
This extends 437725c to PhantomJS because any external process starting with specific port suffers the same race condition.
1 parent 062ef44 commit f35a9dc

File tree

6 files changed

+55
-29
lines changed

6 files changed

+55
-29
lines changed

rb/lib/selenium/webdriver/chrome/bridge.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ def create_capabilities(opts)
103103
'extensions' => data['extensions']
104104
end
105105

106-
107106
chrome_options['binary'] = Chrome.path if Chrome.path
108107
chrome_options['nativeEvents'] = true if native_events
109108
chrome_options['verbose'] = true if verbose

rb/lib/selenium/webdriver/chrome/service.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ module Chrome
2424
#
2525
# @api private
2626
#
27+
2728
class Service
2829
START_TIMEOUT = 20
2930
SOCKET_LOCK_TIMEOUT = 45
@@ -90,6 +91,8 @@ def uri
9091
URI.parse "http://#{@host}:#{@port}"
9192
end
9293

94+
private
95+
9396
def find_free_port
9497
@port = PortProber.above @port
9598
end
@@ -103,9 +106,9 @@ def start_process
103106
end
104107

105108
def connect_until_stable
106-
@socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
109+
socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
107110

108-
unless @socket_poller.connected?
111+
unless socket_poller.connected?
109112
raise Error::WebDriverError, "unable to connect to chromedriver #{@host}:#{@port}"
110113
end
111114
end

rb/lib/selenium/webdriver/common/socket_lock.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919

2020
module Selenium
2121
module WebDriver
22+
23+
#
24+
# @api private
25+
#
26+
2227
class SocketLock
2328

2429
def initialize(port, timeout)

rb/lib/selenium/webdriver/edge/service.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ module Edge
2424
#
2525
# @api private
2626
#
27+
2728
class Service
2829
START_TIMEOUT = 20
2930
SOCKET_LOCK_TIMEOUT = 45
@@ -103,9 +104,9 @@ def start_process
103104
end
104105

105106
def connect_until_stable
106-
@socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
107+
socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
107108

108-
unless @socket_poller.connected?
109+
unless socket_poller.connected?
109110
raise Error::WebDriverError, "unable to connect to MicrosoftWebDriver #{@host}:#{@port}"
110111
end
111112
end

rb/lib/selenium/webdriver/phantomjs/service.rb

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,11 @@ module PhantomJS
2626
#
2727

2828
class Service
29-
START_TIMEOUT = 20
30-
STOP_TIMEOUT = 5
31-
DEFAULT_PORT = 8910
32-
MISSING_TEXT = "Unable to find phantomjs executable."
33-
34-
attr_reader :uri
29+
START_TIMEOUT = 20
30+
SOCKET_LOCK_TIMEOUT = 45
31+
STOP_TIMEOUT = 5
32+
DEFAULT_PORT = 8910
33+
MISSING_TEXT = "Unable to find phantomjs executable."
3534

3635
def self.executable_path
3736
@executable_path ||= (
@@ -44,35 +43,32 @@ def self.executable_path
4443
end
4544

4645
def self.default_service(port = nil)
47-
new executable_path, port || PortProber.above(DEFAULT_PORT)
46+
new executable_path, DEFAULT_PORT
4847
end
4948

5049
def initialize(executable_path, port)
51-
@uri = URI.parse "http://#{Platform.localhost}:#{port}"
50+
@host = Platform.localhost
5251
@executable = executable_path
5352
end
5453

5554
def start(args = [])
5655
if @process && @process.alive?
57-
raise "already started: #{@uri.inspect} #{@executable.inspect}"
56+
raise "already started: #{uri.inspect} #{@executable.inspect}"
5857
end
5958

60-
@process = create_process(args)
61-
@process.start
62-
63-
socket_poller = SocketPoller.new Platform.localhost, @uri.port, START_TIMEOUT
59+
Platform.exit_hook { stop } # make sure we don't leave the server running
6460

65-
unless socket_poller.connected?
66-
raise Error::WebDriverError, "unable to connect to phantomjs @ #{@uri} after #{START_TIMEOUT} seconds"
61+
socket_lock.locked do
62+
find_free_port
63+
start_process(args)
64+
connect_until_stable
6765
end
68-
69-
Platform.exit_hook { stop } # make sure we don't leave the server running
7066
end
7167

7268
def stop
7369
return if @process.nil? || @process.exited?
7470

75-
Net::HTTP.start(uri.host, uri.port) do |http|
71+
Net::HTTP.start(@host, @port) do |http|
7672
http.open_timeout = STOP_TIMEOUT / 2
7773
http.read_timeout = STOP_TIMEOUT / 2
7874

@@ -89,9 +85,19 @@ def stop
8985
end
9086
end
9187

92-
def create_process(args)
93-
server_command = [@executable, "--webdriver=#{@uri.port}", *args]
94-
process = ChildProcess.build(*server_command.compact)
88+
def find_free_port
89+
@port = PortProber.above @port
90+
end
91+
92+
def uri
93+
URI.parse "http://#{@host}:#{@port}"
94+
end
95+
96+
private
97+
98+
def start_process(args)
99+
server_command = [@executable, "--webdriver=#{@port}", *args]
100+
@process = ChildProcess.build(*server_command.compact)
95101

96102
if $DEBUG == true
97103
process.io.inherit!
@@ -100,10 +106,22 @@ def create_process(args)
100106
process.io.stdout = process.io.stderr = File.new(Platform.null_device, 'w')
101107
end
102108

103-
process
109+
@process.start
110+
end
111+
112+
def connect_until_stable
113+
socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
114+
115+
unless socket_poller.connected?
116+
raise Error::WebDriverError, "unable to connect to phantomjs @ #{uri} after #{START_TIMEOUT} seconds"
117+
end
118+
end
119+
120+
def socket_lock
121+
@socket_lock ||= SocketLock.new(@port - 1, SOCKET_LOCK_TIMEOUT)
104122
end
105123

106124
end # Service
107125
end # PhantomJS
108126
end # WebDriver
109-
end # Service
127+
end # Service

rb/spec/unit/selenium/webdriver/chrome/service_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ module Chrome
4141
mock_process
4242
end
4343

44-
Service.default_service.start_process
44+
Service.default_service.send(:start_process)
4545
end
4646

4747
it "finds the Chrome server binary by searching PATH" do

0 commit comments

Comments
 (0)