Пример #1
0
        private void AddClientConnection(ClientConnectionContext connection, OpenConnectionMessage message)
        {
            var instanceId = GetInstanceId(message.Headers);

            _clientConnectionManager.TryAddClientConnection(connection);
            _connectionIds.TryAdd(connection.ConnectionId, instanceId);
        }
Пример #2
0
        private async Task ProcessIncomingMessageAsync(ClientConnectionContext connection)
        {
            Exception exception = null;

            try
            {
                await connection.ApplicationTask;
            }
            catch (Exception e)
            {
                exception = e;
            }
            finally
            {
                // 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 serviceMessage = new CloseConnectionMessage(connection.ConnectionId, errorMessage: exception?.Message);
                    await SafeWriteAsync(serviceMessage);

                    Log.CloseConnection(Logger, connection.ConnectionId);
                }
            }
        }
Пример #3
0
        private Task ProcessClientConnectionAsync(ClientConnectionContext connection)
        {
            // Writing from the application to the service
            var outgoing = ProcessOutgoingMessagesAsync(connection, connection.OutgoingAborted);

            // Waiting for the application to shutdown so we can clean up the connection
            _ = ProcessIncomingMessageAsync(connection);

            // TODO: add more details
            // Current clean up is inside outgoing task when outgoing task completes
            return(outgoing);
        }
Пример #4
0
        private async Task ProcessApplicationTaskAsync(ClientConnectionContext connection)
        {
            // Wait for the application task to complete
            // application task can end when exception, or Context.Abort() from hub
            var app = ProcessApplicationTaskAsyncCore(connection);

            var cancelTask = connection.ApplicationAborted.AsTask();
            var task       = await Task.WhenAny(app, cancelTask);

            if (task != app)
            {
                // cancel the application task, to end the outgoing task
                connection.Application.Input.CancelPendingRead();
                Log.ApplicationTaskCancelled(Logger);
            }
        }
Пример #5
0
        private async Task ProcessIncomingMessageAsync(ClientConnectionContext connection)
        {
            // Wait for the application task to complete
            // application task can end when exception, or Context.Abort() from hub
            var app = ProcessApplicationTaskAsyncCore(connection);

            var cancelTask = connection.ApplicationAborted.AsTask();
            var task       = await Task.WhenAny(app, cancelTask);

            if (task == app)
            {
                await task;
            }
            else
            {
                // cancel the application task, to end the outgoing task
                connection.Application.Input.CancelPendingRead();
                throw new AzureSignalRException("Cancelled running application task, probably caused by time out.");
            }
        }
Пример #6
0
        private async Task ProcessApplicationTaskAsyncCore(ClientConnectionContext connection)
        {
            Exception exception = null;

            try
            {
                // Wait for the application task to complete
                // application task can end when exception, or Context.Abort() from hub
                await _connectionDelegate(connection);
            }
            catch (Exception ex)
            {
                // Capture the exception to communicate it to the transport (this isn't strictly required)
                exception = ex;
                throw;
            }
            finally
            {
                // Close the transport side since the application is no longer running
                connection.Transport.Output.Complete(exception);
                connection.Transport.Input.Complete();
            }
        }
Пример #7
0
        private async Task <bool> SkipHandshakeResponse(ClientConnectionContext connection, CancellationToken token)
        {
            try
            {
                while (true)
                {
                    var result = await connection.Application.Input.ReadAsync(token);

                    if (result.IsCanceled || token.IsCancellationRequested)
                    {
                        return(false);
                    }

                    var buffer = result.Buffer;
                    if (buffer.IsEmpty)
                    {
                        continue;
                    }

                    if (SignalRProtocol.HandshakeProtocol.TryParseResponseMessage(ref buffer, out var message))
                    {
                        connection.Application.Input.AdvanceTo(buffer.Start);
                        return(true);
                    }

                    if (result.IsCompleted)
                    {
                        return(false);
                    }
                }
            }
            catch (Exception ex)
            {
                Log.ErrorSkippingHandshakeResponse(Logger, ex);
            }
            return(false);
        }
Пример #8
0
 public void AddClientConnection(ClientConnectionContext clientConnection)
 {
     _clientConnections[clientConnection.ConnectionId] = clientConnection;
 }
