Browse Source

Update OpenIddict.Client.SystemNetHttp and OpenIddict.Validation.SystemNetHttp to support Microsoft.Extensions.Http.Resilience

pull/1981/head
Kévin Chalet 2 years ago
parent
commit
be31bc2fd5
  1. 1
      Directory.Build.targets
  2. 1
      Directory.Packages.props
  3. 5
      src/OpenIddict.Client.SystemNetHttp/OpenIddict.Client.SystemNetHttp.csproj
  4. 46
      src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpBuilder.cs
  5. 19
      src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpConfiguration.cs
  6. 35
      src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpOptions.cs
  7. 5
      src/OpenIddict.Validation.SystemNetHttp/OpenIddict.Validation.SystemNetHttp.csproj
  8. 46
      src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpBuilder.cs
  9. 19
      src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpConfiguration.cs
  10. 35
      src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpOptions.cs

1
Directory.Build.targets

@ -116,6 +116,7 @@
<PropertyGroup
Condition=" ('$(TargetFrameworkIdentifier)' == '.NETCoreApp' And $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '8.0'))) ">
<DefineConstants>$(DefineConstants);SUPPORTS_HTTP_CLIENT_RESILIENCE</DefineConstants>
<DefineConstants>$(DefineConstants);SUPPORTS_IMMUTABLE_COLLECTIONS_MARSHAL</DefineConstants>
<DefineConstants>$(DefineConstants);SUPPORTS_TIME_PROVIDER</DefineConstants>
</PropertyGroup>

1
Directory.Packages.props

@ -326,6 +326,7 @@
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="8.2.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="8.0.0" />

5
src/OpenIddict.Client.SystemNetHttp/OpenIddict.Client.SystemNetHttp.csproj

@ -42,6 +42,11 @@
<PackageReference Include="System.Net.Http.Json" />
</ItemGroup>
<ItemGroup
Condition=" ('$(TargetFrameworkIdentifier)' == '.NETCoreApp' And $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '8.0'))) ">
<PackageReference Include="Microsoft.Extensions.Http.Resilience" />
</ItemGroup>
<ItemGroup>
<Using Include="System.Net.Http.Json" />
<Using Include="OpenIddict.Abstractions" />

46
src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpBuilder.cs

@ -241,6 +241,52 @@ public sealed class OpenIddictClientSystemNetHttpBuilder
return Configure(options => options.HttpErrorPolicy = policy);
}
#if SUPPORTS_HTTP_CLIENT_RESILIENCE
/// <summary>
/// Replaces the default HTTP resilience pipeline used by the OpenIddict client services.
/// </summary>
/// <param name="configuration">
/// The delegate used to configure the <see cref="ResiliencePipeline{HttpResponseMessage}"/>.
/// </param>
/// <remarks>
/// Note: this option has no effect when an HTTP error policy was explicitly configured
/// using <see cref="SetHttpErrorPolicy(IAsyncPolicy{HttpResponseMessage})"/>.
/// </remarks>
/// <returns>The <see cref="OpenIddictClientSystemNetHttpBuilder"/> instance.</returns>
public OpenIddictClientSystemNetHttpBuilder SetHttpResiliencePipeline(
Action<ResiliencePipelineBuilder<HttpResponseMessage>> configuration)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
var builder = new ResiliencePipelineBuilder<HttpResponseMessage>();
configuration(builder);
return SetHttpResiliencePipeline(builder.Build());
}
/// <summary>
/// Replaces the default HTTP resilience pipeline used by the OpenIddict client services.
/// </summary>
/// <param name="pipeline">The HTTP resilience pipeline.</param>
/// <remarks>
/// Note: this option has no effect when an HTTP error policy was explicitly configured
/// using <see cref="SetHttpErrorPolicy(IAsyncPolicy{HttpResponseMessage})"/>.
/// </remarks>
/// <returns>The <see cref="OpenIddictClientSystemNetHttpBuilder"/> instance.</returns>
public OpenIddictClientSystemNetHttpBuilder SetHttpResiliencePipeline(ResiliencePipeline<HttpResponseMessage> pipeline)
{
if (pipeline is null)
{
throw new ArgumentNullException(nameof(pipeline));
}
return Configure(options => options.HttpResiliencePipeline = pipeline);
}
#endif
/// <summary>
/// Sets the product information used in the "User-Agent" header that is attached
/// to the backchannel HTTP requests sent to the authorization server.

19
src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpConfiguration.cs

