コード例 #1
0
        protected QbservableProtocol(QbservableServiceOptions serviceOptions, CancellationToken cancel)
        {
            Contract.Requires(serviceOptions != null);
            Contract.Ensures(!IsClient);

            ServiceOptions = serviceOptions;
            cancel.Register(protocolCancellation.Cancel, useSynchronizationContext: false);
        }
コード例 #2
0
        internal QbservableProtocol(NetworkStream stream, IRemotingFormatter formatter, QbservableServiceOptions serviceOptions, CancellationToken cancel)
            : this(stream, formatter, cancel)
        {
            Contract.Ensures(!IsClient);

            this.serviceOptions = serviceOptions;
            this.isClient       = false;
        }
コード例 #3
0
 public static IObservable <TcpClientTermination> CreateService <TSource, TResult>(
     IPEndPoint endPoint,
     IRemotingFormatter formatter,
     QbservableServiceOptions options,
     Func <IObservable <TSource>, IQbservable <TResult> > service)
 {
     return(CreateService <TSource, TResult>(endPoint, () => formatter, options, service));
 }
コード例 #4
0
        internal QbservableProtocol(TSource source, QbservableServiceOptions serviceOptions, CancellationToken cancel)
            : base(serviceOptions, cancel)
        {
            Contract.Requires(source != null);
            Contract.Requires(serviceOptions != null);
            Contract.Ensures(!IsClient);

            Source = source;
        }
コード例 #5
0
 public TcpServerQbservableProvider(
     QbservableProtocol protocol,
     QbservableServiceOptions options,
     Func <object, IQbservable <TSource> > sourceSelector)
 {
     this.protocol       = protocol;
     this.options        = options;
     this.sourceSelector = sourceSelector;
 }
コード例 #6
0
 public static IObservable <TcpClientTermination> CreateService <TSource, TResult>(
     AppDomainSetup appDomainSetup,
     IPEndPoint endPoint,
     QbservableServiceOptions options,
     Func <IObservable <TSource>, IQbservable <TResult> > service,
     [CallerMemberName] string appDomainBaseName = null,
     params Assembly[] fullTrustAssemblies)
 {
     return(CreateService <TSource, TResult>(appDomainSetup, endPoint, CreateDefaultFormatter, options, service, appDomainBaseName, fullTrustAssemblies));
 }
コード例 #7
0
        public QbservableServiceOptions(QbservableServiceOptions clone)
        {
            Contract.Requires(clone != null);
            Contract.Ensures(!IsFrozen);

            sendServerErrorsToClients = clone.sendServerErrorsToClients;
            enableDuplex = clone.enableDuplex;
            allowExpressionsUnrestricted = clone.allowExpressionsUnrestricted;
            expressionOptions            = clone.expressionOptions;
            evaluationContext            = clone.evaluationContext;
        }
コード例 #8
0
 public static IObservable <TcpClientTermination> CreateService <TSource, TResult>(
     AppDomainSetup appDomainSetup,
     IPEndPoint endPoint,
     Func <IRemotingFormatter> formatterFactory,
     QbservableServiceOptions options,
     Func <IObservable <TSource>, IObservable <TResult> > service,
     [CallerMemberName] string appDomainBaseName = null,
     params Assembly[] fullTrustAssemblies)
 {
     return(CreateService <TSource, TResult>(appDomainSetup, endPoint, formatterFactory, options, new QbservableServiceConverter <TSource, TResult>(service).Convert, appDomainBaseName, fullTrustAssemblies));
 }
コード例 #9
0
            public IObservable <TcpClientTermination> CreateService(
                IPEndPoint endPoint,
                QbservableServiceOptions options,
                CreateServiceProxyDelegates <TSource, TResult> delegates)
            {
                new PermissionSet(PermissionState.Unrestricted).Assert();

                try
                {
                    InitializeInFullTrust();
                }
                finally
                {
                    PermissionSet.RevertAssert();
                }

                Func <IRemotingFormatter> formatterFactory;
                Func <IObservable <TSource>, IQbservable <TResult> > service;

                /* Retrieving a cross-domain delegate always fails with a SecurityException due to
                 * a Demand for ReflectionPermission, regardless of whether that permission is asserted
                 * here or even whether full trust is asserted (commented line).  It is unclear why the
                 * assertions don't work.  The only solution appears to be that the delegates must
                 * point to public members.
                 *
                 * (Alternatively, adding the ReflectionPermission to the minimum permission set of the
                 * AppDomain works as well, but it's more secure to disallow it entirely to prevent
                 * reflection from executing within clients' expression trees, just in case the host
                 * relaxes the service options to allow unrestricted expressions constrained only by
                 * the minimum AppDomain permissions; i.e., The Principle of Least Surprise.)
                 *
                 * new PermissionSet(PermissionState.Unrestricted).Assert();
                 */

                try
                {
                    formatterFactory = delegates.FormatterFactory;
                    service          = delegates.Service;
                }
                catch (SecurityException ex)
                {
                    throw new ArgumentException(Errors.CreateServiceDelegatesNotPublic, ex);
                }
                finally
                {
                    /* This line is unnecessary - see comments above.
                     * PermissionSet.RevertAssert();
                     */
                }

                return(QbservableTcpServer
                       .CreateService(endPoint, formatterFactory, options, service)
                       .RemotableWithoutConfiguration());
            }
