Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Exception: Unable to find a free port" - SeGrid/Python/Chromedriver/Ubuntu+OSX #1630

Closed
benhastings opened this issue Feb 12, 2016 · 13 comments

Comments

@benhastings
Copy link

Short Description:
on attempting to execute parallel tests on a host using selenium grid, the server-standalone hub accepts the first session and rejects all subsequent requests to create a session until the first terminates.

Longer Description:
I am not a java developer, so I can only dig so deeply, but it does look like something is not being correctly executed in the PortProber.java class. However, it may be something to do with the python requests to create a new session. I spent some time digging into the python code and wasn't able to find a way to force a particular port be requested for the new session.

We're trying to run multiple scripts on a headless virtual. This pattern has worked previously, but we needed to make updates to our chrome version, so we went ahead and updated the selenium-standalone version (I've tried 2.49-2.52), chromedriver (2.20-2.21), and Chrome (48.0.2564.82). In fact, in developing the python test script, I was able to run 4 sessions simultaneously on my OSX laptop. That is no longer the case (server standalone 2.50/2.52 tried - 2.50 previously worked)

I did try the selenium-server-standalone + a quick webdriver.io (nodeJS) script and it appears to work on my laptop.

Script to recreate:
(start one instance, then start another on the same system)

#test.py
from selenium import webdriver
import time

baseURL='http://www.yahoo.com'
url2='http://www.bing.com'

options=webdriver.ChromeOptions()
options.add_argument('--disable-logging')
driver=webdriver.Remote(desired_capabilities=options.to_capabilities())

driver.get(baseURL)
time.sleep(2.5)
print(driver.title)

driver.get(url2)
time.sleep(2.5)
print(driver.title)

driver.get(baseURL)
time.sleep(2.5)
print(driver.title)

driver.get(url2)
time.sleep(2.5)
print(driver.title)

driver.close()
driver.quit()

Python Script Error:

>>>python test.py
Traceback (most recent call last):
  File "test.py", line 11, in <module>
    driver=webdriver.Remote(desired_capabilities=options.to_capabilities())
  File "/usr/local/lib/python2.7/dist-packages/selenium-2.51.1-py2.7.egg/selenium/webdriver/remote/webdriver.py", line 87, in __init__
    self.start_session(desired_capabilities, browser_profile)
  File "/usr/local/lib/python2.7/dist-packages/selenium-2.51.1-py2.7.egg/selenium/webdriver/remote/webdriver.py", line 141, in start_session
    'desiredCapabilities': desired_capabilities,
  File "/usr/local/lib/python2.7/dist-packages/selenium-2.51.1-py2.7.egg/selenium/webdriver/remote/webdriver.py", line 201, in execute
    self.error_handler.check_response(response)
  File "/usr/local/lib/python2.7/dist-packages/selenium-2.51.1-py2.7.egg/selenium/webdriver/remote/errorhandler.py", line 193, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.WebDriverException: Message: Unable to find a free port

selenium-standalone-server error output:

10:59:41.649 INFO - Executing: [new session: Capabilities [{platform=ANY, javascriptEnabled=true, browserName=chrome, chromeOptions={args=[--disable-logging], extensions=[]}, version=}]])
10:59:41.650 INFO - Creating a new session for Capabilities [{platform=ANY, javascriptEnabled=true, browserName=chrome, chromeOptions={args=[--disable-logging], extensions=[]}, version=}]
10:59:41.652 WARN - Exception thrown
java.util.concurrent.ExecutionException: org.openqa.selenium.WebDriverException: java.lang.reflect.InvocationTargetException
Build info: version: '2.52.0', revision: '4c2593c', time: '2016-02-11 19:06:42'
System info: host: '**************', ip: '************', os.name: 'Linux', os.arch: 'amd64', os.version: '3.16.0-43-generic', java.version: '1.7.0_91'
Driver info: driver.version: unknown
  at java.util.concurrent.FutureTask.report(FutureTask.java:122)
  at java.util.concurrent.FutureTask.get(FutureTask.java:188)
  at org.openqa.selenium.remote.server.DefaultSession.execute(DefaultSession.java:183)
  at org.openqa.selenium.remote.server.DefaultSession.<init>(DefaultSession.java:119)
  at org.openqa.selenium.remote.server.DefaultSession.createSession(DefaultSession.java:95)
  at org.openqa.selenium.remote.server.DefaultDriverSessions.newSession(DefaultDriverSessions.java:124)
  at org.openqa.selenium.remote.server.handler.NewSession.handle(NewSession.java:59)
  at org.openqa.selenium.remote.server.handler.NewSession.handle(NewSession.java:1)
  at org.openqa.selenium.remote.server.rest.ResultConfig.handle(ResultConfig.java:111)
  at org.openqa.selenium.remote.server.JsonHttpCommandHandler.handleRequest(JsonHttpCommandHandler.java:79)
  at org.openqa.selenium.remote.server.DriverServlet.handleRequest(DriverServlet.java:202)
  at org.openqa.selenium.remote.server.DriverServlet.doPost(DriverServlet.java:164)
  at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
  at org.openqa.selenium.remote.server.DriverServlet.service(DriverServlet.java:130)
  at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
  at org.openqa.jetty.jetty.servlet.ServletHolder.handle(ServletHolder.java:428)
  at org.openqa.jetty.jetty.servlet.ServletHandler.dispatch(ServletHandler.java:680)
  at org.openqa.jetty.jetty.servlet.ServletHandler.handle(ServletHandler.java:571)
  at org.openqa.jetty.http.HttpContext.handle(HttpContext.java:1526)
  at org.openqa.jetty.http.HttpContext.handle(HttpContext.java:1479)
  at org.openqa.jetty.http.HttpServer.service(HttpServer.java:920)
  at org.openqa.jetty.http.HttpConnection.service(HttpConnection.java:820)
  at org.openqa.jetty.http.HttpConnection.handleNext(HttpConnection.java:986)
  at org.openqa.jetty.http.HttpConnection.handle(HttpConnection.java:837)
  at org.openqa.jetty.http.SocketListener.handleConnection(SocketListener.java:243)
  at org.openqa.jetty.util.ThreadedServer.handle(ThreadedServer.java:358)
  at org.openqa.jetty.util.ThreadPool$PoolThread.run(ThreadPool.java:537)
Caused by: org.openqa.selenium.WebDriverException: java.lang.reflect.InvocationTargetException
Build info: version: '2.52.0', revision: '4c2593c', time: '2016-02-11 19:06:42'
System info: host: '**********', ip: '**********', os.name: 'Linux', os.arch: 'amd64', os.version: '3.16.0-43-generic', java.version: '1.7.0_91'
Driver info: driver.version: unknown
  at org.openqa.selenium.remote.server.DefaultDriverProvider.callConstructor(DefaultDriverProvider.java:113)
  at org.openqa.selenium.remote.server.DefaultDriverProvider.newInstance(DefaultDriverProvider.java:97)
  at org.openqa.selenium.remote.server.DefaultDriverFactory.newInstance(DefaultDriverFactory.java:60)
  at org.openqa.selenium.remote.server.DefaultSession$BrowserCreator.call(DefaultSession.java:222)
  at org.openqa.selenium.remote.server.DefaultSession$BrowserCreator.call(DefaultSession.java:1)
  at java.util.concurrent.FutureTask.run(FutureTask.java:262)
  at org.openqa.selenium.remote.server.DefaultSession$1.run(DefaultSession.java:176)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
  at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.reflect.InvocationTargetException
  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
  at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
  at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
  at org.openqa.selenium.remote.server.DefaultDriverProvider.callConstructor(DefaultDriverProvider.java:103)
  ... 9 more
Caused by: java.lang.RuntimeException: Unable to find a free port
  at org.openqa.selenium.net.PortProber.findFreePort(PortProber.java:67)
  at org.openqa.selenium.remote.service.DriverService$Builder.build(DriverService.java:292)
  at org.openqa.selenium.chrome.ChromeDriverService.createDefaultService(ChromeDriverService.java:88)
  at org.openqa.selenium.chrome.ChromeDriver.<init>(ChromeDriver.java:138)
  ... 14 more
10:59:41.658 WARN - Exception: Unable to find a free port
@benhastings
Copy link
Author

Did some further digging and found a post that suggested amending the ip_local_port_range to not include the '1024' value included in the ProbeProperties.java file worked around the issue.

