Skip to content

Commit 3e26355

Browse files
authored
Merge pull request #21 from myarichuk/develop
minor convenience methods in client builder, some more testing
2 parents 5b34d8f + 68d706e commit 3e26355

10 files changed

Lines changed: 200 additions & 119 deletions

Simple.HttpClientFactory.Tests/BasicClientBuilderTests.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
24
using System.Net;
35
using System.Net.Http;
46
using System.Threading.Tasks;
@@ -24,6 +26,24 @@ public BasicClientBuilderTests()
2426
.WithBody("Hello world!"));
2527
}
2628

29+
[Fact]
30+
public async Task Will_send_default_headers()
31+
{
32+
var trafficRecorder = new TrafficRecorderMessageHandler(new List<string>());
33+
34+
var client = HttpClientFactory
35+
.Create(trafficRecorder)
36+
.WithDefaultHeaders(new Dictionary<string, string> { { "foobar", "xyz123" } })
37+
.Build();
38+
39+
_ = await client.GetAsync(_server.Urls[0] + "/hello/world");
40+
41+
Assert.Single(trafficRecorder.Traffic); //sanity check
42+
Assert.True(trafficRecorder.Traffic[0].Item1.Headers.Contains("foobar"));
43+
Assert.Equal("xyz123", trafficRecorder.Traffic[0].Item1.Headers.GetValues("foobar").FirstOrDefault());
44+
}
45+
46+
2747
[Fact]
2848
public async Task Can_do_http_get_with_plain_client()
2949
{
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
namespace Simple.HttpClientFactory.Tests
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Net.Http;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
9+
/// <summary>
10+
/// Defines the <see cref="EventMessageHandler" />.
11+
/// </summary>
12+
public class EventMessageHandler : DelegatingHandler
13+
{
14+
/// <summary>
15+
/// Defines the Request.
16+
/// </summary>
17+
public event EventHandler<RequestEventArgs> Request;
18+
19+
/// <summary>
20+
/// Defines the Response.
21+
/// </summary>
22+
public event EventHandler<ResponseEventArgs> Response;
23+
24+
/// <summary>
25+
/// Defines the _visitedMiddleware.
26+
/// </summary>
27+
private readonly List<string> _visitedMiddleware;
28+
29+
/// <summary>
30+
/// Initializes a new instance of the <see cref="EventMessageHandler"/> class.
31+
/// </summary>
32+
/// <param name="visitedMiddleware">The visitedMiddleware<see cref="List{string}"/>.</param>
33+
public EventMessageHandler(List<string> visitedMiddleware) => _visitedMiddleware = visitedMiddleware;
34+
35+
/// <summary>
36+
/// Defines the <see cref="RequestEventArgs" />.
37+
/// </summary>
38+
public class RequestEventArgs : EventArgs
39+
{
40+
/// <summary>
41+
/// Gets or sets the Request.
42+
/// </summary>
43+
public HttpRequestMessage Request { get; set; }
44+
}
45+
46+
/// <summary>
47+
/// Defines the <see cref="ResponseEventArgs" />.
48+
/// </summary>
49+
public class ResponseEventArgs : EventArgs
50+
{
51+
/// <summary>
52+
/// Gets or sets the Response.
53+
/// </summary>
54+
public HttpResponseMessage Response { get; set; }
55+
}
56+
57+
/// <summary>
58+
/// The SendAsync.
59+
/// </summary>
60+
/// <param name="request">The request<see cref="HttpRequestMessage"/>.</param>
61+
/// <param name="cancellationToken">The cancellationToken<see cref="CancellationToken"/>.</param>
62+
/// <returns>The <see cref="Task{HttpResponseMessage}"/>.</returns>
63+
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
64+
{
65+
Request?.Invoke(this, new RequestEventArgs { Request = request });
66+
var response = await base.SendAsync(request, cancellationToken);
67+
Response?.Invoke(this, new ResponseEventArgs { Response = response });
68+
_visitedMiddleware.Add(nameof(EventMessageHandler));
69+
return response;
70+
}
71+
}
72+
}

