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

WIP: WebSocket Compression #48470

Closed
wants to merge 52 commits into from
Closed

Conversation

zlatanov
Copy link
Contributor

Creating this WIP Pull Request so others can give feedback. The WebSocket per message compression is fully implemented. I've written several tests and will add more to make sure everything is covered.

I would like to write a few performance tests to make sure there are no regressions and to find out if/where we could optimize if needed.

For the ClientWebSocket I need a working HTTP Server that supports WebSocket compression in order to write integration tests (outer loop). I see that there are some external servers and endpoints that are being used, but I don't know who is responsible for them and how can we add such support before AspNetCore team implements it?

A note to the owners of System.IO.Compression - as discussed with @CarnaViire, I've moved two of the files (ZLibNative.ZStream.cs and ZLibNative.cs) to Common so I can use the Interop.zlib.cs.

Any feedback and/or suggestions are welcomed.

Closes #31088

/cc @CarnaViire, @karelz, @scalablecory, @davidfowl

zlatanov and others added 28 commits February 12, 2021 11:11
…supported platforms because we now depend on native API.
…late was used and a message were to be sent with multiple frames the implementation would have used seprate deflates for each of the frames.
… websocket handles incoming data where we could easily just use a memory stream.
…mStream method in order to keep the name for the ArgumentException that would happen the same as it was.
…nger a valid window bits value, because the underlying gzip library doesn't really support it.
… multiple frames and reading with smaller output buffers.
…xpected to happen but doesn't and we don't have timeouts.
…te flag in the header if decoder is not configured.
…nt sizes. Fixed a bug when receiving 0 byte message. Fixed another bug where the payload length wasn't reduced after receiving non compressed message.
… during client initiated close, the server would not respond with close message and the client's close task would hang until timeout or server closes the connection.
Copy link
Member

@Tratcher Tratcher left a comment

Choose a reason for hiding this comment

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

Just some initial feedback, I haven't gotten to the good parts yet.

@@ -137,29 +137,34 @@ public static ArraySegment<byte> CreateServerBuffer(int receiveBufferSize)
[UnsupportedOSPlatform("browser")]
public static WebSocket CreateFromStream(Stream stream, bool isServer, string? subProtocol, TimeSpan keepAliveInterval)
{
if (stream == null)
if (!WebSocketCreationOptions.IsKeepAliveValid(keepAliveInterval))
Copy link
Member

Choose a reason for hiding this comment

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

Why is KeelAliveInterval validated twice, here and on WebSocketCreationOptions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did this for backward compatibility. The issue is that when the KeepAliveInterval is validated in WebSocketCreationOptions, when invalid an ArgumentOutOfRangeException for paramName "KeepAliveInterval" (PascalCase), but the existing code was throwing ArgumentOutOfRangeException with paramName "keepAliveInterval" (camelCase). There was even a test for that. It felt wrong for me to change the test to check for PascalCase for the name or even more wrong to throw camelCase when validating the property.

If you consider that the CreateFromStream would throw ArgumentOutOfRangeException with different casing than the actual name of the argument OK and not a breaking change, I will remove it and validate it only once in the property setter.

Copy link
Member

@CarnaViire CarnaViire left a comment

Choose a reason for hiding this comment

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

Some small comments for now... I haven't reviewed everything yet.
Also, could you please take a look at the failing tests you can see in CI checks

/// <summary>
/// Provides a wrapper around the ZLib compression API.
/// </summary>
internal sealed class WebSocketDeflater : IDisposable
Copy link
Member

Choose a reason for hiding this comment

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

How is it guaranteed that it will not be used concurrently?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The guarantee comes from the ManagedWebSocket class itself. It disallows concurrent sends or receives.

@zlatanov zlatanov mentioned this pull request Feb 25, 2021
Base automatically changed from master to main March 1, 2021 09:08
@zlatanov
Copy link
Contributor Author

zlatanov commented Mar 8, 2021

Closing this PR in favor of a new implementation that requires no (or least very little) refactoring of the existing ManagedWebSocket code.

@zlatanov zlatanov closed this Mar 8, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Apr 7, 2021
@zlatanov zlatanov deleted the websocket-compression branch April 28, 2021 21:41
@karelz karelz added this to the 6.0.0 milestone May 20, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add compression support in WebSocket
6 participants