I made the change and was just able to run two tests in parallel. Again, i don't know enough of the java code to see why that made a difference, but there seems to be some needed adjustment to the logic of the free port check.

@visionscaper
Copy link

I had exactly the same exception in the selenium-standalone-server error output. It turned out that I had statements in my /etc/hosts file that I had to remove (or comment out), e.g.

127.0.0.1 my-site.local

...

192.168.100.48 localhost   

@monolithed
Copy link

monolithed commented Jan 9, 2017

I confirm that the problem is solved by removing all of the localhost specific rules from /etc/hosts

@fc
Copy link

fc commented Jan 25, 2017

If you still want to be access a server that was defined in your localhost, you can do what is described here:
http://stackoverflow.com/questions/11525703/port-forwarding-in-windows/11535395#11535395

Run this on Windows, example:

netsh interface portproxy add v4tov4 listenport=8000 listenaddress=127.0.0.1 connectport=8000 connectaddress=192.168.100.48

@DavidGDD
Copy link

I have the same problem running on Linux. So after reading the responses and a week thinking that the problem was in my localhost definition or webdriver/driver/selenium versions, i've read the PortProber.java and LinuxEphemeralPortRangeDetector.java. There is a case in createAcceptablePort in which is always returning the same port, not random.

When you have defined in the "/proc/sys/net/ipv4/ip_local_port_range" the port range 1024-65535, the method returns always 1024. So if you change your range defined in the file to 1025-65535 (or whatever) it will work returning random ports.

@p0deje p0deje added C-server and removed C-py labels Jun 7, 2017
@sanjayselectorshub
Copy link

Thank you @DavidGDD
Your solution worked like charm.

@p0deje
Copy link
Member

p0deje commented Aug 1, 2017

Is this still an issue?

@sanjayselectorshub
Copy link

@p0deje No. This is just a config change for Ubuntu. This ticket could be closed now. Thanks.

@DavidGDD
Copy link

DavidGDD commented Oct 6, 2017

It can be solved by host configuration, but i think createAcceptablePort can be improve to manage this problematic case.

@tflori
Copy link
Contributor

tflori commented Nov 29, 2018

I can't confirm that it solves the issue. We have xvfb and selenium running on a linux server using supervisor and we get this message at least twice a day with only two developers working on projects using this ci server.

In /etc/hosts we have two times localhost (ipv4 and ipv6) maybe this is an issue?

/proc/sys/net/ipv4/ip_local_port_range has 1025 65535 - I have no idea why there are 4 spaces between. In /etc/sysctl.d... it is defined with one space as you can find it anywhere else but @DavidGDD writes it with dash. So I don't know I guess for linux it is correct but maybe createAcceptablePort thinks different.

Anyway it's somehow annoying that we can not define the port somewhere and such a simple task as "find an unused port" fails because the java sources have a bug. do { try { listener = listen(random(1025, 65535)); } catch (PortInUse exception) {} } while (listener is null); is not that hard to implement.

Would a pull request be accepted?

@tflori
Copy link
Contributor

tflori commented Nov 29, 2018

I guess the problem is here:
https://github.com/SeleniumHQ/selenium/blob/master/java/client/src/org/openqa/selenium/net/LinuxEphemeralPortRangeDetector.java#L49

The problem is that the file has more than one space between the two ports. So the result will be [1025, , , , 65535] then Integer.parseInt(split[1]); throws and the default is used which leads to the same issue we had without modifying the lower port.

There we have the next problem:
https://github.com/SeleniumHQ/selenium/blob/master/java/client/src/org/openqa/selenium/net/PortProber.java#L88

First we get the highest and lowest ports from the platform and then we overwrite one or the other 1024 or 65535 (not even the constants defined before). I don't get the benefit from this if-else-statement.

@cgoldberg
Copy link
Contributor

Would a pull request be accepted?

perhaps if it includes an explanation that is more coherent than your comment, which is nearly impossible to understand.

@cgoldberg
Copy link
Contributor

@tflori if there is an issue that you have fixed, go ahead and submit a PR... commenting on the fixes in a closed issue isn't helpful.

@lock lock bot locked and limited conversation to collaborators Aug 15, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants