33using Polly ;
44using Polly . Wrap ;
55using System ;
6+ using System . Collections . Concurrent ;
67using System . Collections . Generic ;
78using System . Linq ;
89using System . Net ;
@@ -20,44 +21,56 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http
2021 public class ResilientHttpClient : IHttpClient
2122 {
2223 private HttpClient _client ;
23- private readonly Dictionary < string , PolicyWrap > _policiesPerOrigin ;
24+ private readonly ConcurrentDictionary < string , PolicyWrap > _policiesPerOrigin ;
2425 private ILogger < ResilientHttpClient > _logger ;
2526 private readonly Func < string , IEnumerable < Policy > > _policyCreator ;
26- //public HttpClient Inst => _client;
27+
2728
2829 public ResilientHttpClient ( Func < string , IEnumerable < Policy > > policyCreator , ILogger < ResilientHttpClient > logger )
2930 {
3031 _client = new HttpClient ( ) ;
3132 _logger = logger ;
32- _policiesPerOrigin = new Dictionary < string , PolicyWrap > ( ) ;
33+ _policiesPerOrigin = new ConcurrentDictionary < string , PolicyWrap > ( ) ;
3334 _policyCreator = policyCreator ;
3435 }
3536
36- private Task < T > HttpInvoker < T > ( string origin , Func < Task < T > > action )
37+
38+ public Task < HttpResponseMessage > PostAsync < T > ( string uri , T item , string authorizationToken = null , string requestId = null , string authorizationMethod = "Bearer" )
3739 {
38- var normalizedOrigin = NormalizeOrigin ( origin ) ;
40+ return DoPostPutAsync ( HttpMethod . Post , uri , item , authorizationToken , requestId , authorizationMethod ) ;
41+ }
3942
40- if ( ! _policiesPerOrigin . ContainsKey ( normalizedOrigin ) )
43+ public Task < HttpResponseMessage > PutAsync < T > ( string uri , T item , string authorizationToken = null , string requestId = null , string authorizationMethod = "Bearer" )
44+ {
45+ return DoPostPutAsync ( HttpMethod . Put , uri , item , authorizationToken , requestId , authorizationMethod ) ;
46+ }
47+
48+ public Task < HttpResponseMessage > DeleteAsync ( string uri , string authorizationToken = null , string requestId = null , string authorizationMethod = "Bearer" )
49+ {
50+ var origin = GetOriginFromUri ( uri ) ;
51+
52+ return HttpInvoker ( origin , async ( ) =>
4153 {
42- var newWrapper = Policy . WrapAsync ( _policyCreator ( normalizedOrigin ) . ToArray ( ) ) ;
43- _policiesPerOrigin . Add ( normalizedOrigin , newWrapper ) ;
44- }
54+ var requestMessage = new HttpRequestMessage ( HttpMethod . Delete , uri ) ;
4555
46- var policyWrapper = _policiesPerOrigin [ normalizedOrigin ] ;
56+ if ( authorizationToken != null )
57+ {
58+ requestMessage . Headers . Authorization = new AuthenticationHeaderValue ( authorizationMethod , authorizationToken ) ;
59+ }
4760
48- // Executes the action applying all
49- // the policies defined in the wrapper
50- return policyWrapper . ExecuteAsync ( ( ) => action ( ) ) ;
51- }
61+ if ( requestId != null )
62+ {
63+ requestMessage . Headers . Add ( "x-requestid" , requestId ) ;
64+ }
5265
53- private static string NormalizeOrigin ( string origin )
54- {
55- return origin ? . Trim ( ) ? . ToLower ( ) ;
66+ return await _client . SendAsync ( requestMessage ) ;
67+ } ) ;
5668 }
5769
5870 public Task < string > GetStringAsync ( string uri , string authorizationToken = null , string authorizationMethod = "Bearer" )
5971 {
6072 var origin = GetOriginFromUri ( uri ) ;
73+
6174 return HttpInvoker ( origin , async ( ) =>
6275 {
6376 var requestMessage = new HttpRequestMessage ( HttpMethod . Get , uri ) ;
@@ -73,13 +86,6 @@ public Task<string> GetStringAsync(string uri, string authorizationToken = null,
7386 } ) ;
7487 }
7588
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-
8389 private Task < HttpResponseMessage > DoPostPutAsync < T > ( HttpMethod method , string uri , T item , string authorizationToken = null , string requestId = null , string authorizationMethod = "Bearer" )
8490 {
8591 if ( method != HttpMethod . Post && method != HttpMethod . Put )
@@ -90,6 +96,7 @@ private Task<HttpResponseMessage> DoPostPutAsync<T>(HttpMethod method, string ur
9096 // a new StringContent must be created for each retry
9197 // as it is disposed after each call
9298 var origin = GetOriginFromUri ( uri ) ;
99+
93100 return HttpInvoker ( origin , async ( ) =>
94101 {
95102 var requestMessage = new HttpRequestMessage ( method , uri ) ;
@@ -120,34 +127,49 @@ private Task<HttpResponseMessage> DoPostPutAsync<T>(HttpMethod method, string ur
120127 } ) ;
121128 }
122129
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" )
130+ private Task < T > HttpInvoker < T > ( string origin , Func < Task < T > > action )
128131 {
129- return DoPostPutAsync ( HttpMethod . Put , uri , item , authorizationToken , requestId , authorizationMethod ) ;
132+ var policyWrapper = GetPolicyForOrigin ( origin ) ;
133+
134+ if ( policyWrapper != null )
135+ {
136+ // Executes the action applying all
137+ // the policies defined in the wrapper
138+ return policyWrapper . ExecuteAsync ( ( ) => action ( ) ) ;
139+ }
140+ else
141+ {
142+ throw new InvalidOperationException ( $ "PolicyWrapper can't be created for origin { origin } ") ;
143+ }
130144 }
131- public Task < HttpResponseMessage > DeleteAsync ( string uri , string authorizationToken = null , string requestId = null , string authorizationMethod = "Bearer" )
145+
146+ private PolicyWrap GetPolicyForOrigin ( string origin )
132147 {
133- var origin = GetOriginFromUri ( uri ) ;
134- return HttpInvoker ( origin , async ( ) =>
148+ var normalizedOrigin = NormalizeOrigin ( origin ) ;
149+
150+ if ( ! _policiesPerOrigin . TryGetValue ( normalizedOrigin , out PolicyWrap policyWrapper ) )
135151 {
136- var requestMessage = new HttpRequestMessage ( HttpMethod . Delete , uri ) ;
152+ policyWrapper = Policy . WrapAsync ( _policyCreator ( normalizedOrigin )
153+ . ToArray ( ) ) ;
137154
138- if ( authorizationToken != null )
139- {
140- requestMessage . Headers . Authorization = new AuthenticationHeaderValue ( authorizationMethod , authorizationToken ) ;
141- }
155+ _policiesPerOrigin . TryAdd ( normalizedOrigin , policyWrapper ) ;
156+ }
142157
143- if ( requestId != null )
144- {
145- requestMessage . Headers . Add ( "x-requestid" , requestId ) ;
146- }
158+ return policyWrapper ;
159+ }
147160
148- return await _client . SendAsync ( requestMessage ) ;
149- } ) ;
161+ private static string NormalizeOrigin ( string origin )
162+ {
163+ return origin ? . Trim ( ) ? . ToLower ( ) ;
150164 }
151165
166+ private static string GetOriginFromUri ( string uri )
167+ {
168+ var url = new Uri ( uri ) ;
169+
170+ var origin = $ "{ url . Scheme } ://{ url . DnsSafeHost } :{ url . Port } ";
171+
172+ return origin ;
173+ }
152174 }
153175}
0 commit comments