public async Task HandleAsync(IInvocationStart request, IAppConnection sourceConnection, ITransportChannel sourceChannel)
        {
            IAppConnection       targetConnection = null;
            ITransportChannel    targetChannel    = null;
            InvocationDescriptor callDescriptor   = null;
            var startMs = _stopwatch.ElapsedMilliseconds;

            try
            {
                Log.Info("Handling invocation {0} from {{{1}}}: {{{2}}}", sourceChannel.Id, sourceConnection, request);
                targetConnection = await request.Target.Handle(_resolveTargetConnectionHandler, sourceConnection).ConfigureAwait(false);

                targetChannel = await targetConnection.CreateChannelAsync().ConfigureAwait(false);

                Log.Debug("Created channel {0} for invocation {1} from {{{2}}} to {{{3}}}: {{{4}}}", targetChannel.Id, sourceChannel.Id, sourceConnection, targetConnection, request);
                using (var invocationStarting = _protocolMessageFactory.CreateInvocationStarting())
                {
                    var serialized = _protocolSerializer.Serialize(invocationStarting);
                    try
                    {
                        await sourceChannel.Out.WriteAsync(new TransportMessageFrame(serialized)).ConfigureAwait(false);

                        Log.Trace("Sent starting event for invocation {0}", sourceChannel.Id);
                    }
                    catch
                    {
                        serialized.Dispose();
                        throw;
                    }
                }
                using (var invocationRequested = request.Target.Handle(_createRequestHandler, sourceConnection))
                {
                    startMs        = _stopwatch.ElapsedMilliseconds;
                    callDescriptor = new InvocationDescriptor(
                        sourceConnection.Info,
                        targetConnection.Info,
                        invocationRequested.ServiceId,
                        invocationRequested.ServiceAlias.GetValueOrDefault(),
                        invocationRequested.MethodId);
                    _appLifecycleManager.OnInvocationStarted(new InvocationStartedEventDescriptor(callDescriptor));
                    var serialized = _protocolSerializer.Serialize(invocationRequested);
                    try
                    {
                        await targetChannel.Out.WriteAsync(new TransportMessageFrame(serialized)).ConfigureAwait(false);

                        Log.Trace("Sent requested event for invocation {0} to {1}", targetChannel.Id, targetConnection);
                    }
                    catch
                    {
                        serialized.Dispose();
                        throw;
                    }
                }
                var propagateTask1 = TaskRunner.RunInBackground(() => PropagateAsync(sourceChannel.In, targetChannel.Out));
                var propagateTask2 = TaskRunner.RunInBackground(() => PropagateAsync(targetChannel.In, sourceChannel.Out));
                await Task.WhenAll(propagateTask1, propagateTask2).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                sourceChannel.Out.TryTerminate(ex);
                targetChannel?.Out.TryTerminate(ex);
                throw;
            }
            finally
            {
                try
                {
                    await Task
                    .WhenAll(
                        targetChannel?.In.ConsumeAsync((Action <TransportMessageFrame>)DisposeFrame).IgnoreExceptions() ?? TaskConstants.Completed,
                        sourceChannel.In.ConsumeAsync((Action <TransportMessageFrame>)DisposeFrame).IgnoreExceptions(),
                        targetChannel?.Completion ?? TaskConstants.Completed,
                        sourceChannel.Completion)
                    .ConfigureAwait(false);

                    Log.Info("Completed invocation {0} from {{{1}}} to {{{2}}}: {{{3}}}", sourceChannel.Id, sourceConnection, targetConnection, request);
                    OnActionFinished(callDescriptor, InvocationResult.Succeeded, startMs);
                }
                catch (OperationCanceledException)
                {
                    Log.Info("Canceled invocation {0} from {{{1}}} to {{{2}}}: {{{3}}}", sourceChannel.Id, sourceConnection, targetConnection, request);
                    OnActionFinished(callDescriptor, InvocationResult.Canceled, startMs);
                    throw;
                }
                catch (Exception ex)
                {
                    Log.Warn("Failed invocation {0} from {{{1}}} to {{{2}}}: {{{3}}}. Error: {4}", sourceChannel.Id, sourceConnection, targetConnection, request, ex.FormatTypeAndMessage());
                    OnActionFinished(callDescriptor, InvocationResult.Failed, startMs);
                    throw;
                }
            }
        }
        public async Task HandleAsync(IInvocationStart request, IAppConnection sourceConnection, ITransportChannel sourceChannel)
        {
            IAppConnection    targetConnection = null;
            ITransportChannel targetChannel    = null;

            try
            {
                Log.Info("Handling invocation {0} from {{{1}}}: {{{2}}}", sourceChannel.Id, sourceConnection, request);
                targetConnection = await request.Target.Handle(_resolveTargetConnectionHandler, sourceConnection).ConfigureAwait(false);

                targetChannel = await targetConnection.CreateChannelAsync().ConfigureAwait(false);

                Log.Debug("Created channel {0} for invocation {1} from {{{2}}} to {{{3}}}: {{{4}}}", targetChannel.Id, sourceChannel.Id, sourceConnection, targetConnection, request);
                using (var invocationStarting = _protocolMessageFactory.CreateInvocationStarting())
                {
                    var serialized = _protocolSerializer.Serialize(invocationStarting);
                    try
                    {
                        await sourceChannel.Out.WriteAsync(new TransportMessageFrame(serialized)).ConfigureAwait(false);

                        Log.Trace("Sent starting event for invocation {0}", sourceChannel.Id);
                    }
                    catch
                    {
                        serialized.Dispose();
                        throw;
                    }
                }
                using (var invocationRequested = request.Target.Handle(_createRequestHandler, sourceConnection))
                {
                    var serialized = _protocolSerializer.Serialize(invocationRequested);
                    try
                    {
                        await targetChannel.Out.WriteAsync(new TransportMessageFrame(serialized)).ConfigureAwait(false);

                        Log.Trace("Sent requested event for invocation {0} to {1}", targetChannel.Id, targetConnection);
                    }
                    catch
                    {
                        serialized.Dispose();
                        throw;
                    }
                }
                var propagateTask1 = TaskRunner.RunInBackground(() => PropagateAsync(sourceChannel.In, targetChannel.Out));
                var propagateTask2 = TaskRunner.RunInBackground(() => PropagateAsync(targetChannel.In, sourceChannel.Out));
                await Task.WhenAll(propagateTask1, propagateTask2).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                sourceChannel.Out.TryTerminate(ex);
                targetChannel?.Out.TryTerminate(ex);
                throw;
            }
            finally
            {
                try
                {
                    await Task
                    .WhenAll(
                        targetChannel?.In.ConsumeAsync((Action <TransportMessageFrame>)DisposeFrame).IgnoreExceptions() ?? TaskConstants.Completed,
                        sourceChannel.In.ConsumeAsync((Action <TransportMessageFrame>)DisposeFrame).IgnoreExceptions(),
                        targetChannel?.Completion ?? TaskConstants.Completed,
                        sourceChannel.Completion)
                    .ConfigureAwait(false);

                    Log.Info("Completed invocation {0} from {{{1}}} to {{{2}}}: {{{3}}}", sourceChannel.Id, sourceConnection, targetConnection, request);
                }
                catch (OperationCanceledException)
                {
                    Log.Info("Canceled invocation {0} from {{{1}}} to {{{2}}}: {{{3}}}", sourceChannel.Id, sourceConnection, targetConnection, request);
                    throw;
                }
                catch (Exception ex)
                {
                    Log.Warn("Failed invocation {0} from {{{1}}} to {{{2}}}: {{{3}}}. Error: {4}", sourceChannel.Id, sourceConnection, targetConnection, request, ex.FormatTypeAndMessage());
                    throw;
                }
            }
        }