Skip to content

Conversation

@rokonec
Copy link
Member

@rokonec rokonec commented Apr 1, 2025

Fixes #101022

Context

Unix only.
HttpListenerContext is not released from Dictionary<HttpListenerContext, HttpListenerContext> _listenerContexts when code is written by best and documented practices. Cleaning is done by disposing websocket instance but websocket does not close connection, which is needed to properly cleanup.
This is causing HttpListenerContext piling in _listenerContexts resulting in memory leak.

See #101022 for more details.

Code changes

I have not found other solution than wrapping websocket into private class HttpListenerWrappedWebSocket along with needed objects to cleanup connection at websocket Dispose.

Testing

I have verified that my mini repro (client and sever consoles) no longer reproes after give changes been compiled into experimental dotnet Runtime.

Copilot AI review requested due to automatic review settings April 1, 2025 13:09
@ghost ghost added the area-System.Net label Apr 1, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR addresses a memory leak issue caused by HttpListenerContext not being released from the tracking dictionary by wrapping the WebSocket in a new disposable class.

  • Introduces HttpListenerWrappedWebSocket to ensure connections are properly closed.
  • Modifies instantiation to use the wrapped WebSocket for cleanup on Dispose.
Comments suppressed due to low confidence (1)

src/libraries/System.Net.HttpListener/src/System/Net/Managed/WebSockets/HttpWebSocket.Managed.cs:121

  • Consider wrapping the _context.Response.Close() call in a try/catch block within Dispose to protect against potential exceptions that might disrupt the disposal process.
public override void Dispose()

@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

@ManickaP ManickaP requested a review from a team April 1, 2025 14:32
@ManickaP
Copy link
Member

ManickaP commented Apr 1, 2025

I have verified that my mini repro (client and sever consoles) no longer reproes after give changes been compiled into experimental dotnet Runtime.

Is it possible to translate your repro into a test? Or at least try to design a test to repro it, to get some coverage for this.

@rokonec
Copy link
Member Author

rokonec commented Apr 1, 2025

I have verified that my mini repro (client and sever consoles) no longer reproes after give changes been compiled into experimental dotnet Runtime.

Is it possible to translate your repro into a test? Or at least try to design a test to repro it, to get some coverage for this.

I thought about it and have not found how to do it. I was also looking for similar tests and there are none 🤦
I will think more and try my best, lest hope

Update: After some discussion we decided to not unit test this memory leak as this is complicated and expensive to run.

Copy link
Member

@ManickaP ManickaP left a comment

Choose a reason for hiding this comment

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

LGTM, thanks.
cc @CarnaViire

@MihaZupan
Copy link
Member

MihaZupan commented Apr 7, 2025

Does HttpListener on Windows clean up all resources if I never call response.Close (even for non-WebSocket requests)?

IMO not disposing the WebSocket instance shouldn't lead to a leak either. We guard against such misuse with finalizers in other places (e.g. Sockets, response HttpContent streams).

@rokonec
Copy link
Member Author

rokonec commented Apr 8, 2025

Does HttpListener on Windows clean up all resources if I never call response.Close (even for non-WebSocket requests)?

IMO not disposing the WebSocket instance shouldn't lead to a leak either. We guard against such misuse with finalizers in other places (e.g. Sockets, response HttpContent streams).

Windows implementation is different, it does not instantiate standard WebSocket instance but ServerWebSocket which does some cleaning in its Dispose(). Our managed (unix) implementation is caching Http Listener context internally in dictionary (that is the source of memory leak) but Windows implementation does not cache so instances of HttpListenerContext and its related resources are cleaned when garbage collected.

Edit: After internal discussion we decide to implement cleaning in finalizer, to handle situation when users forget are not disposing websocket which in Windows would not cause memory leak but after migration to linux, it would.

@rokonec rokonec force-pushed the dev/rokonec/101022-httplistner-mem-leak branch from 916466d to 8ee17bf Compare April 9, 2025 15:10
@rokonec rokonec requested a review from MihaZupan April 9, 2025 16:34
@rokonec rokonec merged commit b1d93d0 into dotnet:main Apr 10, 2025
83 of 86 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators May 10, 2025
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.

.NET 6+ WebSocket HttpListener - memory leak

3 participants