1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Linq ;
4+ using System . Linq . Expressions ;
5+ using System . Net . Http ;
6+ using System . Reflection ;
7+ using Dibix . Http . Client ;
8+ using Microsoft . Extensions . DependencyInjection . Extensions ;
9+ using Microsoft . Extensions . Options ;
10+
11+ namespace Microsoft . Extensions . DependencyInjection
12+ {
13+ public static class HttpClientBuilderExtensions
14+ {
15+ public static IHttpClientBuilder AddBuiltinHttpMessageHandlers ( this IHttpClientBuilder builder , params Type [ ] excludedHandlers )
16+ {
17+ AddHttpMessageHandler < TraceProxyHttpMessageHandler > ( builder , excludedHandlers ) ;
18+ AddHttpMessageHandler < EnsureSuccessStatusCodeHttpMessageHandler > ( builder , excludedHandlers ) ;
19+ AddHttpMessageHandler < TraceSourceHttpMessageHandler > ( builder , excludedHandlers ) ;
20+ return builder ;
21+ }
22+
23+ public static void AddDibixHttpClient ( this IHttpClientBuilder httpClientBuilder , Action < IHttpServiceDiscoveryConfiguration > configure )
24+ {
25+ HttpServiceConfiguration configuration = new HttpServiceConfiguration ( httpClientBuilder ) ;
26+ configure ( configuration ) ;
27+
28+ IServiceCollection services = httpClientBuilder . Services ;
29+ services . Configure ( configuration . ClientConfiguration ?? ( _ => { } ) ) ;
30+
31+ RegisterHttpClients ( services , configuration . AssemblyCollector , httpClientBuilder . Name ) ;
32+ }
33+
34+ private static void AddHttpMessageHandler < T > ( IHttpClientBuilder builder , IEnumerable < Type > excludedHandlers ) where T : DelegatingHandler , new ( )
35+ {
36+ if ( excludedHandlers . Contains ( typeof ( T ) ) )
37+ return ;
38+
39+ builder . AddHttpMessageHandler ( ( ) => new T ( ) ) ;
40+ }
41+
42+ private static void RegisterHttpClients ( IServiceCollection services , Func < ICollection < Assembly > > assemblyCollector , string httpClientName )
43+ {
44+ foreach ( Assembly assembly in assemblyCollector ( ) )
45+ {
46+ if ( ! assembly . IsDefined ( typeof ( ArtifactAssemblyAttribute ) ) )
47+ continue ;
48+
49+ foreach ( Type type in assembly . GetTypes ( ) )
50+ {
51+ if ( ! type . IsClass )
52+ continue ;
53+
54+ HttpServiceAttribute httpServiceAttribute = type . GetCustomAttribute < HttpServiceAttribute > ( ) ;
55+ if ( httpServiceAttribute == null )
56+ continue ;
57+
58+ services . AddScoped ( httpServiceAttribute . ContractType , CompileImplementationFactory ( type , httpClientName ) ) ;
59+ }
60+ }
61+ }
62+
63+ private static Func < IServiceProvider , object > CompileImplementationFactory ( Type implementationType , string httpClientName )
64+ {
65+ MethodCallExpression ResolveService ( Type contractType , Expression serviceProvider ) => Expression . Call ( typeof ( ServiceProviderServiceExtensions ) , nameof ( ServiceProviderServiceExtensions . GetRequiredService ) , [ contractType ] , serviceProvider ) ;
66+
67+ Expression CollectParameter ( string parameterName , Type parameterType , Expression serviceProvider ) => parameterName switch
68+ {
69+ "httpClientOptions" when parameterType == typeof ( HttpClientOptions ) => Expression . Property ( ResolveService ( typeof ( IOptions < HttpClientOptions > ) , serviceProvider ) , nameof ( IOptions < object > . Value ) ) ,
70+ "httpClientName" when parameterType == typeof ( string ) => Expression . Constant ( httpClientName ) ,
71+ _ => ResolveService ( parameterType , serviceProvider )
72+ } ;
73+
74+ ParameterExpression serviceProviderParameter = Expression . Parameter ( typeof ( IServiceProvider ) , "serviceProvider" ) ;
75+
76+ ConstructorInfo constructor = HttpServiceConstructorSelector . SelectConstructor ( implementationType ) ;
77+ IEnumerable < Expression > parameters = constructor . GetParameters ( ) . Select ( x => CollectParameter ( x . Name ! , x . ParameterType , serviceProviderParameter ) ) ;
78+ Expression value = Expression . New ( constructor , parameters ) ;
79+
80+ Expression < Func < IServiceProvider , object > > lambda = Expression . Lambda < Func < IServiceProvider , object > > ( value , serviceProviderParameter ) ;
81+ Func < IServiceProvider , object > compiled = lambda . Compile ( ) ;
82+ return compiled ;
83+ }
84+
85+ private sealed class HttpServiceConfiguration : IHttpServiceDiscoveryConfiguration , IHttpServiceInfrastructureConfiguration
86+ {
87+ private readonly IHttpClientBuilder _httpClientBuilder ;
88+
89+ public Func < ICollection < Assembly > > AssemblyCollector { get ; private set ; } = ( ) => [ ] ;
90+ public Action < HttpClientOptions > ClientConfiguration { get ; set ; }
91+
92+ public HttpServiceConfiguration ( IHttpClientBuilder httpClientBuilder )
93+ {
94+ _httpClientBuilder = httpClientBuilder ;
95+ }
96+
97+ IHttpServiceInfrastructureConfiguration IHttpServiceDiscoveryConfiguration . FromAssemblies ( IEnumerable < Assembly > assemblies )
98+ {
99+ AssemblyCollector = ( ) => HttpServiceAssemblyDiscovery . FromAssemblies ( assemblies ) ;
100+ return this ;
101+ }
102+
103+ IHttpServiceInfrastructureConfiguration IHttpServiceDiscoveryConfiguration . FromAssembly ( Assembly assembly )
104+ {
105+ AssemblyCollector = ( ) => HttpServiceAssemblyDiscovery . FromAssembly ( assembly ) ;
106+ return this ;
107+ }
108+
109+ IHttpServiceInfrastructureConfiguration IHttpServiceDiscoveryConfiguration . FromAssemblyContaining ( Type type )
110+ {
111+ AssemblyCollector = ( ) => HttpServiceAssemblyDiscovery . FromAssemblyContaining ( type ) ;
112+ return this ;
113+ }
114+
115+ IHttpServiceInfrastructureConfiguration IHttpServiceInfrastructureConfiguration . WithAuthorizationProvider < TAuthorizationProvider > ( )
116+ {
117+ _httpClientBuilder . Services . TryAddScoped < IHttpAuthorizationProvider , TAuthorizationProvider > ( ) ;
118+ return this ;
119+ }
120+
121+ void IHttpServiceInfrastructureConfiguration . Configure ( Action < HttpClientOptions > configure )
122+ {
123+ ClientConfiguration = configure ;
124+ }
125+ }
126+
127+ private static class HttpServiceAssemblyDiscovery
128+ {
129+ public static ICollection < Assembly > FromAssemblies ( IEnumerable < Assembly > assemblies ) => assemblies . ToArray ( ) ;
130+
131+ public static ICollection < Assembly > FromAssembly ( Assembly assembly ) => [ assembly ] ;
132+
133+ public static ICollection < Assembly > FromAssemblyContaining ( Type type ) => FromAssembly ( type . Assembly ) ;
134+ }
135+ }
136+ }
0 commit comments