public async Task MakeRequest(Batch batch) { Stopwatch watch = new Stopwatch(); _backo.Reset(); try { Uri uri = new Uri(_client.Config.Host + "/v1/import"); // set the current request time batch.SentAt = DateTime.Now.ToString("o"); string json = JsonConvert.SerializeObject(batch); // Basic Authentication // https://segment.io/docs/tracking-api/reference/#authentication #if NET35 _httpClient.Headers.Set("Authorization", "Basic " + BasicAuthHeader(batch.WriteKey, string.Empty)); _httpClient.Headers.Set("Content-Type", "application/json; charset=utf-8"); #else _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", BasicAuthHeader(batch.WriteKey, string.Empty)); #endif // Prepare request data; var requestData = Encoding.UTF8.GetBytes(json); // Compress request data if compression is set if (_client.Config.Gzip) { #if NET35 _httpClient.Headers.Set(HttpRequestHeader.ContentEncoding, "gzip"); #else //_httpClient.DefaultRequestHeaders.Add("Content-Encoding", "gzip"); #endif // Compress request data with GZip using (MemoryStream memory = new MemoryStream()) { using (GZipStream gzip = new GZipStream(memory, CompressionMode.Compress, true)) { gzip.Write(requestData, 0, requestData.Length); } requestData = memory.ToArray(); } } Logger.Info("Sending analytics request to Segment.io ..", new Dict { { "batch id", batch.MessageId }, { "json size", json.Length }, { "batch size", batch.batch.Count } }); // Retries with exponential backoff int statusCode = (int)HttpStatusCode.OK; string responseStr = ""; while (!_backo.HasReachedMax) { #if NET35 watch.Start(); try { var response = Encoding.UTF8.GetString(_httpClient.UploadData(uri, "POST", requestData)); watch.Stop(); Succeed(batch, watch.ElapsedMilliseconds); statusCode = 200; break; } catch (WebException ex) { watch.Stop(); var response = (HttpWebResponse)ex.Response; statusCode = (response != null) ? (int)response.StatusCode : 0; if ((statusCode >= 500 && statusCode <= 600) || statusCode == 429 || statusCode == 0) { // If status code is greater than 500 and less than 600, it indicates server error // Error code 429 indicates rate limited. // Retry uploading in these cases. Thread.Sleep(_backo.AttemptTime()); if (statusCode == 429) { Logger.Info($"Too many request at the moment CurrentAttempt:{_backo.CurrentAttempt} Retrying to send request", new Dict { { "batch id", batch.MessageId }, { "statusCode", statusCode }, { "duration (ms)", watch.ElapsedMilliseconds } }); } else { Logger.Info($"Internal Segment Server error CurrentAttempt:{_backo.CurrentAttempt} Retrying to send request", new Dict { { "batch id", batch.MessageId }, { "statusCode", statusCode }, { "duration (ms)", watch.ElapsedMilliseconds } }); } continue; } else { //If status code is greater or equal than 400 but not 429 should indicate is client error. //All other types of HTTP Status code are not errors (Between 100 and 399) responseStr = String.Format("Status Code {0}. ", statusCode); responseStr += ex.Message; break; } } #else watch.Start(); ByteArrayContent content = new ByteArrayContent(requestData); content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); if (_client.Config.Gzip) { content.Headers.ContentEncoding.Add("gzip"); } HttpResponseMessage response = null; bool retry = false; try { response = await _httpClient.PostAsync(uri, content).ConfigureAwait(false); } catch (TaskCanceledException e) { Logger.Info("HTTP Post failed with exception of type TaskCanceledException", new Dict { { "batch id", batch.MessageId }, { "reason", e.Message }, { "duration (ms)", watch.ElapsedMilliseconds } }); retry = true; } catch (OperationCanceledException e) { Logger.Info("HTTP Post failed with exception of type OperationCanceledException", new Dict { { "batch id", batch.MessageId }, { "reason", e.Message }, { "duration (ms)", watch.ElapsedMilliseconds } }); retry = true; } catch (HttpRequestException e) { Logger.Info("HTTP Post failed with exception of type HttpRequestException", new Dict { { "batch id", batch.MessageId }, { "reason", e.Message }, { "duration (ms)", watch.ElapsedMilliseconds } }); retry = true; } catch (System.Exception e) { Logger.Info("HTTP Post failed with exception of type Exception", new Dict { { "batch id", batch.MessageId }, { "reason", e.Message }, { "duration (ms)", watch.ElapsedMilliseconds } }); retry = true; } watch.Stop(); statusCode = response != null ? (int)response.StatusCode : 0; if (response != null && response.StatusCode == HttpStatusCode.OK) { Succeed(batch, watch.ElapsedMilliseconds); break; } else { if ((statusCode >= 500 && statusCode <= 600) || statusCode == 429 || retry) { // If status code is greater than 500 and less than 600, it indicates server error // Error code 429 indicates rate limited. // Retry uploading in these cases. await _backo.AttemptAsync(); if (statusCode == 429) { Logger.Info($"Too many request at the moment CurrentAttempt:{_backo.CurrentAttempt} Retrying to send request", new Dict { { "batch id", batch.MessageId }, { "statusCode", statusCode }, { "duration (ms)", watch.ElapsedMilliseconds } }); } else { Logger.Info($"Internal Segment Server error CurrentAttempt:{_backo.CurrentAttempt} Retrying to send request", new Dict { { "batch id", batch.MessageId }, { "statusCode", statusCode }, { "duration (ms)", watch.ElapsedMilliseconds } }); } } else { //HTTP status codes smaller than 500 or greater than 600 except for 429 are either Client errors or a correct status //This means it should not retry break; } } #endif } var hasBackoReachedMax = _backo.HasReachedMax; if (hasBackoReachedMax || statusCode != (int)HttpStatusCode.OK) { var message = $"Has Backoff reached max: {hasBackoReachedMax} with number of Attempts:{_backo.CurrentAttempt},\n Status Code: {statusCode}\n, response message: {responseStr}"; Fail(batch, new APIException(statusCode.ToString(), message), watch.ElapsedMilliseconds); } } catch (System.Exception e) { watch.Stop(); Fail(batch, e, watch.ElapsedMilliseconds); } }
public async Task MakeRequest(Batch batch) { Stopwatch watch = new Stopwatch(); try { Uri uri = new Uri(_client.Config.Host + "/v1/import"); // set the current request time batch.SentAt = DateTime.Now.ToString("o"); string json = JsonConvert.SerializeObject(batch); // Basic Authentication // https://segment.io/docs/tracking-api/reference/#authentication #if NET35 _httpClient.Headers.Set("Authorization", "Basic " + BasicAuthHeader(batch.WriteKey, string.Empty)); _httpClient.Headers.Set("Content-Type", "application/json; charset=utf-8"); #else _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", BasicAuthHeader(batch.WriteKey, string.Empty)); #endif // Prepare request data; var requestData = Encoding.UTF8.GetBytes(json); // Compress request data if compression is set if (_client.Config.Gzip) { #if NET35 _httpClient.Headers.Add(HttpRequestHeader.ContentEncoding, "gzip"); #else //_httpClient.DefaultRequestHeaders.Add("Content-Encoding", "gzip"); #endif // Compress request data with GZip using (MemoryStream memory = new MemoryStream()) { using (GZipStream gzip = new GZipStream(memory, CompressionMode.Compress, true)) { gzip.Write(requestData, 0, requestData.Length); } requestData = memory.ToArray(); } } Logger.Info("Sending analytics request to Segment.io ..", new Dict { { "batch id", batch.MessageId }, { "json size", json.Length }, { "batch size", batch.batch.Count } }); // Retries with exponential backoff int statusCode = (int)HttpStatusCode.OK; string responseStr = ""; while (!_backo.HasReachedMax) { #if NET35 watch.Start(); try { var response = Encoding.UTF8.GetString(_httpClient.UploadData(uri, "POST", requestData)); watch.Stop(); Succeed(batch, watch.ElapsedMilliseconds); statusCode = 200; break; } catch (WebException ex) { watch.Stop(); var response = (HttpWebResponse)ex.Response; if (response != null) { statusCode = (int)response.StatusCode; if ((statusCode >= 500 && statusCode <= 600) || statusCode == 429) { // If status code is greater than 500 and less than 600, it indicates server error // Error code 429 indicates rate limited. // Retry uploading in these cases. Thread.Sleep(_backo.AttemptTime()); continue; } else if (statusCode >= 400) { responseStr = String.Format("Status Code {0}. ", statusCode); responseStr += ex.Message; break; } } } #else watch.Start(); ByteArrayContent content = new ByteArrayContent(requestData); content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); if (_client.Config.Gzip) { content.Headers.ContentEncoding.Add("gzip"); } var response = await _httpClient.PostAsync(uri, content).ConfigureAwait(false); watch.Stop(); statusCode = (int)response.StatusCode; if (statusCode == (int)HttpStatusCode.OK) { Succeed(batch, watch.ElapsedMilliseconds); break; } else { if ((statusCode >= 500 && statusCode <= 600) || statusCode == 429) { // If status code is greater than 500 and less than 600, it indicates server error // Error code 429 indicates rate limited. // Retry uploading in these cases. await _backo.AttemptAsync(); continue; } else if (statusCode >= 400) { responseStr = String.Format("Status Code {0}. ", response.StatusCode); responseStr += await response.Content.ReadAsStringAsync().ConfigureAwait(false); break; } } #endif } if (_backo.HasReachedMax || statusCode != (int)HttpStatusCode.OK) { Fail(batch, new APIException("Unexpected Status Code", responseStr), watch.ElapsedMilliseconds); } } catch (System.Exception e) { watch.Stop(); Fail(batch, e, watch.ElapsedMilliseconds); } }