-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Developers can use Compression in WebSocket #20004
Comments
Next step: We need formal API proposal |
Any update on this? |
Also interested in this. I know that "+1" type comments are generally unhelpful, but remarks on other threads by team members have suggested they are helpful in this cause because this issue is being ignored due to a lack of interest expressed by the community. I would suggest that people are simply not saying anything because there are third-party libs that implement this. I would also suggest that waiting for community input is a bad way to measure interest because of that. If HttpClient didn't support compression and MS was dragging their feet on it, the community would just make their own compliant client. The community has done this already, and this effort predates Microsoft's. Have a gander on Nuget. There's over a million downloads of community driven websocket libraries that all address missing features in each other. The community interest has already come and the ship sailed, Microsoft can't gauge interest based on this. |
Thanks for additional comments. Upvotes on top post are best way to help us prioritize. |
@karelz Ok that's cool. I agree that if there's a good community alternative, then it can be relied on for now anyway instead. However I'm not sure how this issue applies across the board. Kestrel implements its own websocket, for example, so you're stuck with whatever that implementation has. Anyway thanks for feedback. Is it possible for the community to work on this, or do you think it's something the core team will want control of initially? |
All of the ASP.NET Core servers expose the opaque upgrade so it's possible to plug in alternate WebSocket implementations. WebSockets isn't even part of the servers, it's shipped as middleware. |
Thanks bruv |
Community can work on almost anything in our repos. It just depends on how deeply invested the contributors are and how much time they are willing to put into it. |
Yes it’s absolutely possible. @anurse implemented an alternative web socket API a while back before we switched to the corefx version. |
Any suggestions for good community alternatives? |
@karelz @Tratcher If the task is still up for grabs, I would like to express interest in contributing here. I have already implemented this in a custom library of mine - https://github.com/zlatanov/websockets but my implementation differs from the suggested API. What I did is introducing the ability to create a send buffer that implements IBufferWriter - see https://github.com/zlatanov/websockets/blob/4d0c4e43e278d3bc6c0e185bb09b828160af2db8/src/Maverick.WebSockets/WebSocketSendBuffer.cs. Based on the websocket capabilities (established during handshake) the send buffer automatically enables compression. It uses array pooling and the last segment being written to is always the uncompressed one. When the segment is full then a new compressed segment is taken from the array pool and the current one is deflated onto it. Since the IBufferWriter interface is synchronous the disadvantage of this technique is that the message must be fully compressed before we start sending it. In my experience though this is not always a problem because messages sent through websockets are never too big. In a real world example I've had uncompressed text message of 1MB become 40KB. Currently using my implementation the API usage would look like this: // We would also have socket.TryCreateBinaryBuffer( out var buffer )
if ( socket.TryCreateTextBuffer( out var buffer ) ) // This would fail if socket isn't connected.
{
// Buffer implements IBufferWriter<Byte> so any API that supports writing
// to it will work. (e.g. the new Json implementation )
// ....
// When we're done writing
await socket.SendAsync( buffer );
// Sending the buffer will consume it and release any resources held
// so it's illegal to use after send
} What do you think? |
When messages can be of an arbitrary size needing to compress them all in memory isn't great. We're working through a lot of similar issues in AspNetCore where too much needs to happen before we can flush data and it impacts scalability. |
I understand. So what if the API looked like this: Task SendAsync(ArraySegment<byte> buffer,
WebSocketMessageType messageType,
bool endOfMessage,
bool compress,
CancellationToken cancellationToken); In this case though we need to be careful when we really do send. For example the compressed version of the payload (when endOfMessage is false) is too small, there will be negative impact if we flush the data to the underlying connection for every send. Is it acceptable for this API to return Task.CompletedTask when the underlying compressed buffer isn't full enough (configurable) and endOfMessage isn't |
Avoiding a signature change for SendAsync would be ideal. Adding new WebSocketMessageType enum values looks like the least invasive option.
Yes that should be fine as long as we flush for message boundaries. |
@Tratcher That makes sense. Can I submit a pull request for this or should we go through a more formal discussion about the changes required? |
They're going to want a formal write-up of the the proposed API changes before a PR. E.g. a diff of the API surface including doc comments describing the new items. |
No changes have been planned yet. You're right that hosting with IIS out-of-proc would be problematic because the proxy wouldn't support compression. Hosting with IIS in-proc should allow it. |
Thanks for clarification. However, is it possible to host ASP .Net Core application with IIS in-proc? Is there any other workaround to enable compression with SignalR? Could one use e.g. long-polling? |
It's possible to host ASP.NET Core apps in-proc in IIS since 2.2 and it's the default when using IIS in ASP.NET Core 3.0 now. More details can be found in our documentation: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/?view=aspnetcore-2.2#hosting-models
SignalR actually tries to bypass and disable response compression as much as possible since compression generally requires some level of buffering, which disrupts the real-time nature of SignalR communication. Can you provide more context on why you want WebSocket compression? You can always compress payloads you are sending yourself and decompress them on the client side. |
We have a fairly large and complex application in ASP .NET Core. We use regular http Ajax calls to retrieve page metadata/data and SignalR to push updates to the browser. We could compress and decompress the payloads ourselves, The only disadvantage is that we need to use an external javascript based decompression library. It is also a bit slower then letting a browser do the decompression. |
Same reason web-request clients can ask for content to be gzipped, so the applications on both ends don't need to have compression in their app layers. It's transparent. |
Understood that it would be more convenient at the websocket layer. In the meantime though you'll need to do the app level compression. |
Another option may be to switch to using SignalR's binary format (based on MessagePack). That may reduce the overhead enough to help in your situation. If you still need to compress text, then yes, WebSocket compression is the only transparent way to do that. |
Triage: We need solid API proposal with use case samples, etc. Any takers? |
@karelz I would like to volunteer. I've put this off long enough, and now that 3.0 has been released I would like to find time to do it. I know the internals of the WebSocket well, and what needs to be done to efficiently support compression. |
We'd certainly like to be able to have an end-to-end scenario around WebSocket compression. ASP.NET Core does share some code (via the |
@zlatanov cool, as next step, can you please propose the API shape? See API process for details and examples. Thanks! |
Close via #49304? |
We have to enable it. |
@davidfowl what has to be enabled where? Is there a tracking work item? |
Thanks @Tratcher, I added it to the list of dev work items in top post. |
Is there a difference between this issue and the one in aspnetcore? Can we close this one if there isn't |
@BrennanConroy This is User Story plugged into overall .NET 6 planning, the ASP.NET Core is a child. |
The remaining work was addressed in dotnet/aspnetcore#2715 by PR dotnet/aspnetcore#32600 |
AB#1118550
https://tools.ietf.org/html/rfc7692
aspnet/WebSockets#19
Implementation notes:
The System.Net.WebSockets.WebSocket.SendAsync API is poorly suited to compression/extension. Compression is a per-message flag set on the first frame using the RSV1 bit. SendAsync sends frames and only takes the buffer, message type, an EndOfMessage bool, and a cancellationToken. There's no simple way to indicate if the data is or should be compressed / transformed. Possible solutions:
ReceiveAsync is easier, it could:
The compression extension could not be correctly implemented as wrapper over the current WebSocket API due to the framing requirement. Compression either needs to be built in or the API needs to expose additional frame fields (e.g. RSV1).
Dev work:
The text was updated successfully, but these errors were encountered: