Skip to content

Commit 63f6ed3

Browse files
authored
Merge pull request #23 from mikeparker/delegating-handler-support
Add ability to insert delegating handlers into HttpClient
2 parents d8bb2f6 + 414e686 commit 63f6ed3

File tree

6 files changed

+116
-17
lines changed

6 files changed

+116
-17
lines changed

src/HttpOverStream.Client/DialMessageHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
102102
await request.Content.CopyToAsync(stream).ConfigureAwait(false);
103103
}
104104

105-
_logger.LogVerbose("HttpOS Client: stream.FlushAsync");
105+
_logger.LogVerbose("HttpOS Client: stream.FlushAsync");
106106
await stream.FlushAsync(cancellationToken).ConfigureAwait(false);
107107
_logger.LogVerbose("HttpOS Client: Finished writing request");
108108
}, cancellationToken);
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System;
2+
using System.Net.Http;
3+
using HttpOverStream.Logging;
4+
5+
namespace HttpOverStream.NamedPipe
6+
{
7+
public class NamedPipeHttpClientBuilder
8+
{
9+
private string _pipeName;
10+
private ILoggerHttpOverStream _logger;
11+
private DelegatingHandler _outerHandler;
12+
private TimeSpan? _perRequestTimeout;
13+
private Version _httpVersion;
14+
15+
public NamedPipeHttpClientBuilder(string pipeName)
16+
{
17+
_pipeName = pipeName;
18+
}
19+
20+
public NamedPipeHttpClientBuilder WithLogger(ILoggerHttpOverStream logger)
21+
{
22+
_logger = logger;
23+
return this;
24+
}
25+
26+
public NamedPipeHttpClientBuilder WithDelegatingHandler(DelegatingHandler outerHandler)
27+
{
28+
_outerHandler = outerHandler;
29+
return this;
30+
}
31+
32+
public NamedPipeHttpClientBuilder WithPerRequestTimeout(TimeSpan perRequestTimeout)
33+
{
34+
_perRequestTimeout = perRequestTimeout;
35+
return this;
36+
}
37+
38+
public NamedPipeHttpClientBuilder WithHttpVersion(Version httpVersion)
39+
{
40+
_httpVersion = httpVersion;
41+
return this;
42+
}
43+
44+
public HttpClient Build()
45+
{
46+
return NamedPipeHttpClientFactory.ForPipeName(_pipeName, _logger, _perRequestTimeout, _httpVersion, _outerHandler);
47+
}
48+
}
49+
}

