Repository: aio-libs/aiohttp

Issue:
Index out of range in create_wsgi_environ
Gunicorn version 19.4.1
aiohttp version 0.19.0
nginx 1.4.6
Ubuntu 14.04

> [2015-12-03 13:24:27 -0500] [12308] [ERROR] Error handling request
> Traceback (most recent call last):
> File "/home/vagrant/.virtualenvs/mighty/lib/python3.4/site-packages/aiohttp/server.py", line 262, in start
>    yield from self.handle_request(message, payload)
>  File "/home/vagrant/.virtualenvs/mighty/lib/python3.4/site-packages/aiohttp/wsgi.py", line 127, in handle_request
>    environ = self.create_wsgi_environ(message, payload)
>  File "/home/vagrant/.virtualenvs/mighty/lib/python3.4/site-packages/aiohttp/wsgi.py", line 93, in     create_wsgi_environ
>    environ['REMOTE_ADDR'] = remote[0]
> IndexError: index out of range

This change occurred in between the two releases https://github.com/KeepSafe/aiohttp/commit/d904071c30737baa7189ed1c842c76954274c129#diff-58081209a1b73c318d74d4fe7ad41460L91
to 
https://github.com/KeepSafe/aiohttp/commit/eb54e6c010f55c02e49a36cb2e74b077f8aa77e4#diff-58081209a1b73c318d74d4fe7ad41460R92
which may be related to the issue

This gunicorn issue from June 2014 is somewhat similar https://github.com/benoitc/gunicorn/issues/785.

Downgrading to aiohttp 0.18.4 fixes the issue.



Discussion:

Resolving patch:
diff --git a/aiohttp/wsgi.py b/aiohttp/wsgi.py
--- a/aiohttp/wsgi.py
+++ b/aiohttp/wsgi.py
@@ -88,16 +88,22 @@ def create_wsgi_environ(self, message, payload):
         # http://www.ietf.org/rfc/rfc3875
 
         remote = self.transport.get_extra_info('peername')
-        environ['REMOTE_ADDR'] = remote[0]
-        environ['REMOTE_PORT'] = remote[1]
-
-        sockname = self.transport.get_extra_info('sockname')
-        environ['SERVER_PORT'] = str(sockname[1])
-        host = message.headers.get("HOST", None)
-        if host:
-            environ['SERVER_NAME'] = host.split(":")[0]
+        if remote:
+            environ['REMOTE_ADDR'] = remote[0]
+            environ['REMOTE_PORT'] = remote[1]
+            _host, port = self.transport.get_extra_info('sockname')
+            environ['SERVER_PORT'] = str(port)
+            host = message.headers.get("HOST", None)
+            # SERVER_NAME should be set to value of Host header, but this
+            # header is not required. In this case we shoud set it to local
+            # address of socket
+            environ['SERVER_NAME'] = host.split(":")[0] if host else _host
         else:
-            environ['SERVER_NAME'] = sockname[0]
+            # Dealing with unix socket, so request was received from client by
+            # upstream server and this data may be found in the headers
+            for header in ('REMOTE_ADDR', 'REMOTE_PORT',
+                           'SERVER_NAME', 'SERVER_PORT'):
+                environ[header] = message.headers.get(header, '')
 
         path_info = uri_parts.path
         if script_name:


Test patch:
diff --git a/tests/test_wsgi.py b/tests/test_wsgi.py
--- a/tests/test_wsgi.py
+++ b/tests/test_wsgi.py
@@ -275,3 +275,16 @@ def test_http_1_0_no_host(self):
         environ = self._make_one()
         self.assertEqual(environ['SERVER_NAME'], '2.3.4.5')
         self.assertEqual(environ['SERVER_PORT'], '80')
+
+    def test_unix_socket(self):
+        self.transport.get_extra_info = unittest.mock.Mock(return_value=None)
+        headers = multidict.MultiDict({
+            'SERVER_NAME': '1.2.3.4', 'SERVER_PORT': '5678',
+            'REMOTE_ADDR': '4.3.2.1', 'REMOTE_PORT': '8765'})
+        self.message = protocol.RawRequestMessage(
+            'GET', '/', (1, 0), headers, True, 'deflate')
+        environ = self._make_one()
+        self.assertEqual(environ['SERVER_NAME'], '1.2.3.4')
+        self.assertEqual(environ['SERVER_PORT'], '5678')
+        self.assertEqual(environ['REMOTE_ADDR'], '4.3.2.1')
+        self.assertEqual(environ['REMOTE_PORT'], '8765')