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 WebSocket compression support #32600

Merged
merged 14 commits into from
May 27, 2021
Merged

Add WebSocket compression support #32600

merged 14 commits into from
May 27, 2021

Conversation

BrennanConroy
Copy link
Member

@BrennanConroy BrennanConroy commented May 11, 2021

Fixes #2715

Main questions are around usability. Do we want to allow setting specific deflate options or just have a bool for enabling compression and let the client negotiate choose what it wants to use?

Added an AcceptWebSocketAsync overload to WebSocketManager to see how it looks to pass per connection options.

https://tools.ietf.org/html/rfc7692#section-5 for general negotiation details
https://tools.ietf.org/html/rfc7692#section-7 for permessage-deflate details

namespace Microsoft.Net.Http.Headers
{
    public static class HeaderNames
    {
+        public static readonly string SecWebSocketExtensions = "Sec-WebSocket-Extensions";
    }
}

namespace Microsoft.AspNetCore.Http
{
    public abstract class WebSocketManager
    {
+        public virtual Task<WebSocket> AcceptWebSocketAsync(WebSocketAcceptContext acceptContext) => throw new NotImplementedException();
    }
}

namespace Microsoft.AspNetCore.Http
{
    public partial interface IHeaderDictionary
    {
+        StringValues SecWebSocketExtensions { get; set; }
    }
}

namespace Microsoft.AspNetCore.Http
{
    internal sealed class DefaultWebSocketManager : WebSocketManager
    {
+        public override Task<WebSocket> AcceptWebSocketAsync(WebSocketAcceptContext acceptContext);
    }
}

namespace Microsoft.AspNetCore.WebSockets
{
    public class ExtendedWebSocketAcceptContext : WebSocketAcceptContext
    {
+        public bool DangerousEnableCompression { get; set; }
+        public bool DisableServerContextTakeover { get; set; } = false;
+        public int ServerMaxWindowBits { get; set; } = 15;
    }
}

namespace Microsoft.AspNetCore.Http
{
+    public static class WebSocketManagerExtensions
+    {
+        public static Task<WebSocket> AcceptWebSocketAsync(this WebSocketManager webSocketManager, ExtendedWebSocketAcceptContext acceptContext);
+    }
}

@BrennanConroy BrennanConroy added this to the 6.0-preview5 milestone May 11, 2021
@BrennanConroy BrennanConroy added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label May 11, 2021
src/Middleware/WebSockets/src/WebSocketMiddleware.cs Outdated Show resolved Hide resolved
src/Middleware/WebSockets/src/HandshakeHelpers.cs Outdated Show resolved Hide resolved
src/Middleware/WebSockets/src/HandshakeHelpers.cs Outdated Show resolved Hide resolved
src/Middleware/WebSockets/src/HandshakeHelpers.cs Outdated Show resolved Hide resolved
src/Middleware/WebSockets/src/HandshakeHelpers.cs Outdated Show resolved Hide resolved
src/Middleware/WebSockets/src/HandshakeHelpers.cs Outdated Show resolved Hide resolved
@BrennanConroy
Copy link
Member Author

BrennanConroy commented May 13, 2021

  • Changed the options to only have server side settings
    • We could make the settings nullable and if either is set then assume compression is enabled (so we can remove the bool)
  • Question: do we reject server_no_context if servercontext=true?

@Tratcher
Copy link
Member

  • We could make the settings nullable and if either is set then assume compression is enabled (so we can remove the bool)

No, better to have an explicit on-off switch.

@Tratcher
Copy link
Member

  • Question: do we reject server_no_context if servercontext=true?

If the peer server doesn't use context
takeover, the client doesn't need to reserve memory to retain the
LZ77 sliding window between messages.

Sounds like the client is supposed to be able to opt out, so it's fine for them to override servercontext=true.

@BrennanConroy
Copy link
Member Author

Sounds like the client is supposed to be able to opt out, so it's fine for them to override servercontext=true.

At that point, why even expose an option for this? Just in the case where the client doesn't specify an option?

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.

At that point, why even expose an option for this? Just in the case where the client doesn't specify an option?

So the server can also opt-out. It sounds like context takeover uses more memory on both client and server.

