private async Task ProcessClientConnectionAsync()

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();
        }
    }