1616
1717#endregion
1818
19+ using System ;
1920using System . Diagnostics . CodeAnalysis ;
2021using Grpc . Net . ClientFactory ;
2122using Grpc . Net . ClientFactory . Internal ;
@@ -289,9 +290,7 @@ private static IHttpClientBuilder AddGrpcClientCore<
289290 // because we access it by reaching into the service collection.
290291 services . TryAddSingleton ( new GrpcClientMappingRegistry ( ) ) ;
291292
292- IHttpClientBuilder clientBuilder = services . AddGrpcHttpClient < TClient > ( name ) ;
293-
294- return clientBuilder ;
293+ return services . AddGrpcHttpClient < TClient > ( name ) ;
295294 }
296295
297296 /// <summary>
@@ -306,25 +305,22 @@ private static IHttpClientBuilder AddGrpcHttpClient<
306305 {
307306 ArgumentNullThrowHelper . ThrowIfNull ( services ) ;
308307
309- services
310- . AddHttpClient ( name )
311- . ConfigurePrimaryHttpMessageHandler ( ( ) =>
312- {
313- // Set PrimaryHandler to null so we can track whether the user
314- // set a value or not. If they didn't set their own handler then
315- // one will be created by PostConfigure.
316- return null ! ;
317- } ) ;
308+ var builder = services . AddHttpClient ( name ) ;
318309
319- services . PostConfigure < HttpClientFactoryOptions > ( name , options =>
310+ builder . Services . AddTransient < TClient > ( s =>
320311 {
321- options . HttpMessageHandlerBuilderActions . Add ( builder =>
312+ var clientFactory = s . GetRequiredService < GrpcClientFactory > ( ) ;
313+ return clientFactory . CreateClient < TClient > ( name ) ;
314+ } ) ;
315+
316+ // Insert primary handler before other configuration so there is the opportunity to override it.
317+ // This should run before ConfigureDefaultHttpClient so the handler can be overriden in defaults.
318+ var configurePrimaryHandler = ServiceDescriptor . Singleton < IConfigureOptions < HttpClientFactoryOptions > > ( new ConfigureNamedOptions < HttpClientFactoryOptions > ( name , options =>
319+ {
320+ options . HttpMessageHandlerBuilderActions . Add ( b =>
322321 {
323- if ( builder . PrimaryHandler == null )
322+ if ( HttpHandlerFactory . TryCreatePrimaryHandler ( out var handler ) )
324323 {
325- // This will throw in .NET Standard 2.0 with a prompt that a user must set a handler.
326- // Because it throws it should only be called in PostConfigure if no handler has been set.
327- var handler = HttpHandlerFactory . CreatePrimaryHandler ( ) ;
328324#if NET5_0_OR_GREATER
329325 if ( handler is SocketsHttpHandler socketsHttpHandler )
330326 {
@@ -336,37 +332,34 @@ private static IHttpClientBuilder AddGrpcHttpClient<
336332 }
337333#endif
338334
339- builder . PrimaryHandler = handler ;
335+ b . PrimaryHandler = handler ;
336+ }
337+ else
338+ {
339+ b . PrimaryHandler = UnsupportedHttpHandler . Instance ;
340340 }
341341 } ) ;
342- } ) ;
343-
344- var builder = new DefaultHttpClientBuilder ( services , name ) ;
342+ } ) ) ;
343+ services . Insert ( 0 , configurePrimaryHandler ) ;
345344
346- builder . Services . AddTransient < TClient > ( s =>
345+ // Some platforms don't have a built-in handler that supports gRPC.
346+ // Validate that a handler was set by the app to after all configuration has run.
347+ services . PostConfigure < HttpClientFactoryOptions > ( name , options =>
347348 {
348- var clientFactory = s . GetRequiredService < GrpcClientFactory > ( ) ;
349- return clientFactory . CreateClient < TClient > ( builder . Name ) ;
349+ options . HttpMessageHandlerBuilderActions . Add ( builder =>
350+ {
351+ if ( builder . PrimaryHandler == UnsupportedHttpHandler . Instance )
352+ {
353+ throw HttpHandlerFactory . CreateUnsupportedHandlerException ( ) ;
354+ }
355+ } ) ;
350356 } ) ;
351357
352358 ReserveClient ( builder , typeof ( TClient ) , name ) ;
353359
354360 return builder ;
355361 }
356362
357- private class DefaultHttpClientBuilder : IHttpClientBuilder
358- {
359- public DefaultHttpClientBuilder ( IServiceCollection services , string name )
360- {
361- Services = services ;
362- Name = name ;
363- }
364-
365- public string Name { get ; }
366-
367- public IServiceCollection Services { get ; }
368- }
369-
370363 private static void ReserveClient ( IHttpClientBuilder builder , Type type , string name )
371364 {
372365 var registry = ( GrpcClientMappingRegistry ? ) builder . Services . Single ( sd => sd . ServiceType == typeof ( GrpcClientMappingRegistry ) ) . ImplementationInstance ;
@@ -384,4 +377,14 @@ private static void ReserveClient(IHttpClientBuilder builder, Type type, string
384377
385378 registry . NamedClientRegistrations [ name ] = type ;
386379 }
380+
381+ private sealed class UnsupportedHttpHandler : HttpMessageHandler
382+ {
383+ public static readonly UnsupportedHttpHandler Instance = new UnsupportedHttpHandler ( ) ;
384+
385+ protected override Task < HttpResponseMessage > SendAsync ( HttpRequestMessage request , CancellationToken cancellationToken )
386+ {
387+ return Task . FromException < HttpResponseMessage > ( HttpHandlerFactory . CreateUnsupportedHandlerException ( ) ) ;
388+ }
389+ }
387390}
0 commit comments