Пример #9
0
        private async Task ProcessOutgoingMessagesAsync(ClientConnectionContext connection, CancellationToken token = default)
        {
            try
            {
                if (connection.IsMigrated)
                {
                    using var timeoutToken = new CancellationTokenSource(DefaultHandshakeTimeout);
                    using var source       = CancellationTokenSource.CreateLinkedTokenSource(token, timeoutToken.Token);

                    // A handshake response is not expected to be given
                    // if the connection was migrated from another server,
                    // since the connection hasn't been `dropped` from the client point of view.
                    if (!await SkipHandshakeResponse(connection, source.Token))
                    {
                        return;
                    }
                }

                while (true)
                {
                    var result = await connection.Application.Input.ReadAsync(token);

                    if (result.IsCanceled)
                    {
                        break;
                    }

                    var buffer = result.Buffer;

                    if (!buffer.IsEmpty)
                    {
                        try
                        {
                            // Forward the message to the service
                            await WriteAsync(new ConnectionDataMessage(connection.ConnectionId, buffer));
                        }
                        catch (Exception ex)
                        {
                            Log.ErrorSendingMessage(Logger, ex);
                        }
                    }

                    if (result.IsCompleted)
                    {
                        // This connection ended (the application itself shut down) we should remove it from the list of connections
                        break;
                    }

                    connection.Application.Input.AdvanceTo(buffer.End);
                }
            }
            catch (Exception ex)
            {
                // The exception means application fail to process input anymore
                // Cancel any pending flush so that we can quit and perform disconnect
                // Here is abort close and WaitOnApplicationTask will send close message to notify client to disconnect
                Log.SendLoopStopped(Logger, connection.ConnectionId, ex);
                connection.Application.Output.CancelPendingFlush();
            }
            finally
            {
                connection.Application.Input.Complete();
            }
        }
Пример #10
0
        private async Task ProcessClientConnectionAsync(ClientConnectionContext connection)
        {
            try
            {
                // Writing from the application to the service
                var transport = ProcessOutgoingMessagesAsync(connection, connection.OutgoingAborted);

                // Waiting for the application to shutdown so we can clean up the connection
                var app = ProcessApplicationTaskAsyncCore(connection);

                var task = await Task.WhenAny(app, transport);

                // remove it from the connection list
                RemoveClientConnection(connection.ConnectionId);

                // 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(_closeTimeOutMilliseconds);

                    // 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 serviceMessage =
                        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(serviceMessage);

                    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();
            }
        }
 private ServiceMessage CreateMessage(string connectionId, string methodName, object[] args, ClientConnectionContext serviceConnectionContext)
 {
     if (serviceConnectionContext.Protocol != null)
     {
         var message = new ConnectionDataMessage(connectionId, SerializeProtocol(serviceConnectionContext.Protocol, methodName, args)).WithTracingId();
         if (message.TracingId != null)
         {
             MessageLog.StartToSendMessageToConnection(Logger, message);
         }
         return(message);
     }
     else
     {
         var message = new MultiConnectionDataMessage(new[] { connectionId }, SerializeAllProtocols(methodName, args)).WithTracingId();
         if (message.TracingId != null)
         {
             MessageLog.StartToSendMessageToConnections(Logger, message);
         }
         return(message);
     }
 }
        private ServiceMessage CreateMessage(string connectionId, string methodName, object[] args, ClientConnectionContext serviceConnectionContext)
        {
            IDictionary <string, ReadOnlyMemory <byte> > payloads;

            if (serviceConnectionContext.Protocol != null)
            {
                payloads = new Dictionary <string, ReadOnlyMemory <byte> >()
                {
                    { serviceConnectionContext.Protocol, SerializeProtocol(serviceConnectionContext.Protocol, methodName, args) }
                };
            }
            else
            {
                payloads = SerializeAllProtocols(methodName, args);
            }

            // don't use ConnectionDataMessage here, since handshake message is also wrapped into ConnectionDataMessage.
            // otherwise it may cause the handshake failure due to hub invocation message is sent to client before handshake message, when there's high preasure on server.
            // do use ConnectionDataMessage when the message is sent from client.
            var message = new MultiConnectionDataMessage(new[] { connectionId }, payloads).WithTracingId();

            if (message.TracingId != null)
            {
                MessageLog.StartToSendMessageToConnections(Logger, message);
            }
            return(message);
        }
Пример #13
0
 public bool TryRemoveClientConnection(string connectionId, out ClientConnectionContext connection)
 {
     return(_clientConnections.TryRemove(connectionId, out connection));
 }
Пример #14
0
 public bool TryAddClientConnection(ClientConnectionContext connection)
 {
     return(_clientConnections.TryAdd(connection.ConnectionId, connection));
 }