Repository: aio-libs/aiohttp

Issue:
Client use aiohttp, will be hang up ,when server return 304
As http rfc2616( http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html):
4.4 Message Length
    1.Any response message which "MUST NOT" include a message-body (such as the 1xx, 204, and 304 responses and any response to a HEAD request) is always terminated by the first empty line after the header fields, regardless of the entity-header fields present in the message.

So, server return 304 without content-length header .but aiohttp client will waiting for content-length.



Discussion:
Sorry, I don't understand what part of aiohttp is broken.
Code sample (properly formed unittest or even Pull Request are my favorites) may help very well.

Here I find out an example, the client output should be `''` instead of `'test'` because it is 304 response. `requests` package also gets `''` output. I can help to give a PR.

Server:

``` python
import socket

# Standard socket stuff:
host = ''
port = 7777
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((host, port))
sock.listen(1)

# Loop forever, listening for requests:
while True:
    csock, caddr = sock.accept()
    req = csock.recv(1024)  # get the request, 1kB max
    print(req)
    csock.send(
        b'HTTP/1.1 304 NOT MODIFIED\r\n' +
        b'Connection: keep-alive\r\n' +
        b'\r\ntest'
    )
    csock.close()
```

client:

``` python
import asyncio
import aiohttp


@asyncio.coroutine
def fetch_page(url):
    response = yield from aiohttp.request('GET', url)
    return (yield from response.read())

content = asyncio.get_event_loop().run_until_complete(
    fetch_page('http://127.0.0.1:7777')
)
print(content)
```

Got it. 
Not critical issue but worth to be fixed.


Resolving patch:
diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py
--- a/aiohttp/client_reqrep.py
+++ b/aiohttp/client_reqrep.py
@@ -583,6 +583,10 @@ def _setup_connection(self, connection):
         self.content = self.flow_control_class(
             connection.reader, loop=connection.loop)
 
+    def _need_parse_response_body(self):
+        return (self.method.lower() != 'head' and
+                self.status not in [204, 304])
+
     @asyncio.coroutine
     def start(self, connection, read_until_eof=False):
         """Start response processing."""
@@ -610,7 +614,7 @@ def start(self, connection, read_until_eof=False):
         self.headers = CIMultiDictProxy(message.headers)
 
         # payload
-        response_with_body = self.method.lower() != 'head'
+        response_with_body = self._need_parse_response_body()
         self._reader.set_parser(
             aiohttp.HttpPayloadParser(message,
                                       readall=read_until_eof,


Test patch:
diff --git a/tests/test_client_functional_newstyle.py b/tests/test_client_functional_newstyle.py
--- a/tests/test_client_functional_newstyle.py
+++ b/tests/test_client_functional_newstyle.py
@@ -119,3 +119,45 @@ def go():
             connector.close()
 
         self.loop.run_until_complete(go())
+
+    def test_HTTP_304(self):
+        @asyncio.coroutine
+        def handler(request):
+            body = yield from request.read()
+            self.assertEqual(b'', body)
+            return web.Response(status=304)
+
+        @asyncio.coroutine
+        def go():
+            _, srv, url = yield from self.create_server('GET', '/', handler)
+            connector = aiohttp.TCPConnector(loop=self.loop)
+            r = yield from client.request('GET', url,
+                                          connector=connector, loop=self.loop)
+            content = yield from r.read()
+            self.assertEqual(r.status, 304)
+            self.assertEqual(content, b'')
+            r.release()
+            connector.close()
+
+        self.loop.run_until_complete(go())
+
+    def test_HTTP_304_WITH_BODY(self):
+        @asyncio.coroutine
+        def handler(request):
+            body = yield from request.read()
+            self.assertEqual(b'', body)
+            return web.Response(body=b'test', status=304)
+
+        @asyncio.coroutine
+        def go():
+            _, srv, url = yield from self.create_server('GET', '/', handler)
+            connector = aiohttp.TCPConnector(loop=self.loop)
+            r = yield from client.request('GET', url,
+                                          connector=connector, loop=self.loop)
+            content = yield from r.read()
+            self.assertEqual(r.status, 304)
+            self.assertEqual(content, b'')
+            r.release()
+            connector.close()
+
+        self.loop.run_until_complete(go())