1# Licensed to the Software Freedom Conservancy (SFC) under one
2# or more contributor license agreements.  See the NOTICE file
3# distributed with this work for additional information
4# regarding copyright ownership.  The SFC licenses this file
5# to you under the Apache License, Version 2.0 (the
6# "License"); you may not use this file except in compliance
7# with the License.  You may obtain a copy of the License at
8#
9#   http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing,
12# software distributed under the License is distributed on an
13# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14# KIND, either express or implied.  See the License for the
15# specific language governing permissions and limitations
16# under the License.
17
18"""
19The Utils methods.
20"""
21import socket
22from selenium.webdriver.common.keys import Keys
23
24try:
25    basestring
26except NameError:
27    # Python 3
28    basestring = str
29
30
31def free_port():
32    """
33    Determines a free port using sockets.
34    """
35    free_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
36    free_socket.bind(('0.0.0.0', 0))
37    free_socket.listen(5)
38    port = free_socket.getsockname()[1]
39    free_socket.close()
40    return port
41
42
43def find_connectable_ip(host, port=None):
44    """Resolve a hostname to an IP, preferring IPv4 addresses.
45
46    We prefer IPv4 so that we don't change behavior from previous IPv4-only
47    implementations, and because some drivers (e.g., FirefoxDriver) do not
48    support IPv6 connections.
49
50    If the optional port number is provided, only IPs that listen on the given
51    port are considered.
52
53    :Args:
54        - host - A hostname.
55        - port - Optional port number.
56
57    :Returns:
58        A single IP address, as a string. If any IPv4 address is found, one is
59        returned. Otherwise, if any IPv6 address is found, one is returned. If
60        neither, then None is returned.
61
62    """
63    try:
64        addrinfos = socket.getaddrinfo(host, None)
65    except socket.gaierror:
66        return None
67
68    ip = None
69    for family, _, _, _, sockaddr in addrinfos:
70        connectable = True
71        if port:
72            connectable = is_connectable(port, sockaddr[0])
73
74        if connectable and family == socket.AF_INET:
75            return sockaddr[0]
76        if connectable and not ip and family == socket.AF_INET6:
77            ip = sockaddr[0]
78    return ip
79
80
81def join_host_port(host, port):
82    """Joins a hostname and port together.
83
84    This is a minimal implementation intended to cope with IPv6 literals. For
85    example, _join_host_port('::1', 80) == '[::1]:80'.
86
87    :Args:
88        - host - A hostname.
89        - port - An integer port.
90
91    """
92    if ':' in host and not host.startswith('['):
93        return '[%s]:%d' % (host, port)
94    return '%s:%d' % (host, port)
95
96
97def is_connectable(port, host="localhost"):
98    """
99    Tries to connect to the server at port to see if it is running.
100
101    :Args:
102     - port - The port to connect.
103    """
104    socket_ = None
105    try:
106        socket_ = socket.create_connection((host, port), 1)
107        result = True
108    except socket.error:
109        result = False
110    finally:
111        if socket_:
112            socket_.close()
113    return result
114
115
116def is_url_connectable(port):
117    """
118    Tries to connect to the HTTP server at /status path
119    and specified port to see if it responds successfully.
120
121    :Args:
122     - port - The port to connect.
123    """
124    try:
125        from urllib import request as url_request
126    except ImportError:
127        import urllib2 as url_request
128
129    try:
130        res = url_request.urlopen("http://127.0.0.1:%s/status" % port)
131        if res.getcode() == 200:
132            return True
133        else:
134            return False
135    except Exception:
136        return False
137
138
139def keys_to_typing(value):
140    """Processes the values that will be typed in the element."""
141    typing = []
142    for val in value:
143        if isinstance(val, Keys):
144            typing.append(val)
145        elif isinstance(val, int):
146            val = str(val)
147            for i in range(len(val)):
148                typing.append(val[i])
149        else:
150            for i in range(len(val)):
151                typing.append(val[i])
152    return typing
153