Skip to content

SystemWeb.Adapters deadlocks after requesting a blazor page using InteractiveServer #543

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

Open
pockets3407 opened this issue Sep 10, 2024 · 2 comments
Labels
Needs: Triage 🔍 Label added to new issues which need Triage

Comments

@pockets3407
Copy link
Contributor

Describe the bug

SessionLoadMiddleware is deadlocking on Blazor's InteractveServer request. When a page is requested using InteractiveServer the following 3 requests come through the middelware

  1. GET: /counter
  2. POST: /_blazor/negotiate
  3. CONNECT: /_blazor

Since the CONNECT request stays open, the session is not properly committed and will stay in the heartbeat loop on the framework side.

Eventually, we will hit the HttpClient timeout on future requests.

To Reproduce

Using the WebFormsToBlazor sample, replace the following in Program.cs

app.UseWhen(
    (context) => HttpMethods.IsConnect(context.Request.Method) == false,
    appBuilder => appBuilder.UseSystemWebAdapters());

with

app.UseSystemWebAdapters();
  1. Navigate to the counter page
  2. Navigate to any other page

Nothing will happen until the 100 second HttpClient timeout is hit

Exceptions (if any)

HttpClient timeout

Microsoft.AspNetCore.SystemWebAdapters.SessionState.RemoteSession.RemoteAppSessionStateManager: Error: Unable to load remote session state for session <session id>

System.Threading.Tasks.TaskCanceledException: The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing.
 ---> System.TimeoutException: The operation was canceled.
 ---> System.Threading.Tasks.TaskCanceledException: The operation was canceled.
 ---> System.IO.IOException: Unable to read data from the transport connection: The I/O operation has been aborted because of either a thread exit or an application request..
 ---> System.Net.Sockets.SocketException (995): The I/O operation has been aborted because of either a thread exit or an application request.
   --- End of inner exception stack trace ---
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource<System.Int32>.GetResult(Int16 token)
   at System.Net.Security.SslStream.EnsureFullTlsFrameAsync[TIOAdapter](CancellationToken cancellationToken, Int32 estimatedSize)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at System.Net.Security.SslStream.ReadAsyncInternal[TIOAdapter](Memory`1 buffer, CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at System.Net.Http.HttpConnection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.HttpConnection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   --- End of inner exception stack trace ---
   --- End of inner exception stack trace ---
   at System.Net.Http.HttpClient.HandleFailure(Exception e, Boolean telemetryStarted, HttpResponseMessage response, CancellationTokenSource cts, CancellationToken cancellationToken, CancellationTokenSource pendingRequestsCts)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   at Microsoft.AspNetCore.SystemWebAdapters.SessionState.RemoteSession.RemoteAppSessionStateManager.GetSessionDataAsync(String sessionId, Boolean readOnly, HttpContext callingContext, CancellationToken token)
   at Microsoft.AspNetCore.SystemWebAdapters.SessionState.RemoteSession.RemoteAppSessionStateManager.CreateAsync(HttpContext context, SessionAttribute metadata) 
@twsouthwick
Copy link
Member

#577 encountered this as well, but the approach described here didn't work. That was fixed by:

app.UseWhen(context => context.Request.Path != "/_blazor", appBuilder => appBuilder.UseSystemWebAdapters());

@twsouthwick
Copy link
Member

@javiercn hope you can provide some guidance here.

It appears that the middleware we have for the adapters conflicts with blazor. It appears that a way to get around this is to condition our middleware to connections that are not the connection for blazor, of which two attempts are made in this issue and a connected one.

What would be the best way to identify a request as the CONNECT blazor call and is this a stable way to do it? Also, if you have additional thoughts or questions let me know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs: Triage 🔍 Label added to new issues which need Triage
Projects
None yet
Development

No branches or pull requests

2 participants