Repository: mardiros/aioxmlrpc

Issue:
AttributeError when server closes socket
If the XMLRPC server closes the socket, which is rather common if it's Apache, I get this error:

```
Traceback (most recent call last):
  File "/usr/share/routest/env/lib/python3.5/site-packages/aioxmlrpc/client.py", line 71, in request
    connector=self._connector, loop=self._loop)
  File "/usr/share/routest/env/lib/python3.5/site-packages/aiohttp/client.py", line 605, in __iter__
    return (yield from self._coro)
  File "/usr/share/routest/env/lib/python3.5/site-packages/aiohttp/client.py", line 161, in _request
    raise RuntimeError('Session is closed')
RuntimeError: Session is closed
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/usr/share/routest/env/lib/python3.5/site-packages/routest/resource/space/serializers.py", line 159, in check_job_status
    self.job_status = await self.lava.scheduler.job_status(self.job_id)
  File "/usr/share/routest/env/lib/python3.5/site-packages/aioxmlrpc/client.py", line 37, in __call__
    ret = yield from self.__send(self.__name, args)
  File "/usr/share/routest/env/lib/python3.5/site-packages/aioxmlrpc/client.py", line 130, in __request
    verbose=self.__verbose
  File "/usr/share/routest/env/lib/python3.5/site-packages/aioxmlrpc/client.py", line 80, in request
    raise ProtocolError(url, response.status,
AttributeError: 'NoneType' object has no attribute 'status'
```

Discussion:

Patch:
diff --git a/aioxmlrpc/client.py b/aioxmlrpc/client.py
index 35bca86..5ed292b 100644
--- a/aioxmlrpc/client.py
+++ b/aioxmlrpc/client.py
@@ -79,8 +79,14 @@ class AioTransport(xmlrpc.Transport):
             raise
         except Exception as exc:
             log.error('Unexpected error', exc_info=True)
-            raise ProtocolError(url, response.status,
-                                str(exc), response.headers)
+            if response is not None:
+                errcode = response.status
+                headers = response.headers
+            else:
+                errcode = 0
+                headers = {}
+
+            raise ProtocolError(url, errcode, str(exc), headers)
         return self.parse_response(body)
 
     def parse_response(self, body):


Test patch:
diff --git a/aioxmlrpc/tests/test_client.py b/aioxmlrpc/tests/test_client.py
index 304045d..98fef6b 100644
--- a/aioxmlrpc/tests/test_client.py
+++ b/aioxmlrpc/tests/test_client.py
@@ -128,3 +128,28 @@ class ServerProxyTestCase(TestCase):
       self.assertEqual(response, 1)
       self.assertIs(self.loop, client._loop)
       self.assertTrue(transp._connector.close.called)
+
+
+@asyncio.coroutine
+def failing_request(*args, **kwargs):
+    raise OSError
+
+
+class HTTPErrorTestCase(TestCase):
+
+    def setUp(self):
+        self.loop = asyncio.new_event_loop()
+        asyncio.set_event_loop(None)
+        self.aiohttp_request = mock.patch('aiohttp.request', new=failing_request)
+        self.aiohttp_request.start()
+
+    def tearDown(self):
+        self.aiohttp_request.stop()
+
+    def test_http_error(self):
+        from aioxmlrpc.client import ServerProxy, ProtocolError
+        client = ServerProxy('http://nonexistent/nonexistent', loop=self.loop)
+        self.assertRaises(ProtocolError,
+                          self.loop.run_until_complete,
+                          client.name.space.proxfyiedcall()
+                          )