Repository: internap/python-hostsresolver

Issue:
gaierror when using docker-py
Hello!

I stumbled on a little problem while using the host resolver and docker-py at the same time:
```
ConnectionError: ('Connection aborted.', gaierror(8, 'nodename nor servname provided, or not known'))
```

Seems like docker-py speaks to the docker engine via unix socket and have a unix socket such as `/var/run/docker.sock` so when this pass into gethostbyname (https://github.com/internap/python-hostsresolver/blob/master/hostsresolver/cache.py#L26) and it's not in the host cache, it invokes socket.gethostbyname which raises a gaierror.... BUT if you pass the socket to socket.connect it will be fine.  

Fix incoming soon :) 

Discussion:

Patch:
diff --git a/hostsresolver/cache.py b/hostsresolver/cache.py
index 1318ecb..45c6ad6 100644
--- a/hostsresolver/cache.py
+++ b/hostsresolver/cache.py
@@ -41,13 +41,19 @@ def create_connection(address, *args, **kwargs):
 
 
 class SocketType(_SocketType):
+    def _use_host_cache(self, address):
+        try:
+            return (gethostbyname(address[0]), address[1])
+        except socket.gaierror:
+            # The address[0] could be an unknown host to socket.gethostbyname
+            # but known to socket.connect, such cases include unix sockets.
+            return address
+
     def connect(self, address):
-        new_address = (gethostbyname(address[0]), address[1])
-        return _SocketType.connect(self, new_address)
+        return _SocketType.connect(self, self._use_host_cache(address))
 
     def connect_ex(self, address):
-        new_address = (gethostbyname(address[0]), address[1])
-        return _SocketType.connect_ex(self, new_address)
+        return _SocketType.connect_ex(self, self._use_host_cache(address))
 
 
 def update(hosts):


Test patch:
diff --git a/tests/test_cache.py b/tests/test_cache.py
index 612edbe..ecb0024 100644
--- a/tests/test_cache.py
+++ b/tests/test_cache.py
@@ -15,6 +15,8 @@
 import socket
 import unittest
 
+import mock
+
 from hostsresolver import cache
 
 
@@ -103,3 +105,33 @@ class TestGetAddrInfo(unittest.TestCase):
              (10, 1, 6, '', ('2001:4860:4860::8888', 53, 0, 0)),
              (10, 2, 17, '', ('2001:4860:4860::8888', 53, 0, 0)),
              (10, 3, 0, '', ('2001:4860:4860::8888', 53, 0, 0))])
+
+
+class TestConnect(unittest.TestCase):
+    def setUp(self):
+        cache.install()
+
+    def tearDown(self):
+        cache.uninstall()
+
+    @mock.patch("hostsresolver.cache._gethostbyname")
+    @mock.patch("hostsresolver.cache._SocketType")
+    def test_uses_real_gethostname_result(self, socket_mock, gethost_mock):
+        gethost_mock.return_value = "1.1.1.1"
+
+        s = socket.SocketType()
+        s.connect(("some_url", 123))
+
+        socket_mock.connect.assert_called_once_with(s, ("1.1.1.1", 123))
+        gethost_mock.assert_called_once_with("some_url")
+
+    @mock.patch("hostsresolver.cache._gethostbyname")
+    @mock.patch("hostsresolver.cache._SocketType")
+    def test_ignore_unresolved_hosts_and_pass_them_to_connect(self, socket_mock, gethost_mock):
+        gethost_mock.side_effect = socket.gaierror
+
+        s = socket.SocketType()
+        s.connect(("/var/run/something", 123))
+
+        socket_mock.connect.assert_called_once_with(s, ("/var/run/something", 123))
+        gethost_mock.assert_called_once_with("/var/run/something")