Skip to content

Commit 4d485da

Browse files
committed
First iteration over ResilientHttpClient. In this case ConcurrentDictionary is used and control concurrency to add new origin and policies
1 parent dbbcc95 commit 4d485da

1 file changed

Lines changed: 66 additions & 44 deletions

File tree

src/BuildingBlocks/Resilience/Resilience.Http/ResilientHttpClient.cs

Lines changed: 66 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Polly;
44
using Polly.Wrap;
55
using System;
6+
using System.Collections.Concurrent;
67
using System.Collections.Generic;
78
using System.Linq;
89
using 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

Comments
 (0)