private object ServerSendDuplexMessage(
            int clientId,
            Func <DuplexCallbackId, TMessage> messageFactory,
            Func <IServerDuplexQbservableProtocolSink, Func <int, Action <object>, Action <ExceptionDispatchInfo>, DuplexCallbackId> > registrationSelector)
        {
            Contract.Requires(messageFactory != null);
            Contract.Requires(registrationSelector != null);

            var waitForResponse = new ManualResetEventSlim(false);

            ExceptionDispatchInfo error = null;
            object result = null;

            var duplexSink = FindSink <IServerDuplexQbservableProtocolSink>();

            var id = registrationSelector(duplexSink)(
                clientId,
                value =>
            {
                result = value;
                waitForResponse.Set();
            },
                ex =>
            {
                error = ex;
                waitForResponse.Set();
            });

            var message = messageFactory(id);

            SendMessageSafeAsync(message).Wait(Cancel);

            waitForResponse.Wait(Cancel);

            if (error != null)
            {
                error.Throw();
            }

            return(result);
        }
Example #2
0
        private async Task SendObservableAsync(object untypedObservable, Type dataType, bool sendServerErrorsToClients, CancellationToken cancel)
        {
            Contract.Requires(untypedObservable != null);
            Contract.Requires(dataType != null);

            var networkErrors = new ConcurrentBag <ExceptionDispatchInfo>();

            ExceptionDispatchInfo expressionSecurityError     = null;
            ExceptionDispatchInfo qbservableSubscriptionError = null;
            ExceptionDispatchInfo qbservableError             = null;

            var terminationKind = NotificationKind.OnCompleted;

            try
            {
                var cancelSubscription = new CancellationTokenSource();

                cancel.Register(cancelSubscription.Cancel);

                IObservable <object> observable;

#if CAS
                new PermissionSet(PermissionState.Unrestricted).Assert();
#endif

                try
                {
                    observable = dataType.UpCast(untypedObservable);
                }
                finally
                {
#if CAS
                    PermissionSet.RevertAssert();
#endif
                }

                await observable
                .TraceSubscriptions(ServiceObservableName, true, true, ClientId)
                .TraceNotifications(ServiceObservableName, true, true, ClientId)
                .ForEachAsync(
                    async data =>
                {
                    try
                    {
                        await ServerSendAsync(NotificationKind.OnNext, data).ConfigureAwait(false);
                    }
                    catch (OperationCanceledException)
                    {
                    }
                    catch (Exception ex)
                    {
                        /* Collecting exceptions handles a possible race condition.  Since this code is using a fire-and-forget model to
                         * subscribe to the observable, due to the async OnNext handler, it's possible that more than one SendAsync task
                         * can be executing concurrently.  As a result, canceling the cancelSubscription below does not guarantee that
                         * this catch block won't run again.
                         */
                        networkErrors.Add(ExceptionDispatchInfo.Capture(ex));

                        cancelSubscription.Cancel();
                    }
                },
                    cancelSubscription.Token)
                .ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
                if (cancel.IsCancellationRequested && networkErrors.Count == 0)
                {
                    throw;
                }
            }
            catch (ExpressionSecurityException ex)
            {
                ShutdownReason = QbservableProtocolShutdownReason.ExpressionSecurityViolation;

                expressionSecurityError = ExceptionDispatchInfo.Capture(ex);
            }
            catch (QbservableSubscriptionException ex)
            {
                ShutdownReason = QbservableProtocolShutdownReason.ExpressionSubscriptionException;

                qbservableSubscriptionError = ExceptionDispatchInfo.Capture(ex.InnerException ?? ex);
            }
            catch (TargetInvocationException ex)
            {
                if (ex.InnerException is QbservableSubscriptionException)
                {
                    ShutdownReason = QbservableProtocolShutdownReason.ExpressionSubscriptionException;

                    qbservableSubscriptionError = ExceptionDispatchInfo.Capture(ex.InnerException.InnerException ?? ex.InnerException);
                }
                else
                {
                    qbservableSubscriptionError = ExceptionDispatchInfo.Capture(ex);
                }
            }
            catch (Exception ex)
            {
                terminationKind = NotificationKind.OnError;
                qbservableError = ExceptionDispatchInfo.Capture(ex);
            }

            var error = expressionSecurityError ?? qbservableSubscriptionError;

            if (error != null)
            {
                if (networkErrors.Count > 0)
                {
                    // It's not technically a network error, but since the client can't receive it anyway add it so that it's thrown later
                    networkErrors.Add(error);
                }
                else
                {
                    if (sendServerErrorsToClients || expressionSecurityError != null)
                    {
                        var exception = expressionSecurityError == null
              ? error.SourceException
              : new SecurityException(error.SourceException.Message);   // Remove stack trace

                        await ServerSendAsync(NotificationKind.OnError, exception).ConfigureAwait(false);
                    }

                    error.Throw();
                }
            }

            /* There's an acceptable race condition here whereby ForEachAsync is canceled by the external cancellation token though
             * it's still executing a fire-and-forget task.  It's possible for the fire-and-forget task to throw before seeing the
             * cancellation, yet after the following code has already executed.  In that case, since the cancellation was requested
             * externally, it's acceptable for the cancellation to simply beat the send error, thus the error can safely be ignored.
             */
            if (networkErrors.Count > 0)
            {
                throw new AggregateException(networkErrors.Select(e => e.SourceException));
            }
            else
            {
                try
                {
                    await ServerSendAsync(terminationKind, (qbservableError == null ? null : qbservableError.SourceException)).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                }

                if (qbservableError != null)
                {
                    qbservableError.Throw();
                }
            }
        }
Example #3
0
        public async Task ExecuteServerAsync(IQbservableProvider provider)
        {
            Task receivingAsync = null;
            ExceptionDispatchInfo fatalException = null;

            try
            {
                await InitializeSinksAsync().ConfigureAwait(false);

                var input = await ServerReceiveQueryAsync().ConfigureAwait(false);

                receivingAsync = ServerReceiveAsync();

                try
                {
                    await ExecuteServerQueryAsync(input, provider).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    ShutdownReason |= QbservableProtocolShutdownReason.BadClientRequest;

                    fatalException = ExceptionDispatchInfo.Capture(ex);

                    CancelAllCommunication(fatalException);
                }
            }
            catch (OperationCanceledException ex)
            {
                var exception = TryRollupExceptions();

                if (exception != null)
                {
                    ShutdownReason |= QbservableProtocolShutdownReason.ServerError;

                    fatalException = exception;
                }

                ShutdownReason |= QbservableProtocolShutdownReason.ClientTerminated;

                fatalException = ExceptionDispatchInfo.Capture(ex);
            }
            catch (Exception ex)
            {
                fatalException = ExceptionDispatchInfo.Capture(ex);

                CancelAllCommunication(fatalException);
            }

            if (receivingAsync != null)
            {
                try
                {
                    await receivingAsync.ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                }
                catch (Exception ex)
                {
                    AddError(ExceptionDispatchInfo.Capture(ex));
                }
            }

            if (fatalException != null)
            {
                fatalException.Throw();
            }
        }
Example #4
0
        public async Task ExecuteServerAsync(IQbservableProvider provider)
        {
            Contract.Requires(!IsClient);

            Task receivingAsync = null;
            ExceptionDispatchInfo fatalException = null;

            try
            {
                await InitializeSinksAsync().ConfigureAwait(false);

                var input = await ServerReceiveQueryAsync().ConfigureAwait(false);

                receivingAsync = ServerReceiveAsync();

                try
                {
                    await ExecuteServerQueryAsync(input, provider).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    if (ShutdownReason == QbservableProtocolShutdownReason.None)
                    {
                        ShutdownReason = QbservableProtocolShutdownReason.BadClientRequest;
                    }

                    CancelAllCommunication(ex);

                    fatalException = ExceptionDispatchInfo.Capture(ex);
                }
            }
            catch (OperationCanceledException ex)
            {
                if (errors.Count == 1)
                {
                    if (ShutdownReason == QbservableProtocolShutdownReason.None)
                    {
                        ShutdownReason = QbservableProtocolShutdownReason.ServerError;
                    }

                    fatalException = errors[0];
                }
                else if (errors.Count > 1)
                {
                    if (ShutdownReason == QbservableProtocolShutdownReason.None)
                    {
                        ShutdownReason = QbservableProtocolShutdownReason.ServerError;
                    }

                    fatalException = ExceptionDispatchInfo.Capture(new AggregateException(errors.Select(e => e.SourceException)));
                }

                if (ShutdownReason == QbservableProtocolShutdownReason.None)
                {
                    ShutdownReason = QbservableProtocolShutdownReason.ClientTerminated;
                }

                fatalException = ExceptionDispatchInfo.Capture(ex);
            }
            catch (Exception ex)
            {
                CancelAllCommunication(ex);

                fatalException = ExceptionDispatchInfo.Capture(ex);
            }

            if (receivingAsync != null)
            {
                try
                {
                    await receivingAsync.ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                }
                catch (Exception ex)
                {
                    errors.Add(ExceptionDispatchInfo.Capture(ex));
                }
            }

            if (fatalException != null)
            {
                fatalException.Throw();
            }
        }