Repository: urllib3/urllib3

Issue:
TestSNI:test_hostname_in_first_request_packet hangs
My system supports IPv6 and resolves `localhost` to `127.0.0.1`.
When I run the test suite on my system, `TestSNI:test_hostname_in_first_request_packet` hangs.
This also fail about 80 more tests with `MaxRetryError`.

### Expected Result
1. The test should pass.
2. The test should never hang.

### Actual Result
The test uses `_start_server` to create a `SocketServerThread` that binds on an IPv6 address by default (if possible):
```python
 70 HAS_IPV6_AND_DNS = _has_ipv6("localhost")
...
 90     USE_IPV6 = HAS_IPV6_AND_DNS
...
101         if self.USE_IPV6:
102             sock = socket.socket(socket.AF_INET6)
```
but the client connection might still resolve to IPv4 address and use it:
```python
 61     for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
 62         af, socktype, proto, canonname, sa = res
 63         sock = None
 64         try:
 65             sock = socket.socket(af, socktype, proto)
...
103     if HAS_IPV6:
104         family = socket.AF_UNSPEC
```
The test waits until an event is set, which is never, because the server socket never gets accepted.

### Reproduction Steps
```
$ cat noxfile-single.patch
30a31,32
>         "-o", "log_cli=true",
>         "-k", "test_hostname_in_first_request_packet",
$ patch noxfile.py noxfile-single.patch
patching file noxfile.py
$ nox --sessions test-2.7
nox > Running session test-2.7
nox > Creating virtual environment (virtualenv) using python2.7 in .nox/test-2-7
nox > pip install -r dev-requirements.txt
nox > pip install .[socks,secure,brotli]
nox > pip --version
pip 20.0.2 from /home/user/dev/urllib3/.nox/test-2-7/lib/python2.7/site-packages/pip (python 2.7)
nox > python --version
Python 2.7.17
nox > python -c import struct; print(struct.calcsize('P') * 8)
64
nox > python -m OpenSSL.debug
pyOpenSSL: 19.1.0
cryptography: 2.8
cffi: 1.14.0
cryptography's compiled against OpenSSL: OpenSSL 1.1.1d  10 Sep 2019
cryptography's linked OpenSSL: OpenSSL 1.1.1d  10 Sep 2019
Pythons's OpenSSL: OpenSSL 1.1.1  11 Sep 2018
Python executable: /home/user/dev/urllib3/.nox/test-2-7/bin/python
Python version: 2.7.17 (default, Nov  7 2019, 10:07:09)
[GCC 7.4.0]
Platform: linux2
sys.path: ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-x86_64-linux-gnu', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/home/user/dev/urllib3/.nox/test-2-7/lib/python2.7/site-packages']
nox > coverage run --parallel-mode -m pytest -o log_cli=true -k test_hostname_in_first_request_packet -r a --tb=native --no-success-flaky-report test/
==================================== test session starts ====================================
platform linux2 -- Python 2.7.17, pytest-4.6.6, py-1.8.1, pluggy-0.13.1
rootdir: /home/user/dev/urllib3, inifile: setup.cfg
plugins: flaky-3.6.1, timeout-1.3.3
collected 1342 items / 1339 deselected / 3 selected

test/contrib/test_pyopenssl.py::TestSNI::test_hostname_in_first_request_packet
[hangs]
```

### System Information
```
$ git rev-parse --short HEAD
eee53a69
$ cat /etc/hosts
127.0.0.1       localhost
127.0.1.1       ubuntu

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
```

### Fix
I can add a timeout to the test, but that's a solution to the root problem.
I suggest that `ServerSocketThread` treat a system where `localhost` does not resolve to `::1` as having insane IPv6 configuration and use the current IPv4 fallback mechanism.


Discussion:

Resolving patch:
diff --git a/dummyserver/server.py b/dummyserver/server.py
--- a/dummyserver/server.py
+++ b/dummyserver/server.py
@@ -40,6 +40,20 @@
 DEFAULT_CA_KEY = os.path.join(CERTS_PATH, "cacert.key")
 
 
+def _resolves_to_ipv6(host):
+    """ Returns True if the system resolves host to an IPv6 address by default. """
+    resolves_to_ipv6 = False
+    try:
+        for res in socket.getaddrinfo(host, None, socket.AF_UNSPEC):
+            af, _, _, _, _ = res
+            if af == socket.AF_INET6:
+                resolves_to_ipv6 = True
+    except socket.gaierror:
+        pass
+
+    return resolves_to_ipv6
+
+
 def _has_ipv6(host):
     """ Returns True if the system can bind an IPv6 address. """
     sock = None
@@ -54,7 +68,7 @@ def _has_ipv6(host):
         try:
             sock = socket.socket(socket.AF_INET6)
             sock.bind((host, 0))
-            has_ipv6 = True
+            has_ipv6 = _resolves_to_ipv6("localhost")
         except Exception:
             pass
 


Test patch:
diff --git a/test/with_dummyserver/test_socketlevel.py b/test/with_dummyserver/test_socketlevel.py
--- a/test/with_dummyserver/test_socketlevel.py
+++ b/test/with_dummyserver/test_socketlevel.py
@@ -104,7 +104,8 @@ def socket_handler(listener):
                 pool.request("GET", "/", retries=0)
             except MaxRetryError:  # We are violating the protocol
                 pass
-            done_receiving.wait()
+            successful = done_receiving.wait(LONG_TIMEOUT)
+            assert successful, "Timed out waiting for connection accept"
             assert (
                 self.host.encode("ascii") in self.buf
             ), "missing hostname in SSL handshake"