/// <summary>Reads the response buffer little by little.</summary> private void ReadCallBack(IAsyncResult asyncResult) { RequestState state = asyncResult.AsyncState as RequestState; try { Stream responseStream = state.StreamResponse; int read = responseStream.EndRead(asyncResult); // Read the HTML page and then print it to the console. if (read > 0) { state.ResponseBuffer.Write(state.BufferRead, 0, read); responseStream.BeginRead(state.BufferRead, 0, RequestState.BufferSize, new AsyncCallback(ReadCallBack), state); return; } else { // Finished reading responseStream.Close(); HttpResponse result = new HttpResponse(); HttpWebResponse response = state.Response; result.StatusCode = (int) response.StatusCode; foreach (string key in response.Headers) { result.Headers[key] = response.Headers[key]; } // Read the body result.Body = state.ResponseBuffer.ToArray(); // Logging state.LogResponse(result); FinishWithRequest(state, result); } } catch (Exception e) { Common.LogWarning("Failed to read response: " + e.ToString()); FinishWithRequest(state, new HttpResponse(e)); } }
/// <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); }
/// <summary>To be used when an HTTP request has failed. Will extract a default error code (server error, network error) from the HTTP request.</summary> internal CotcException(HttpResponse response, string failureDescription = null) { Json = response.BodyJson; HttpStatusCode = response.StatusCode; if (response.HasFailed) { ErrorCode = ErrorCode.NetworkError; ErrorInformation = failureDescription; } else if (response.StatusCode < 200 || response.StatusCode >= 300) { ErrorCode = ErrorCode.ServerError; ErrorInformation = failureDescription; } else { throw new InvalidOperationException("Should only call this for a failed request"); } }
/// <summary>Called upon timeout.</summary> private static void TimeoutCallback(object state, bool timedOut) { if (timedOut) { WebRequest requestState = state as WebRequest; requestState.AbortRequest(); HttpResponse response = new HttpResponse(new HttpTimeoutException()); Common.LogWarning("Request timed out"); requestState.Client.FinishWithRequest(requestState, response); } }
/// <summary>Prints information about the response for debugging purposes.</summary> internal void LogResponse(HttpResponse response) { if (!VerboseMode) { return; } StringBuilder sb = new StringBuilder(); HttpWebRequest req = Request; sb.AppendLine("[" + RequestId + "] " + response.StatusCode + " on " + req.Method + "ed on " + req.RequestUri); sb.AppendLine("Recv. headers:"); foreach (var pair in response.Headers) { sb.AppendLine("\t" + pair.Key + ": " + pair.Value); } if (response.HasBody) { sb.AppendLine("Body: " + response.BodyString); } Common.Log(sb.ToString()); }
private IEnumerator ProcessRequest(UnityWebRequest req) { yield return req.Send(); if (req.isError) { if (!WasAborted) { string errorMessage = "Failed web request: " + req.error; Common.Log(errorMessage); self.FinishWithRequest(this, new HttpResponse(new Exception(errorMessage))); } } else { // Extracts asset bundle HttpResponse response = new HttpResponse(); response.Body = Request.downloadHandler.data; response.StatusCode = (int)Request.responseCode; foreach (var pair in Request.GetResponseHeaders()) { response.Headers[pair.Key] = pair.Value; } LogResponse(response); self.FinishWithRequest(this, response); } }
/// <summary>Prints information about the response for debugging purposes.</summary> internal void LogResponse(HttpResponse response) { if (!VerboseMode) { return; } StringBuilder sb = new StringBuilder(); sb.AppendLine("[" + RequestId + "] " + response.StatusCode + " on " + Request.method + "ed on " + Request.url); sb.AppendLine("Recv. headers:"); foreach (var pair in response.Headers) { if (String.Compare(pair.Key, "Content-Type", true) == 0) { ContentType = pair.Value; } else { sb.AppendLine("\t" + pair.Key + ": " + pair.Value); } } if (response.HasBody) { sb.AppendLine("Body: " + response.BodyString); } Common.Log(sb.ToString()); }
private void ProcessEvent(HttpResponse res) { try { EventLoopArgs args = new EventLoopArgs(res.BodyJson); if (receivedEvent != null) receivedEvent(this, args); Cotc.NotifyReceivedMessage(this, args); } catch (Exception e) { Common.LogError("Exception in the event chain: " + e.ToString()); } }