tungstenite-rs
tungstenite-rs copied to clipboard
Add `permessage-deflate` support, again
In #328, @kazk implemented support for the permessage-deflate extention. Implementing this extention required the ability to parse the Sec-Websocket-Extentions header, so @kazk submitted a pull request to the headers crate. However, the maintainers of the headers crate don't wan't to support this header yet, as they don't wan't to commit to any particular API. As such, they suggested that it should be implemented in this or some other crate, tweaked as necessary and potentially moved into the headers crate in the future.
This PR reverts the commit that reverted @kazk's commit that added permessage-deflate support. It also copies his implementation of SecWebsocketExtentions intended for the headers crate.
The implementation of SecWebsocketExtentions relied on some internal utilities of the headers crate. I've removed some of those dependencies and re-implemented others. I have also gated the implementation behind the existing handshake feature, as well as any code that relies on it, since it relies on the headers crate, which in turn relies on many other HTTP crates.
Blocking issues
- @nakedible-p found an error in the implementation. As I know very little about the Websocket spec, I'm unsure if I can fix it properly.
- The
headerscrate is licensed under the MIT license, while this crate uses a dual MIT+Apache license. We would need to add a proper license notice, or @kazk would need to consent to the re-licensing of his PR.
Unresolved questions
- It is impossible to use the
deflatefeature without havingtungstenitehandle the Websocket handshake. There is aWebSocket::from_raw_socket_with_extensionsmethod to allow for exactly that, but it takes anExtensionsstruct, which has no public constructor. - The only way to get such a struct is from the
WebSocketConfig::accept_offersmethod, which implements the server part of extension negotiation and requires thehandshakefeature. This method is intended to allow fortungsteniteto be integrated into web frameworks. No similar public method exists for clients, although that might not be a big issue, since a client knows whether a request will be a Websocket request up front. - To me, the
WebSocketConfig::accept_offersfeels out of place on theWebSocketConfig. We could make it a standalone function, perhaps in theextensionsmodule. - I have made the
SecWebsocketExtentionsimplementation public. This is necessary to make theWebSocketConfig::accept_offersmethod usable. This does mean that ifSecWebsocketExtentionswere to be added toheadersin the future, switching to that implementation would (I think) be a breaking change. An alternative would be to haveWebSocketConfig::accept_offerstake and return aHeaderValue, which it would parse itself. This would allow us to makeSecWebsocketExtentionsa private implementation detail. - This is a breaking change, since a new field was added to
WebSocketConfigand a new variant was added to theErrorenum. Since this field and variant depends on thedeflatefeature, I've annotated both with#[non_exhaustive]. I've done the same toProtocolError, which currently has variants gated behindhandshake. This sadly means that you cannot createWebSocketConfigusing the initializer syntax, instead you have to create an instance withWebSocketConfig::defaultand mutate it afterward. Sadly, I see no way to avoid this. It would be possible to disable#[non_exhaustive]if all relevant features are enabled.