0

I have an ASP.NET Core console application, that runs a REST interface and a pool of websockets. When I press Ctrl-C in the console, it shuts down correctly, unless I have a WebSocket connected. That socket is then blocked in ReceiveAsync. ReceiveAsync takes a cancellation token, but I fail to get the 'right' token for this to work.

What I have now is this, but that doesn't work. When I Ctrl-C, it only stops with a timeout:

    public async Task<WebSocketReceiveResult> ReceiveAsync()
    {
        byte[] buffer = new byte[1024];
        var arraysegment = new ArraySegment<byte>(buffer);

        var cancellationTokenSource = new CancellationTokenSource();
        AppDomain.CurrentDomain.ProcessExit += (s, e) =>
        {
            cancellationTokenSource.Cancel();
        };

        return await _websocket.ReceiveAsync(arraysegment, cancellationTokenSource.Token);
    }

The timeout exception that I get is this:

2025-01-07 16:19:14.966 [E] [Restinterface] Request 'GET http://127.0.0.1:5000/ws' failed: 'The connection was aborted because the server is shutting down and request processing didn't complete within the time specified by HostOptions.ShutdownTimeout.', returned HTTP500. This should not happen!
2025-01-07 16:19:14.992 [D] [Restinterface]    at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)
   at System.IO.Pipelines.Pipe.GetReadAsyncResult()
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1UpgradeMessageBody.ReadAsyncInternalAwaited(ValueTask`1 readTask, CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpRequestStream.ReadAsyncInternal(Memory`1 destination, CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at System.IO.Stream.ReadAtLeastAsyncCore(Memory`1 buffer, Int32 minimumBytes, Boolean throwOnEndOfStream, CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at System.Net.WebSockets.ManagedWebSocket.EnsureBufferContainsAsync(Int32 minimumRequiredBytes, CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
   at System.Net.WebSockets.ManagedWebSocket.ReceiveAsyncPrivate[TResult](Memory`1 payloadBuffer, CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at System.Threading.Tasks.ValueTask`1.ValueTaskSourceAsTask.<>c.<.cctor>b__4_0(Object state)
--- End of stack trace from previous location ---
   at PtlRestInterface.WebSocketAdapter.ReceiveAsync() in C:\Users\bart-devel\source\repos\ptl\PTLRestInterface\WebSocket.cs:line 50
   at PtlRestInterface.RestInterface.WebSocketLoopAsync(IWebSocket webSocket) in C:\Users\bart-devel\source\repos\ptl\PTLRestInterface\RestHandlers\WebSocketHandler.cs:line 31
   at PtlRestInterface.RestInterface.HandleWebSocketAsync(HttpContext context) in C:\Users\bart-devel\source\repos\ptl\PTLRestInterface\RestHandlers\WebSocketHandler.cs:line 15
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at PtlRestInterface.RestInterface.<Configure>b__79_0(HttpContext context, Func`1 next) in C:\Users\bart-devel\source\repos\ptl\PTLRestInterface\RestInterface.cs:line 79

I want the ReceiveAsync to gracefully close when I press Ctrl-C on the console (or when sending a SIGINT).

1 Answer 1

0

I added this code to the host application to catch the Ctrl-C:

var applicationLifetime = app.ApplicationServices.GetRequiredService<IHostApplicationLifetime>(); 
applicationLifetime.ApplicationStopping.Register(() => 
{ 
    CancellationTokenSource.Cancel(); 
});

I can then explicitly Cancel() the CancellationTokenSource, that I have provided to the WebSocket handler earlier.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.