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

WebSocket.Dispose() throws exception if DefaultHttpContext.Uninitialize had been called in background #59772

Open
1 task done
t1g0rs opened this issue Jan 8, 2025 · 5 comments
Labels
area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions

Comments

@t1g0rs
Copy link

t1g0rs commented Jan 8, 2025

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

When websocket is accepted and processed in background it could happen, that web server uninitialize DefaultHttpContext, causing ObjectDisposedException, when calling websocket.Dispose()

Expected Behavior

calling Dispose does not throw exception

Steps To Reproduce

Simple repro, that works as expected in .net8.0, but throws exception in .net9.0

Exceptions (if any)

ObjectDisposedException at
at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.ThrowContextDisposed()
at Microsoft.AspNetCore.Http.DefaultHttpContext.Abort()
at Microsoft.AspNetCore.WebSockets.AbortStream.Dispose(Boolean disposing)
at System.IO.Stream.Close()
at System.Net.WebSockets.ManagedWebSocket.DisposeCore()
at System.Net.WebSockets.ManagedWebSocket.Dispose()

.NET Version

9.0.101

Anything else?

No response

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions label Jan 8, 2025
@martincostello
Copy link
Member

HttpContexts are pooled by the server and are reset when the HTTP request ends to use in a subsequent request. Using the HttpContext in a background thread after the HTTP request is completed isn't supported. You should copy whatever state you need to use in the background from the HTTP context before the request that initiates that work completes.

@t1g0rs
Copy link
Author

t1g0rs commented Jan 8, 2025

While WebSocket is processed, middleware stays blocked, preventing webserver from resetting HttpContexts as explained in docs

If you're using a background service to write data to a WebSocket, make sure you keep the middleware pipeline running.

Problem is during disposing, if webserver disposes httpcontext quicker, then code reaches end of using block

@BrennanConroy
Copy link
Member

Why are you manually calling DefaultHttpContext.Uninitialize? That's not really meant for user code.

The problem here is that you're disposing the websocket after cleaning up the HttpContext (which you shouldn't be doing anyways and let the host do that for you).

Incorrect code:

using var websocket = ...;

httpContext.Uninitialize();

Fixed code:

{
    using var websocket = ...;
}

httpContext.Uninitialize();

Or

var websocket = ...;

websocket.Dispose();
httpContext.Uninitialize();

@t1g0rs
Copy link
Author

t1g0rs commented Jan 9, 2025

I'm not calling Uninitialize manually, it is in reproduction to illustrate problem. In reality webSocket is passed to background listener that has long cleanup process when socket is aborted. That is enough time for aspnet server to decide that http context can be uninitialized. Real code literally looks like this:

async Task StartWebSocketListener(HttpContext httpContext)
{
     using var webSocket = await httpContext.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false);
     await Listen(webSocket).ConfigureAwait(false);
}

In my opinion using var webSocket = should not throw exceptions.

@BrennanConroy
Copy link
Member

that has long cleanup process when socket is aborted. That is enough time for aspnet server to decide that http context can be uninitialized

That sounds like you aren't waiting for cleanup before returning from your middleware. Which goes against the quote you grabbed from the docs

If you're using a background service to write data to a WebSocket, make sure you keep the middleware pipeline running.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions
Projects
None yet
Development

No branches or pull requests

3 participants