From a0bd3aa0ded3a881c54ec694840bcac111765a8b Mon Sep 17 00:00:00 2001 From: David Courtel Date: Sun, 24 Mar 2024 16:25:12 +0100 Subject: [PATCH 1/8] Add more information on failures --- .../Middleware/RetryHandlerTests.cs | 19 +++--- src/Middleware/RetryHandler.cs | 62 ++++++++++++------- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/Microsoft.Kiota.Http.HttpClientLibrary.Tests/Middleware/RetryHandlerTests.cs b/Microsoft.Kiota.Http.HttpClientLibrary.Tests/Middleware/RetryHandlerTests.cs index 805fc68..e8f89cb 100644 --- a/Microsoft.Kiota.Http.HttpClientLibrary.Tests/Middleware/RetryHandlerTests.cs +++ b/Microsoft.Kiota.Http.HttpClientLibrary.Tests/Middleware/RetryHandlerTests.cs @@ -51,7 +51,6 @@ public void RetryHandlerConstructor() Assert.IsType(retry); } - [Fact] public void RetryHandlerHttpMessageHandlerConstructor() { @@ -89,7 +88,6 @@ public async Task OkStatusShouldPassThrough() Assert.NotNull(response.RequestMessage); Assert.Same(response.RequestMessage, httpRequestMessage); Assert.False(response.RequestMessage.Headers.Contains(RetryAttempt), "The request add header wrong."); - } [Theory] @@ -116,7 +114,6 @@ public async Task ShouldRetryWithAddRetryAttemptHeader(HttpStatusCode statusCode Assert.Equal(values.First(), 1.ToString()); } - [Theory] [InlineData(HttpStatusCode.GatewayTimeout)] // 504 [InlineData(HttpStatusCode.ServiceUnavailable)] // 503 @@ -140,7 +137,6 @@ public async Task ShouldRetryWithBuffedContent(HttpStatusCode statusCode) Assert.NotNull(response.RequestMessage.Content); Assert.NotNull(response.RequestMessage.Content.Headers.ContentLength); Assert.Equal("Hello World", await response.RequestMessage.Content.ReadAsStringAsync()); - } [Theory] @@ -196,7 +192,6 @@ public async Task ShouldNotRetryWithPutStreaming(HttpStatusCode statusCode) Assert.Equal(response.RequestMessage.Content.Headers.ContentLength, -1); } - [Theory(Skip = "Test takes a while to run")] [InlineData(HttpStatusCode.GatewayTimeout)] // 504 [InlineData(HttpStatusCode.ServiceUnavailable)] // 503 @@ -246,16 +241,15 @@ public async Task ShouldDelayBasedOnRetryAfterHeaderWithHttpDate(HttpStatusCode // Arrange var retryResponse = new HttpResponseMessage(statusCode); var futureTime = DateTime.Now + TimeSpan.FromSeconds(3);// 3 seconds from now - var futureTimeString = futureTime.ToString(CultureInfo.InvariantCulture.DateTimeFormat.RFC1123Pattern); + var futureTimeString = futureTime.ToString(CultureInfo.InvariantCulture.DateTimeFormat.RFC1123Pattern, CultureInfo.InvariantCulture); Assert.Contains("GMT", futureTimeString); // http date always end in GMT according to the spec - retryResponse.Headers.TryAddWithoutValidation(RetryAfter, futureTimeString); + Assert.True(retryResponse.Headers.TryAddWithoutValidation(RetryAfter, futureTimeString)); // Act await DelayTestWithMessage(retryResponse, 1, "Init"); // Assert Assert.Equal("Init Work 1", Message); } - [Theory(Skip = "Skipped as this takes 9 minutes to run for each scenario")] // Takes 9 minutes to run for each scenario [InlineData(HttpStatusCode.GatewayTimeout)] // 504 [InlineData(HttpStatusCode.ServiceUnavailable)] // 503 @@ -351,7 +345,6 @@ public async Task ShouldRetryBasedOnRetryAfterHeaderWithHttpDate(HttpStatusCode Assert.NotSame(response.RequestMessage, httpRequestMessage); } - [Theory] [InlineData(1, HttpStatusCode.BadGateway, true)] [InlineData(2, HttpStatusCode.BadGateway, true)] @@ -411,14 +404,16 @@ public async Task ShouldRetryBasedOnCustomShouldRetryDelegate(int expectedMaxRet catch(Exception exception) { // Assert - Assert.IsType(exception); + Assert.IsType(exception); + var aggregateException = exception as AggregateException; Assert.True(isExceptionExpected); - Assert.Equal("Too many retries performed", exception.Message); + Assert.StartsWith("Too many retries performed.", aggregateException.Message); + Assert.Equal(1 + expectedMaxRetry, aggregateException.InnerExceptions.Count); + Assert.All(aggregateException.InnerExceptions, innerexception => Assert.Contains(expectedStatusCode.ToString(), innerexception.Message)); } // Assert mockHttpMessageHandler.Protected().Verify>("SendAsync", Times.Exactly(1 + expectedMaxRetry), ItExpr.IsAny(), It.IsAny()); - } private async Task DelayTestWithMessage(HttpResponseMessage response, int count, string message, int delay = RetryHandlerOption.MaxDelay) diff --git a/src/Middleware/RetryHandler.cs b/src/Middleware/RetryHandler.cs index 56df4ca..c20df77 100644 --- a/src/Middleware/RetryHandler.cs +++ b/src/Middleware/RetryHandler.cs @@ -46,6 +46,7 @@ public RetryHandler(RetryHandlerOption? retryOption = null) /// /// The HTTP requestneeds to be sent. /// The for the request. + /// Thrown when too many retries are performed. /// protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { @@ -69,7 +70,6 @@ protected override async Task SendAsync(HttpRequestMessage try { - var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); // Check whether retries are permitted and that the MaxRetry value is a non - negative, non - zero value @@ -93,27 +93,20 @@ protected override async Task SendAsync(HttpRequestMessage /// The for the retry. /// The for the retry. /// The for the retry. + /// Thrown when too many retries are performed." /// private async Task SendRetryAsync(HttpResponseMessage response, RetryHandlerOption retryOption, CancellationToken cancellationToken, ActivitySource? activitySource) { int retryCount = 0; TimeSpan cumulativeDelay = TimeSpan.Zero; + List exceptions = new(); while(retryCount < retryOption.MaxRetry) { + exceptions.Add(await GetInnerException(response, cancellationToken)); using var retryActivity = activitySource?.StartActivity($"{nameof(RetryHandler)}_{nameof(SendAsync)} - attempt {retryCount}"); retryActivity?.SetTag("http.retry_count", retryCount); retryActivity?.SetTag("http.status_code", response.StatusCode); - // Drain response content to free connections. Need to perform this - // before retry attempt and before the TooManyRetries ServiceException. - if(response.Content != null) - { -#if NET5_0_OR_GREATER - await response.Content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(false); -#else - await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false); -#endif - } // Call Delay method to get delay time from response's Retry-After header or by exponential backoff Task delay = RetryHandler.Delay(response, retryCount, retryOption.Delay, out double delayInSeconds, cancellationToken); @@ -155,20 +148,9 @@ private async Task SendRetryAsync(HttpResponseMessage respo } } - // Drain response content to free connections. Need to perform this - // before retry attempt and before the TooManyRetries ServiceException. - if(response.Content != null) - { -#if NET5_0_OR_GREATER - await response.Content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(false); -#else - await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false); -#endif - } + exceptions.Add(await GetInnerException(response, cancellationToken)); - throw new InvalidOperationException( - "Too many retries performed", - new Exception($"More than {retryCount} retries encountered while sending the request.")); + throw new AggregateException("Too many retries performed.", exceptions); } /// @@ -248,5 +230,37 @@ private static bool ShouldRetry(HttpStatusCode statusCode) _ => false }; } + + private static async Task GetInnerException(HttpResponseMessage response, CancellationToken cancellationToken) + { + var httpStatusCode = response.StatusCode; + var errorMessage = "The response has not content."; + + // Drain response content to free connections. Need to perform this + // before retry attempt and before the TooManyRetries ServiceException. + if(response.Content != null) + { +#if NET5_0_OR_GREATER + var responseContent = await response.Content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(false); +#else + var responseContent = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false); +#endif + errorMessage = GetStringFromContent(responseContent); + } + + return new Exception($"HTTP request failed with status code: {httpStatusCode}. Error Message: {errorMessage}"); + } + + private static string GetStringFromContent(byte[] content) + { + try + { + return System.Text.Encoding.UTF8.GetString(content); + } + catch(Exception) + { + return "The Graph retry handler was unable to cast the response into a UTF8 string."; + } + } } } From c764430378d07e3f6852080341014156c31dd5ec Mon Sep 17 00:00:00 2001 From: David Courtel Date: Sun, 24 Mar 2024 16:46:42 +0100 Subject: [PATCH 2/8] Shorten some tests time. --- .../Middleware/RetryHandlerTests.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Microsoft.Kiota.Http.HttpClientLibrary.Tests/Middleware/RetryHandlerTests.cs b/Microsoft.Kiota.Http.HttpClientLibrary.Tests/Middleware/RetryHandlerTests.cs index e8f89cb..ac8f39f 100644 --- a/Microsoft.Kiota.Http.HttpClientLibrary.Tests/Middleware/RetryHandlerTests.cs +++ b/Microsoft.Kiota.Http.HttpClientLibrary.Tests/Middleware/RetryHandlerTests.cs @@ -192,7 +192,7 @@ public async Task ShouldNotRetryWithPutStreaming(HttpStatusCode statusCode) Assert.Equal(response.RequestMessage.Content.Headers.ContentLength, -1); } - [Theory(Skip = "Test takes a while to run")] + [Theory] [InlineData(HttpStatusCode.GatewayTimeout)] // 504 [InlineData(HttpStatusCode.ServiceUnavailable)] // 503 [InlineData((HttpStatusCode)429)] // 429 @@ -203,16 +203,23 @@ public async Task ExceedMaxRetryShouldReturn(HttpStatusCode statusCode) var retryResponse = new HttpResponseMessage(statusCode); var response2 = new HttpResponseMessage(statusCode); this._testHttpMessageHandler.SetHttpResponse(retryResponse, response2); + var retryHandler = new RetryHandler + { + InnerHandler = _testHttpMessageHandler, + RetryOption = new RetryHandlerOption { Delay = 1 } + }; + var invoker = new HttpMessageInvoker(retryHandler); // Act try { - await _invoker.SendAsync(httpRequestMessage, new CancellationToken()); + await invoker.SendAsync(httpRequestMessage, new CancellationToken()); } catch(Exception exception) { // Assert - Assert.IsType(exception); - Assert.Equal("Too many retries performed", exception.Message); + Assert.IsType(exception); + var aggregateException = exception as AggregateException; + Assert.StartsWith("Too many retries performed.", aggregateException.Message); Assert.False(httpRequestMessage.Headers.TryGetValues(RetryAttempt, out _), "Don't set Retry-Attempt Header"); } } @@ -250,7 +257,7 @@ public async Task ShouldDelayBasedOnRetryAfterHeaderWithHttpDate(HttpStatusCode Assert.Equal("Init Work 1", Message); } - [Theory(Skip = "Skipped as this takes 9 minutes to run for each scenario")] // Takes 9 minutes to run for each scenario + [Theory] [InlineData(HttpStatusCode.GatewayTimeout)] // 504 [InlineData(HttpStatusCode.ServiceUnavailable)] // 503 [InlineData((HttpStatusCode)429)] // 429 @@ -263,7 +270,7 @@ public async Task ShouldDelayBasedOnExponentialBackOff(HttpStatusCode statusCode for(int count = 0; count < 3; count++) { // Act - await DelayTestWithMessage(retryResponse, count, "Init"); + await DelayTestWithMessage(retryResponse, count, "Init", 1); // Assert Assert.Equal(Message, compareMessage + count); } From d815e336dd970a7159aae8a1a19e871eef780bb7 Mon Sep 17 00:00:00 2001 From: David Courtel Date: Sun, 24 Mar 2024 17:02:58 +0100 Subject: [PATCH 3/8] Remove default error message text. --- src/Middleware/RetryHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Middleware/RetryHandler.cs b/src/Middleware/RetryHandler.cs index c20df77..4087f6e 100644 --- a/src/Middleware/RetryHandler.cs +++ b/src/Middleware/RetryHandler.cs @@ -234,7 +234,7 @@ private static bool ShouldRetry(HttpStatusCode statusCode) private static async Task GetInnerException(HttpResponseMessage response, CancellationToken cancellationToken) { var httpStatusCode = response.StatusCode; - var errorMessage = "The response has not content."; + string? errorMessage = null; // Drain response content to free connections. Need to perform this // before retry attempt and before the TooManyRetries ServiceException. @@ -248,7 +248,7 @@ private static async Task GetInnerException(HttpResponseMessage respo errorMessage = GetStringFromContent(responseContent); } - return new Exception($"HTTP request failed with status code: {httpStatusCode}. Error Message: {errorMessage}"); + return new Exception($"HTTP request failed with status code: {httpStatusCode}.{errorMessage}"); } private static string GetStringFromContent(byte[] content) From dba777e334c50b15da063a6345827a5490a121fb Mon Sep 17 00:00:00 2001 From: David COURTEL Date: Mon, 25 Mar 2024 09:39:41 +0100 Subject: [PATCH 4/8] Update src/Middleware/RetryHandler.cs Co-authored-by: Eastman --- src/Middleware/RetryHandler.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Middleware/RetryHandler.cs b/src/Middleware/RetryHandler.cs index 4087f6e..b33a255 100644 --- a/src/Middleware/RetryHandler.cs +++ b/src/Middleware/RetryHandler.cs @@ -231,7 +231,8 @@ private static bool ShouldRetry(HttpStatusCode statusCode) }; } - private static async Task GetInnerException(HttpResponseMessage response, CancellationToken cancellationToken) + private static async Task GetInnerExceptionAsync(HttpResponseMessage response, CancellationToken cancellationToken) + { var httpStatusCode = response.StatusCode; string? errorMessage = null; From 242df12093aac03126749ca0b92536dfd50d4d40 Mon Sep 17 00:00:00 2001 From: David COURTEL Date: Mon, 25 Mar 2024 09:40:09 +0100 Subject: [PATCH 5/8] Update src/Middleware/RetryHandler.cs Co-authored-by: Eastman --- src/Middleware/RetryHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Middleware/RetryHandler.cs b/src/Middleware/RetryHandler.cs index b33a255..7f068b7 100644 --- a/src/Middleware/RetryHandler.cs +++ b/src/Middleware/RetryHandler.cs @@ -150,7 +150,7 @@ private async Task SendRetryAsync(HttpResponseMessage respo exceptions.Add(await GetInnerException(response, cancellationToken)); - throw new AggregateException("Too many retries performed.", exceptions); + throw new AggregateException($"Too many retries performed. More than {retryCount} retries encountered while sending the request.", exceptions); } /// From 906a4fd4934f097d489faadf7ce06f3213aaf452 Mon Sep 17 00:00:00 2001 From: David COURTEL Date: Mon, 25 Mar 2024 09:42:38 +0100 Subject: [PATCH 6/8] Update src/Middleware/RetryHandler.cs Co-authored-by: Eastman --- src/Middleware/RetryHandler.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Middleware/RetryHandler.cs b/src/Middleware/RetryHandler.cs index 7f068b7..b81793e 100644 --- a/src/Middleware/RetryHandler.cs +++ b/src/Middleware/RetryHandler.cs @@ -242,9 +242,10 @@ private static async Task GetInnerExceptionAsync(HttpResponseMessage if(response.Content != null) { #if NET5_0_OR_GREATER - var responseContent = await response.Content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(false); + errorMessage = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); #else - var responseContent = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false); + errorMessage = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + #endif errorMessage = GetStringFromContent(responseContent); } From 34f91dae508456ddbd1553418b6e6ea697eacf68 Mon Sep 17 00:00:00 2001 From: David COURTEL Date: Mon, 25 Mar 2024 09:44:17 +0100 Subject: [PATCH 7/8] Update src/Middleware/RetryHandler.cs Co-authored-by: Eastman --- src/Middleware/RetryHandler.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Middleware/RetryHandler.cs b/src/Middleware/RetryHandler.cs index b81793e..b74a98e 100644 --- a/src/Middleware/RetryHandler.cs +++ b/src/Middleware/RetryHandler.cs @@ -250,7 +250,11 @@ private static async Task GetInnerExceptionAsync(HttpResponseMessage errorMessage = GetStringFromContent(responseContent); } - return new Exception($"HTTP request failed with status code: {httpStatusCode}.{errorMessage}"); + return new ApiException($"HTTP request failed with status code: {httpStatusCode}.{errorMessage}") + { + ResponseStatusCode = (int)response.StatusCode, + ResponseHeaders = response.Headers.ToDictionary() + }; } private static string GetStringFromContent(byte[] content) From 376eb069bbc76ef29648cf0e186953f6f54a4a18 Mon Sep 17 00:00:00 2001 From: David Courtel Date: Mon, 25 Mar 2024 10:35:46 +0100 Subject: [PATCH 8/8] Resolve comments --- CHANGELOG.md | 7 ++++ .../Middleware/RetryHandlerTests.cs | 3 ++ Microsoft.Kiota.Http.HttpClientLibrary.sln | 9 +++--- ...rosoft.Kiota.Http.HttpClientLibrary.csproj | 2 +- src/Middleware/RetryHandler.cs | 32 ++++--------------- 5 files changed, 23 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f69eced..9382cd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.3.8] - 2024-03-25] + +## Changed + +- When too many retries are attempted, the RetryHandler will now throw an `AggregateException` (instead of an `InvalidOperationException`). + The `InnerExceptions` property of the `AggregateException` will contain a list of `ApiException` with the HTTP status code and an error message if available. + ## [1.3.7] - 2024-02-26 ### Changed diff --git a/Microsoft.Kiota.Http.HttpClientLibrary.Tests/Middleware/RetryHandlerTests.cs b/Microsoft.Kiota.Http.HttpClientLibrary.Tests/Middleware/RetryHandlerTests.cs index ac8f39f..426490b 100644 --- a/Microsoft.Kiota.Http.HttpClientLibrary.Tests/Middleware/RetryHandlerTests.cs +++ b/Microsoft.Kiota.Http.HttpClientLibrary.Tests/Middleware/RetryHandlerTests.cs @@ -6,6 +6,7 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Microsoft.Kiota.Abstractions; using Microsoft.Kiota.Http.HttpClientLibrary.Middleware; using Microsoft.Kiota.Http.HttpClientLibrary.Middleware.Options; using Microsoft.Kiota.Http.HttpClientLibrary.Tests.Mocks; @@ -220,6 +221,8 @@ public async Task ExceedMaxRetryShouldReturn(HttpStatusCode statusCode) Assert.IsType(exception); var aggregateException = exception as AggregateException; Assert.StartsWith("Too many retries performed.", aggregateException.Message); + Assert.All(aggregateException.InnerExceptions, innerexception => Assert.IsType(innerexception)); + Assert.All(aggregateException.InnerExceptions, innerexception => Assert.True((innerexception as ApiException).ResponseStatusCode == (int)statusCode)); Assert.False(httpRequestMessage.Headers.TryGetValues(RetryAttempt, out _), "Don't set Retry-Attempt Header"); } } diff --git a/Microsoft.Kiota.Http.HttpClientLibrary.sln b/Microsoft.Kiota.Http.HttpClientLibrary.sln index e6837d4..0251aa9 100644 --- a/Microsoft.Kiota.Http.HttpClientLibrary.sln +++ b/Microsoft.Kiota.Http.HttpClientLibrary.sln @@ -1,18 +1,19 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30114.105 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34714.143 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Kiota.Http.HttpClientLibrary", "src\Microsoft.Kiota.Http.HttpClientLibrary.csproj", "{769E84B2-FD14-4173-B83B-6792DFB0BA14}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{178B3904-FBB4-4E5A-A569-B5082BABC124}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + CHANGELOG.md = CHANGELOG.md EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Kiota.Http.HttpClientLibrary.Tests", "Microsoft.Kiota.Http.HttpClientLibrary.Tests\Microsoft.Kiota.Http.HttpClientLibrary.Tests.csproj", "{CCD20728-D593-48F8-8627-6E7A57B31A43}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Kiota.Http.HttpClientLibrary.Tests", "Microsoft.Kiota.Http.HttpClientLibrary.Tests\Microsoft.Kiota.Http.HttpClientLibrary.Tests.csproj", "{CCD20728-D593-48F8-8627-6E7A57B31A43}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KiotaGenerated", "Kiota.Generated\KiotaGenerated.csproj", "{6667C82F-EFF1-4D7C-828D-F981629C8256}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KiotaGenerated", "Kiota.Generated\KiotaGenerated.csproj", "{6667C82F-EFF1-4D7C-828D-F981629C8256}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/Microsoft.Kiota.Http.HttpClientLibrary.csproj b/src/Microsoft.Kiota.Http.HttpClientLibrary.csproj index 5bfda1d..e3efef6 100644 --- a/src/Microsoft.Kiota.Http.HttpClientLibrary.csproj +++ b/src/Microsoft.Kiota.Http.HttpClientLibrary.csproj @@ -14,7 +14,7 @@ https://aka.ms/kiota/docs true true - 1.3.7 + 1.3.8 true diff --git a/src/Middleware/RetryHandler.cs b/src/Middleware/RetryHandler.cs index b74a98e..b98c652 100644 --- a/src/Middleware/RetryHandler.cs +++ b/src/Middleware/RetryHandler.cs @@ -5,12 +5,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Microsoft.Kiota.Abstractions; using Microsoft.Kiota.Http.HttpClientLibrary.Extensions; using Microsoft.Kiota.Http.HttpClientLibrary.Middleware.Options; @@ -103,7 +105,7 @@ private async Task SendRetryAsync(HttpResponseMessage respo while(retryCount < retryOption.MaxRetry) { - exceptions.Add(await GetInnerException(response, cancellationToken)); + exceptions.Add(await GetInnerExceptionAsync(response)); using var retryActivity = activitySource?.StartActivity($"{nameof(RetryHandler)}_{nameof(SendAsync)} - attempt {retryCount}"); retryActivity?.SetTag("http.retry_count", retryCount); retryActivity?.SetTag("http.status_code", response.StatusCode); @@ -148,7 +150,7 @@ private async Task SendRetryAsync(HttpResponseMessage respo } } - exceptions.Add(await GetInnerException(response, cancellationToken)); + exceptions.Add(await GetInnerExceptionAsync(response)); throw new AggregateException($"Too many retries performed. More than {retryCount} retries encountered while sending the request.", exceptions); } @@ -231,42 +233,22 @@ private static bool ShouldRetry(HttpStatusCode statusCode) }; } - private static async Task GetInnerExceptionAsync(HttpResponseMessage response, CancellationToken cancellationToken) - + private static async Task GetInnerExceptionAsync(HttpResponseMessage response) { - var httpStatusCode = response.StatusCode; string? errorMessage = null; // Drain response content to free connections. Need to perform this // before retry attempt and before the TooManyRetries ServiceException. if(response.Content != null) { -#if NET5_0_OR_GREATER - errorMessage = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); -#else errorMessage = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - -#endif - errorMessage = GetStringFromContent(responseContent); } - return new ApiException($"HTTP request failed with status code: {httpStatusCode}.{errorMessage}") + return new ApiException($"HTTP request failed with status code: {response.StatusCode}.{errorMessage}") { ResponseStatusCode = (int)response.StatusCode, - ResponseHeaders = response.Headers.ToDictionary() + ResponseHeaders = response.Headers.ToDictionary(header => header.Key, header => header.Value), }; } - - private static string GetStringFromContent(byte[] content) - { - try - { - return System.Text.Encoding.UTF8.GetString(content); - } - catch(Exception) - { - return "The Graph retry handler was unable to cast the response into a UTF8 string."; - } - } } }