|
|
@ -24,7 +24,8 @@ namespace OpenIddict.Client.SystemNetHttp; |
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
[EditorBrowsable(EditorBrowsableState.Advanced)] |
|
|
[EditorBrowsable(EditorBrowsableState.Advanced)] |
|
|
public sealed class OpenIddictClientSystemNetHttpConfiguration : IConfigureOptions<OpenIddictClientOptions>, |
|
|
public sealed class OpenIddictClientSystemNetHttpConfiguration : IConfigureOptions<OpenIddictClientOptions>, |
|
|
IConfigureNamedOptions<HttpClientFactoryOptions> |
|
|
IConfigureNamedOptions<HttpClientFactoryOptions>, |
|
|
|
|
|
IPostConfigureOptions<HttpClientFactoryOptions> |
|
|
{ |
|
|
{ |
|
|
private readonly IServiceProvider _provider; |
|
|
private readonly IServiceProvider _provider; |
|
|
|
|
|
|
|
|
@ -73,7 +74,7 @@ public sealed class OpenIddictClientSystemNetHttpConfiguration : IConfigureOptio |
|
|
|
|
|
|
|
|
var settings = _provider.GetRequiredService<IOptionsMonitor<OpenIddictClientSystemNetHttpOptions>>().CurrentValue; |
|
|
var settings = _provider.GetRequiredService<IOptionsMonitor<OpenIddictClientSystemNetHttpOptions>>().CurrentValue; |
|
|
|
|
|
|
|
|
options.HttpClientActions.Add(client => |
|
|
options.HttpClientActions.Add(static client => |
|
|
{ |
|
|
{ |
|
|
// By default, HttpClient uses a default timeout of 100 seconds and allows payloads of up to 2GB.
|
|
|
// By default, HttpClient uses a default timeout of 100 seconds and allows payloads of up to 2GB.
|
|
|
// To help reduce the effects of malicious responses (e.g responses returned at a very slow pace
|
|
|
// To help reduce the effects of malicious responses (e.g responses returned at a very slow pace
|
|
|
@ -95,6 +96,61 @@ public sealed class OpenIddictClientSystemNetHttpConfiguration : IConfigureOptio |
|
|
#else
|
|
|
#else
|
|
|
var options = _provider.GetRequiredService<IOptionsMonitor<OpenIddictClientSystemNetHttpOptions>>(); |
|
|
var options = _provider.GetRequiredService<IOptionsMonitor<OpenIddictClientSystemNetHttpOptions>>(); |
|
|
#endif
|
|
|
#endif
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
foreach (var action in settings.HttpClientHandlerActions) |
|
|
|
|
|
{ |
|
|
|
|
|
options.HttpMessageHandlerBuilderActions.Add(builder => action(registration, |
|
|
|
|
|
builder.PrimaryHandler as HttpClientHandler ?? |
|
|
|
|
|
throw new InvalidOperationException(SR.FormatID0373(typeof(HttpClientHandler).FullName)))); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
|
|
public void PostConfigure(string? name, HttpClientFactoryOptions options) |
|
|
|
|
|
{ |
|
|
|
|
|
if (options is null) |
|
|
|
|
|
{ |
|
|
|
|
|
throw new ArgumentNullException(nameof(options)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Only amend the HTTP client factory options if the instance is managed by OpenIddict.
|
|
|
|
|
|
if (string.IsNullOrEmpty(name) || !TryResolveRegistrationId(name, out string? identifier)) |
|
|
|
|
|
{ |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
options.HttpMessageHandlerBuilderActions.Insert(0, static builder => |
|
|
|
|
|
{ |
|
|
|
|
|
// Note: Microsoft.Extensions.Http 9.0+ no longer uses HttpClientHandler as the default instance
|
|
|
|
|
|
// for PrimaryHandler on platforms that support SocketsHttpHandler. Since OpenIddict requires an
|
|
|
|
|
|
// HttpClientHandler instance, it is manually reassigned here if it's not an HttpClientHandler.
|
|
|
|
|
|
if (builder.PrimaryHandler is not HttpClientHandler) |
|
|
|
|
|
{ |
|
|
|
|
|
builder.PrimaryHandler = new HttpClientHandler(); |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
options.HttpMessageHandlerBuilderActions.Add(static builder => |
|
|
|
|
|
{ |
|
|
if (builder.PrimaryHandler is not HttpClientHandler handler) |
|
|
if (builder.PrimaryHandler is not HttpClientHandler handler) |
|
|
{ |
|
|
{ |
|
|
throw new InvalidOperationException(SR.FormatID0373(typeof(HttpClientHandler).FullName)); |
|
|
throw new InvalidOperationException(SR.FormatID0373(typeof(HttpClientHandler).FullName)); |
|
|
@ -135,48 +191,22 @@ public sealed class OpenIddictClientSystemNetHttpConfiguration : IConfigureOptio |
|
|
//
|
|
|
//
|
|
|
// To avoid that, cookies support is explicitly disabled here, for security reasons.
|
|
|
// To avoid that, cookies support is explicitly disabled here, for security reasons.
|
|
|
handler.UseCookies = false; |
|
|
handler.UseCookies = false; |
|
|
|
|
|
|
|
|
// 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.
|
|
|
static bool TryResolveRegistrationId(string name, [NotNullWhen(true)] out string? value) |
|
|
foreach (var action in settings.HttpClientHandlerActions) |
|
|
{ |
|
|
{ |
|
|
var assembly = typeof(OpenIddictClientSystemNetHttpOptions).Assembly.GetName(); |
|
|
options.HttpMessageHandlerBuilderActions.Add(builder => action(registration, |
|
|
|
|
|
builder.PrimaryHandler as HttpClientHandler ?? |
|
|
|
|
|
throw new InvalidOperationException(SR.FormatID0373(typeof(HttpClientHandler).FullName)))); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static bool TryResolveRegistrationId(string name, [NotNullWhen(true)] out string? value) |
|
|
if (!name.StartsWith(assembly.Name!, StringComparison.Ordinal) || |
|
|
|
|
|
name.Length < assembly.Name!.Length + 1 || |
|
|
|
|
|
name[assembly.Name.Length] is not ':') |
|
|
{ |
|
|
{ |
|
|
var assembly = typeof(OpenIddictClientSystemNetHttpOptions).Assembly.GetName(); |
|
|
value = null; |
|
|
|
|
|
return false; |
|
|
if (!name.StartsWith(assembly.Name!, StringComparison.Ordinal) || |
|
|
|
|
|
name.Length < assembly.Name!.Length + 1 || |
|
|
|
|
|
name[assembly.Name.Length] is not ':') |
|
|
|
|
|
{ |
|
|
|
|
|
value = null; |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
value = name[(assembly.Name.Length + 1)..]; |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
value = name[(assembly.Name.Length + 1)..]; |
|
|
|
|
|
return true; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|