src/Middleware/WebSockets/src/WebSocketMiddleware.cs Outdated Show resolved Hide resolved
src/Middleware/WebSockets/src/WebSocketMiddleware.cs Outdated Show resolved Hide resolved

namespace Microsoft.AspNetCore.WebSockets.Test
{
public class WebSocketCompressionMiddlewareTests : LoggedTest
Copy link
Member

Choose a reason for hiding this comment

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

Have you enabled the autobahn tests yet?

Copy link
Member Author

Choose a reason for hiding this comment

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

I ran it earlier, but wanting to get everything cleaned up before trying again

Copy link
Member Author

Choose a reason for hiding this comment

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

All Autobahn tests passed

@BrennanConroy
Copy link
Member Author

🆙 📅
added support for quoted values, duplicate parameters from a negotiate request is invalid so added that, renamed the server context takeover option to make it obvious that it's an opt-out, changed the base parsing tests to not use a server.

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.

It's looking really good. What's left, autobahn?

@BrennanConroy
Copy link
Member Author

What's left, autobahn?

Yeah, need to do another round of that, would like to figure out if we can make the AcceptWebSocketAsync api with the ExtendedWebSocketAcceptContext discoverable.

@BrennanConroy
Copy link
Member Author

BrennanConroy commented May 21, 2021

Would we be against moving the new properties to the base WebSocketAcceptContext?

Or maybe we can make an extension in the websocket project for AcceptWebSocketAsync(ExtendedWebSocketAcceptContext)

@Tratcher
Copy link
Member

Would we be against moving the new properties to the base WebSocketAcceptContext?

We'd originally left them separate because the base WebSocketAcceptContext was supposed to be implementation agnostic. Now that we only use one implementation I'm not sure if that matters anymore.

Or maybe we can make an extension in the websocket project for AcceptWebSocketAsync(ExtendedWebSocketAcceptContext)

Let's start with that.

@BrennanConroy
Copy link
Member Author

The extension method is a bit weird, it doesn't get called when passing an ExtendedWebSocketAcceptContext, but it at least shows in intellisense.

@ghost
Copy link

ghost commented May 21, 2021

Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:

  • The PR contains changes to the reference-assembly that describe the API change. Or, you have included a snippet of reference-assembly-style code that illustrates the API change.
  • The PR describes the impact to users, both positive (useful new APIs) and negative (breaking changes).
  • Someone is assigned to "champion" this change in the meeting, and they understand the impact and design of the change.

@BrennanConroy
Copy link
Member Author

API review:
Remove AcceptWebSocketAsync extension
Move options from ExtendedWebSocketAcceptContext to WebSocketAcceptContext
Make KeepAliveInterval virtual on WebSocketAcceptContext
Obsolete ExtendedWebSocketAcceptContext

@BrennanConroy BrennanConroy added api-approved API was approved in API review, it can be implemented and removed api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews labels May 24, 2021
@davidfowl
Copy link
Member

Make KeepAliveInterval virtual on WebSocketAcceptContext

Turns out this is a breaking change.

@Tratcher
Copy link
Member

So how do we consolidate ExtendedWebSocketAcceptContext?

@BrennanConroy
Copy link
Member Author

❌ DISALLOWED: Adding the virtual keyword to a member

We're adding a member to WebSocketAcceptContext, not making an existing member virtual so does it actually fall under this category?

We're still going to as cast to ExtendedWebSocketAcceptContext in the middleware, so old usage should still work, and new code will use the base type and we'll reference the new member.

@BrennanConroy BrennanConroy marked this pull request as ready for review May 25, 2021 02:21
@BrennanConroy BrennanConroy enabled auto-merge (squash) May 27, 2021 00:10
@BrennanConroy BrennanConroy merged commit 028470e into main May 27, 2021
@BrennanConroy BrennanConroy deleted the brecon/wscompression branch May 27, 2021 01:53
@ghost ghost modified the milestones: 6.0-preview5, 6.0-preview6 May 27, 2021
@amcasey amcasey added area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions and removed area-runtime labels Jun 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-approved API was approved in API review, it can be implemented area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions feature-websockets Includes: WebSockets
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add WebSockets compressions support
4 participants