Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial support for PerMessage Deflate #2273

Merged
merged 34 commits into from
Sep 27, 2017
Merged
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
00bae99
Add initial support for PerMessage Deflate
fanthos Sep 16, 2017
405be9a
Add Client websocket deflate init support.(not tested)
fanthos Sep 16, 2017
5017f01
Update parser to support compress parse
fanthos Sep 17, 2017
70ab0e9
fix for CI
fanthos Sep 17, 2017
e0a459c
add document for compress
fanthos Sep 17, 2017
52de28c
Improve compress detect logic
fanthos Sep 17, 2017
048a8f2
Complete Client websocket deflate support
fanthos Sep 17, 2017
72127c5
Merge branch 'master' into master
fanthos Sep 17, 2017
4c55414
update
fanthos Sep 17, 2017
3c6029e
sort import
fanthos Sep 17, 2017
79714a7
Add more tests
fanthos Sep 17, 2017
e51f48d
fix typo
fanthos Sep 17, 2017
f8f0f21
add 2 more tests
fanthos Sep 17, 2017
63bd563
Using regex to parse header
fanthos Sep 18, 2017
895ea75
remove dead code
fanthos Sep 18, 2017
adf31e0
coverage
fanthos Sep 18, 2017
39a15ef
Merge branch 'master' into master
asvetlov Sep 18, 2017
b45c0ba
Add reader deflate flag support.
fanthos Sep 19, 2017
a04c1c9
Merge branch 'master' of https://github.com/fanthos/aiohttp
fanthos Sep 19, 2017
c3c640e
Merge branch 'master' into master
asvetlov Sep 19, 2017
99b50d5
fixed
fanthos Sep 19, 2017
268da36
Merge branch 'master' of https://github.com/fanthos/aiohttp
fanthos Sep 19, 2017
2e961b3
Merge branch 'master' into master
asvetlov Sep 20, 2017
b91d1c7
Using array.join instead of strcat.
fanthos Sep 21, 2017
c95a7f4
Merge branch 'master' of https://github.com/fanthos/aiohttp
fanthos Sep 21, 2017
c911749
fix
fanthos Sep 22, 2017
01ea9b1
Update http_websocket.py
asvetlov Sep 22, 2017
97a2c1e
Merge branch 'master' into master
asvetlov Sep 23, 2017
c98c777
Update 2273.feature
asvetlov Sep 23, 2017
fff88de
add docs for server
fanthos Sep 23, 2017
26e7bac
Merge branch 'master' into master
asvetlov Sep 23, 2017
e5c651d
Merge branch 'master' into master
asvetlov Sep 25, 2017
8da8460
Update client.py
asvetlov Sep 26, 2017
22fb3f9
Update client_reference.rst
asvetlov Sep 26, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add Client websocket deflate init support.(not tested)
Add no takeover extension to server and client.
fanthos committed Sep 16, 2017
commit 405be9a0b18cb39c473c671452ad8057f9dda14b
33 changes: 29 additions & 4 deletions aiohttp/client.py
Original file line number Diff line number Diff line change
@@ -349,7 +349,8 @@ def ws_connect(self, url, *,
origin=None,
headers=None,
proxy=None,
proxy_auth=None):
proxy_auth=None,
compress=False):
"""Initiate websocket connection."""
return _WSRequestContextManager(
self._ws_connect(url,
@@ -363,7 +364,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, *,
@@ -377,7 +379,8 @@ def _ws_connect(self, url, *,
origin=None,
headers=None,
proxy=None,
proxy_auth=None):
proxy_auth=None,
compress=False):

if headers is None:
headers = CIMultiDict()
@@ -399,6 +402,8 @@ def _ws_connect(self, url, *,
headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = ','.join(protocols)
if origin is not None:
headers[hdrs.ORIGIN] = origin
if compress:
headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = 'permessage-deflate'

# send request
resp = yield from self.get(url, headers=headers,
@@ -457,12 +462,32 @@ def _ws_connect(self, url, *,
protocol = proto
break

# websocket compress
compress_notakeover = False
if compress:
if hdrs.SEC_WEBSOCKET_EXTENSIONS not in resp.headers:
compress = False
else:
exts = resp.headers[
hdrs.SEC_WEBSOCKET_EXTENSIONS].split(',')
for ext in exts:
params = [x.strip() for x in ext.split(';')]
if params[0] == 'permessage-deflate':
for param in params:
if param == 'client_no_context_takeover':
compress_notakeover = True
break
if compress_notakeover:
break

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=compress_notakeover)
except Exception:
resp.close()
raise
40 changes: 27 additions & 13 deletions aiohttp/http_websocket.py
Original file line number Diff line number Diff line change
@@ -7,11 +7,10 @@
import json
import random
import sys
import zlib
from enum import IntEnum
from struct import Struct

import zlib

from . import hdrs
from .helpers import NO_EXTENSIONS, noop
from .http_exceptions import HttpBadRequest, HttpProcessingError
@@ -261,7 +260,8 @@ def _feed_data(self, data):
payload_merged = b''.join(self._partial)

if compressed:
payload_merged = self._decompressobj.decompress(payload_merged + _WS_DEFLATE_TRAILING)
payload_merged = self._decompressobj.decompress(
payload_merged + _WS_DEFLATE_TRAILING)

self._partial.clear()

@@ -428,12 +428,13 @@ class WebSocketWriter:

def __init__(self, stream, *,
use_mask=False, limit=DEFAULT_LIMIT, random=random.Random(),
compress=False):
compress=False, notakeover=False):
self.stream = stream
self.writer = stream.transport
self.use_mask = use_mask
self.randrange = random.randrange
self.compress = compress
self.notakeover = notakeover
self._closing = False
self._limit = limit
self._output_size = 0
@@ -445,8 +446,10 @@ def _send_frame(self, message, opcode):
ws_logger.warning('websocket connection is closing.')

rsv = 0
if self.compress and opcode < 8:
if not self._compressobj:

# Only compress larger packets
if self.compress and opcode < 8 and len(message) > 124:
if not self._compressobj or self.notakeover:
self._compressobj = zlib.compressobj(wbits=-self.compress)

message = self._compressobj.compress(message)
@@ -592,30 +595,41 @@ def do_handshake(method, headers, stream,
hashlib.sha1(key.encode() + WS_KEY).digest()).decode())]

compress = 0
compress_notakeover = False

extensions = headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)
if extensions:
extensions = [ [s.strip() for s in s1.split(';')]
for s1 in extensions.split(',')]
extensions = [[s.strip() for s in s1.split(';')]
for s1 in extensions.split(',')]

for ext in extensions:
if ext[0] == 'permessage-deflate':
enabledext = ['permessage-deflate']
compress = 15
for param in ext[1:]:
if param.startswith('server_max_window_bits'):
compress = int(param.split('=')[1])
break
enabledext.append((
'server_max_window_bits=' + str(compress)))
elif param == 'server_no_context_takeover':
compress_notakeover = True
enabledext.append(('server_no_context_takeover'))
# Ignore Client Takeover
# elif param == 'client_no_context_takeover':
# compress_notakeover |= WSCompressNoTakeover.NT_CLIENT

response_headers.append((
hdrs.SEC_WEBSOCKET_EXTENSIONS, '; '.join(enabledext)))
break

if compress:
response_headers.append((hdrs.SEC_WEBSOCKET_EXTENSIONS, 'permessage-deflate'))

if protocol:
response_headers.append((hdrs.SEC_WEBSOCKET_PROTOCOL, protocol))

# response code, headers, None, writer, protocol
return (101,
response_headers,
None,
WebSocketWriter(stream, limit=write_buffer_size, compress=compress),
WebSocketWriter(
stream, limit=write_buffer_size,
compress=compress, notakeover=compress_notakeover),
protocol)