コード例 #10
0
        public static IObservable <ClientTermination> CreateService <TSource, TResult>(
            IQactiveProvider provider,
            QbservableServiceOptions options,
            Func <IObservable <TSource>, IObservable <TResult> > service)
        {
            Contract.Requires(provider != null);
            Contract.Requires(options != null);
            Contract.Requires(service != null);
            Contract.Ensures(Contract.Result <IObservable <ClientTermination> >() != null);

            return(CreateService <TSource, TResult>(provider, options, request => service(request).AsQbservable()));
        }
コード例 #11
0
ファイル: TcpQbservable.cs プロジェクト: trimonovds/Qactive
        public static IObservable <ClientTermination> ServeTcp <TSource>(
            this IQbservable <TSource> source,
            IPEndPoint endPoint,
            QbservableServiceOptions options)
        {
            Contract.Requires(source != null);
            Contract.Requires(endPoint != null);
            Contract.Requires(options != null);
            Contract.Ensures(Contract.Result <IObservable <ClientTermination> >() != null);

            return(TcpQbservableServer.CreateService <object, TSource>(endPoint, options, _ => source));
        }
コード例 #12
0
        public static IObservable <ClientTermination> CreateService <TSource, TResult>(
            Uri uri,
            QbservableServiceOptions options,
            Func <IObservable <TSource>, IQbservable <TResult> > service)
        {
            Contract.Requires(uri != null);
            Contract.Requires(options != null);
            Contract.Requires(service != null);
            Contract.Ensures(Contract.Result <IObservable <ClientTermination> >() != null);

            return(QbservableServer.CreateService(WebSocketQactiveProvider.Server(uri), options, service));
        }
コード例 #13
0
        public static IObservable <ClientTermination> CreateService <TSource, TResult>(
            IPEndPoint endPoint,
            QbservableServiceOptions options,
            Func <IObservable <TSource>, IQbservable <TResult> > service)
        {
            Contract.Requires(endPoint != null);
            Contract.Requires(options != null);
            Contract.Requires(service != null);
            Contract.Ensures(Contract.Result <IObservable <ClientTermination> >() != null);

            return(QbservableServer.CreateService(TcpQactiveProvider.Server(endPoint), options, service));
        }
コード例 #14
0
        public static IObservable <ClientTermination> ServeWebSocket <TSource>(
            this IQbservable <TSource> source,
            Uri uri,
            QbservableServiceOptions options)
        {
            Contract.Requires(source != null);
            Contract.Requires(uri != null);
            Contract.Requires(options != null);
            Contract.Ensures(Contract.Result <IObservable <ClientTermination> >() != null);

            return(WebSocketQbservableServer.CreateService <object, TSource>(uri, options, _ => source));
        }
コード例 #15
0
        public StreamQbservableProtocol(Stream stream, IRemotingFormatter formatter, QbservableServiceOptions serviceOptions, CancellationToken cancel)
            : base(stream, serviceOptions, cancel)
        {
            Contract.Requires(stream != null);
            Contract.Requires(formatter != null);
            Contract.Requires(serviceOptions != null);

            this.formatter = formatter;

            sendQ.UnhandledExceptions.Subscribe(AddError);
            receiveQ.UnhandledExceptions.Subscribe(AddError);
        }