Simple.HttpClientFactory.Tests/MiddlewareAndPolicyTests.cs

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System.Linq;
66
using System.Net;
77
using System.Net.Http;
8-
using System.Threading;
98
using System.Threading.Tasks;
109
using WireMock.RequestBuilders;
1110
using WireMock.ResponseBuilders;
@@ -14,7 +13,7 @@
1413

1514
namespace Simple.HttpClientFactory.Tests
1615
{
17-
public class MiddlewareAndPolicyTests
16+
public partial class MiddlewareAndPolicyTests
1817
{
1918
private readonly WireMockServer _server;
2019
private readonly List<string> _visitedMiddleware = new List<string>();
@@ -182,54 +181,5 @@ public async Task Retry_policy_should_work_with_single_middleware()
182181
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
183182
Assert.Equal("Hello world!", await response.Content.ReadAsStringAsync());
184183
}
185-
186-
187-
public class EventMessageHandler : DelegatingHandler
188-
{
189-
public event EventHandler<RequestEventArgs> Request;
190-
public event EventHandler<ResponseEventArgs> Response;
191-
192-
private readonly List<string> _visitedMiddleware;
193-
194-
public EventMessageHandler(List<string> visitedMiddleware) => _visitedMiddleware = visitedMiddleware;
195-
196-
public class RequestEventArgs : EventArgs
197-
{
198-
public HttpRequestMessage Request { get; set; }
199-
}
200-
201-
public class ResponseEventArgs : EventArgs
202-
{
203-
public HttpResponseMessage Response { get; set; }
204-
}
205-
206-
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
207-
{
208-
Request?.Invoke(this ,new RequestEventArgs { Request = request });
209-
var response = await base.SendAsync(request, cancellationToken);
210-
Response?.Invoke(this, new ResponseEventArgs { Response = response });
211-
_visitedMiddleware.Add(nameof(EventMessageHandler));
212-
return response;
213-
}
214-
}
215-
216-
public class TrafficRecorderMessageHandler : DelegatingHandler
217-
{
218-
public List<(HttpRequestMessage, HttpResponseMessage)> Traffic { get; } = new List<(HttpRequestMessage, HttpResponseMessage)>();
219-
220-
private readonly List<string> _visitedMiddleware;
221-
222-
public TrafficRecorderMessageHandler(List<string> visitedMiddleware) => _visitedMiddleware = visitedMiddleware;
223-
224-
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
225-
{
226-
request.Headers.Add("foobar", "foobar");
227-
var response = await base.SendAsync(request, cancellationToken);
228-
_visitedMiddleware.Add(nameof(TrafficRecorderMessageHandler));
229-
Traffic.Add((request, response));
230-
231-
return response;
232-
}
233-
}
234184
}
235185
}

Simple.HttpClientFactory.Tests/MiddlewareDelegateTests.cs

Lines changed: 2 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
using System.Collections.Generic;
33
using System.Linq;
44
using System.Net;
5-
using System.Net.Http;
6-
using System.Threading;
75
using System.Threading.Tasks;
86
using WireMock.RequestBuilders;
97
using WireMock.ResponseBuilders;
@@ -12,7 +10,7 @@
1210

1311
namespace Simple.HttpClientFactory.Tests
1412
{
15-
public class MiddlewareDelegateTests
13+
public partial class MiddlewareDelegateTests
1614
{
1715
private readonly WireMockServer _server;
1816
private readonly List<string> _visitedMiddleware = new List<string>();
@@ -89,54 +87,6 @@ public async Task Multiple_middleware_handlers_with_reverse_order_should_work()
8987
Assert.Equal(HttpStatusCode.OK, trafficRecorderMessageHandler.Traffic[0].Item2.StatusCode);
9088

9189
Assert.Equal(new [] { nameof(EventMessageHandler), nameof(TrafficRecorderMessageHandler) }, _visitedMiddleware);
92-
}
93-
94-
public class EventMessageHandler : DelegatingHandler
95-
{
96-
public event EventHandler<RequestEventArgs> Request;
97-
public event EventHandler<ResponseEventArgs> Response;
98-
99-
private readonly List<string> _visitedMiddleware;
100-
101-
public EventMessageHandler(List<string> visitedMiddleware) => _visitedMiddleware = visitedMiddleware;
102-
103-
public class RequestEventArgs : EventArgs
104-
{
105-
public HttpRequestMessage Request { get; set; }
106-
}
107-
108-
public class ResponseEventArgs : EventArgs
109-
{
110-
public HttpResponseMessage Response { get; set; }
111-
}
112-
113-
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
114-
{
115-
Request?.Invoke(this ,new RequestEventArgs { Request = request });
116-
var response = await base.SendAsync(request, cancellationToken);
117-
Response?.Invoke(this, new ResponseEventArgs { Response = response });
118-
_visitedMiddleware.Add(nameof(EventMessageHandler));
119-
return response;
120-
}
121-
}
122-
123-
public class TrafficRecorderMessageHandler : DelegatingHandler
124-
{
125-
public List<(HttpRequestMessage, HttpResponseMessage)> Traffic { get; } = new List<(HttpRequestMessage, HttpResponseMessage)>();
126-
127-
private readonly List<string> _visitedMiddleware;
128-
129-
public TrafficRecorderMessageHandler(List<string> visitedMiddleware) => _visitedMiddleware = visitedMiddleware;
130-
131-
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
132-
{
133-
request.Headers.Add("foobar", "foobar");
134-
var response = await base.SendAsync(request, cancellationToken);
135-
_visitedMiddleware.Add(nameof(TrafficRecorderMessageHandler));
136-
Traffic.Add((request, response));
137-
138-
return response;
139-
}
140-
}
90+
}
14191
}
14292
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using Polly;
2+
using Simple.HttpClientFactory.Polly;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Net;
6+
using System.Net.Http;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
using Xunit;
10+
11+
namespace Simple.HttpClientFactory.Tests
12+
{
13+
//some sanity checks
14+
public class PollyHttpMessageHandlerTests
15+
{
16+
[Fact]
17+
public void Ctor_with_null_should_throw() =>
18+
Assert.Throws<ArgumentNullException>(() => new PolicyHttpMessageHandler(null));
19+
20+
[Fact]
21+
public async Task Null_param_in_send_async_should_throw()
22+
{
23+
var middlewareHandler = new PolicyHttpMessageHandler(Policy<HttpResponseMessage>
24+
.Handle<HttpRequestException>()
25+
.OrResult(result => (int)result.StatusCode >= 500 || result.StatusCode == HttpStatusCode.RequestTimeout)
26+
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(1)));
27+
28+
using(var client = HttpClientFactory.Create(middlewareHandler).Build())
29+
await Assert.ThrowsAsync<ArgumentNullException>(() => client.SendAsync(null));
30+
}
31+
}
32+
}

Simple.HttpClientFactory.Tests/Simple.HttpClientFactory.Tests.csproj

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,9 @@
1111
<TargetFrameworks>netcoreapp3.1;net472</TargetFrameworks>
1212
</PropertyGroup>
1313
<ItemGroup>
14-
<PackageReference Include="coverlet.msbuild" Version="2.8.1">
15-
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
16-
<PrivateAssets>all</PrivateAssets>
17-
</PackageReference>
1814
<PackageReference Include="Flurl" Version="2.8.2" />
1915
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
20-
<PackageReference Include="WireMock.Net" Version="1.2.6" />
16+
<PackageReference Include="WireMock.Net" Version="1.2.7" />
2117
<PackageReference Include="xunit" Version="2.4.1" />
2218
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
2319
<PrivateAssets>all</PrivateAssets>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
namespace Simple.HttpClientFactory.Tests
2+
{
3+
using System.Collections.Generic;
4+
using System.Net.Http;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
8+
/// <summary>
9+
/// Defines the <see cref="TrafficRecorderMessageHandler" />.
10+
/// </summary>
11+
public class TrafficRecorderMessageHandler : DelegatingHandler
12+
{
13+
/// <summary>
14+
/// Gets the Traffic.
15+
/// </summary>
16+
public List<(HttpRequestMessage, HttpResponseMessage)> Traffic { get; } = new List<(HttpRequestMessage, HttpResponseMessage)>();
17+
18+
/// <summary>
19+
/// Defines the _visitedMiddleware.
20+
/// </summary>
21+
private readonly List<string> _visitedMiddleware;
22+
23+
/// <summary>
24+
/// Initializes a new instance of the <see cref="TrafficRecorderMessageHandler"/> class.
25+
/// </summary>
26+
/// <param name="visitedMiddleware">The visitedMiddleware<see cref="List{string}"/>.</param>
27+
public TrafficRecorderMessageHandler(List<string> visitedMiddleware) => _visitedMiddleware = visitedMiddleware;
28+
29+
/// <summary>
30+
/// The SendAsync.
31+
/// </summary>
32+
/// <param name="request">The request<see cref="HttpRequestMessage"/>.</param>
33+
/// <param name="cancellationToken">The cancellationToken<see cref="CancellationToken"/>.</param>
34+
/// <returns>The <see cref="Task{HttpResponseMessage}"/>.</returns>
35+
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
36+
{
37+
request.Headers.Add("foobar", "foobar");
38+
var response = await base.SendAsync(request, cancellationToken);
39+
_visitedMiddleware.Add(nameof(TrafficRecorderMessageHandler));
40+
Traffic.Add((request, response));
41+
42+
return response;
43+
}
44+
}
45+
}

Simple.HttpClientFactory/HttpClientBuilder.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Generic;
55
using System.Linq;
66
using System.Net.Http;
7+
using System.Runtime.CompilerServices;
78
#if NETCOREAPP2_1
89
using System.Net.Security;
910
#endif
@@ -19,13 +20,20 @@ internal class HttpClientBuilder : IHttpClientBuilder
1920
private readonly List<DelegatingHandler> _middlewareHandlers = new List<DelegatingHandler>();
2021
private readonly Dictionary<string, string> _defaultHeaders = new Dictionary<string, string>();
2122

23+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
24+
public IHttpClientBuilder WithDefaultHeader(string name, string value)
25+
{
26+
if(!_defaultHeaders.ContainsKey(name))
27+
_defaultHeaders.Add(name, value);
28+
29+
return this;
30+
}
31+
2232
public IHttpClientBuilder WithDefaultHeaders(IReadOnlyDictionary<string, string> headers)
2333
{
2434
foreach(var kvp in headers)
25-
{
26-
if(!_defaultHeaders.ContainsKey(kvp.Key))
27-
_defaultHeaders.Add(kvp.Key, kvp.Value);
28-
}
35+
WithDefaultHeader(kvp.Key, kvp.Value);
36+
2937
return this;
3038
}
3139

Simple.HttpClientFactory/IHttpClientBuilder.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ namespace Simple.HttpClientFactory
99
public interface IHttpClientBuilder
1010
{
1111
/// <summary>
12-
/// Add default headers to be added to teach request
12+
/// Add default headers to be added to each request
13+
/// </summary>
14+
IHttpClientBuilder WithDefaultHeader(string name, string value);
15+
16+
/// <summary>
17+
/// Add default headers to be added to each request
1318
/// </summary>
1419
IHttpClientBuilder WithDefaultHeaders(IReadOnlyDictionary<string, string> headers);
1520

0 commit comments

Comments
 (0)