Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix/retry attributes #497

Merged
merged 3 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.16.1] - 2024-12-18

### Changed

- Aligned retry open telemetry attributes names with latest specification. [#324](https://github.com/microsoft/kiota-dotnet/issues/324)
- Fixed a bug where the client would fail on 301/302 responses with no location headers. [#272](https://github.com/microsoft/kiota-dotnet/issues/272)

## [1.16.0] - 2024-12-13
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<!-- Common default project properties for ALL projects-->
<PropertyGroup>
<VersionPrefix>1.16.0</VersionPrefix>
<VersionPrefix>1.16.1</VersionPrefix>
<VersionSuffix></VersionSuffix>
<!-- This is overidden in test projects by setting to true-->
<IsTestProject>false</IsTestProject>
Expand Down
4 changes: 3 additions & 1 deletion src/http/httpClient/HttpClientRequestAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use for cancelling the request.</param>
/// <returns>The deserialized primitive response model.</returns>
#if NET5_0_OR_GREATER
public async Task<ModelType?> SendPrimitiveAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] ModelType>(RequestInformation requestInfo, Dictionary<string, ParsableFactory<IParsable>>? errorMapping = default, CancellationToken cancellationToken = default)

Check warning on line 228 in src/http/httpClient/HttpClientRequestAdapter.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor this method to reduce its Cognitive Complexity from 32 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)

Check warning on line 228 in src/http/httpClient/HttpClientRequestAdapter.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor this method to reduce its Cognitive Complexity from 32 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)
#else
public async Task<ModelType?> SendPrimitiveAsync<ModelType>(RequestInformation requestInfo, Dictionary<string, ParsableFactory<IParsable>>? errorMapping = default, CancellationToken cancellationToken = default)
#endif
Expand Down Expand Up @@ -487,13 +487,13 @@
if(contentStream == Stream.Null || (contentStream.CanSeek && contentStream.Length == 0))
return null;// ensure a useful stream is passed to the factory
#pragma warning disable CS0618 // Type or member is obsolete
//TODO remove with v2

Check warning on line 490 in src/http/httpClient/HttpClientRequestAdapter.cs

View workflow job for this annotation

GitHub Actions / Build

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)

Check warning on line 490 in src/http/httpClient/HttpClientRequestAdapter.cs

View workflow job for this annotation

GitHub Actions / Build

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)

Check warning on line 490 in src/http/httpClient/HttpClientRequestAdapter.cs

View workflow job for this annotation

GitHub Actions / Build

Complete the task associated to this 'TODO' comment. (https://rules.sonarsource.com/csharp/RSPEC-1135)
var rootNode = pNodeFactory is IAsyncParseNodeFactory asyncParseNodeFactory ? await asyncParseNodeFactory.GetRootParseNodeAsync(responseContentType!, contentStream, cancellationToken).ConfigureAwait(false) : pNodeFactory.GetRootParseNode(responseContentType!, contentStream);
#pragma warning restore CS0618 // Type or member is obsolete
return rootNode;
}
private const string ClaimsKey = "claims";
private const string BearerAuthenticationScheme = "Bearer";

Check warning on line 496 in src/http/httpClient/HttpClientRequestAdapter.cs

View workflow job for this annotation

GitHub Actions / Build

Remove the unused private field 'BearerAuthenticationScheme'. (https://rules.sonarsource.com/csharp/RSPEC-1144)

Check warning on line 496 in src/http/httpClient/HttpClientRequestAdapter.cs

View workflow job for this annotation

GitHub Actions / Build

Remove the unused private field 'BearerAuthenticationScheme'. (https://rules.sonarsource.com/csharp/RSPEC-1144)

Check warning on line 496 in src/http/httpClient/HttpClientRequestAdapter.cs

View workflow job for this annotation

GitHub Actions / Build

Remove the unused private field 'BearerAuthenticationScheme'. (https://rules.sonarsource.com/csharp/RSPEC-1144)
private async Task<HttpResponseMessage> GetHttpResponseMessageAsync(RequestInformation requestInfo, CancellationToken cancellationToken, Activity? activityForAttributes, string? claims = default, bool isStreamResponse = false)
{
using var span = activitySource?.StartActivity(nameof(GetHttpResponseMessageAsync));
Expand Down Expand Up @@ -541,6 +541,8 @@
/// </summary>
public const string AuthenticateChallengedEventKey = "com.microsoft.kiota.authenticate_challenge_received";

internal const string RetryCountAttributeName = "http.request.resend_count";

private async Task<HttpResponseMessage> RetryCAEResponseIfRequiredAsync(HttpResponseMessage response, RequestInformation requestInfo, CancellationToken cancellationToken, string? claims, Activity? activityForAttributes)
{
using var span = activitySource?.StartActivity(nameof(RetryCAEResponseIfRequiredAsync));
Expand All @@ -554,7 +556,7 @@
return response;
}
span?.AddEvent(new ActivityEvent(AuthenticateChallengedEventKey));
activityForAttributes?.SetTag("http.retry_count", 1);
activityForAttributes?.SetTag(RetryCountAttributeName, 1);
requestInfo.Content?.Seek(0, SeekOrigin.Begin);
await DrainAsync(response, cancellationToken).ConfigureAwait(false);
return await GetHttpResponseMessageAsync(requestInfo, cancellationToken, activityForAttributes, responseClaims).ConfigureAwait(false);
Expand All @@ -575,7 +577,7 @@
else throw new InvalidOperationException($"Could not convert the request information to a {typeof(T).Name}");
}

private HttpRequestMessage GetRequestMessageFromRequestInformation(RequestInformation requestInfo, Activity? activityForAttributes)

Check warning on line 580 in src/http/httpClient/HttpClientRequestAdapter.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor this method to reduce its Cognitive Complexity from 25 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)

Check warning on line 580 in src/http/httpClient/HttpClientRequestAdapter.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor this method to reduce its Cognitive Complexity from 25 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)
{
using var span = activitySource?.StartActivity(nameof(GetRequestMessageFromRequestInformation));
SetBaseUrlForRequestInformation(requestInfo);// this method can also be called from a different context so ensure the baseUrl is added.
Expand Down
13 changes: 7 additions & 6 deletions src/http/httpClient/Middleware/RetryHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,12 @@ private async Task<HttpResponseMessage> SendRetryAsync(HttpResponseMessage respo
{
exceptions.Add(await GetInnerExceptionAsync(response).ConfigureAwait(false));
using var retryActivity = activitySource?.StartActivity($"{nameof(RetryHandler)}_{nameof(SendAsync)} - attempt {retryCount}");
retryActivity?.SetTag("http.retry_count", retryCount);
retryActivity?.SetTag(HttpClientRequestAdapter.RetryCountAttributeName, retryCount);
retryActivity?.SetTag("http.response.status_code", response.StatusCode);

// Call Delay method to get delay time from response's Retry-After header or by exponential backoff
Task delay = RetryHandler.DelayAsync(response, retryCount, retryOption.Delay, out double delayInSeconds, cancellationToken);
var delay = DelayAsync(response, retryCount, retryOption.Delay, out double delayInSeconds, cancellationToken);
retryActivity?.SetTag("http.request.resend_delay", delayInSeconds);

// If client specified a retries time limit, let's honor it
if(retryOption.RetriesTimeLimit > TimeSpan.Zero)
Expand Down Expand Up @@ -176,12 +177,12 @@ private static void AddOrUpdateRetryAttempt(HttpRequestMessage request, int retr
/// <param name="delayInSeconds"></param>
/// <param name="cancellationToken">The cancellationToken for the Http request</param>
/// <returns>The <see cref="Task"/> for delay operation.</returns>
internal static Task DelayAsync(HttpResponseMessage response, int retryCount, int delay, out double delayInSeconds, CancellationToken cancellationToken)
static internal Task DelayAsync(HttpResponseMessage response, int retryCount, int delay, out double delayInSeconds, CancellationToken cancellationToken)
{
delayInSeconds = delay;
if(response.Headers.TryGetValues(RetryAfter, out IEnumerable<string>? values))
if(response.Headers.TryGetValues(RetryAfter, out var values))
{
using IEnumerator<string> v = values.GetEnumerator();
using var v = values.GetEnumerator();
string retryAfter = v.MoveNext() ? v.Current : throw new InvalidOperationException("Retry-After header is empty.");
// the delay could be in the form of a seconds or a http date. See https://httpwg.org/specs/rfc7231.html#header.retry-after
if(int.TryParse(retryAfter, out int delaySeconds))
Expand All @@ -200,7 +201,7 @@ internal static Task DelayAsync(HttpResponseMessage response, int retryCount, in
delayInSeconds = CalculateExponentialDelay(retryCount, delay);
}

TimeSpan delayTimeSpan = TimeSpan.FromSeconds(Math.Min(delayInSeconds, RetryHandlerOption.MaxDelay));
var delayTimeSpan = TimeSpan.FromSeconds(Math.Min(delayInSeconds, RetryHandlerOption.MaxDelay));
delayInSeconds = delayTimeSpan.TotalSeconds;
return Task.Delay(delayTimeSpan, cancellationToken);
}
Expand Down
Loading