Skip to content

Commit 6aa6e05

Browse files
juangjAutomatedTester
authored andcommitted
Resolve IPv6 addresses in Python remote WebDriver.
The RemoteConnection resolves the hostname in the remote server's address to an IP in order to avoid the cost of looking up the IP on every request. This change enables IPv6 resolution; IPv4 is still preferred, so an IPv6 address is only returned in cases where the host is not available over IPv4.
1 parent d72d483 commit 6aa6e05

File tree

1 file changed

+53
-5
lines changed

1 file changed

+53
-5
lines changed

py/selenium/webdriver/remote/remote_connection.py

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,52 @@
3636
LOGGER = logging.getLogger(__name__)
3737

3838

39+
def _resolve_ip(host):
40+
"""Resolve a hostname to an IP, preferring IPv4 addresses.
41+
42+
We prefer IPv4 so that we don't change behavior from previous IPv4-only
43+
implementations, and because some drivers (e.g., FirefoxDriver) do not support
44+
IPv6 connections.
45+
46+
:Args:
47+
- host - A hostname.
48+
49+
:Returns:
50+
A single IP address, as a string. If any IPv4 address is found, one is
51+
returned. Otherwise, if any IPv6 address is found, one is returned. If
52+
neither, then None is returned.
53+
54+
"""
55+
try:
56+
addrinfos = socket.getaddrinfo(host, None)
57+
except socket.gaierror:
58+
return None
59+
60+
ip = None
61+
for family, _, _, _, sockaddr in addrinfos:
62+
if family == socket.AF_INET:
63+
return sockaddr[0]
64+
if not ip and family == socket.AF_INET6:
65+
ip = sockaddr[0]
66+
return ip
67+
68+
69+
def _join_host_port(host, port):
70+
"""Joins a hostname and port together.
71+
72+
This is a minimal implementation intended to cope with IPv6 literals. For
73+
example, _join_host_port('::1', 80) == '[::1]:80'.
74+
75+
:Args:
76+
- host - A hostname.
77+
- port - An integer port.
78+
79+
"""
80+
if ':' in host and not host.startswith('['):
81+
return '[%s]:%d' % (host, port)
82+
return '%s:%d' % (host, port)
83+
84+
3985
class Request(url_request.Request):
4086
"""
4187
Extends the url_request.Request to support all HTTP request types.
@@ -167,11 +213,12 @@ def __init__(self, remote_server_addr, keep_alive=False, resolve_ip=True):
167213
parsed_url = parse.urlparse(remote_server_addr)
168214
addr = ""
169215
if parsed_url.hostname and resolve_ip:
170-
try:
171-
netloc = socket.gethostbyname(parsed_url.hostname)
216+
ip = _resolve_ip(parsed_url.hostname)
217+
if ip:
218+
netloc = ip
172219
addr = netloc
173220
if parsed_url.port:
174-
netloc += ':%d' % parsed_url.port
221+
netloc = _join_host_port(netloc, parsed_url.port)
175222
if parsed_url.username:
176223
auth = parsed_url.username
177224
if parsed_url.password:
@@ -180,8 +227,9 @@ def __init__(self, remote_server_addr, keep_alive=False, resolve_ip=True):
180227
remote_server_addr = parse.urlunparse(
181228
(parsed_url.scheme, netloc, parsed_url.path,
182229
parsed_url.params, parsed_url.query, parsed_url.fragment))
183-
except socket.gaierror:
184-
LOGGER.info('Could not get IP address for host: %s' % parsed_url.hostname)
230+
else:
231+
LOGGER.info('Could not get IP address for host: %s' %
232+
parsed_url.hostname)
185233

186234
self._url = remote_server_addr
187235
if keep_alive:

0 commit comments

Comments
 (0)