Skip to content

Commit

Permalink
Add initial support for PerMessage Deflate (#2273)
Browse files Browse the repository at this point in the history
* Add initial support for PerMessage Deflate

* Add Client websocket deflate init support.(not tested)
Add no takeover extension to server and client.

* Update parser to support compress parse

* fix for CI
TODO: add tests

* add document for compress

* Improve compress detect logic
Add parser test for rsv1

* Complete Client websocket deflate support
Add tests

* update

* sort import

* Add more tests
Client deflate support should now more complete
fix linting

* fix typo

* add 2 more tests

* Using regex to parse header
Fix client code
Add more tests

* remove dead code

* coverage

* Add reader deflate flag support.
If reader created with compress=0, it will raise PROTOCOL_ERROR if rvb1 = 1.

* fixed

* Using array.join instead of strcat.
Using exception instead of return value.

* fix

* Update http_websocket.py

* Update 2273.feature

* add docs for server

* Update client.py

Disable websocket compression on client side by default

* Update client_reference.rst
  • Loading branch information
fanthos authored and asvetlov committed Sep 27, 2017
1 parent f8f83a8 commit c988fd0
Show file tree
Hide file tree
Showing 14 changed files with 695 additions and 70 deletions.
1 change: 1 addition & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Arthur Darcet
Ben Bader
Benedikt Reinartz
Boris Feld
Boyi Chen
Brett Cannon
Brian C. Lane
Brian Muller
Expand Down
39 changes: 34 additions & 5 deletions aiohttp/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from .helpers import (PY_35, CeilTimeout, ProxyInfo, TimeoutHandle,
_BaseCoroMixin, deprecated_noop, sentinel)
from .http import WS_KEY, WebSocketReader, WebSocketWriter
from .http_websocket import WSHandshakeError, ws_ext_gen, ws_ext_parse
from .streams import FlowControlDataQueue


Expand Down Expand Up @@ -370,7 +371,8 @@ def ws_connect(self, url, *,
origin=None,
headers=None,
proxy=None,
proxy_auth=None):
proxy_auth=None,
compress=0):
"""Initiate websocket connection."""
return _WSRequestContextManager(
self._ws_connect(url,
Expand All @@ -384,7 +386,8 @@ def ws_connect(self, url, *,
origin=origin,
headers=headers,
proxy=proxy,
proxy_auth=proxy_auth))
proxy_auth=proxy_auth,
compress=compress))

@asyncio.coroutine
def _ws_connect(self, url, *,
Expand All @@ -398,7 +401,8 @@ def _ws_connect(self, url, *,
origin=None,
headers=None,
proxy=None,
proxy_auth=None):
proxy_auth=None,
compress=0):

if headers is None:
headers = CIMultiDict()
Expand All @@ -420,6 +424,9 @@ def _ws_connect(self, url, *,
headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = ','.join(protocols)
if origin is not None:
headers[hdrs.ORIGIN] = origin
if compress:
extstr = ws_ext_gen(compress=compress)
headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr

# send request
resp = yield from self.get(url, headers=headers,
Expand Down Expand Up @@ -478,12 +485,32 @@ def _ws_connect(self, url, *,
protocol = proto
break

# websocket compress
notakeover = False
if compress:
compress_hdrs = resp.headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)
if compress_hdrs:
try:
compress, notakeover = ws_ext_parse(compress_hdrs)
except WSHandshakeError as exc:
raise WSServerHandshakeError(
resp.request_info,
resp.history,
message=exc.args[0],
code=resp.status,
headers=resp.headers)
else:
compress = 0
notakeover = False

proto = resp.connection.protocol
reader = FlowControlDataQueue(
proto, limit=2 ** 16, loop=self._loop)
proto.set_parser(WebSocketReader(reader), reader)
resp.connection.writer.set_tcp_nodelay(True)
writer = WebSocketWriter(resp.connection.writer, use_mask=True)
writer = WebSocketWriter(
resp.connection.writer, use_mask=True,
compress=compress, notakeover=notakeover)
except Exception:
resp.close()
raise
Expand All @@ -497,7 +524,9 @@ def _ws_connect(self, url, *,
autoping,
self._loop,
receive_timeout=receive_timeout,
heartbeat=heartbeat)
heartbeat=heartbeat,
compress=compress,
client_notakeover=notakeover)

def _prepare_headers(self, headers):
""" Add default headers and transform it to CIMultiDict
Expand Down
13 changes: 12 additions & 1 deletion aiohttp/client_ws.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ class ClientWebSocketResponse:

def __init__(self, reader, writer, protocol,
response, timeout, autoclose, autoping, loop, *,
receive_timeout=None, heartbeat=None):
receive_timeout=None, heartbeat=None,
compress=0, client_notakeover=False):
self._response = response
self._conn = response.connection

Expand All @@ -35,6 +36,8 @@ def __init__(self, reader, writer, protocol,
self._loop = loop
self._waiting = None
self._exception = None
self._compress = compress
self._client_notakeover = client_notakeover

self._reset_heartbeat()

Expand Down Expand Up @@ -82,6 +85,14 @@ def close_code(self):
def protocol(self):
return self._protocol

@property
def compress(self):
return self._compress

@property
def client_notakeover(self):
return self._client_notakeover

def get_extra_info(self, name, default=None):
"""extra info from connection transport"""
try:
Expand Down
1 change: 1 addition & 0 deletions aiohttp/hdrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
SEC_WEBSOCKET_ACCEPT = istr('SEC-WEBSOCKET-ACCEPT')
SEC_WEBSOCKET_VERSION = istr('SEC-WEBSOCKET-VERSION')
SEC_WEBSOCKET_PROTOCOL = istr('SEC-WEBSOCKET-PROTOCOL')
SEC_WEBSOCKET_EXTENSIONS = istr('SEC-WEBSOCKET-EXTENSIONS')
SEC_WEBSOCKET_KEY = istr('SEC-WEBSOCKET-KEY')
SEC_WEBSOCKET_KEY1 = istr('SEC-WEBSOCKET-KEY1')
SERVER = istr('SERVER')
Expand Down
Loading

0 comments on commit c988fd0

Please sign in to comment.