• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

loresoft / HashGate / 29697765278

19 Jul 2026 05:55PM UTC coverage: 65.982% (+2.8%) from 63.149%
29697765278

push

github

pwelter34
Add AddHmacClient and runtime options updates

235 of 472 branches covered (49.79%)

Branch coverage included in aggregate %.

146 of 173 new or added lines in 11 files covered. (84.39%)

1 existing line in 1 file now uncovered.

696 of 939 relevant lines covered (74.12%)

26.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.1
/src/HashGate.HttpClient/DependencyInjectionExtensions.cs
1
using HashGate.HttpClient.Options;
2

3
using Microsoft.Extensions.DependencyInjection;
4
using Microsoft.Extensions.Options;
5

6
namespace HashGate.HttpClient;
7

8
/// <summary>
9
/// Provides extension methods for configuring HMAC authentication services in the dependency injection container.
10
/// </summary>
11
public static class DependencyInjectionExtensions
12
{
13
    /// <summary>
14
    /// Adds HMAC authentication services to the specified <see cref="IServiceCollection"/>.
15
    /// This method configures the required services for HTTP client-side HMAC authentication,
16
    /// including options binding from configuration and the HTTP message handler.
17
    /// </summary>
18
    /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
19
    /// <param name="configureOptions">
20
    /// An optional action to configure the <see cref="HmacAuthenticationOptions"/>.
21
    /// This allows for programmatic configuration in addition to configuration binding.
22
    /// If provided, this configuration will be applied after the configuration binding.
23
    /// </param>
24
    /// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
25
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="services"/> is <c>null</c>.</exception>
26
    /// <remarks>
27
    /// <para>
28
    /// This method performs the following registrations:
29
    /// </para>
30
    /// <list type="bullet">
31
    /// <item><description>Configures <see cref="HmacAuthenticationOptions"/> with automatic binding to the "HmacAuthentication" configuration section</description></item>
32
    /// <item><description>Registers the <see cref="HmacAuthenticationHttpHandler"/> as a transient service for HTTP message processing</description></item>
33
    /// <item><description>Registers the runtime options updater so provisioned settings can be applied after startup</description></item>
34
    /// </list>
35
    /// <para>
36
    /// The configuration is automatically bound from the "HmacAuthentication" section in your application configuration.
37
    /// Ensure your appsettings.json includes the required Client and Secret values:
38
    /// </para>
39
    /// <code>
40
    /// {
41
    ///   "HmacAuthentication": {
42
    ///     "Client": "your-client-id",
43
    ///     "Secret": "your-secret-key",
44
    ///     "BaseAddress": "https://api.example.com",
45
    ///     "SignedHeaders": ["host", "x-timestamp", "x-content-sha256"]
46
    ///   }
47
    /// }
48
    /// </code>
49
    /// </remarks>
50
    /// <example>
51
    /// <para>Basic usage with configuration binding:</para>
52
    /// <code>
53
    /// services.AddHmacAuthentication();
54
    /// </code>
55
    /// <para>Usage with additional programmatic configuration:</para>
56
    /// <code>
57
    /// services.AddHmacAuthentication(options =>
58
    /// {
59
    ///     options.Client = "override-client-id";
60
    ///     options.SignedHeaders = ["host", "x-timestamp", "x-content-sha256", "content-type"];
61
    /// });
62
    /// </code>
63
    /// <para>Usage with HttpClient factory:</para>
64
    /// <code>
65
    /// services.AddHmacAuthentication();
66
    /// services.AddHttpClient("ApiClient")
67
    ///     .AddHttpMessageHandler&lt;HmacAuthenticationHttpHandler&gt;();
68
    /// </code>
69
    /// </example>
70
    public static IServiceCollection AddHmacAuthentication(
71
        this IServiceCollection services,
72
        Action<HmacAuthenticationOptions>? configureOptions = null)
73
        => services.AddHmacAuthentication(HmacAuthenticationOptions.ConfigurationName, configureOptions);
11✔
74

75
    /// <summary>
76
    /// Adds HMAC authentication services to the specified <see cref="IServiceCollection"/> using the specified configuration section.
77
    /// </summary>
78
    /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
79
    /// <param name="configurationSection">The name of the configuration section to bind to <see cref="HmacAuthenticationOptions"/>.</param>
80
    /// <param name="configureOptions">
81
    /// An optional action to configure the <see cref="HmacAuthenticationOptions"/> after configuration binding.
82
    /// </param>
83
    /// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
84
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="services"/> or <paramref name="configurationSection"/> is <c>null</c>.</exception>
85
    /// <exception cref="ArgumentException">Thrown when <paramref name="configurationSection"/> is empty or whitespace.</exception>
86
    public static IServiceCollection AddHmacAuthentication(
87
        this IServiceCollection services,
88
        string configurationSection,
89
        Action<HmacAuthenticationOptions>? configureOptions = null)
90
    {
91
        if (services == null)
13!
92
            throw new ArgumentNullException(nameof(services));
×
93

94
        if (string.IsNullOrWhiteSpace(configurationSection))
13!
NEW
95
            throw new ArgumentException("The configuration section name cannot be empty.", nameof(configurationSection));
×
96

97
        var optionsBuilder = services
13✔
98
            .AddOptions<HmacAuthenticationOptions>()
13✔
99
            .BindConfiguration(configurationSection);
13✔
100

101
        if (configureOptions != null)
13✔
102
            services.PostConfigure(configureOptions);
5✔
103

104
        services.AddOptionsUpdater<HmacAuthenticationOptions>(Microsoft.Extensions.Options.Options.DefaultName);
13✔
105

106
        services.AddTransient(sp => new HmacAuthenticationHttpHandler(sp.GetRequiredService<IOptionsMonitor<HmacAuthenticationOptions>>()));
16✔
107

108
        return services;
13✔
109
    }
110

111

112
    /// <summary>
113
    /// Adds the default (unnamed) HTTP client that signs outgoing requests with HMAC authentication.
114
    /// This is the HMAC-enabled equivalent of <c>AddHttpClient()</c>.
115
    /// </summary>
116
    /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
117
    /// <param name="configureClient">An optional action to configure the default <see cref="System.Net.Http.HttpClient"/>.</param>
118
    /// <param name="configureOptions">An optional action to configure the default <see cref="HmacAuthenticationOptions"/>.</param>
119
    /// <returns>An <see cref="IHttpClientBuilder"/> that can be used to further configure the default client.</returns>
120
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="services"/> is <c>null</c>.</exception>
121
    /// <remarks>
122
    /// The client is registered using <see cref="Microsoft.Extensions.Options.Options.DefaultName"/> and binds its
123
    /// <see cref="HmacAuthenticationOptions"/> from the default "HmacAuthentication" configuration section.
124
    /// </remarks>
125
    /// <example>
126
    /// <code>
127
    /// services.AddHmacClient((sp, client) =&gt; client.BaseAddress = new Uri("https://api.example.com"));
128
    /// </code>
129
    /// </example>
130
    public static IHttpClientBuilder AddHmacClient(
131
        this IServiceCollection services,
132
        Action<IServiceProvider, System.Net.Http.HttpClient>? configureClient = null,
133
        Action<HmacAuthenticationOptions>? configureOptions = null)
NEW
134
        => services.AddHmacClient(Microsoft.Extensions.Options.Options.DefaultName, HmacAuthenticationOptions.ConfigurationName, configureClient, configureOptions);
×
135

136
    /// <summary>
137
    /// Adds a named HTTP client that signs outgoing requests with HMAC authentication.
138
    /// </summary>
139
    /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
140
    /// <param name="name">The name of the HTTP client to register.</param>
141
    /// <param name="configureClient">An optional action to configure the named <see cref="System.Net.Http.HttpClient"/>.</param>
142
    /// <param name="configureOptions">An optional action to configure the named <see cref="HmacAuthenticationOptions"/>.</param>
143
    /// <returns>An <see cref="IHttpClientBuilder"/> that can be used to further configure the named client.</returns>
144
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="services"/> is <c>null</c>.</exception>
145
    /// <exception cref="ArgumentException">Thrown when <paramref name="name"/> is empty or whitespace.</exception>
146
    public static IHttpClientBuilder AddHmacClient(
147
        this IServiceCollection services,
148
        string name,
149
        Action<IServiceProvider, System.Net.Http.HttpClient>? configureClient = null,
150
        Action<HmacAuthenticationOptions>? configureOptions = null)
151
        => services.AddHmacClient(name, HmacAuthenticationOptions.ConfigurationName, configureClient, configureOptions);
1✔
152

153
    /// <summary>
154
    /// Adds a named HTTP client that signs outgoing requests with HMAC authentication using the specified configuration section.
155
    /// </summary>
156
    /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
157
    /// <param name="name">The name of the HTTP client to register.</param>
158
    /// <param name="configurationSection">The name of the configuration section to bind to <see cref="HmacAuthenticationOptions"/>.</param>
159
    /// <param name="configureClient">An optional action to configure the named <see cref="System.Net.Http.HttpClient"/>.</param>
160
    /// <param name="configureOptions">An optional action to configure the named <see cref="HmacAuthenticationOptions"/>.</param>
161
    /// <returns>An <see cref="IHttpClientBuilder"/> that can be used to further configure the named client.</returns>
162
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="services"/> is <c>null</c>.</exception>
163
    /// <exception cref="ArgumentException">Thrown when <paramref name="name"/> or <paramref name="configurationSection"/> is empty or whitespace.</exception>
164
    public static IHttpClientBuilder AddHmacClient(
165
        this IServiceCollection services,
166
        string name,
167
        string configurationSection,
168
        Action<IServiceProvider, System.Net.Http.HttpClient>? configureClient = null,
169
        Action<HmacAuthenticationOptions>? configureOptions = null)
170
    {
171
        if (services == null)
1!
NEW
172
            throw new ArgumentNullException(nameof(services));
×
173

174
        if (string.IsNullOrWhiteSpace(name))
1!
NEW
175
            throw new ArgumentException("The HTTP client name cannot be empty.", nameof(name));
×
176

177
        if (string.IsNullOrWhiteSpace(configurationSection))
1!
NEW
178
            throw new ArgumentException("The configuration section name cannot be empty.", nameof(configurationSection));
×
179

180
        var optionsBuilder = services
1✔
181
            .AddOptions<HmacAuthenticationOptions>(name)
1✔
182
            .BindConfiguration(configurationSection);
1✔
183

184
        if (configureOptions != null)
1!
NEW
185
            services.PostConfigure(name, configureOptions);
×
186

187
        services.AddOptionsUpdater<HmacAuthenticationOptions>(name);
1✔
188

189
        return services
1✔
190
            .AddHttpClient(name, (sp, httpClient) =>
1✔
191
            {
1✔
192
                var options = sp.GetRequiredService<IOptionsMonitor<HmacAuthenticationOptions>>().Get(name);
1✔
193

1✔
194
                // Always assign a base address so relative request URIs are accepted. When the base address
1✔
195
                // is provisioned after startup, fall back to the deferred placeholder, which HmacAuthenticationHttpHandler
1✔
196
                // rewrites using the current options at request time.
1✔
197
                httpClient.BaseAddress = options.BaseAddress ?? HmacAuthenticationHttpHandler.DeferredBaseAddress;
1!
198

1✔
199
                configureClient?.Invoke(sp, httpClient);
1!
NEW
200
            })
×
201
            .AddHttpMessageHandler(sp =>
1✔
202
                new HmacAuthenticationHttpHandler(
1✔
203
                    optionsMonitor: sp.GetRequiredService<IOptionsMonitor<HmacAuthenticationOptions>>(),
1✔
204
                    optionsName: name
1✔
205
                )
1✔
206
            );
1✔
207
    }
208

209
    /// <summary>
210
    /// Adds a typed HTTP client that signs outgoing requests with HMAC authentication.
211
    /// </summary>
212
    /// <typeparam name="TClient">The typed client to register.</typeparam>
213
    /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
214
    /// <param name="configureClient">An optional action to configure the typed <see cref="System.Net.Http.HttpClient"/>.</param>
215
    /// <param name="configureOptions">An optional action to configure the named <see cref="HmacAuthenticationOptions"/>.</param>
216
    /// <returns>An <see cref="IHttpClientBuilder"/> that can be used to further configure the typed client.</returns>
217
    /// <remarks>
218
    /// For runtime provisioning, register the client during startup, then load the provisioned settings and apply them
219
    /// by resolving <see cref="OptionsUpdater{TOptions}"/> for
220
    /// <see cref="HmacAuthenticationOptions"/> and calling
221
    /// <see cref="OptionsUpdaterExtensions.Update{TClient}(OptionsUpdater{HmacAuthenticationOptions}, Action{HmacAuthenticationOptions})"/>
222
    /// before sending requests with the typed client.
223
    /// </remarks>
224
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="services"/> is <c>null</c>.</exception>
225
    public static IHttpClientBuilder AddHmacClient<TClient>(
226
        this IServiceCollection services,
227
        Action<IServiceProvider, System.Net.Http.HttpClient>? configureClient = null,
228
        Action<HmacAuthenticationOptions>? configureOptions = null)
229
        where TClient : class
230
        => services.AddHmacClient<TClient>(HmacAuthenticationOptions.ConfigurationName, configureClient, configureOptions);
9✔
231

232
    /// <summary>
233
    /// Adds a typed HTTP client that signs outgoing requests with HMAC authentication using the specified configuration section.
234
    /// </summary>
235
    /// <typeparam name="TClient">The typed client to register.</typeparam>
236
    /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
237
    /// <param name="configurationSection">The name of the configuration section to bind to <see cref="HmacAuthenticationOptions"/>.</param>
238
    /// <param name="configureClient">An optional action to configure the typed <see cref="System.Net.Http.HttpClient"/>.</param>
239
    /// <param name="configureOptions">An optional action to configure the named <see cref="HmacAuthenticationOptions"/>.</param>
240
    /// <returns>An <see cref="IHttpClientBuilder"/> that can be used to further configure the typed client.</returns>
241
    /// <exception cref="ArgumentNullException">Thrown when <paramref name="services"/> is <c>null</c>.</exception>
242
    /// <remarks>
243
    /// The typed client is registered using the full name of <typeparamref name="TClient"/> as the options name,
244
    /// binding its <see cref="HmacAuthenticationOptions"/> from the specified <paramref name="configurationSection"/>.
245
    /// </remarks>
246
    public static IHttpClientBuilder AddHmacClient<TClient>(
247
        this IServiceCollection services,
248
        string configurationSection,
249
        Action<IServiceProvider, System.Net.Http.HttpClient>? configureClient = null,
250
        Action<HmacAuthenticationOptions>? configureOptions = null)
251
        where TClient : class
252
    {
253
        if (services == null)
12!
NEW
254
            throw new ArgumentNullException(nameof(services));
×
255

256
        if (string.IsNullOrWhiteSpace(configurationSection))
12!
NEW
257
            throw new ArgumentException("The configuration section name cannot be empty.", nameof(configurationSection));
×
258

259
        var optionsName = typeof(TClient).FullName ?? typeof(TClient).Name;
12!
260

261
        services
12✔
262
            .AddOptions<HmacAuthenticationOptions>(optionsName)
12✔
263
            .BindConfiguration(configurationSection);
12✔
264

265
        if (configureOptions != null)
12✔
266
            services.PostConfigure(optionsName, configureOptions);
1✔
267

268
        services.AddOptionsUpdater<HmacAuthenticationOptions>(optionsName);
12✔
269

270
        return services
12✔
271
            .AddHttpClient<TClient>(optionsName, (sp, httpClient) =>
12✔
272
            {
12✔
273
                var options = sp.GetRequiredService<IOptionsMonitor<HmacAuthenticationOptions>>().Get(optionsName);
7✔
274

12✔
275
                // Always assign a base address so relative request URIs are accepted. When the base address
12✔
276
                // is provisioned after startup, fall back to the deferred placeholder, which HmacAuthenticationHttpHandler
12✔
277
                // rewrites using the current options at request time.
12✔
278
                httpClient.BaseAddress = options.BaseAddress ?? HmacAuthenticationHttpHandler.DeferredBaseAddress;
7✔
279

12✔
280
                configureClient?.Invoke(sp, httpClient);
7!
NEW
281
            })
×
282
            .AddHttpMessageHandler(sp =>
12✔
283
                new HmacAuthenticationHttpHandler(
7✔
284
                    optionsMonitor: sp.GetRequiredService<IOptionsMonitor<HmacAuthenticationOptions>>(),
7✔
285
                    optionsName: optionsName
7✔
286
                )
7✔
287
            );
12✔
288
    }
289
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc