Add support for cancelling regular hub invocations from client #64098
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixes #11542
Summary
This PR implements cancellation support for regular (non-streaming) hub method invocations from the client. Previously, only streaming invocations could be cancelled by the client. Now, when a client passes a
CancellationToken
toInvokeAsync
and cancels it, the server-side hub method will be cancelled as well.Example Usage
Server-side hub method:
Client-side invocation:
Implementation Details
Client Changes
HubConnection.InvokeCoreAsyncCore
to sendCancelInvocationMessage
when the cancellation token is triggeredServer Changes
CancellationToken
parameters to streaming methods onlyCancellationTokenSource
inActiveRequestCancellationSources
, allowingCancelInvocationMessage
to cancel themTests
CancelInvocationMessage
is sent for regular invocationsCancellationToken
Breaking Changes
None. This is purely additive functionality. Existing code continues to work as before.
Notes
As mentioned by @BrennanConroy in #11542 (comment), the server infrastructure was already structured to support this - it just needed to be enabled for regular invocations in addition to streams.
Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
fakeurl.org
/home/REDACTED/work/aspnetcore/aspnetcore/.dotnet/dotnet exec --depsfile /home/REDACTED/work/aspnetcore/aspnetcore/artifacts/bin/Microsoft.AspNetCore.SignalR.Client.Tests/Debug/net10.0/Microsoft.AspNetCore.SignalR.Client.Tests.deps.json --runtimeconfig /home/REDACTED/work/aspnetcore/aspnetcore/artifacts/bin/Microsoft.AspNetCore.SignalR.Client.Tests/Debug/net10.0/Microsoft.AspNetCore.SignalR.Client.Tests.runtimeconfig.json /home/REDACTED/.nuget/packages/xunit.REDACTED.console/2.9.2/tools/netcoreapp2.0/xunit.console.dll /home/REDACTED/work/aspnetcore/aspnetcore/artifacts/bin/Microsoft.AspNetCore.SignalR.Client.Tests/Debug/net10.0/Microsoft.AspNetCore.SignalR.Client.Tests.dll -noautoreporters -xml /home/REDACTED/work/aspnetcore/aspnetcore/artifacts/TestResults/Debug/Microsoft.AspNetCore.SignalR.Client.Tests_net10.0_x64.xml -html /home/REDACTED/work/aspnetcore/aspnetcore/artifacts/TestResults/Debug/Microsoft.AspNetCore.SignalR.Client.Tests_net10.0_x64.html -notrait Quarantined=true -nocolor
(dns block)/home/REDACTED/work/aspnetcore/aspnetcore/.dotnet/dotnet exec --runtimeconfig /home/REDACTED/work/aspnetcore/aspnetcore/artifacts/bin/Microsoft.AspNetCore.SignalR.Client.Tests/Debug/net10.0/Microsoft.AspNetCore.SignalR.Client.Tests.runtimeconfig.json --depsfile /home/REDACTED/work/aspnetcore/aspnetcore/artifacts/bin/Microsoft.AspNetCore.SignalR.Client.Tests/Debug/net10.0/Microsoft.AspNetCore.SignalR.Client.Tests.deps.json /home/REDACTED/work/aspnetcore/aspnetcore/artifacts/bin/Microsoft.AspNetCore.SignalR.Client.Tests/Debug/net10.0/testhost.dll --port 43893 --endpoint 127.0.0.1:043893 --role client --parentprocessid 22550 --telemetryoptedin false
(dns block)If you need me to access, download, or install something from one of these locations, you can either:
Original prompt
This section details on the original issue you should resolve
<issue_title>[SignalR] Possibility to cancel long running hub method from client</issue_title>
<issue_description>I´m not quit sure if this post is more of a question or a feature request.
I noticed that the
InvokeAsync<TResult>(HubConnection, String, CancellationToken)
method accepts aCancellationToken
. As far as I understood this token only cancels the invocation (and waiting for a result, ...) on the client, while on the server side the method proceeds.Is this correct?
If so wouldn´t it be a nice feature to allow such a cancellation? I saw that Google offers this possibility in their gRPC framework aswell:
When my understanding is wrong please provide me an example on how to do a cancellation of long running hub methods from the client.</issue_description>
<agent_instructions>See comment in #11542 (comment)</agent_instructions>
Comments on the Issue (you are @copilot in this section)
@davidfowl Today we only support cancellation for streams but there's nothing wrong with supporting this for other invocations. @BrennanConroy @anurse I don't think we do support that, but we structured the code in such a way that we could add support for it in the future. @ We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our [Triage Process](https://github.com/dotnet/aspnetcore/blob/master/docs/TriageProcess.md). @davidfowl @muzopraha What's the scenario you have with a long running hub method? @davidfowl @thorgeirk11 No updates, best to add your scenario so we have a record of what people want to use it for before we invest the time. @BrennanConroy File a new issue and show your code. @BrennanConroy CancellationToken in the hub method signature is only supported for Server to client streaming currently.This issue is tracking adding it for other cases.</comment_new>
<comment_new>@BrennanConroy
Since this feature isn't implemented yet that method signature won't work. It's basically trying to receive a serialized
CancellationToken
from the client, which isn't going to work.The cancellation token in
InvokeAsync
will cancel the client waiting for a server response, but it doesn't pass that info along to the server yet. That's what this issue is tracking adding support for.public async Task UpdateChannelAsync(TPMChannels channel, CancellationToken cts = default)
->public async Task UpdateChannelAsync(TPMChannels channel)
</comment_new><comment_new>@davidfowl
I'll defer to @BrennanConroy here.</comment_new>
<comment_new>@BrennanConroy
Basically we would need to update the client(s) to send a
CancelInvocationMessage
when the cancellation token passed in toInvokeAsync
is canceled.We currently only send that message type when the client cancels a stream
aspnetcore/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs
Line 708 in 54c0cc8
The server is mostly setup to handle
CancelInvocationMessage
aspnetcore/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs
Lines 183 to 189 in 54c0cc8
The logs would likely need to be updated since it assumes streams are the only thing that can be canceled.
We'd also need to update the logic around which hub methods can have a synthetic argument (
CancellationToken
)aspnetcore/src/SignalR/server/Core/src/Internal/HubMethodDescriptor.cs
Lines 64 to 66 in 54c0cc8
A massive stretch goal would be to do the same thing for client results, which would require adding synthetic argument support on the client side and sending
CancelInvocationMessage
from the server. That work is tracked by #44831 and shouldn't restrict this issue from being worke...Fixes #11542
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.