Browse Source

Introduce ConfigureHttpClient()/ConfigureHttpClientHandler() to simplify customizing the HttpClient/HttpClientHandler used by the client and validation stacks

pull/1765/head
Kévin Chalet 3 years ago
parent
commit
fbc4a0c56e
  1. 134
      src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpBuilder.cs
  2. 29
      src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpConfiguration.cs
  3. 12
      src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpOptions.cs
  4. 34
      src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpBuilder.cs
  5. 23
      src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpConfiguration.cs
  6. 12
      src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpOptions.cs

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

@ -32,7 +32,7 @@ public sealed class OpenIddictClientSystemNetHttpBuilder
public IServiceCollection Services { get; }
/// <summary>
/// Amends the default OpenIddict client/server integration configuration.
/// Amends the default OpenIddict client/System.Net.Http configuration.
/// </summary>
/// <param name="configuration">The delegate used to configure the OpenIddict options.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
@ -49,6 +49,138 @@ public sealed class OpenIddictClientSystemNetHttpBuilder
return this;
}
/// <summary>
/// Configures the <see cref="HttpClient"/> used by the OpenIddict client/System.Net.Http integration.
/// </summary>
/// <remarks>
/// Note: customizations configured using this method apply to all providers.
/// </remarks>
/// <param name="configuration">The delegate used to configure the <see cref="HttpClient"/>.</param>
/// <returns>The <see cref="OpenIddictClientSystemNetHttpBuilder"/> instance.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictClientSystemNetHttpBuilder ConfigureHttpClient(Action<HttpClient> configuration)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
return Configure(options =>
{
if (options.HttpClientActions.TryGetValue(string.Empty, out var actions))
{
actions.Add(configuration);
}
else
{
options.HttpClientActions[string.Empty] = new(capacity: 1) { configuration };
}
});
}
/// <summary>
/// Configures the <see cref="HttpClient"/> used by the OpenIddict client/System.Net.Http integration.
/// </summary>
/// <remarks>
/// Note: customizations configured using this method only apply to the specified provider.
/// </remarks>
/// <param name="provider">The provider name, to which the customizations are applied.</param>
/// <param name="configuration">The delegate used to configure the <see cref="HttpClient"/>.</param>
/// <returns>The <see cref="OpenIddictClientSystemNetHttpBuilder"/> instance.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictClientSystemNetHttpBuilder ConfigureHttpClient(string provider, Action<HttpClient> configuration)
{
if (string.IsNullOrEmpty(provider))
{
throw new ArgumentException(SR.FormatID0366(nameof(provider)), nameof(provider));
}
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
return Configure(options =>
{
if (options.HttpClientActions.TryGetValue(provider, out var actions))
{
actions.Add(configuration);
}
else
{
options.HttpClientActions[provider] = new(capacity: 1) { configuration };
}
});
}
/// <summary>
/// Configures the <see cref="HttpClientHandler"/> used by the OpenIddict client/System.Net.Http integration.
/// </summary>
/// <remarks>
/// Note: customizations configured using this method apply to all providers.
/// </remarks>
/// <param name="configuration">The delegate used to configure the <see cref="HttpClientHandler"/>.</param>
/// <returns>The <see cref="OpenIddictClientSystemNetHttpBuilder"/> instance.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictClientSystemNetHttpBuilder ConfigureHttpClientHandler(Action<HttpClientHandler> configuration)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
return Configure(options =>
{
if (options.HttpClientHandlerActions.TryGetValue(string.Empty, out var actions))
{
actions.Add(configuration);
}
else
{
options.HttpClientHandlerActions[string.Empty] = new(capacity: 1) { configuration };
}
});
}
/// <summary>
/// Configures the <see cref="HttpClientHandler"/> used by the OpenIddict client/System.Net.Http integration.
/// </summary>
/// <remarks>
/// Note: customizations configured using this method only apply to the specified provider.
/// </remarks>
/// <param name="provider">The provider name, to which the customizations are applied.</param>
/// <param name="configuration">The delegate used to configure the <see cref="HttpClientHandler"/>.</param>
/// <returns>The <see cref="OpenIddictClientSystemNetHttpBuilder"/> instance.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictClientSystemNetHttpBuilder ConfigureHttpClientHandler(string provider, Action<HttpClientHandler> configuration)
{
if (string.IsNullOrEmpty(provider))
{
throw new ArgumentException(SR.FormatID0366(nameof(provider)), nameof(provider));
}
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
return Configure(options =>
{
if (options.HttpClientHandlerActions.TryGetValue(provider, out var actions))
{
actions.Add(configuration);
}
else
{
options.HttpClientHandlerActions[provider] = new(capacity: 1) { configuration };
}
});
}
/// <summary>
/// Sets the contact address used in the "From" header that is attached
/// to the backchannel HTTP requests sent to the authorization server.

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

@ -19,7 +19,6 @@ namespace OpenIddict.Client.SystemNetHttp;
public sealed class OpenIddictClientSystemNetHttpConfiguration : IConfigureOptions<OpenIddictClientOptions>,
IConfigureNamedOptions<HttpClientFactoryOptions>
{
#if !SUPPORTS_SERVICE_PROVIDER_IN_HTTP_MESSAGE_HANDLER_BUILDER
private readonly IServiceProvider _provider;
/// <summary>
@ -28,7 +27,6 @@ public sealed class OpenIddictClientSystemNetHttpConfiguration : IConfigureOptio
/// <param name="provider">The service provider.</param>
public OpenIddictClientSystemNetHttpConfiguration(IServiceProvider provider)
=> _provider = provider ?? throw new ArgumentNullException(nameof(provider));
#endif
/// <inheritdoc/>
public void Configure(OpenIddictClientOptions options)
@ -60,15 +58,26 @@ public sealed class OpenIddictClientSystemNetHttpConfiguration : IConfigureOptio
return;
}
options.HttpClientActions.Add(options =>
var settings = _provider.GetRequiredService<IOptionsMonitor<OpenIddictClientSystemNetHttpOptions>>().CurrentValue;
options.HttpClientActions.Add(client =>
{
// 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
// or containing an infine amount of data), the default values are amended to use lower values.
options.MaxResponseContentBufferSize = 10 * 1024 * 1024;
options.Timeout = TimeSpan.FromMinutes(1);
client.MaxResponseContentBufferSize = 10 * 1024 * 1024;
client.Timeout = TimeSpan.FromMinutes(1);
});
// Register the user-defined HTTP client actions.
foreach (var action in settings.HttpClientActions
.Where(action => string.IsNullOrEmpty(action.Key) || // Note: actions that have an empty key apply to all providers.
action.Key.AsSpan().Equals(name.AsSpan(assembly.Name!.Length + 1), StringComparison.Ordinal))
.SelectMany(action => action.Value))
{
options.HttpClientActions.Add(action);
}
options.HttpMessageHandlerBuilderActions.Add(builder =>
{
#if SUPPORTS_SERVICE_PROVIDER_IN_HTTP_MESSAGE_HANDLER_BUILDER
@ -98,5 +107,15 @@ public sealed class OpenIddictClientSystemNetHttpConfiguration : IConfigureOptio
builder.AdditionalHandlers.Add(new PolicyHttpMessageHandler(policy));
}
});
// Register the user-defined HTTP client handler actions.
foreach (var action in settings.HttpClientHandlerActions
.Where(action => string.IsNullOrEmpty(action.Key) || // Note: actions that have an empty key apply to all providers.
action.Key.AsSpan().Equals(name.AsSpan(assembly.Name!.Length + 1), StringComparison.Ordinal))
.SelectMany(action => action.Value))
{
options.HttpMessageHandlerBuilderActions.Add(builder => action(builder.PrimaryHandler as HttpClientHandler ??
throw new InvalidOperationException(SR.FormatID0373(typeof(HttpClientHandler).FullName))));
}
}
}

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

@ -36,4 +36,16 @@ public sealed class OpenIddictClientSystemNetHttpOptions
/// attached to the backchannel HTTP requests sent to the authorization server.
/// </summary>
public ProductInfoHeaderValue? ProductInformation { get; set; }
/// <summary>
/// Gets the user-defined actions used to amend the <see cref="HttpClient"/>
/// instances created by the OpenIddict client/System.Net.Http integration.
/// </summary>
public Dictionary<string, List<Action<HttpClient>>> HttpClientActions { get; } = new();
/// <summary>
/// Gets the user-defined actions used to amend the <see cref="HttpClientHandler"/>
/// instances created by the OpenIddict client/System.Net.Http integration.
/// </summary>
public Dictionary<string, List<Action<HttpClientHandler>>> HttpClientHandlerActions { get; } = new();
}

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

