protected override void OnStart(IConnection connection,
                                        string data,
                                        CancellationToken disconnectToken,
                                        Action initializeCallback,
                                        Action <Exception> errorCallback)
        {
            var requestHandler       = new PollingRequestHandler(HttpClient);
            var negotiateInitializer = new NegotiateInitializer(initializeCallback, errorCallback, ConnectDelay);

            // Save the success and abort cases so we can remove them after transport is initialized
            Action <string>   initializeSuccess = message => { negotiateInitializer.Complete(); };
            Action <IRequest> initializeAbort   = request => { negotiateInitializer.Abort(disconnectToken); };

            requestHandler.OnMessage += initializeSuccess;
            requestHandler.OnError   += negotiateInitializer.Complete;
            requestHandler.OnAbort   += initializeAbort;

            // Once we've initialized the connection we need to tear down the initializer functions
            negotiateInitializer.Initialized += () =>
            {
                requestHandler.OnMessage -= initializeSuccess;
                requestHandler.OnError   -= negotiateInitializer.Complete;
                requestHandler.OnAbort   -= initializeAbort;
            };

            // Add additional actions to each of the PollingRequestHandler events
            PollingSetup(connection, data, disconnectToken, requestHandler);

            requestHandler.Start();
            // Start initialization, essentially if we have an assume sucess clause in our negotiateInitializer
            // then we will start the countdown from the point which we start initialization.
            negotiateInitializer.Initialize();
        }
        protected override void OnStart(IConnection connection,
                                        string connectionData,
                                        CancellationToken disconnectToken,
                                        TransportInitializationHandler initializeHandler)
        {
            var requestHandler       = new PollingRequestHandler(HttpClient);
            var negotiateInitializer = new NegotiateInitializer(initializeHandler);

            Action <IRequest> initializeAbort = request => { negotiateInitializer.Abort(disconnectToken); };

            requestHandler.OnError += negotiateInitializer.Complete;
            requestHandler.OnAbort += initializeAbort;

            // If the transport fails to initialize we want to silently stop
            initializeHandler.OnFailure += () =>
            {
                requestHandler.Stop();
            };

            // Once we've initialized the connection we need to tear down the initializer functions and assign the appropriate onMessage function
            negotiateInitializer.Initialized += () =>
            {
                requestHandler.OnError -= negotiateInitializer.Complete;
                requestHandler.OnAbort -= initializeAbort;
            };

            // Add additional actions to each of the PollingRequestHandler events
            PollingSetup(connection, connectionData, disconnectToken, requestHandler, negotiateInitializer.Complete);

            requestHandler.Start();
        }
        protected override void OnStart(IConnection connection,
                                        string connectionData,
                                        CancellationToken disconnectToken,
                                        TransportInitializationHandler initializeHandler)
        {
            var requestHandler = new PollingRequestHandler(HttpClient);
            var negotiateInitializer = new NegotiateInitializer(initializeHandler);

            Action<IRequest> initializeAbort = request => { negotiateInitializer.Abort(disconnectToken); };

            requestHandler.OnError += negotiateInitializer.Complete;
            requestHandler.OnAbort += initializeAbort;

            // If the transport fails to initialize we want to silently stop
            initializeHandler.OnFailure += () =>
            {
                requestHandler.Stop();
            };

            // Once we've initialized the connection we need to tear down the initializer functions and assign the appropriate onMessage function
            negotiateInitializer.Initialized += () =>
            {
                requestHandler.OnError -= negotiateInitializer.Complete;
                requestHandler.OnAbort -= initializeAbort;
            };

            // Add additional actions to each of the PollingRequestHandler events
            PollingSetup(connection, connectionData, disconnectToken, requestHandler, negotiateInitializer.Complete);

            requestHandler.Start();
        }
        protected override void OnStart(IConnection connection,
                                        string data,
                                        CancellationToken disconnectToken,
                                        Action initializeCallback,
                                        Action<Exception> errorCallback)
        {
            var requestHandler = new PollingRequestHandler(HttpClient);
            var negotiateInitializer = new NegotiateInitializer(initializeCallback, errorCallback, ConnectDelay);

            // Save the success and abort cases so we can remove them after transport is initialized
            Action<string> initializeSuccess = message => { negotiateInitializer.Complete(); };
            Action<IRequest> initializeAbort = request => { negotiateInitializer.Abort(disconnectToken); };

            requestHandler.OnMessage += initializeSuccess;
            requestHandler.OnError += negotiateInitializer.Complete;
            requestHandler.OnAbort += initializeAbort;

            // Once we've initialized the connection we need to tear down the initializer functions
            negotiateInitializer.Initialized += () =>
            {
                requestHandler.OnMessage -= initializeSuccess;
                requestHandler.OnError -= negotiateInitializer.Complete;
                requestHandler.OnAbort -= initializeAbort;
            };

            // Add additional actions to each of the PollingRequestHandler events
            PollingSetup(connection, data, disconnectToken, requestHandler);

            requestHandler.Start();
            // Start initialization, essentially if we have an assume sucess clause in our negotiateInitializer
            // then we will start the countdown from the point which we start initialization.
            negotiateInitializer.Initialize();
        }
        public void PollingRequestHandlerDoesNotPollAfterClose()
        {
            var httpClient = new CustomHttpClient();
            var requestHandler = new PollingRequestHandler(httpClient);
            var active = true;

            Action verifyActive = () =>
            {
                Assert.True(active);
            };

            requestHandler.ResolveUrl = () =>
            {
                Assert.True(active);

                return "";
            };

            requestHandler.PrepareRequest += request =>
            {
                verifyActive();
            };

            requestHandler.OnPolling += verifyActive;

            requestHandler.OnAfterPoll += exception =>
            {
                verifyActive();
                return TaskAsyncHelper.Empty;
            };

            requestHandler.OnError += exception =>
            {
                verifyActive();
            };

            requestHandler.OnMessage += message =>
            {
                verifyActive();
            };

            requestHandler.OnAbort += request =>
            {
                active = false;
            };

            requestHandler.Start();

            // Let the request handler run for three seconds
            Thread.Sleep(TimeSpan.FromSeconds(.1));

            requestHandler.Stop();

            // Let all requests finish to see if we get any unintended results
            Thread.Sleep(TimeSpan.FromSeconds(1));
        }
        private void PollingSetup(IConnection connection,
                                  string data,
                                  CancellationToken disconnectToken,
                                  PollingRequestHandler requestHandler,
                                  Action onInitialized)
        {
            // reconnectInvoker is created new on each poll
            var reconnectInvoker = new ThreadSafeInvoker();

            var disconnectRegistration = disconnectToken.SafeRegister(state =>
            {
                reconnectInvoker.Invoke();
                requestHandler.Stop();
            }, null);

            requestHandler.ResolveUrl = () =>
            {
                var url = connection.Url;

                if (connection.MessageId == null)
                {
                    url += "connect";
                    connection.Trace(TraceLevels.Events, "LP Connect: {0}", url);
                }
                else if (IsReconnecting(connection))
                {
                    url += "reconnect";
                    connection.Trace(TraceLevels.Events, "LP Reconnect: {0}", url);
                }
                else
                {
                    url += "poll";
                    connection.Trace(TraceLevels.Events, "LP Poll: {0}", url);
                }

                url += GetReceiveQueryString(connection, data);

                return(url);
            };

            requestHandler.PrepareRequest += req =>
            {
                connection.PrepareRequest(req);
            };

            requestHandler.OnMessage += message =>
            {
                var shouldReconnect      = false;
                var disconnectedReceived = false;

                connection.Trace(TraceLevels.Messages, "LP: OnMessage({0})", message);

                TransportHelper.ProcessResponse(connection,
                                                message,
                                                out shouldReconnect,
                                                out disconnectedReceived,
                                                onInitialized);

                if (IsReconnecting(connection))
                {
                    // If the timeout for the reconnect hasn't fired as yet just fire the
                    // event here before any incoming messages are processed
                    TryReconnect(connection, reconnectInvoker);
                }

                if (shouldReconnect)
                {
                    // Transition into reconnecting state
                    connection.EnsureReconnecting();
                }

                if (disconnectedReceived)
                {
                    connection.Trace(TraceLevels.Messages, "Disconnect command received from server.");
                    connection.Disconnect();
                }
            };

            requestHandler.OnError += exception =>
            {
                reconnectInvoker.Invoke();

                if (!TransportHelper.VerifyLastActive(connection))
                {
                    return;
                }

                // Transition into reconnecting state
                connection.EnsureReconnecting();

                // Sometimes a connection might have been closed by the server before we get to write anything
                // so just try again and raise OnError.
                if (!ExceptionHelper.IsRequestAborted(exception) && !(exception is IOException))
                {
                    connection.OnError(exception);
                }
                else
                {
                    requestHandler.Stop();
                }
            };

            requestHandler.OnPolling += () =>
            {
                // Capture the cleanup within a closure so it can persist through multiple requests
                TryDelayedReconnect(connection, reconnectInvoker);
            };

            requestHandler.OnAfterPoll = exception =>
            {
                if (AbortHandler.TryCompleteAbort())
                {
                    // Abort() was called, so don't reconnect
                    requestHandler.Stop();
                }
                else
                {
                    reconnectInvoker = new ThreadSafeInvoker();

                    if (exception != null)
                    {
                        // Delay polling by the error delay
                        return(TaskAsyncHelper.Delay(ErrorDelay));
                    }
                }

                return(TaskAsyncHelper.Empty);
            };

            requestHandler.OnAbort += _ =>
            {
                disconnectRegistration.Dispose();

                // Complete any ongoing calls to Abort()
                // If someone calls Abort() later, have it no-op
                AbortHandler.CompleteAbort();
            };
        }
Exemple #7
0
        public void CancelledTaskHandledinLongPolling()
        {
            var tcs = new TaskCompletionSource<IResponse>();
            var wh = new TaskCompletionSource<Exception>();

            tcs.TrySetCanceled();

            var httpClient = new Mock<Microsoft.AspNet.SignalR.Client.Http.IHttpClient>();

            httpClient.Setup(c => c.Post(It.IsAny<string>(),
                It.IsAny<Action<Client.Http.IRequest>>(), It.IsAny<IDictionary<string, string>>(), It.IsAny<bool>()))
                .Returns(tcs.Task);

            var pollingHandler = new PollingRequestHandler(httpClient.Object);
            pollingHandler.Start();

            pollingHandler.OnError += (ex) => { wh.TrySetResult(ex); };

            Assert.True(wh.Task.Wait(TimeSpan.FromSeconds(5)));
            Assert.IsType(typeof(OperationCanceledException), wh.Task.Result);
        }
        public void PollingRequestHandlerDoesNotPollAfterCloseMidRequest()
        {
            var httpClient = new CustomHttpClient();
            var requestHandler = new PollingRequestHandler(httpClient);
            var active = true;
            var killRequest = false;
            Action verifyActive = () =>
            {
                Assert.True(active);
            };

            requestHandler.ResolveUrl = () =>
            {
                Assert.True(active);

                return "";
            };

            requestHandler.PrepareRequest += request =>
            {
                if (killRequest)
                {
                    // Execute the stop on a different thread so it does not share the lock
                    // This is to simulate a real world situation in which the user requests the connection to stop
                    ThreadPool.QueueUserWorkItem(state =>
                    {
                        requestHandler.Stop();
                    });
                }

                verifyActive();
            };

            requestHandler.OnPolling += verifyActive;

            requestHandler.OnMessage += message =>
            {
                verifyActive();
            };

            requestHandler.OnAfterPoll += exception =>
            {
                verifyActive();
                return TaskAsyncHelper.Empty;
            };

            requestHandler.OnError += exception =>
            {
                verifyActive();
            };

            requestHandler.OnAbort += request =>
            {
                active = false;
            };

            requestHandler.Start();

            // Let the request handler run for three seconds
            Thread.Sleep(TimeSpan.FromSeconds(.1));

            killRequest = true;

            // Let all requests finish to see if we get any unintended results
            Thread.Sleep(TimeSpan.FromSeconds(1));
        }
        private void PollingSetup(IConnection connection,
                                  string data,
                                  CancellationToken disconnectToken,
                                  PollingRequestHandler requestHandler)
        {
            // These are created new on each poll
            var reconnectInvoker = new ThreadSafeInvoker();
            var requestDisposer  = new Disposer();

            requestHandler.ResolveUrl = () =>
            {
                var url = connection.Url;

                if (connection.MessageId == null)
                {
                    url += "connect";
                }
                else if (IsReconnecting(connection))
                {
                    url += "reconnect";
                }
                else
                {
                    url += "poll";
                }

                url += GetReceiveQueryString(connection, data);

                return(url);
            };

            requestHandler.PrepareRequest += req =>
            {
                connection.PrepareRequest(req);
            };

            requestHandler.OnMessage += message =>
            {
                var shouldReconnect      = false;
                var disconnectedReceived = false;

                connection.Trace(TraceLevels.Messages, "LP: OnMessage({0})", message);

                TransportHelper.ProcessResponse(connection,
                                                message,
                                                out shouldReconnect,
                                                out disconnectedReceived);

                if (IsReconnecting(connection))
                {
                    // If the timeout for the reconnect hasn't fired as yet just fire the
                    // event here before any incoming messages are processed
                    TryReconnect(connection, reconnectInvoker);
                }

                if (shouldReconnect)
                {
                    // Transition into reconnecting state
                    connection.EnsureReconnecting();
                }

                if (AbortResetEvent != null)
                {
                    AbortResetEvent.Set();
                }
                else if (disconnectedReceived)
                {
                    connection.Disconnect();
                }
            };

            requestHandler.OnError += exception =>
            {
                reconnectInvoker.Invoke();

                // Transition into reconnecting state
                connection.EnsureReconnecting();

                // Sometimes a connection might have been closed by the server before we get to write anything
                // so just try again and raise OnError.
                if (!ExceptionHelper.IsRequestAborted(exception) && !(exception is IOException))
                {
                    connection.OnError(exception);
                }
                else
                {
                    // If we aborted purposely then we need to stop the request handler
                    requestHandler.Stop();
                }
            };

            requestHandler.OnPolling += () =>
            {
                // Capture the cleanup within a closure so it can persist through multiple requests
                TryDelayedReconnect(connection, reconnectInvoker);

                requestDisposer.Set(disconnectToken.SafeRegister(state =>
                {
                    reconnectInvoker.Invoke();
                    requestHandler.Stop();
                }, null));
            };

            requestHandler.OnAfterPoll = exception =>
            {
                requestDisposer.Dispose();
                requestDisposer  = new Disposer();
                reconnectInvoker = new ThreadSafeInvoker();

                if (exception != null)
                {
                    // Delay polling by the error delay
                    return(TaskAsyncHelper.Delay(ErrorDelay));
                }

                return(TaskAsyncHelper.Empty);
            };
        }
        private void PollingSetup(IConnection connection,
                                  string data,
                                  CancellationToken disconnectToken,
                                  PollingRequestHandler requestHandler,
                                  Action onInitialized)
        {
            // reconnectInvoker is created new on each poll
            var reconnectInvoker = new ThreadSafeInvoker();

            var disconnectRegistration = disconnectToken.SafeRegister(state =>
            {
                reconnectInvoker.Invoke();
                requestHandler.Stop();
            }, null);

            requestHandler.ResolveUrl = () =>
            {
                var url = connection.Url;

                if (connection.MessageId == null)
                {
                    url += "connect";
                    connection.Trace(TraceLevels.Events, "LP Connect: {0}", url);
                }
                else if (IsReconnecting(connection))
                {
                    url += "reconnect";
                    connection.Trace(TraceLevels.Events, "LP Reconnect: {0}", url);
                }
                else
                {
                    url += "poll";
                    connection.Trace(TraceLevels.Events, "LP Poll: {0}", url);
                }

                url += GetReceiveQueryString(connection, data);

                return url;
            };

            requestHandler.PrepareRequest += req =>
            {
                connection.PrepareRequest(req);
            };

            requestHandler.OnMessage += message =>
            {
                var shouldReconnect = false;
                var disconnectedReceived = false;

                connection.Trace(TraceLevels.Messages, "LP: OnMessage({0})", message);

                TransportHelper.ProcessResponse(connection,
                                                message,
                                                out shouldReconnect,
                                                out disconnectedReceived,
                                                onInitialized);

                if (IsReconnecting(connection))
                {
                    // If the timeout for the reconnect hasn't fired as yet just fire the 
                    // event here before any incoming messages are processed
                    TryReconnect(connection, reconnectInvoker);
                }

                if (shouldReconnect)
                {
                    // Transition into reconnecting state
                    connection.EnsureReconnecting();
                }

                if (disconnectedReceived)
                {
                    connection.Disconnect();
                }
            };

            requestHandler.OnError += exception =>
            {
                reconnectInvoker.Invoke();

                // Transition into reconnecting state
                connection.EnsureReconnecting();

                // Sometimes a connection might have been closed by the server before we get to write anything
                // so just try again and raise OnError.
                if (!ExceptionHelper.IsRequestAborted(exception) && !(exception is IOException))
                {
                    connection.OnError(exception);
                }
                else
                {
                    requestHandler.Stop();
                }
            };

            requestHandler.OnPolling += () =>
            {
                // Capture the cleanup within a closure so it can persist through multiple requests
                TryDelayedReconnect(connection, reconnectInvoker);
            };

            requestHandler.OnAfterPoll = exception =>
            {
                if (AbortHandler.TryCompleteAbort())
                {
                    // Abort() was called, so don't reconnect
                    requestHandler.Stop();
                }
                else
                {
                    reconnectInvoker = new ThreadSafeInvoker();

                    if (exception != null)
                    {
                        // Delay polling by the error delay
                        return TaskAsyncHelper.Delay(ErrorDelay);
                    }
                }

                return TaskAsyncHelper.Empty;
            };

            requestHandler.OnAbort += _ =>
            {
                disconnectRegistration.Dispose();

                // Complete any ongoing calls to Abort()
                // If someone calls Abort() later, have it no-op
                AbortHandler.CompleteAbort();
            };
        }