diff --git a/src/MagicOnion.Server/Hubs/StreamingHub.cs b/src/MagicOnion.Server/Hubs/StreamingHub.cs index cbf9d22bd..303b91e3a 100644 --- a/src/MagicOnion.Server/Hubs/StreamingHub.cs +++ b/src/MagicOnion.Server/Hubs/StreamingHub.cs @@ -134,10 +134,13 @@ internal async Task? callbackAction) public void Unregister() { + if (unregistered) return; + manager.Unregister(ServiceContext); timeoutToken.CancelAfter(Timeout.InfiniteTimeSpan); timeoutTimerIsRunning = false; + unregistered = true; } public void Dispose() { if (disposed) return; + disposed = true; onAckCallback = null; - manager.Unregister(ServiceContext); + if (!unregistered) + { + manager.Unregister(ServiceContext); + } timeoutToken.Dispose(); } } diff --git a/tests/MagicOnion.Server.Tests/StreamingHubHeartbeat/StreamingHubServerHeartbeatTest.cs b/tests/MagicOnion.Server.Tests/StreamingHubHeartbeat/StreamingHubServerHeartbeatTest.cs index 18fc82533..b21b86517 100644 --- a/tests/MagicOnion.Server.Tests/StreamingHubHeartbeat/StreamingHubServerHeartbeatTest.cs +++ b/tests/MagicOnion.Server.Tests/StreamingHubHeartbeat/StreamingHubServerHeartbeatTest.cs @@ -134,6 +134,7 @@ public async Task Timeout() // Assert Assert.True((bool)Fixture.Items.GetValueOrDefault("Disconnected")); + Assert.True((bool)Fixture.Items.GetValueOrDefault("Heartbeat/TimeoutToken/IsCancellationRequested")); Assert.True(client.WaitForDisconnect().IsCompleted); } @@ -269,7 +270,11 @@ public class StreamingHubServerHeartbeatTestHub_TimeoutBehavior([FromKeyedServic { protected override ValueTask OnDisconnected() { + var httpContext = Context.CallContext.GetHttpContext(); + var heartbeatFeature = httpContext.Features.GetRequiredFeature(); + items["Disconnected"] = true; + items["Heartbeat/TimeoutToken/IsCancellationRequested"] = heartbeatFeature.TimeoutToken.IsCancellationRequested; return base.OnDisconnected(); } }