src/HttpOverStream.NamedPipe/NamedPipeHttpClientFactory.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,21 @@ namespace HttpOverStream.NamedPipe
77
{
88
public class NamedPipeHttpClientFactory
99
{
10-
public static HttpClient ForPipeName(string pipeName, ILoggerHttpOverStream logger = null, TimeSpan? perRequestTimeout = null, Version httpVersion = null)
10+
public static HttpClient ForPipeName(string pipeName, ILoggerHttpOverStream logger = null, TimeSpan? perRequestTimeout = null, Version httpVersion = null, DelegatingHandler outerHandler = null )
1111
{
12-
var httpClient = new HttpClient(new DialMessageHandler(new NamedPipeDialer(pipeName), logger, httpVersion))
12+
var innerHandler = new DialMessageHandler(new NamedPipeDialer(pipeName), logger, httpVersion);
13+
HttpClient httpClient;
14+
if (outerHandler != null)
1315
{
14-
BaseAddress = new Uri("http://localhost")
15-
};
16+
outerHandler.InnerHandler = innerHandler;
17+
httpClient = new HttpClient(outerHandler);
18+
}
19+
else
20+
{
21+
httpClient = new HttpClient(innerHandler);
22+
}
1623

24+
httpClient.BaseAddress = new Uri("http://localhost");
1725
if (perRequestTimeout != null)
1826
{
1927
httpClient.Timeout = perRequestTimeout.Value;

src/HttpOverStream.Server.Owin.Tests/EndToEndTests.cs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,17 @@ private async Task TestGet_Impl(int numberOfRequests = 1, Version httpVersion =
105105
{
106106
for (int i = 0; i < numberOfRequests; i++)
107107
{
108-
var client = new HttpClient(new DialMessageHandler(new NamedPipeDialer(TestContext.TestName), null, httpVersion));
109-
client.Timeout = TimeSpan.FromSeconds(5);
108+
var client = new NamedPipeHttpClientBuilder(TestContext.TestName)
109+
.WithPerRequestTimeout(TimeSpan.FromSeconds(5))
110+
.WithHttpVersion(httpVersion)
111+
.WithDelegatingHandler(new TestLoggingHandler())
112+
.Build();
110113
var result = await client.GetAsync("http://localhost/api/e2e-tests/hello-world");
111114
Assert.AreEqual("Hello World", await result.Content.ReadAsAsync<string>());
112115
}
113116
}
114117
}
115118

116-
117119
[TestMethod]
118120
public async Task TestGetStressTest_ClientBeforeServer()
119121
{
@@ -140,8 +142,9 @@ private async Task TestClientStartsFirst_ServerDropsClientButComesUpAfterwards_I
140142
{
141143
for (int i = 0; i < numberOfRequests; i++)
142144
{
143-
var client2 = new HttpClient(new DialMessageHandler(new NamedPipeDialer(TestContext.TestName)));
144-
client2.Timeout = TimeSpan.FromSeconds(5);
145+
var client2 = new NamedPipeHttpClientBuilder(TestContext.TestName)
146+
.WithPerRequestTimeout(TimeSpan.FromSeconds(5))
147+
.Build();
145148
var result = await client2.GetAsync("http://localhost/api/e2e-tests/hello-world");
146149
Assert.AreEqual("Hello World", await result.Content.ReadAsAsync<string>());
147150
}
@@ -161,8 +164,9 @@ private async Task TestBadRequest_BadMediaType_Impl()
161164
{
162165
using (CustomListenerHost.Start(SetupDefaultAppBuilder, new NamedPipeListener(TestContext.TestName)))
163166
{
164-
var client = new HttpClient(new DialMessageHandler(new NamedPipeDialer(TestContext.TestName)));
165-
client.Timeout = TimeSpan.FromSeconds(1);
167+
var client = new NamedPipeHttpClientBuilder(TestContext.TestName)
168+
.WithPerRequestTimeout(TimeSpan.FromSeconds(1))
169+
.Build();
166170
var badContent = new StringContent("{ ", Encoding.UTF8, "application/broken");
167171
var result = await client.PostAsJsonAsync("http://localhost/api/e2e-tests/hello", badContent);
168172
var wlcMsg = await result.Content.ReadAsAsync<WelcomeMessage>();
@@ -189,7 +193,7 @@ private async Task TestBadRequest_NonexistentEndpoint_Impl()
189193
{
190194
using (CustomListenerHost.Start(SetupDefaultAppBuilder, new NamedPipeListener(TestContext.TestName)))
191195
{
192-
var client = new HttpClient(new DialMessageHandler(new NamedPipeDialer(TestContext.TestName)));
196+
var client = NamedPipeHttpClientFactory.ForPipeName(TestContext.TestName);
193197
try
194198
{
195199
var badContent = new StringContent("{ }");
@@ -273,7 +277,7 @@ private async Task TestPost_Impl()
273277
{
274278
using (CustomListenerHost.Start(SetupDefaultAppBuilder, new NamedPipeListener(TestContext.TestName)))
275279
{
276-
var client = new HttpClient(new DialMessageHandler(new NamedPipeDialer(TestContext.TestName)));
280+
var client = NamedPipeHttpClientFactory.ForPipeName(TestContext.TestName);
277281
var result = await client.PostAsJsonAsync("http://localhost/api/e2e-tests/hello", new PersonMessage { Name = "Test" });
278282
var wlcMsg = await result.Content.ReadAsAsync<WelcomeMessage>();
279283
Assert.AreEqual("Hello Test", wlcMsg.Text);
@@ -283,7 +287,7 @@ private async Task TestPost_Impl()
283287
[TestMethod]
284288
public async Task TestPost_WhenNoServerListening_ThrowsTimeoutException()
285289
{
286-
var client = new HttpClient(new DialMessageHandler(new NamedPipeDialer(TestContext.TestName)));
290+
var client = NamedPipeHttpClientFactory.ForPipeName(TestContext.TestName);
287291
await Assert.ThrowsExceptionAsync<TimeoutException>(async () => await client.PostAsJsonAsync("http://localhost/api/e2e-tests/hello", new PersonMessage { Name = "Test" }));
288292
}
289293

@@ -334,8 +338,7 @@ public async Task TestClientTimeoutIsRespectedWhenServerTakesTooLong()
334338
{
335339
using (CustomListenerHost.Start(SetupDefaultAppBuilder, new NamedPipeListener(TestContext.TestName)))
336340
{
337-
var client = new HttpClient(new DialMessageHandler(new NamedPipeDialer(TestContext.TestName)));
338-
client.Timeout = TimeSpan.FromMilliseconds(100);
341+
var client = NamedPipeHttpClientFactory.ForPipeName(TestContext.TestName, null, TimeSpan.FromMilliseconds(100));
339342
var sw = Stopwatch.StartNew();
340343
await Assert.ThrowsExceptionAsync<TaskCanceledException>(async () =>
341344
{

src/HttpOverStream.Server.Owin.Tests/HttpOverStream.Server.Owin.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
</Reference>
8181
</ItemGroup>
8282
<ItemGroup>
83+
<Compile Include="TestLoggingHandler.cs" />
8384
<Compile Include="EndToEndTests.cs" />
8485
<Compile Include="OnceTests.cs" />
8586
<Compile Include="Properties\AssemblyInfo.cs" />
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.Net.Http;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
7+
namespace HttpOverStream.Server.Owin.Tests
8+
{
9+
public class TestLoggingHandler : DelegatingHandler
10+
{
11+
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
12+
{
13+
var correlationId = Guid.NewGuid();
14+
LogRequest(request, correlationId);
15+
var stopwatch = Stopwatch.StartNew();
16+
var response = await base.SendAsync(request, cancellationToken);
17+
stopwatch.Stop();
18+
LogResponse(request, response, stopwatch.Elapsed, correlationId);
19+
return response;
20+
}
21+
22+
private void LogRequest(HttpRequestMessage request, Guid correlationId)
23+
{
24+
if (request == null)
25+
{
26+
Console.WriteLine("Null request");
27+
return;
28+
}
29+
30+
Console.WriteLine($"[{correlationId}] {request.Method?.Method} {request.RequestUri}");
31+
}
32+
33+
private void LogResponse(HttpRequestMessage request, HttpResponseMessage response, TimeSpan stopwatchElapsed, Guid correlationId)
34+
{
35+
Console.WriteLine($"[{correlationId}] {request?.Method?.Method} {request?.RequestUri} -> {(int)response.StatusCode} {response.StatusCode} took {(int)stopwatchElapsed.TotalMilliseconds}ms");
36+
}
37+
}
38+
}

0 commit comments

Comments
 (0)