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(); }; }
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(); }; }