in src/Microsoft.Azure.SignalR/ServerConnections/ServiceConnection.cs [291:365]
private async Task ProcessClientConnectionAsync(ClientConnectionContext connection, SignalRProtocol.IHubProtocol protocol)
{
try
{
// Writing from the application to the service
var transport = connection.ProcessOutgoingMessagesAsync(protocol);
// Waiting for the application to shutdown so we can clean up the connection
var app = connection.ProcessApplicationAsync(_connectionDelegate);
var task = await Task.WhenAny(app, transport);
// remove it from the connection list
TryRemoveClientConnection(connection.ConnectionId, out _);
// This is the exception from application
Exception exception = null;
if (task == app)
{
exception = app.Exception?.GetBaseException();
// there is no need to write to the transport as application is no longer running
Log.WaitingForTransport(Logger);
// app task completes connection.Transport.Output, which will completes connection.Application.Input and ends the transport
// Transports are written by us and are well behaved, wait for them to drain
connection.CancelOutgoing(true);
// transport never throws
await transport;
}
else
{
// transport task ends first, no data will be dispatched out
Log.WaitingForApplication(Logger);
try
{
// always wait for the application to complete
await app;
}
catch (Exception e)
{
exception = e;
}
}
if (exception != null)
{
Log.ApplicationTaskFailed(Logger, exception);
}
// If we aren't already aborted, we send the abort message to the service
if (connection.AbortOnClose)
{
// Inform the Service that we will remove the client because SignalR told us it is disconnected.
var closeConnectionMessage = new CloseConnectionMessage(connection.ConnectionId, errorMessage: exception?.Message);
// when it fails, it means the underlying connection is dropped
// service is responsible for closing the client connections in this case and there is no need to throw
await SafeWriteAsync(closeConnectionMessage);
Log.CloseConnection(Logger, connection.ConnectionId);
}
Log.ConnectedEnding(Logger, connection.ConnectionId);
}
catch (Exception e)
{
// When it throws, there must be something wrong
Log.ProcessConnectionFailed(Logger, connection.ConnectionId, e);
}
finally
{
connection.OnCompleted();
}
}