コード例 #16
0
        public static IObservable <TcpClientTermination> CreateService <TSource, TResult>(
            AppDomainSetup appDomainSetup,
            PermissionSet permissions,
            IPEndPoint endPoint,
            Func <IRemotingFormatter> formatterFactory,
            QbservableServiceOptions options,
            Func <IObservable <TSource>, IQbservable <TResult> > service,
            [CallerMemberName] string appDomainBaseName = null,
            params Assembly[] fullTrustAssemblies)
        {
            var minimumPermissions = new PermissionSet(PermissionState.None);

            minimumPermissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
            minimumPermissions.AddPermission(new SocketPermission(NetworkAccess.Accept, TransportType.Tcp, endPoint.Address.ToString(), endPoint.Port));

            var entryAssembly = Assembly.GetEntryAssembly();

            var domain = AppDomain.CreateDomain(
                Interlocked.Increment(ref appDomainNumber) + ':' + appDomainBaseName,
                null,
                appDomainSetup, minimumPermissions.Union(permissions),
                new[]
            {
                typeof(QbservableTcpServer).Assembly.Evidence.GetHostEvidence <StrongName>(),
                typeof(System.Reactive.Linq.Observable).Assembly.Evidence.GetHostEvidence <StrongName>(),
                typeof(System.Reactive.Linq.Qbservable).Assembly.Evidence.GetHostEvidence <StrongName>(),
                typeof(System.Reactive.Notification).Assembly.Evidence.GetHostEvidence <StrongName>(),
                typeof(System.Reactive.IEventPattern <,>).Assembly.Evidence.GetHostEvidence <StrongName>(),
                typeof(System.Reactive.Concurrency.TaskPoolScheduler).Assembly.Evidence.GetHostEvidence <StrongName>()
            }
                .Concat(entryAssembly == null ? new StrongName[0] : new[] { entryAssembly.Evidence.GetHostEvidence <StrongName>() })
                .Concat(fullTrustAssemblies.Select(assembly => assembly.Evidence.GetHostEvidence <StrongName>()))
                .ToArray());

            try
            {
                var handle = Activator.CreateInstanceFrom(
                    domain,
                    typeof(CreateServiceProxy <TSource, TResult>).Assembly.ManifestModule.FullyQualifiedName,
                    typeof(CreateServiceProxy <TSource, TResult>).FullName);

                var proxy = (CreateServiceProxy <TSource, TResult>)handle.Unwrap();

                return(proxy.CreateService(endPoint, options, new CreateServiceProxyDelegates <TSource, TResult>(formatterFactory, service))
                       .Finally(() => AppDomain.Unload(domain)));
            }
            catch
            {
                AppDomain.Unload(domain);
                throw;
            }
        }
コード例 #17
0
        public ServerQbservableProvider(
            IQbservableProtocol protocol,
            QbservableServiceOptions options,
            Func <object, IQbservable <TSource> > sourceSelector)
        {
            Contract.Requires(protocol != null);
            Contract.Requires(options != null);
            Contract.Requires(sourceSelector != null);

            Protocol            = protocol;
            Options             = options;
            this.sourceSelector = sourceSelector;
        }
コード例 #18
0
        public static IObservable <TcpClientTermination> CreateService <TSource, TResult>(
            AppDomainSetup appDomainSetup,
            IPEndPoint endPoint,
            Func <IRemotingFormatter> formatterFactory,
            QbservableServiceOptions options,
            Func <IObservable <TSource>, IQbservable <TResult> > service,
            [CallerMemberName] string appDomainBaseName = null,
            params Assembly[] fullTrustAssemblies)
        {
            var permissions = new PermissionSet(PermissionState.None);

            return(CreateService <TSource, TResult>(appDomainSetup, permissions, endPoint, formatterFactory, options, service, appDomainBaseName, fullTrustAssemblies));
        }
コード例 #19
0
ファイル: TcpQbservable.cs プロジェクト: trimonovds/Qactive
        public static IObservable <ClientTermination> ServeQbservableTcp <TSource>(
            this IObservable <TSource> source,
            IPEndPoint endPoint,
            ITcpQactiveProviderTransportInitializer transportInitializer,
            QbservableServiceOptions options)
        {
            Contract.Requires(source != null);
            Contract.Requires(endPoint != null);
            Contract.Requires(transportInitializer != null);
            Contract.Requires(options != null);
            Contract.Ensures(Contract.Result <IObservable <ClientTermination> >() != null);

            return(TcpQbservableServer.CreateService <object, TSource>(endPoint, transportInitializer, options, _ => source));
        }
