44using Polly . Wrap ;
55using System ;
66using System . Collections . Generic ;
7+ using System . Linq ;
78using System . Net ;
89using System . Net . Http ;
10+ using System . Net . Http . Headers ;
911using System . Threading . Tasks ;
1012
1113namespace Microsoft . eShopOnContainers . BuildingBlocks . Resilience . Http
@@ -18,47 +20,134 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
1820 public class ResilientHttpClient : IHttpClient
1921 {
2022 private HttpClient _client ;
21- private PolicyWrap _policyWrapper ;
23+ private readonly Dictionary < string , PolicyWrap > _policiesPerOrigin ;
2224 private ILogger < ResilientHttpClient > _logger ;
23- public HttpClient Inst => _client ;
25+ private readonly Func < string , IEnumerable < Policy > > _policyCreator ;
26+ //public HttpClient Inst => _client;
2427
25- public ResilientHttpClient ( Policy [ ] policies , ILogger < ResilientHttpClient > logger )
28+ public ResilientHttpClient ( Func < string , IEnumerable < Policy > > policyCreator , ILogger < ResilientHttpClient > logger )
2629 {
2730 _client = new HttpClient ( ) ;
2831 _logger = logger ;
32+ _policiesPerOrigin = new Dictionary < string , PolicyWrap > ( ) ;
33+ _policyCreator = policyCreator ;
34+ }
2935
30- // Add Policies to be applied
31- _policyWrapper = Policy . WrapAsync ( policies ) ;
32- }
36+ private Task < T > HttpInvoker < T > ( string origin , Func < Task < T > > action )
37+ {
38+ var normalizedOrigin = NormalizeOrigin ( origin ) ;
39+
40+ if ( ! _policiesPerOrigin . ContainsKey ( normalizedOrigin ) )
41+ {
42+ var newWrapper = Policy . WrapAsync ( _policyCreator ( normalizedOrigin ) . ToArray ( ) ) ;
43+ _policiesPerOrigin . Add ( normalizedOrigin , newWrapper ) ;
44+ }
45+
46+ var policyWrapper = _policiesPerOrigin [ normalizedOrigin ] ;
47+
48+ // Executes the action applying all
49+ // the policies defined in the wrapper
50+ return policyWrapper . ExecuteAsync ( ( ) => action ( ) ) ;
51+ }
52+
53+ private static string NormalizeOrigin ( string origin )
54+ {
55+ return origin ? . Trim ( ) ? . ToLower ( ) ;
56+ }
3357
34- public Task < string > GetStringAsync ( string uri ) =>
35- HttpInvoker ( ( ) =>
36- _client . GetStringAsync ( uri ) ) ;
58+ public Task < string > GetStringAsync ( string uri , string authorizationToken = null , string authorizationMethod = "Bearer" )
59+ {
60+ var origin = GetOriginFromUri ( uri ) ;
61+ return HttpInvoker ( origin , async ( ) =>
62+ {
63+ var requestMessage = new HttpRequestMessage ( HttpMethod . Get , uri ) ;
64+
65+ if ( authorizationToken != null )
66+ {
67+ requestMessage . Headers . Authorization = new AuthenticationHeaderValue ( authorizationMethod , authorizationToken ) ;
68+ }
69+
70+ var response = await _client . SendAsync ( requestMessage ) ;
71+
72+ return await response . Content . ReadAsStringAsync ( ) ;
73+ } ) ;
74+ }
75+
76+ private static string GetOriginFromUri ( string uri )
77+ {
78+ var url = new Uri ( uri ) ;
79+ var origin = $ "{ url . Scheme } ://{ url . DnsSafeHost } :{ url . Port } ";
80+ return origin ;
81+ }
82+
83+ private Task < HttpResponseMessage > DoPostPutAsync < T > ( HttpMethod method , string uri , T item , string authorizationToken = null , string requestId = null , string authorizationMethod = "Bearer" )
84+ {
85+ if ( method != HttpMethod . Post && method != HttpMethod . Put )
86+ {
87+ throw new ArgumentException ( "Value must be either post or put." , nameof ( method ) ) ;
88+ }
3789
38- public Task < HttpResponseMessage > PostAsync < T > ( string uri , T item ) =>
3990 // a new StringContent must be created for each retry
4091 // as it is disposed after each call
41- HttpInvoker ( ( ) =>
92+ var origin = GetOriginFromUri ( uri ) ;
93+ return HttpInvoker ( origin , async ( ) =>
4294 {
43- var response = _client . PostAsync ( uri , new StringContent ( JsonConvert . SerializeObject ( item ) , System . Text . Encoding . UTF8 , "application/json" ) ) ;
95+ var requestMessage = new HttpRequestMessage ( method , uri ) ;
96+
97+ requestMessage . Content = new StringContent ( JsonConvert . SerializeObject ( item ) , System . Text . Encoding . UTF8 , "application/json" ) ;
98+
99+ if ( authorizationToken != null )
100+ {
101+ requestMessage . Headers . Authorization = new AuthenticationHeaderValue ( authorizationMethod , authorizationToken ) ;
102+ }
103+
104+ if ( requestId != null )
105+ {
106+ requestMessage . Headers . Add ( "x-requestid" , requestId ) ;
107+ }
108+
109+ var response = await _client . SendAsync ( requestMessage ) ;
110+
44111 // raise exception if HttpResponseCode 500
45112 // needed for circuit breaker to track fails
46- if ( response . Result . StatusCode == HttpStatusCode . InternalServerError )
113+
114+ if ( response . StatusCode == HttpStatusCode . InternalServerError )
47115 {
48116 throw new HttpRequestException ( ) ;
49117 }
50118
51119 return response ;
52120 } ) ;
121+ }
53122
54- public Task < HttpResponseMessage > DeleteAsync ( string uri ) =>
55- HttpInvoker ( ( ) => _client . DeleteAsync ( uri ) ) ;
123+ public Task < HttpResponseMessage > PostAsync < T > ( string uri , T item , string authorizationToken = null , string requestId = null , string authorizationMethod = "Bearer" )
124+ {
125+ return DoPostPutAsync ( HttpMethod . Post , uri , item , authorizationToken , requestId , authorizationMethod ) ;
126+ }
127+ public Task < HttpResponseMessage > PutAsync < T > ( string uri , T item , string authorizationToken = null , string requestId = null , string authorizationMethod = "Bearer" )
128+ {
129+ return DoPostPutAsync ( HttpMethod . Put , uri , item , authorizationToken , requestId , authorizationMethod ) ;
130+ }
131+ public Task < HttpResponseMessage > DeleteAsync ( string uri , string authorizationToken = null , string requestId = null , string authorizationMethod = "Bearer" )
132+ {
133+ var origin = GetOriginFromUri ( uri ) ;
134+ return HttpInvoker ( origin , async ( ) =>
135+ {
136+ var requestMessage = new HttpRequestMessage ( HttpMethod . Delete , uri ) ;
56137
138+ if ( authorizationToken != null )
139+ {
140+ requestMessage . Headers . Authorization = new AuthenticationHeaderValue ( authorizationMethod , authorizationToken ) ;
141+ }
57142
58- private Task < T > HttpInvoker < T > ( Func < Task < T > > action ) =>
59- // Executes the action applying all
60- // the policies defined in the wrapper
61- _policyWrapper . ExecuteAsync ( ( ) => action ( ) ) ;
62- }
143+ if ( requestId != null )
144+ {
145+ requestMessage . Headers . Add ( "x-requestid" , requestId ) ;
146+ }
63147
148+ return await _client . SendAsync ( requestMessage ) ;
149+ } ) ;
150+ }
151+
152+ }
64153}
0 commit comments