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

permessage-deflate: implement context takeover #26

Merged
merged 4 commits into from
Jul 4, 2019

Conversation

pushkarnk
Copy link
Contributor

@pushkarnk pushkarnk commented May 28, 2019

This is an an implementation of context takeover.

For every WebSocket connection on which compression using permessage-deflate is negotiated, we configure a compressor and a decompressor on the channel pipeline. Context takeover is then implemented as:

  1. If the client requests for "no client context takeover", we agree. The decompressor reuses context otherwise.
  2. If the client requests for "no server context takeover", we agree. The compressor reuses the context otherwise.
  3. A context reuse implies reusing the wrapped zlib stream without initialising it.
  4. A context reuse also implies the wrapped zlib stream is never deinitialized.

@pushkarnk pushkarnk force-pushed the context-takeover-impl branch from e915cbf to b05842e Compare May 28, 2019 19:24
@pushkarnk pushkarnk marked this pull request as ready for review May 29, 2019 07:56
@pushkarnk pushkarnk force-pushed the context-takeover-impl branch from b05842e to 59cb531 Compare May 29, 2019 18:14
@pushkarnk
Copy link
Contributor Author

Seeing some autobahn failures. Investigating.

@pushkarnk
Copy link
Contributor Author

pushkarnk commented May 30, 2019

For future reference: every compression test in autobahn sends this header
permessage-deflate; client_no_context_takeover; client_max_window_bits

@pushkarnk
Copy link
Contributor Author

pushkarnk commented May 30, 2019

A regression was introduced in release 2.0.1. We've stopped running compression test in the CI because some tests take a really long time and Travis fails.

@pushkarnk
Copy link
Contributor Author

Submitted #28

@pushkarnk pushkarnk force-pushed the context-takeover-impl branch from 59cb531 to 7e108ba Compare June 10, 2019 16:38
@pushkarnk
Copy link
Contributor Author

#28 seems to be fixing all the issues we saw. I ran the compression tests from autobahn on Linux and all of them pass.

@pushkarnk
Copy link
Contributor Author

@bridger I think this solves the problem you reported in #22 Can you please test your app with this branch, whenever possible? Could you also do a quick code review? Thanks!

Pushkar Kulkarni added 2 commits June 20, 2019 10:11
The compression format of the output produced by zlibs deflater is decided
by the windowBits values it is configured with. We used positive windowBits
values which, according to the zlib manual, emits the zlib stream starting
with a zlib header. WebSockets use raw deflate streams, and not zlib streams.
Consequently, we had to strip the zlib header.

The deflater can be configured to emit a raw deflate format by using negative
values for the windowBits. With this in place, there's no zlib header in the
output stream.
The server_max_window_bits value sent by the client in the negotiation
offer is used to configure the deflater on the server. This acceptance
is also notified to the client by returning the server_max_window_bits
header in the negotiation response.

A special case arises when we receive server_max_window_bits = 8.
There's an open zlib issue with the window size of 256 (windoBits=8).
For zlib streams, the library silently changes the windowBits to 9 and
informs the inflater via the zlib header. However, this apparent hack
is not feasible for raw deflate streams, which are used in WebSockets.
To take care of this fact, zlib has been patched to reject a window
size of 256, via a deflateInit2() failure.

Details here: madler/zlib#171

As a result, we need to handle a window size of 256 (windowBits=8) as a
special case. We silently change it to 9 and inform the client via a
suitable header in the negotiation response.
@pushkarnk pushkarnk force-pushed the context-takeover-impl branch from 5431beb to c9d9b44 Compare June 20, 2019 05:35
Without context takeover, the deflater and inflater are initialized and closed
on every message. However, with context takeover, the deflater and inflater
persist for the entire lifetime of the connection. They need to be closed on a
connection close.

The deflater is on a ChannelOutboundHandler and must be closed when the
WebSocketConnections issues a channel.close(). The inflater is on a
ChannelInboundHandler, it must be closed when we receive a `closeConnection`
frame from the remote peer.
Copy link
Contributor

@bridger bridger left a comment

Choose a reason for hiding this comment

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

This looks great! I tried it out with my app (which as a native iOS client and a web client) and it worked in all cases that I tried. Thank you for all the fixes. I'm excited that compression should make the app load much faster.

@pushkarnk
Copy link
Contributor Author

Thanks @bridger for confirming! And thanks for raising all those issues, its helped us fix some fundamental bugs :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants