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

Conversation

fanthos
Copy link
Contributor

@fanthos fanthos commented Sep 16, 2017

What do these changes do?

Add initial support for WebSocket deflate compress.
Right now both server and client are supported.

Are there changes in behavior for the user?

No

Related issue number

#673

Checklist

  • I think the code is well written
  • Unit tests for the changes exist
  • Documentation reflects the changes
  • If you provide code modification, please add yourself to CONTRIBUTORS.txt
    • The format is <Name> <Surname>.
    • Please keep alphabetical order, the file is sorted by names.
  • Add a new news fragment into the changes folder
    • name it <issue_id>.<type> for example (588.bug)
    • if you don't have an issue_id change it to the pr id after creating the pr
    • ensure type is one of the following:
      • .feature: Signifying a new feature.
      • .bugfix: Signifying a bug fix.
      • .doc: Signifying a documentation improvement.
      • .removal: Signifying a deprecation or removal of public API.
      • .misc: A ticket has been closed, but it is not of interest to users.
    • Make sure to use full sentences with correct case and punctuation, for example: "Fix issue with non-ascii contents in doctest text files."

@fafhrd91
Copy link
Member

this is good! but could you add unit and functional tests

@fanthos
Copy link
Contributor Author

fanthos commented Sep 17, 2017

The file upload tests in test_client_functional.py keep failing with HTTP 500 on my device.
I am using Windows 10 1703 with Python 3.6.2.

@codecov-io
Copy link

codecov-io commented Sep 17, 2017

Codecov Report

Merging #2273 into master will decrease coverage by <.01%.
The diff coverage is 97.77%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2273      +/-   ##
==========================================
- Coverage   97.24%   97.24%   -0.01%     
==========================================
  Files          39       39              
  Lines        8096     8201     +105     
  Branches     1411     1439      +28     
==========================================
+ Hits         7873     7975     +102     
- Misses         97       98       +1     
- Partials      126      128       +2
Impacted Files Coverage Δ
aiohttp/hdrs.py 100% <100%> (ø) ⬆️
aiohttp/http_websocket.py 98.49% <97.75%> (-0.25%) ⬇️
examples/web_ws.py 94.86% <0%> (-0.32%) ⬇️
examples/client_ws.py 96.44% <0%> (+0.11%) ⬆️
tests/autobahn/client.py 94.72% <0%> (+0.21%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f8f83a8...22fb3f9. Read the comment docs.

Copy link
Member

@asvetlov asvetlov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not review carefully but left some notes.

@@ -380,7 +384,8 @@ def _ws_connect(self, url, *,
origin=None,
headers=None,
proxy=None,
proxy_auth=None):
proxy_auth=None,
compress=0):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use compress=15 as initial value and don't accept True

@@ -26,6 +26,8 @@
from .helpers import (PY_35, CeilTimeout, TimeoutHandle, _BaseCoroMixin,
deprecated_noop, sentinel)
from .http import WS_KEY, WebSocketReader, WebSocketWriter
from .http_websocket import extensions_gen as ws_ext_gen
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you rename imports?
Just use ws_ext_gen and ws_ext_parse in http_websocket.py.

client_notakeover=False):
# compress wbit 8 does not support in zlib
if compress < 9 or compress > 15:
return False
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Raise ValueError here.

for ext in extensions:
if ext.startswith('permessage-deflate'):
compress = 15
for param in [s.strip() for s in ext.split(';')][1:]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe regex parse is more robust.

@@ -420,6 +424,11 @@ def _ws_connect(self, url, *,
headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = ','.join(protocols)
if origin is not None:
headers[hdrs.ORIGIN] = origin
if compress:
if compress is True:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check is not needed: let's assume compress is always integer.

Copy link
Member

@asvetlov asvetlov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for non-instant review.
The PR is complex, the RFC https://tools.ietf.org/html/rfc7692 is complicated.

Everything looks good but please fix my notes.
Also please make https://codecov.io/gh/aio-libs/aiohttp/pull/2273/diff green.
The PR changes default websockets behavior, I have a feeling that is very important change

if compress < 9 or compress > 15:
raise ValueError('Compress wbits must between 9 and 15, '
'zlib does not support wbits=8')
enabledext = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use a list: enabledext = [].
Append an element to the list every time if needed.
Return '; '.join(enabledext) -- or just a list maybe, do joining in caller.
Join is recommended way for performing such operations, it's faster and more memory effective.
Take a look on https://github.com/python/cpython/blob/master/Lib/asyncio/base_futures.py#L54-L71 for inspiration

)
if compress == 0:
pass
elif compress == -1:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could live with this code style but it looks too C-ish.
What's about raising internal HandshakeError exception defined in http_websocket.py as

class HandshakeError(Exception):
     pass

try:
    raise HandshakeError('Invalid window size')
except HandshakeError as exc:
    raise WSServerHandshakeError(
                            resp.request_info,
                            resp.history,
                            message=exc.args[0],
                            code=resp.status,
                            headers=resp.headers)

compress, notakeover = ws_ext_parse(
resp.headers[hdrs.SEC_WEBSOCKET_EXTENSIONS]
)
if compress == 0:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What the check does?

# websocket compress
notakeover = False
if compress:
if hdrs.SEC_WEBSOCKET_EXTENSIONS in resp.headers:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use resp.headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS), do getting the header only once.

class WSHandshakeError(Exception):
"""WebSocket protocol handshake error."""

def __init__(self, message):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constructor from parent class is ok, isn't it?
Drop __init__ function.

@@ -524,6 +524,8 @@ The client session supports the context manager protocol for self closing.
:param aiohttp.BasicAuth proxy_auth: an object that represents proxy HTTP
Basic Authorization (optional)

:param bool compress: Enable Per-Message Compress Extension support
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update method signature too.

@asvetlov
Copy link
Member

Last question: should we enable websockets by default?
It could break existing programs. But we can fix bugs quickly I hope.
As an option for smooth transition we could use 0 compression by default in next release and switch to 15 in after next.
Thoughts?

@fanthos
Copy link
Contributor Author

fanthos commented Sep 23, 2017

I think we can disable compression for client side by default, maybe need more test with other server software to make sure they are compatible.
The server code is tested with Firefox and Chrome and works as excepted.

@asvetlov
Copy link
Member

@fanthos sounds good

@asvetlov
Copy link
Member

WebSocketResponse(..., compress=True) should be documented

@asvetlov asvetlov merged commit c988fd0 into aio-libs:master Sep 27, 2017
@asvetlov
Copy link
Member

Thanks!

@lock
Copy link

lock bot commented Oct 28, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a [new issue] for related bugs.
If you feel like there's important points made in this discussion, please include those exceprts into that [new issue].
[new issue]: https://github.com/aio-libs/aiohttp/issues/new

@lock lock bot added the outdated label Oct 28, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Oct 28, 2019
@psf-chronographer psf-chronographer bot added the bot:chronographer:provided There is a change note present in this PR label Oct 28, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bot:chronographer:provided There is a change note present in this PR outdated
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants