Пример #1
0
        protected override Task OnClientConnectedAsync(OpenConnectionMessage openConnectionMessage)
        {
            // Create empty transport with only channel for async processing messages
            var connectionId  = openConnectionMessage.ConnectionId;
            var clientContext = new ClientConnectionContext(this, connectionId, GetInstanceId(openConnectionMessage.Headers));

            bool isDiagnosticClient = false;

            openConnectionMessage.Headers.TryGetValue(Constants.AsrsIsDiagnosticClient, out var isDiagnosticClientValue);
            if (!StringValues.IsNullOrEmpty(isDiagnosticClientValue))
            {
                isDiagnosticClient = Convert.ToBoolean(isDiagnosticClientValue.FirstOrDefault());
            }

            // todo: ignore asp.net for now
            using (new ClientConnectionScope(outboundConnection: this, isDiagnosticClient: isDiagnosticClient))
            {
                if (_clientConnectionManager.TryAddClientConnection(clientContext))
                {
                    _clientConnections.TryAdd(connectionId, clientContext);
                    clientContext.ApplicationTask = ProcessMessageAsync(clientContext, clientContext.CancellationToken);
                    return(ForwardMessageToApplication(connectionId, openConnectionMessage));
                }
                else
                {
                    // the manager still contains this connectionId, probably this connection is not yet cleaned up
                    Log.DuplicateConnectionId(Logger, connectionId, null);
                    return(SafeWriteAsync(
                               new CloseConnectionMessage(connectionId, $"Duplicate connection ID {connectionId}")));
                }
            }
        }
Пример #2
0
        private async Task ProcessMessageAsync(ClientConnectionContext clientContext, CancellationToken cancellation)
        {
            var connectionId = clientContext.ConnectionId;

            try
            {
                // Check if channel is closed.
                while (await clientContext.Input.WaitToReadAsync(cancellation))
                {
                    while (clientContext.Input.TryRead(out var serviceMessage))
                    {
                        cancellation.ThrowIfCancellationRequested();

                        switch (serviceMessage)
                        {
                        case OpenConnectionMessage openConnectionMessage:
                            await OnConnectedAsyncCore(clientContext, openConnectionMessage);

                            break;

                        case CloseConnectionMessage closeConnectionMessage:
                            // should not wait for application task when inside the application task
                            // As the messages are in a queue, close message should be after all the other messages
                            await PerformDisconnectCore(closeConnectionMessage.ConnectionId, false);

                            return;

                        case ConnectionDataMessage connectionDataMessage:
                            ProcessOutgoingMessages(clientContext, connectionDataMessage);
                            break;

                        default:
                            break;
                        }
                    }
                }
            }
            catch (OperationCanceledException e)
            {
                Log.SendLoopStopped(Logger, connectionId, e);
            }
            catch (Exception e)
            {
                // Internal exception is already caught and here only for channel exception.
                // Notify client to disconnect.
                Log.SendLoopStopped(Logger, connectionId, e);
                _ = PerformDisconnectCore(connectionId, false);
                _ = SafeWriteAsync(new CloseConnectionMessage(connectionId, e.Message));
            }
        }
Пример #3
0
        private async Task OnConnectedAsyncCore(ClientConnectionContext clientContext, OpenConnectionMessage message)
        {
            var connectionId = message.ConnectionId;

            try
            {
                clientContext.Transport = await _clientConnectionManager.CreateConnection(message);

                Log.ConnectedStarting(Logger, connectionId);
            }
            catch (Exception e)
            {
                Log.ConnectedStartingFailed(Logger, connectionId, e);
                // Should not wait for application task inside the application task
                _ = PerformDisconnectCore(connectionId, false);
                _ = SafeWriteAsync(new CloseConnectionMessage(connectionId, e.Message));
            }
        }
        public static Task WriteAsync(
            this ClientConnectionContext connection,
            string connectionId,
            object value,
            IServiceProtocol protocol,
            JsonSerializer serializer,
            IMemoryPool pool)
        {
            using var writer = new MemoryPoolTextWriter(pool);
            serializer.Serialize(writer, value);
            writer.Flush();

            // Reuse ConnectionDataMessage to wrap the payload
            var wrapped = new ConnectionDataMessage(string.Empty, writer.Buffer);
            var message = new ConnectionDataMessage(connectionId, protocol.GetMessageBytes(wrapped));

            return(connection.WriteMessageAsync(message));
        }
Пример #5
0
        private void ProcessOutgoingMessages(ClientConnectionContext clientContext, ConnectionDataMessage connectionDataMessage)
        {
            var connectionId = connectionDataMessage.ConnectionId;

            try
            {
                var payload = connectionDataMessage.Payload;
                Log.WriteMessageToApplication(Logger, payload.Length, connectionId);
                var message = GetString(payload);
                if (message == ReconnectMessage)
                {
                    clientContext.Transport?.Reconnected?.Invoke();
                }
                else
                {
                    clientContext.Transport?.OnReceived(message);
                }
            }
            catch (Exception e)
            {
                Log.FailToWriteMessageToApplication(Logger, nameof(ConnectionDataMessage), connectionDataMessage.ConnectionId, connectionDataMessage.TracingId, e);
            }
        }
Пример #6
0
        private async Task WaitForApplicationTask(ClientConnectionContext clientContext, bool closeGracefully)
        {
            clientContext.Output.TryComplete();
            var app = clientContext.ApplicationTask;

            if (!app.IsCompleted)
            {
                try
                {
                    if (!closeGracefully)
                    {
                        clientContext.CancelPendingRead();
                    }

                    using (var delayCts = new CancellationTokenSource())
                    {
                        var resultTask =
                            await Task.WhenAny(app, Task.Delay(CloseApplicationTimeout, delayCts.Token));

                        if (resultTask != app)
                        {
                            // Application task timed out and it might never end writing to Transport.Output, cancel reading the pipe so that our ProcessOutgoing ends
                            clientContext.CancelPendingRead();
                            Log.ApplicationTaskTimedOut(Logger);
                        }
                        else
                        {
                            delayCts.Cancel();
                        }
                    }
                }
                catch (Exception ex)
                {
                    Log.ApplicationTaskFailed(Logger, ex);
                }
            }
        }
Пример #7
0
 public bool TryGetClientConnection(string connectionId, out ClientConnectionContext connection)
 {
     return(_clientConnections.TryGetValue(connectionId, out connection));
 }
Пример #8
0
 public bool TryRemoveClientConnection(string connectionId, out ClientConnectionContext connection)
 {
     return(_clientConnections.TryRemove(connectionId, out connection));
 }
Пример #9
0
 public bool TryAddClientConnection(ClientConnectionContext connection)
 {
     return(_clientConnections.TryAdd(connection.ConnectionId, connection));
 }