parent
f58ea8133b
commit
b61dc35ee6
|
|
@ -97,7 +97,18 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
if (_needKeepAlive)
|
if (_needKeepAlive)
|
||||||
{
|
{
|
||||||
_logger.ResettingKeepAliveTimer();
|
_logger.ResettingKeepAliveTimer();
|
||||||
_timeoutTimer.Change(ServerTimeout, Timeout.InfiniteTimeSpan);
|
|
||||||
|
// If the connection is disposed while this callback is firing, or if the callback is fired after dispose
|
||||||
|
// (which can happen because of some races), this will throw ObjectDisposedException. That's OK, because
|
||||||
|
// we don't need the timer anyway.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_timeoutTimer.Change(ServerTimeout, Timeout.InfiniteTimeSpan);
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
// This is OK!
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,8 +168,10 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
|
|
||||||
private async Task DisposeAsyncCore()
|
private async Task DisposeAsyncCore()
|
||||||
{
|
{
|
||||||
_timeoutTimer.Dispose();
|
|
||||||
await _connection.DisposeAsync();
|
await _connection.DisposeAsync();
|
||||||
|
|
||||||
|
// Dispose the timer AFTER shutting down the connection.
|
||||||
|
_timeoutTimer.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Client return values/tasks?
|
// TODO: Client return values/tasks?
|
||||||
|
|
|
||||||
|
|
@ -207,6 +207,21 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
Assert.Equal("Server timeout (100.00ms) elapsed without receiving a message from the server.", exception.Message);
|
Assert.Equal("Server timeout (100.00ms) elapsed without receiving a message from the server.", exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task OnReceivedAfterTimerDisposedDoesNotThrow()
|
||||||
|
{
|
||||||
|
var connection = new TestConnection();
|
||||||
|
var hubConnection = new HubConnection(connection, new JsonHubProtocol(), new LoggerFactory());
|
||||||
|
await hubConnection.StartAsync().OrTimeout();
|
||||||
|
await hubConnection.DisposeAsync().OrTimeout();
|
||||||
|
|
||||||
|
// Fire callbacks, they shouldn't fail
|
||||||
|
foreach (var registration in connection.Callbacks)
|
||||||
|
{
|
||||||
|
await registration.InvokeAsync(new byte[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
private bool _closed;
|
private bool _closed;
|
||||||
private object _closedLock = new object();
|
private object _closedLock = new object();
|
||||||
|
|
||||||
private readonly List<ReceiveCallback> _callbacks = new List<ReceiveCallback>();
|
public List<ReceiveCallback> Callbacks { get; } = new List<ReceiveCallback>();
|
||||||
|
|
||||||
public IFeatureCollection Features { get; } = new FeatureCollection();
|
public IFeatureCollection Features { get; } = new FeatureCollection();
|
||||||
|
|
||||||
|
|
@ -130,9 +130,9 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
while (_receivedMessages.Reader.TryRead(out var message))
|
while (_receivedMessages.Reader.TryRead(out var message))
|
||||||
{
|
{
|
||||||
ReceiveCallback[] callbackCopies;
|
ReceiveCallback[] callbackCopies;
|
||||||
lock (_callbacks)
|
lock (Callbacks)
|
||||||
{
|
{
|
||||||
callbackCopies = _callbacks.ToArray();
|
callbackCopies = Callbacks.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var callback in callbackCopies)
|
foreach (var callback in callbackCopies)
|
||||||
|
|
@ -170,14 +170,14 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
public IDisposable OnReceived(Func<byte[], object, Task> callback, object state)
|
public IDisposable OnReceived(Func<byte[], object, Task> callback, object state)
|
||||||
{
|
{
|
||||||
var receiveCallBack = new ReceiveCallback(callback, state);
|
var receiveCallBack = new ReceiveCallback(callback, state);
|
||||||
lock (_callbacks)
|
lock (Callbacks)
|
||||||
{
|
{
|
||||||
_callbacks.Add(receiveCallBack);
|
Callbacks.Add(receiveCallBack);
|
||||||
}
|
}
|
||||||
return new Subscription(receiveCallBack, _callbacks);
|
return new Subscription(receiveCallBack, Callbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ReceiveCallback
|
public class ReceiveCallback
|
||||||
{
|
{
|
||||||
private readonly Func<byte[], object, Task> _callback;
|
private readonly Func<byte[], object, Task> _callback;
|
||||||
private readonly object _state;
|
private readonly object _state;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue