Repository: aio-libs/aiohttp

Issue:
Webscockets client timeout when connecting to Kodi
## Long story short

There is an incompatibility causing a timeout in aiohttp when connecting to the Kodi websockets interface. Kodi is working with the `websockets` python library. This is very possibly some poor behavior on Kodi's part.

Background: https://github.com/home-assistant/home-assistant/pull/4951#discussion_r93008906

## Expected behaviour

Websocket connection is opened successfully.

## Actual behaviour

aiohttp is hanging here: https://github.com/KeepSafe/aiohttp/blob/v1.2.0/aiohttp/client.py#L302 In the logs, `HTTP/1.1 400 Bad Request` is shown.

## Steps to reproduce

```python
import aiohttp
import asyncio

@asyncio.coroutine
def main(loop):
    client = aiohttp.ClientSession(loop=loop)
    ws = yield from client.ws_connect('ws://192.168.1.193:9090/jsonrpc')
    while True:
        msg = yield from ws.receive()
        if msg.type == aiohttp.WSMsgType.TEXT:
            print(msg)
        elif msg.type == aiohttp.WSMsgType.CLOSED:
            break
        elif msg.type == aiohttp.WSMsgType.ERROR:
            break

loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
```

## Your environment
Python 3.4.3
aiohttp 1.2.0
Kodi 16.1


Discussion:
To add to this, `HTTP/1.1 400 Bad Request` may be due to the payload created in the steps being incorrect.
Try `'http://192.168.1.193:9090/jsonrpc'` URL
@asvetlov no dice :(
Just as an FYI for anyone else looking into this, `aiohttp` is ultimately freezing up here, https://github.com/KeepSafe/aiohttp/blob/v1.2.0/aiohttp/streams.py#L475 meaning that the DataQueue isn't getting data fed. Below is a stacktrace for debugging purposes. Line numbers might be off by a few because of debugging print statements.

```
Traceback (most recent call last):
  File "test.py", line 18, in <module>
    loop.run_until_complete(main(loop))
  File "/usr/lib64/python3.4/asyncio/base_events.py", line 373, in run_until_complete
    return future.result()
  File "/usr/lib64/python3.4/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/usr/lib64/python3.4/asyncio/tasks.py", line 240, in _step
    result = coro.send(None)
  File "test.py", line 7, in main
    ws = yield from client.ws_connect('ws://192.168.1.193:9090/jsonrpc')
  File "/home/adam/src/python/aiohttp/aiohttp/client.py", line 537, in __iter__
    resp = yield from self._coro
  File "/home/adam/src/python/aiohttp/aiohttp/client.py", line 313, in _ws_connect
    proxy_auth=proxy_auth)
  File "/home/adam/src/python/aiohttp/aiohttp/client.py", line 537, in __iter__
    resp = yield from self._coro
  File "/home/adam/src/python/aiohttp/aiohttp/client.py", line 187, in _request
    yield from resp.start(conn, read_until_eof)
  File "/home/adam/src/python/aiohttp/aiohttp/client_reqrep.py", line 616, in start
    message = yield from httpstream.read()
  File "/home/adam/src/python/aiohttp/aiohttp/streams.py", line 644, in read
    result = yield from super().read()
  File "/home/adam/src/python/aiohttp/aiohttp/streams.py", line 477, in read
    raise Exception("TEST")
```

Resolving patch:
diff --git a/aiohttp/protocol.py b/aiohttp/protocol.py
--- a/aiohttp/protocol.py
+++ b/aiohttp/protocol.py
@@ -684,7 +684,7 @@ def _add_default_headers(self):
         # set the connection header
         connection = None
         if self.upgrade:
-            connection = 'upgrade'
+            connection = 'Upgrade'
         elif not self.closing if self.keepalive is None else self.keepalive:
             if self.version == HttpVersion10:
                 connection = 'keep-alive'


Test patch:
diff --git a/tests/test_protocol.py b/tests/test_protocol.py
--- a/tests/test_protocol.py
+++ b/tests/test_protocol.py
@@ -216,7 +216,7 @@ def test_default_headers_connection_upgrade(transport):
     msg.upgrade = True
     msg._add_default_headers()
 
-    assert msg.headers['Connection'] == 'upgrade'
+    assert msg.headers['Connection'] == 'Upgrade'
 
 
 def test_default_headers_connection_close(transport):