@ -12,6 +12,10 @@ using Microsoft.Extensions.Http;
using Microsoft.Extensions.Options;
using Polly;
#if SUPPORTS_HTTP_CLIENT_RESILIENCE
using Microsoft.Extensions.Http.Resilience;
#endif
namespace OpenIddict.Client.SystemNetHttp;
/// <summary>
@ -105,12 +109,23 @@ public sealed class OpenIddictClientSystemNetHttpConfiguration : IConfigureOptio
// To avoid that, cookies support is explicitly disabled here, for security reasons.
handler.UseCookies = false;
// Unless the HTTP error policy was explicitly disabled in the options,
// add the HTTP handler responsible for replaying failed HTTP requests.
// If applicable, add the handler responsible for replaying failed HTTP requests.
//
// Note: on .NET 8.0 and higher, the HTTP error policy is always set
// to null by default and an HTTP resilience pipeline is used instead.
if (options.CurrentValue.HttpErrorPolicy is IAsyncPolicy<HttpResponseMessage> policy)
{
builder.AdditionalHandlers.Add(new PolicyHttpMessageHandler(policy));
}
#if SUPPORTS_HTTP_CLIENT_RESILIENCE
else if (options.CurrentValue.HttpResiliencePipeline is ResiliencePipeline<HttpResponseMessage> pipeline)
{
#pragma warning disable EXTEXP0001
builder.AdditionalHandlers.Add(new ResilienceHandler(pipeline));
#pragma warning restore EXTEXP0001
}
#endif
});
// Register the user-defined HTTP client handler actions.

35
src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpOptions.cs

@ -11,6 +11,10 @@ using System.Net.Mail;
using Polly;
using Polly.Extensions.Http;
#if SUPPORTS_HTTP_CLIENT_RESILIENCE
using Microsoft.Extensions.Http.Resilience;
#endif
namespace OpenIddict.Client.SystemNetHttp;
/// <summary>
@ -21,10 +25,37 @@ public sealed class OpenIddictClientSystemNetHttpOptions
/// <summary>
/// Gets or sets the HTTP Polly error policy used by the internal OpenIddict HTTP clients.
/// </summary>
/// <remarks>
/// Note: on .NET 8.0 and higher, this property is set to <see langword="null"/> by default.
/// </remarks>
public IAsyncPolicy<HttpResponseMessage>? HttpErrorPolicy { get; set; }
#if !SUPPORTS_HTTP_CLIENT_RESILIENCE
= HttpPolicyExtensions.HandleTransientHttpError()
.OrResult(response => response.StatusCode == HttpStatusCode.NotFound)
.WaitAndRetryAsync(4, attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)));
.OrResult(static response => response.StatusCode is HttpStatusCode.NotFound)
.WaitAndRetryAsync(4, static attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)));
#endif
#if SUPPORTS_HTTP_CLIENT_RESILIENCE
/// <summary>
/// Gets or sets the HTTP resilience pipeline used by the internal OpenIddict HTTP clients.
/// </summary>
/// <remarks>
/// Note: this property is not used when <see cref="HttpErrorPolicy"/>
/// is explicitly set to a non-<see langword="null"/> value.
/// </remarks>
public ResiliencePipeline<HttpResponseMessage>? HttpResiliencePipeline { get; set; }
= new ResiliencePipelineBuilder<HttpResponseMessage>()
.AddRetry(new HttpRetryStrategyOptions
{
DelayGenerator = static arguments => new(
TimeSpan.FromSeconds(Math.Pow(2, arguments.AttemptNumber))),
MaxRetryAttempts = 4,
ShouldHandle = static arguments => new(
HttpClientResiliencePredicates.IsTransient(arguments.Outcome) ||
arguments.Outcome.Result?.StatusCode is HttpStatusCode.NotFound)
})
.Build();
#endif
/// <summary>
/// Gets or sets the contact mail address used in the "From" header that is

5
src/OpenIddict.Validation.SystemNetHttp/OpenIddict.Validation.SystemNetHttp.csproj

@ -42,6 +42,11 @@
<PackageReference Include="System.Net.Http.Json" />
</ItemGroup>
<ItemGroup
Condition=" ('$(TargetFrameworkIdentifier)' == '.NETCoreApp' And $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '8.0'))) ">
<PackageReference Include="Microsoft.Extensions.Http.Resilience" />
</ItemGroup>
<ItemGroup>
<Using Include="System.Net.Http.Json" />
<Using Include="OpenIddict.Abstractions" />

46
src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpBuilder.cs

@ -129,6 +129,52 @@ public sealed class OpenIddictValidationSystemNetHttpBuilder
return Configure(options => options.HttpErrorPolicy = policy);
}
#if SUPPORTS_HTTP_CLIENT_RESILIENCE
/// <summary>
/// Replaces the default HTTP resilience pipeline used by the OpenIddict client services.
/// </summary>
/// <param name="configuration">
/// The delegate used to configure the <see cref="ResiliencePipeline{HttpResponseMessage}"/>.
/// </param>
/// <remarks>
/// Note: this option has no effect when an HTTP error policy was explicitly configured
/// using <see cref="SetHttpErrorPolicy(IAsyncPolicy{HttpResponseMessage})"/>.
/// </remarks>
/// <returns>The <see cref="OpenIddictValidationSystemNetHttpBuilder"/> instance.</returns>
public OpenIddictValidationSystemNetHttpBuilder SetHttpResiliencePipeline(
Action<ResiliencePipelineBuilder<HttpResponseMessage>> configuration)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
var builder = new ResiliencePipelineBuilder<HttpResponseMessage>();
configuration(builder);
return SetHttpResiliencePipeline(builder.Build());
}
/// <summary>
/// Replaces the default HTTP resilience pipeline used by the OpenIddict client services.
/// </summary>
/// <param name="pipeline">The HTTP resilience pipeline.</param>
/// <remarks>
/// Note: this option has no effect when an HTTP error policy was explicitly configured
/// using <see cref="SetHttpErrorPolicy(IAsyncPolicy{HttpResponseMessage})"/>.
/// </remarks>
/// <returns>The <see cref="OpenIddictValidationSystemNetHttpBuilder"/> instance.</returns>
public OpenIddictValidationSystemNetHttpBuilder SetHttpResiliencePipeline(ResiliencePipeline<HttpResponseMessage> pipeline)
{
if (pipeline is null)
{
throw new ArgumentNullException(nameof(pipeline));
}
return Configure(options => options.HttpResiliencePipeline = pipeline);
}
#endif
/// <summary>
/// Sets the product information used in the "User-Agent" header that is attached
/// to the backchannel HTTP requests sent to the authorization server.

19
src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpConfiguration.cs

@ -11,6 +11,10 @@ using Microsoft.Extensions.Http;
using Microsoft.Extensions.Options;
using Polly;
#if SUPPORTS_HTTP_CLIENT_RESILIENCE
using Microsoft.Extensions.Http.Resilience;
#endif
namespace OpenIddict.Validation.SystemNetHttp;
/// <summary>
@ -98,12 +102,23 @@ public sealed class OpenIddictValidationSystemNetHttpConfiguration : IConfigureO
// To avoid that, cookies support is explicitly disabled here, for security reasons.
handler.UseCookies = false;
// Unless the HTTP error policy was explicitly disabled in the options,
// add the HTTP handler responsible for replaying failed HTTP requests.
// If applicable, add the handler responsible for replaying failed HTTP requests.
//
// Note: on .NET 8.0 and higher, the HTTP error policy is always set
// to null by default and an HTTP resilience pipeline is used instead.
if (options.CurrentValue.HttpErrorPolicy is IAsyncPolicy<HttpResponseMessage> policy)
{
builder.AdditionalHandlers.Add(new PolicyHttpMessageHandler(policy));
}
#if SUPPORTS_HTTP_CLIENT_RESILIENCE
else if (options.CurrentValue.HttpResiliencePipeline is ResiliencePipeline<HttpResponseMessage> pipeline)
{
#pragma warning disable EXTEXP0001
builder.AdditionalHandlers.Add(new ResilienceHandler(pipeline));
#pragma warning restore EXTEXP0001
}
#endif
});
// Register the user-defined HTTP client handler actions.

35
src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpOptions.cs

@ -11,6 +11,10 @@ using System.Net.Mail;
using Polly;
using Polly.Extensions.Http;
#if SUPPORTS_HTTP_CLIENT_RESILIENCE
using Microsoft.Extensions.Http.Resilience;
#endif
namespace OpenIddict.Validation.SystemNetHttp;
/// <summary>
@ -21,10 +25,37 @@ public sealed class OpenIddictValidationSystemNetHttpOptions
/// <summary>
/// Gets or sets the HTTP Polly error policy used by the internal OpenIddict HTTP clients.
/// </summary>
/// <remarks>
/// Note: on .NET 8.0 and higher, this property is set to <see langword="null"/> by default.
/// </remarks>
public IAsyncPolicy<HttpResponseMessage>? HttpErrorPolicy { get; set; }
#if !SUPPORTS_HTTP_CLIENT_RESILIENCE
= HttpPolicyExtensions.HandleTransientHttpError()
.OrResult(response => response.StatusCode == HttpStatusCode.NotFound)
.WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)));
.OrResult(static response => response.StatusCode is HttpStatusCode.NotFound)
.WaitAndRetryAsync(4, static attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)));
#endif
#if SUPPORTS_HTTP_CLIENT_RESILIENCE
/// <summary>
/// Gets or sets the HTTP resilience pipeline used by the internal OpenIddict HTTP clients.
/// </summary>
/// <remarks>
/// Note: this property is not used when <see cref="HttpErrorPolicy"/>
/// is explicitly set to a non-<see langword="null"/> value.
/// </remarks>
public ResiliencePipeline<HttpResponseMessage>? HttpResiliencePipeline { get; set; }
= new ResiliencePipelineBuilder<HttpResponseMessage>()
.AddRetry(new HttpRetryStrategyOptions
{
DelayGenerator = static arguments => new(
TimeSpan.FromSeconds(Math.Pow(2, arguments.AttemptNumber))),
MaxRetryAttempts = 4,
ShouldHandle = static arguments => new(
HttpClientResiliencePredicates.IsTransient(arguments.Outcome) ||
arguments.Outcome.Result?.StatusCode is HttpStatusCode.NotFound)
})
.Build();
#endif
/// <summary>
/// Gets or sets the contact mail address used in the "From" header that is

Loading…
Cancel
Save