コード例 #20
0
        public static IObservable <ClientTermination> ServeQbservableWebSocket <TSource>(
            this IObservable <TSource> source,
            Uri uri,
            IWebSocketQactiveProviderTransportInitializer transportInitializer,
            QbservableServiceOptions options)
        {
            Contract.Requires(source != null);
            Contract.Requires(uri != null);
            Contract.Requires(transportInitializer != null);
            Contract.Requires(options != null);
            Contract.Ensures(Contract.Result <IObservable <ClientTermination> >() != null);

            return(WebSocketQbservableServer.CreateService <object, TSource>(uri, transportInitializer, options, _ => source));
        }
コード例 #21
0
        public override IObservable <ClientTermination> Listen(QbservableServiceOptions options, Func <IQbservableProtocol, IParameterizedQbservableProvider> providerFactory)
        {
            var l = new HttpListener()
            {
                Prefixes =
                {
                    Uri.ToString()
                }
            };

            return(from listener in Observable.Return(l)
                   .Do(listener =>
            {
                Starting();

                listener.Start();

                if (transportInitializer != null)
                {
                    transportInitializer.StartedListener(serverNumber.Value, Uri);
                }

                Started();
            })
                   from context in Observable.FromAsync(listener.GetContextAsync)
                   .SelectMany(context => context.AcceptWebSocketAsync(protocol))
                   .Repeat()
                   .Finally(() =>
            {
                Stopping();

                listener.Stop();

                if (transportInitializer != null)
                {
                    transportInitializer.StoppedListener(serverNumber.Value, Uri);
                }

                Stopped();
            })
                   let capturedId = new CapturedId(Id + " C" + Interlocked.Increment(ref lastServerClientNumber) + " " + context.Origin)
                                    let capturedShutdownReason = new CapturedShutdownReason()
                                                                 from termination in Observable.StartAsync(cancel => AcceptAsync(context, capturedId, capturedShutdownReason, options, providerFactory, cancel))
                                                                 .Finally(() => Shutdown(context.WebSocket, capturedShutdownReason.Value, capturedId.Value))
                                                                 select termination);
        }
コード例 #22
0
        public static IObservable <ClientTermination> CreateService <TSource, TResult>(
            IQactiveProvider provider,
            QbservableServiceOptions options,
            Func <IObservable <TSource>, IQbservable <TResult> > service)
        {
            Contract.Requires(provider != null);
            Contract.Requires(options != null);
            Contract.Requires(service != null);
            Contract.Ensures(Contract.Result <IObservable <ClientTermination> >() != null);

            return(provider.Listen(
                       options,
                       protocol => new ServerQbservableProvider <TResult>(
                           protocol,
                           options,
                           argument => argument == null && typeof(TSource).GetIsValueType()
                                ? service(Observable.Return(default(TSource)))
                                : service(Observable.Return((TSource)argument)))));
        }
コード例 #23
0
        public SecurityExpressionVisitor(QbservableServiceOptions serviceOptions)
        {
            this.options = serviceOptions.ExpressionOptions;
            this.context = serviceOptions.EvaluationContext;

            if (options.HasFlag(ExpressionOptions.AllowTypeTests) &&
                options.HasFlag(ExpressionOptions.AllowExplicitConversions))
            {
                context.EnsureHasKnownOperator("Cast");
                context.EnsureHasKnownOperator("OfType");
            }

            if (options.HasFlag(ExpressionOptions.AllowCatchBlocks))
            {
                context.EnsureHasKnownOperator("Catch");
                context.EnsureHasKnownOperator("OnErrorResumeNext");
                context.EnsureHasKnownOperator("Retry");
            }
        }
コード例 #24
0
        public SecurityExpressionVisitor(QbservableServiceOptions serviceOptions)
        {
            Contract.Requires(serviceOptions != null);

            Options = serviceOptions.ExpressionOptions;
            Context = serviceOptions.EvaluationContext;

            if (Options.HasFlag(ExpressionOptions.AllowTypeTests) &&
                Options.HasFlag(ExpressionOptions.AllowExplicitConversions))
            {
                Context.EnsureHasKnownOperator("Cast");
                Context.EnsureHasKnownOperator("OfType");
            }

            if (Options.HasFlag(ExpressionOptions.AllowCatchBlocks))
            {
                Context.EnsureHasKnownOperator("Catch");
                Context.EnsureHasKnownOperator("OnErrorResumeNext");
                Context.EnsureHasKnownOperator("Retry");
            }
        }
コード例 #25
0
        public override IObservable <ClientTermination> Listen(QbservableServiceOptions options, Func <IQbservableProtocol, IParameterizedQbservableProvider> providerFactory)
        {
            return(from listener in Observable.Return(new TcpListener(EndPoint))
                   .Do(listener =>
            {
                Starting();

                listener.Start();

                if (transportInitializer != null)
                {
                    transportInitializer.StartedListener(serverNumber.Value, listener.LocalEndpoint);
                }

                Started();
            })
                   from client in Observable.FromAsync(listener.AcceptTcpClientAsync).Repeat()
                   .Finally(() =>
            {
                Stopping();

                var endPoint = listener.LocalEndpoint;

                listener.Stop();

                if (transportInitializer != null)
                {
                    transportInitializer.StoppedListener(serverNumber.Value, endPoint);
                }

                Stopped();
            })
                   let capturedId = new CapturedId(Id + " C" + Interlocked.Increment(ref lastServerClientNumber) + " " + client.Client.RemoteEndPoint)
                                    from termination in Observable.StartAsync(cancel => AcceptAsync(client, capturedId, options, providerFactory, cancel))
                                    .Finally(() => Shutdown(client.Client, capturedId.Value))
                                    select termination);
        }
コード例 #26
0
 protected QbservableProtocol(NetworkStream stream, IRemotingFormatter formatter, QbservableServiceOptions serviceOptions, CancellationToken cancel)
     : base(stream, formatter, serviceOptions, cancel)
 {
     Contract.Ensures(!IsClient);
 }
コード例 #27
0
        private static async Task <IStreamQbservableProtocol> NegotiateServerAsync(object baseId, Stream stream, IRemotingFormatter formatter, QbservableServiceOptions serviceOptions, CancellationToken cancel)
        {
            Contract.Requires(stream != null);
            Contract.Requires(formatter != null);
            Contract.Requires(serviceOptions != null);

            var protocol = StreamQbservableProtocolFactory.CreateServer(stream, formatter, serviceOptions, cancel);

            var lengthBuffer = new byte[4];

            await protocol.ReceiveAsync(lengthBuffer, 0, 4).ConfigureAwait(false);

            var length = BitConverter.ToInt32(lengthBuffer, 0);

            if (length <= 0 || length > 255)
            {
                throw new InvalidOperationException("Invalid client ID received. (" + length + " bytes)");
            }

            var clientIdBuffer = new byte[length];

            await protocol.ReceiveAsync(clientIdBuffer, 0, clientIdBuffer.Length).ConfigureAwait(false);

            var clientId = Encoding.ASCII.GetString(clientIdBuffer);

            if (clientId == null || string.IsNullOrWhiteSpace(clientId))
            {
                throw new InvalidOperationException("Invalid client ID received (empty or only whitespace).");
            }

            await protocol.SendAsync(clientIdBuffer, 0, clientIdBuffer.Length).ConfigureAwait(false);

            protocol.ClientId = baseId + " (" + clientId + ")";

            return(protocol);
        }
コード例 #28
0
        private async Task <TcpClientTermination> AcceptAsync(
            TcpClient client,
            CapturedId capturedId,
            QbservableServiceOptions options,
            Func <IQbservableProtocol, IParameterizedQbservableProvider> providerFactory,
            CancellationToken cancel)
        {
            Contract.Requires(client != null);
            Contract.Requires(capturedId != null);
            Contract.Requires(options != null);
            Contract.Requires(providerFactory != null);

            ReceivingConnection(idOverride: capturedId.Value);

            // These default settings enable a proper graceful shutdown. DisconnectAsync is used instead of Close on the server-side to request
            // that the client terminates the connection ASAP. This is important because it prevents the server-side socket from going into a
            // TIME_WAIT state rather than the client. The linger option is meant to ensure that any outgoing data, such as an exception, is
            // completely transmitted to the client before the socket terminates. The seconds specified is arbitrary, though chosen to be large
            // enough to transfer any remaining data successfully and small enough to cause a timely disconnection. A custom prepareSocket
            // implementation can always change it via SetSocketOption, if necessary.
            //
            // https://msdn.microsoft.com/en-us/library/system.net.sockets.socket.disconnect(v=vs.110).aspx
            client.LingerState.Enabled    = true;
            client.LingerState.LingerTime = LingerTimeInSeconds;

            prepareSocket(client.Client);

            var watch = Stopwatch.StartNew();

            var localEndPoint  = client.Client.LocalEndPoint;
            var remoteEndPoint = client.Client.RemoteEndPoint;

            var exceptions     = new List <ExceptionDispatchInfo>();
            var shutdownReason = QbservableProtocolShutdownReason.None;

            try
            {
                using (var stream = new NetworkStream(client.Client, ownsSocket: false))
                    using (var protocol = await NegotiateServerAsync(capturedId.Value, stream, formatterFactory(), options, cancel).ConfigureAwait(false))
                    {
                        capturedId.Value = protocol.ClientId;

                        var provider = providerFactory(protocol);

                        ReceivedConnection(idOverride: capturedId.Value);

                        try
                        {
                            await protocol.ExecuteServerAsync(provider).ConfigureAwait(false);
                        }
                        catch (OperationCanceledException)
                        {
                        }
                        catch (Exception ex)
                        {
                            exceptions.Add(ExceptionDispatchInfo.Capture(ex));
                        }
                        finally
                        {
                            shutdownReason = protocol.ShutdownReason;
                        }

                        var protocolExceptions = protocol.Exceptions;

                        if (protocolExceptions != null)
                        {
                            foreach (var exception in protocolExceptions)
                            {
                                exceptions.Add(exception);
                            }
                        }
                    }
            }
            catch (OperationCanceledException)
            {
                shutdownReason = QbservableProtocolShutdownReason.ProtocolNegotiationCanceled;
            }
            catch (Exception ex)
            {
                shutdownReason = QbservableProtocolShutdownReason.ProtocolNegotiationError;

                exceptions.Add(ExceptionDispatchInfo.Capture(ex));
            }

            return(new TcpClientTermination(localEndPoint, remoteEndPoint, watch.Elapsed, shutdownReason, exceptions));
        }
コード例 #29
0
        public TestQbservableProtocol(object clientId, IObservable <TestMessage> source, IObserver <TestMessage> other, QbservableServiceOptions serviceOptions)
            : base(source, serviceOptions, CancellationToken.None)
        {
            this.other = other;

            ClientId = clientId;
        }
コード例 #30
0
        private async Task <WebSocketClientTermination> AcceptAsync(
            HttpListenerWebSocketContext context,
            CapturedId capturedId,
            CapturedShutdownReason capturedShutdownReason,
            QbservableServiceOptions options,
            Func <IQbservableProtocol, IParameterizedQbservableProvider> providerFactory,
            CancellationToken cancel)
        {
            Contract.Requires(context != null);
            Contract.Requires(capturedId != null);
            Contract.Requires(capturedShutdownReason != null);
            Contract.Requires(options != null);
            Contract.Requires(providerFactory != null);

            ReceivingConnection(idOverride: capturedId.Value);

            prepareSocket(context.WebSocket);

            var watch = Stopwatch.StartNew();

            var exceptions     = new List <ExceptionDispatchInfo>();
            var shutdownReason = QbservableProtocolShutdownReason.None;

            try
            {
                using (var stream = new WebSocketStream(context.WebSocket))
                    using (var protocol = await NegotiateServerAsync(capturedId.Value, stream, formatterFactory(), options, cancel).ConfigureAwait(false))
                    {
                        capturedId.Value = protocol.ClientId;

                        var provider = providerFactory(protocol);

                        ReceivedConnection(idOverride: capturedId.Value);

                        try
                        {
                            await protocol.ExecuteServerAsync(provider).ConfigureAwait(false);
                        }
                        catch (OperationCanceledException)
                        {
                        }
                        catch (Exception ex)
                        {
                            exceptions.Add(ExceptionDispatchInfo.Capture(ex));
                        }
                        finally
                        {
                            shutdownReason = protocol.ShutdownReason;
                        }

                        var protocolExceptions = protocol.Exceptions;

                        if (protocolExceptions != null)
                        {
                            foreach (var exception in protocolExceptions)
                            {
                                exceptions.Add(exception);
                            }
                        }
                    }
            }
            catch (OperationCanceledException)
            {
                shutdownReason = QbservableProtocolShutdownReason.ProtocolNegotiationCanceled;
            }
            catch (Exception ex)
            {
                shutdownReason = QbservableProtocolShutdownReason.ProtocolNegotiationError;

                exceptions.Add(ExceptionDispatchInfo.Capture(ex));
            }
            finally
            {
                capturedShutdownReason.Value = shutdownReason;
            }

            return(new WebSocketClientTermination(Uri, context.Origin, watch.Elapsed, shutdownReason, exceptions));
        }