in src/Microsoft.Azure.SignalR.Common/ServiceConnections/ServiceConnectionBase.cs [152:218]
public async Task StartAsync(string target = null)
{
if (Interlocked.CompareExchange(ref _started, 1, 0) != 0)
{
throw new InvalidOperationException("Connection already started!");
}
Status = ServiceConnectionStatus.Connecting;
var connection = await EstablishConnectionAsync(target);
if (connection != null)
{
_connectionContext = connection;
Status = ServiceConnectionStatus.Connected;
_serviceConnectionStartTcs.TrySetResult(true);
try
{
TimerAwaitable syncTimer = null;
try
{
if (HubEndpoint != null && HubEndpoint.AccessKey is MicrosoftEntraAccessKey key)
{
syncTimer = new TimerAwaitable(TimeSpan.Zero, DefaultSyncAzureIdentityInterval);
_ = UpdateAzureIdentityAsync(key, syncTimer);
}
await ProcessIncomingAsync(connection);
}
finally
{
// mark the status as Disconnected so that no one will write to this connection anymore
Status = ServiceConnectionStatus.Disconnected;
syncTimer?.Stop();
// when ProcessIncoming completes, clean up the connection
// TODO: Never cleanup connections unless Service asks us to do that
// Current implementation is based on assumption that Service will drop clients
// if server connection fails.
await CleanupClientConnections();
}
}
catch (Exception ex)
{
Log.ConnectionDropped(Logger, _endpointName, ConnectionId, ex);
}
finally
{
// wait until all the connections are cleaned up to close the outgoing pipe
// Don't allow write anymore when the connection is disconnected
await _writeLock.WaitAsync();
try
{
// close the underlying connection
await DisposeConnection(connection);
}
finally
{
_writeLock.Release();
}
}
}
else
{
Status = ServiceConnectionStatus.Disconnected;
_serviceConnectionStartTcs.TrySetResult(false);
}
}