Skip to content

Dev/rokonec/101022 httplistner mem leak - #114098

Merged
rokonec merged 4 commits into
dotnet:mainfrom
rokonec:dev/rokonec/101022-httplistner-mem-leak
Apr 10, 2025
Merged

rokonec merged 4 commits into
dotnet:mainfrom
rokonec:dev/rokonec/101022-httplistner-mem-leak

Conversation

@rokonec

@rokonec rokonec commented Apr 1, 2025

Copy link
Copy Markdown
Member

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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

ManickaP commented Apr 1, 2025

Copy link
Copy Markdown
Member

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

rokonec commented Apr 1, 2025 •

Copy link
Copy Markdown
Member Author

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.

@ManickaP ManickaP left a comment

Copy link
Copy Markdown
Member

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

MihaZupan commented Apr 7, 2025 •

Copy link
Copy Markdown
Member

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

rokonec commented Apr 8, 2025 •

Copy link
Copy Markdown
Member Author

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
@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

4 participants