Skip to content

Commit

Permalink
Fix some more IPv6 stuff.
Browse files Browse the repository at this point in the history
utils.is_connectable() will try every IP to which the 'host' parameter
resolves, so on dual-stack hosts it should still find the target server
even if the target is only listening on one of IPv4 and IPv6.
  • Loading branch information
juangj authored and AutomatedTester committed May 18, 2016
1 parent 5e6de50 commit 8f1721d
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 56 deletions.
4 changes: 2 additions & 2 deletions py/selenium/webdriver/common/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def service_url(self):
"""
Gets the url of the Service
"""
return "http://localhost:%d" % self.port
return "http://%s" % utils.join_host_port('localhost', self.port)

def command_line_args(self):
raise NotImplemented("This method needs to be implemented in a sub class")
Expand Down Expand Up @@ -100,7 +100,7 @@ def send_remote_shutdown_command(self):
URLError = urllib2.URLError

try:
url_request.urlopen("http://127.0.0.1:%d/shutdown" % self.port)
url_request.urlopen("%s/shutdown" % self.service_url)
except URLError:
return
count = 0
Expand Down
57 changes: 51 additions & 6 deletions py/selenium/webdriver/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"""
The Utils methods.
"""

import socket
from selenium.webdriver.common.keys import Keys

Expand All @@ -40,22 +39,68 @@ def free_port():
free_socket.close()
return port

def is_connectable(port):
def resolve_ip(host):
"""Resolve a hostname to an IP, preferring IPv4 addresses.
We prefer IPv4 so that we don't change behavior from previous IPv4-only
implementations, and because some drivers (e.g., FirefoxDriver) do not
support IPv6 connections.
:Args:
- host - A hostname.
:Returns:
A single IP address, as a string. If any IPv4 address is found, one is
returned. Otherwise, if any IPv6 address is found, one is returned. If
neither, then None is returned.
"""
try:
addrinfos = socket.getaddrinfo(host, None)
except socket.gaierror:
return None

ip = None
for family, _, _, _, sockaddr in addrinfos:
if family == socket.AF_INET:
return sockaddr[0]
if not ip and family == socket.AF_INET6:
ip = sockaddr[0]
return ip


def join_host_port(host, port):
"""Joins a hostname and port together.
This is a minimal implementation intended to cope with IPv6 literals. For
example, _join_host_port('::1', 80) == '[::1]:80'.
:Args:
- host - A hostname.
- port - An integer port.
"""
if ':' in host and not host.startswith('['):
return '[%s]:%d' % (host, port)
return '%s:%d' % (host, port)


def is_connectable(port, host="localhost"):
"""
Tries to connect to the server at port to see if it is running.
:Args:
- port: The port to connect.
"""
socket_ = None
try:
socket_ = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket_.settimeout(1)
socket_.connect(("127.0.0.1", port))
socket_ = socket.create_connection((host, port), 1)
result = True
except socket.error:
result = False
finally:
socket_.close()
if socket_:
socket_.close()
return result

def is_url_connectable(port):
Expand Down
52 changes: 4 additions & 48 deletions py/selenium/webdriver/remote/remote_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,59 +29,14 @@
import urllib2 as url_request
import urlparse as parse

from selenium.webdriver.common import utils as common_utils
from .command import Command
from .errorhandler import ErrorCode
from . import utils

LOGGER = logging.getLogger(__name__)


def _resolve_ip(host):
"""Resolve a hostname to an IP, preferring IPv4 addresses.
We prefer IPv4 so that we don't change behavior from previous IPv4-only
implementations, and because some drivers (e.g., FirefoxDriver) do not support
IPv6 connections.
:Args:
- host - A hostname.
:Returns:
A single IP address, as a string. If any IPv4 address is found, one is
returned. Otherwise, if any IPv6 address is found, one is returned. If
neither, then None is returned.
"""
try:
addrinfos = socket.getaddrinfo(host, None)
except socket.gaierror:
return None

ip = None
for family, _, _, _, sockaddr in addrinfos:
if family == socket.AF_INET:
return sockaddr[0]
if not ip and family == socket.AF_INET6:
ip = sockaddr[0]
return ip


def _join_host_port(host, port):
"""Joins a hostname and port together.
This is a minimal implementation intended to cope with IPv6 literals. For
example, _join_host_port('::1', 80) == '[::1]:80'.
:Args:
- host - A hostname.
- port - An integer port.
"""
if ':' in host and not host.startswith('['):
return '[%s]:%d' % (host, port)
return '%s:%d' % (host, port)


class Request(url_request.Request):
"""
Extends the url_request.Request to support all HTTP request types.
Expand Down Expand Up @@ -213,12 +168,13 @@ def __init__(self, remote_server_addr, keep_alive=False, resolve_ip=True):
parsed_url = parse.urlparse(remote_server_addr)
addr = ""
if parsed_url.hostname and resolve_ip:
ip = _resolve_ip(parsed_url.hostname)
ip = common_utils.resolve_ip(parsed_url.hostname)
if ip:
netloc = ip
addr = netloc
if parsed_url.port:
netloc = _join_host_port(netloc, parsed_url.port)
netloc = common_utils.join_host_port(netloc,
parsed_url.port)
if parsed_url.username:
auth = parsed_url.username
if parsed_url.password:
Expand Down

0 comments on commit 8f1721d

Please sign in to comment.