@ -32,7 +32,7 @@ public sealed class OpenIddictValidationSystemNetHttpBuilder
public IServiceCollection Services { get; }
/// <summary>
/// Amends the default OpenIddict validation/server integration configuration.
/// Amends the default OpenIddict validation/System.Net.Http configuration.
/// </summary>
/// <param name="configuration">The delegate used to configure the OpenIddict options.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
@ -49,6 +49,38 @@ public sealed class OpenIddictValidationSystemNetHttpBuilder
return this;
}
/// <summary>
/// Configures the <see cref="HttpClient"/> used by the OpenIddict validation/System.Net.Http integration.
/// </summary>
/// <param name="configuration">The delegate used to configure the <see cref="HttpClient"/>.</param>
/// <returns>The <see cref="OpenIddictValidationSystemNetHttpBuilder"/> instance.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictValidationSystemNetHttpBuilder ConfigureHttpClient(Action<HttpClient> configuration)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
return Configure(options => options.HttpClientActions.Add(configuration));
}
/// <summary>
/// Configures the <see cref="HttpClientHandler"/> used by the OpenIddict client/System.Net.Http integration.
/// </summary>
/// <param name="configuration">The delegate used to configure the <see cref="HttpClientHandler"/>.</param>
/// <returns>The <see cref="OpenIddictValidationSystemNetHttpBuilder"/> instance.</returns>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public OpenIddictValidationSystemNetHttpBuilder ConfigureHttpClientHandler(Action<HttpClientHandler> configuration)
{
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
return Configure(options => options.HttpClientHandlerActions.Add(configuration));
}
/// <summary>
/// Sets the contact address used in the "From" header that is attached
/// to the backchannel HTTP requests sent to the authorization server.

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

@ -19,7 +19,6 @@ namespace OpenIddict.Validation.SystemNetHttp;
public sealed class OpenIddictValidationSystemNetHttpConfiguration : IConfigureOptions<OpenIddictValidationOptions>,
IConfigureNamedOptions<HttpClientFactoryOptions>
{
#if !SUPPORTS_SERVICE_PROVIDER_IN_HTTP_MESSAGE_HANDLER_BUILDER
private readonly IServiceProvider _provider;
/// <summary>
@ -28,7 +27,6 @@ public sealed class OpenIddictValidationSystemNetHttpConfiguration : IConfigureO
/// <param name="provider">The service provider.</param>
public OpenIddictValidationSystemNetHttpConfiguration(IServiceProvider provider)
=> _provider = provider ?? throw new ArgumentNullException(nameof(provider));
#endif
/// <inheritdoc/>
public void Configure(OpenIddictValidationOptions options)
@ -60,15 +58,23 @@ public sealed class OpenIddictValidationSystemNetHttpConfiguration : IConfigureO
return;
}
options.HttpClientActions.Add(options =>
var settings = _provider.GetRequiredService<IOptionsMonitor<OpenIddictValidationSystemNetHttpOptions>>().CurrentValue;
options.HttpClientActions.Add(client =>
{
// 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
// or containing an infine amount of data), the default values are amended to use lower values.
options.MaxResponseContentBufferSize = 10 * 1024 * 1024;
options.Timeout = TimeSpan.FromMinutes(1);
client.MaxResponseContentBufferSize = 10 * 1024 * 1024;
client.Timeout = TimeSpan.FromMinutes(1);
});
// Register the user-defined HTTP client actions.
foreach (var action in settings.HttpClientActions)
{
options.HttpClientActions.Add(action);
}
options.HttpMessageHandlerBuilderActions.Add(builder =>
{
#if SUPPORTS_SERVICE_PROVIDER_IN_HTTP_MESSAGE_HANDLER_BUILDER
@ -98,5 +104,12 @@ public sealed class OpenIddictValidationSystemNetHttpConfiguration : IConfigureO
builder.AdditionalHandlers.Add(new PolicyHttpMessageHandler(policy));
}
});
// Register the user-defined HTTP client handler actions.
foreach (var action in settings.HttpClientHandlerActions)
{
options.HttpMessageHandlerBuilderActions.Add(builder => action(builder.PrimaryHandler as HttpClientHandler ??
throw new InvalidOperationException(SR.FormatID0373(typeof(HttpClientHandler).FullName))));
}
}
}

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

@ -36,4 +36,16 @@ public sealed class OpenIddictValidationSystemNetHttpOptions
/// attached to the backchannel HTTP requests sent to the authorization server.
/// </summary>
public ProductInfoHeaderValue? ProductInformation { get; set; }
/// <summary>
/// Gets the user-defined actions used to amend the <see cref="HttpClient"/>
/// instances created by the OpenIddict validation/System.Net.Http integration.
/// </summary>
public List<Action<HttpClient>> HttpClientActions { get; } = new();
/// <summary>
/// Gets the user-defined actions used to amend the <see cref="HttpClientHandler"/>
/// instances created by the OpenIddict validation/System.Net.Http integration.
/// </summary>
public List<Action<HttpClientHandler>> HttpClientHandlerActions { get; } = new();
}

Loading…
Cancel
Save