Honor InherentKeepAliveFeature for server timeout (#2727)
This commit is contained in:
parent
f0c4170e42
commit
e78e3db6f4
|
|
@ -12,6 +12,7 @@ using System.Threading;
|
||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Connections;
|
using Microsoft.AspNetCore.Connections;
|
||||||
|
using Microsoft.AspNetCore.Connections.Features;
|
||||||
using Microsoft.AspNetCore.Internal;
|
using Microsoft.AspNetCore.Internal;
|
||||||
using Microsoft.AspNetCore.SignalR.Client.Internal;
|
using Microsoft.AspNetCore.SignalR.Client.Internal;
|
||||||
using Microsoft.AspNetCore.SignalR.Protocol;
|
using Microsoft.AspNetCore.SignalR.Protocol;
|
||||||
|
|
@ -47,6 +48,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
private long _nextActivationServerTimeout;
|
private long _nextActivationServerTimeout;
|
||||||
private long _nextActivationSendPing;
|
private long _nextActivationSendPing;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
private bool _hasInherentKeepAlive;
|
||||||
|
|
||||||
private readonly ConnectionLogScope _logScope;
|
private readonly ConnectionLogScope _logScope;
|
||||||
|
|
||||||
|
|
@ -302,6 +304,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
// Start the connection
|
// Start the connection
|
||||||
var connection = await _connectionFactory.ConnectAsync(_protocol.TransferFormat);
|
var connection = await _connectionFactory.ConnectAsync(_protocol.TransferFormat);
|
||||||
var startingConnectionState = new ConnectionState(connection, this);
|
var startingConnectionState = new ConnectionState(connection, this);
|
||||||
|
_hasInherentKeepAlive = connection.Features.Get<IConnectionInherentKeepAliveFeature>()?.HasInherentKeepAlive ?? false;
|
||||||
|
|
||||||
// From here on, if an error occurs we need to shut down the connection because
|
// From here on, if an error occurs we need to shut down the connection because
|
||||||
// we still own it.
|
// we still own it.
|
||||||
|
|
@ -898,19 +901,25 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
// await returns True until `timer.Stop()` is called in the `finally` block of `ReceiveLoop`
|
// await returns True until `timer.Stop()` is called in the `finally` block of `ReceiveLoop`
|
||||||
while (await timer)
|
while (await timer)
|
||||||
{
|
{
|
||||||
if (DateTime.UtcNow.Ticks > Volatile.Read(ref _nextActivationServerTimeout))
|
await RunTimerActions();
|
||||||
{
|
|
||||||
OnServerTimeout();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DateTime.UtcNow.Ticks > Volatile.Read(ref _nextActivationSendPing))
|
|
||||||
{
|
|
||||||
await PingServer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Internal for testing
|
||||||
|
internal async Task RunTimerActions()
|
||||||
|
{
|
||||||
|
if (!_hasInherentKeepAlive && DateTime.UtcNow.Ticks > Volatile.Read(ref _nextActivationServerTimeout))
|
||||||
|
{
|
||||||
|
OnServerTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DateTime.UtcNow.Ticks > Volatile.Read(ref _nextActivationSendPing))
|
||||||
|
{
|
||||||
|
await PingServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void OnServerTimeout()
|
private void OnServerTimeout()
|
||||||
{
|
{
|
||||||
if (Debugger.IsAttached)
|
if (Debugger.IsAttached)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Connections;
|
using Microsoft.AspNetCore.Connections;
|
||||||
|
using Microsoft.AspNetCore.Connections.Features;
|
||||||
using Microsoft.AspNetCore.SignalR.Protocol;
|
using Microsoft.AspNetCore.SignalR.Protocol;
|
||||||
using Microsoft.AspNetCore.SignalR.Tests;
|
using Microsoft.AspNetCore.SignalR.Tests;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
@ -119,6 +120,33 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
Assert.Equal($"Server timeout ({hubConnection.ServerTimeout.TotalMilliseconds:0.00}ms) elapsed without receiving a message from the server.", exception.Message);
|
Assert.Equal($"Server timeout ({hubConnection.ServerTimeout.TotalMilliseconds:0.00}ms) elapsed without receiving a message from the server.", exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ServerTimeoutIsDisabledWhenUsingTransportWithInherentKeepAlive()
|
||||||
|
{
|
||||||
|
using (StartVerifiableLog(out var loggerFactory))
|
||||||
|
{
|
||||||
|
var testConnection = new TestConnection();
|
||||||
|
testConnection.Features.Set<IConnectionInherentKeepAliveFeature>(new TestKeepAliveFeature() { HasInherentKeepAlive = true });
|
||||||
|
var hubConnection = CreateHubConnection(testConnection, loggerFactory: loggerFactory);
|
||||||
|
hubConnection.ServerTimeout = TimeSpan.FromMilliseconds(1);
|
||||||
|
|
||||||
|
await hubConnection.StartAsync().OrTimeout();
|
||||||
|
|
||||||
|
var closeTcs = new TaskCompletionSource<Exception>();
|
||||||
|
hubConnection.Closed += ex =>
|
||||||
|
{
|
||||||
|
closeTcs.TrySetResult(ex);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
await hubConnection.RunTimerActions().OrTimeout();
|
||||||
|
|
||||||
|
Assert.False(closeTcs.Task.IsCompleted);
|
||||||
|
|
||||||
|
await hubConnection.DisposeAsync().OrTimeout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task PendingInvocationsAreTerminatedIfServerTimeoutIntervalElapsesWithNoMessages()
|
public async Task PendingInvocationsAreTerminatedIfServerTimeoutIntervalElapsesWithNoMessages()
|
||||||
{
|
{
|
||||||
|
|
@ -145,6 +173,11 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct TestKeepAliveFeature : IConnectionInherentKeepAliveFeature
|
||||||
|
{
|
||||||
|
public bool HasInherentKeepAlive { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
// Moq really doesn't handle out parameters well, so to make these tests work I added a manual mock -anurse
|
// Moq really doesn't handle out parameters well, so to make these tests work I added a manual mock -anurse
|
||||||
private class MockHubProtocol : IHubProtocol
|
private class MockHubProtocol : IHubProtocol
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue