/// <summary>Called after an HTTP request has been processed in any way (error or failure). Decides what to do next.</summary> protected void FinishWithRequest(WebRequest state, HttpResponse response) { // This "hack" is needed because sometimes the callback is called multiple times for a single request (notably from RespCallback) if (state.AlreadyFinished) return; state.AlreadyFinished = true; // IDEA This function could probably be moved to another file with a little gymnastic… HttpRequest nextReq; // Avoid timeout to be triggered after that AllDone.Set(); lock (this) { // No need to continue, dismiss the result if (Terminated) return; RunningRequests.Remove(state); } // Has failed? if (response.ShouldBeRetried(state.OriginalRequest) && !state.Aborted) { if (state.OriginalRequest.FailedHandler != null) { // See whether to try again var eventArgs = new HttpRequestFailedEventArgs(state.OriginalRequest, state.PreviousUserData); // Invoke the failure handler try { state.OriginalRequest.FailedHandler(eventArgs); if (eventArgs.RetryDelay < 0) throw new InvalidOperationException("HTTP request failed handler called but didn't tell what to do next."); if (eventArgs.RetryDelay > 0) { Common.LogWarning("[" + state.RequestId + "] Request failed, retrying in " + eventArgs.RetryDelay + "ms."); Thread.Sleep(eventArgs.RetryDelay); ChooseLoadBalancer(state.OriginalRequest); ProcessRequest(state.OriginalRequest, eventArgs.UserData); return; } } catch (Exception e) { Common.LogError("Error in failure handler: " + e.ToString()); } } // Maximum failure count reached, will simply process the next request Common.LogWarning("[" + state.RequestId + "] Request failed."); } // Final result for this request if (state.OriginalRequest.Callback != null) { Cotc.RunOnMainThread(() => state.OriginalRequest.Callback(response)); } // Was independent? if (state.OriginalRequest.DoNotEnqueue) return; // Process next request lock (this) { // Note: currently another request is only launched after synchronously processed by the callback. This behavior is slower but might be safer. if (!state.OriginalRequest.DoNotEnqueue && PendingRequests.Count == 0) { IsProcessingRequest = false; return; } nextReq = PendingRequests[0]; PendingRequests.RemoveAt(0); } ProcessRequest(nextReq); }