From 414e68650ea9976c670f892c6ab6bde79e9285a0 Mon Sep 17 00:00:00 2001 From: Mike Parker Date: Tue, 22 Oct 2019 17:55:53 +0100 Subject: [PATCH] Add ability to insert delegating handlers into HttpClient; Make a Builder pattern for the HttpClient (as theres a lot of optional parameters now). Add a test logging handler and check it works. Signed-off-by: Mike Parker --- .../DialMessageHandler.cs | 2 +- .../NamedPipeHttpClientBuilder.cs | 49 +++++++++++++++++++ .../NamedPipeHttpClientFactory.cs | 16 ++++-- .../EndToEndTests.cs | 27 +++++----- .../HttpOverStream.Server.Owin.Tests.csproj | 1 + .../TestLoggingHandler.cs | 38 ++++++++++++++ 6 files changed, 116 insertions(+), 17 deletions(-) create mode 100644 src/HttpOverStream.NamedPipe/NamedPipeHttpClientBuilder.cs create mode 100644 src/HttpOverStream.Server.Owin.Tests/TestLoggingHandler.cs diff --git a/src/HttpOverStream.Client/DialMessageHandler.cs b/src/HttpOverStream.Client/DialMessageHandler.cs index 757f1de..425ca1b 100644 --- a/src/HttpOverStream.Client/DialMessageHandler.cs +++ b/src/HttpOverStream.Client/DialMessageHandler.cs @@ -102,7 +102,7 @@ namespace HttpOverStream.Client await request.Content.CopyToAsync(stream).ConfigureAwait(false); } - _logger.LogVerbose("HttpOS Client: stream.FlushAsync"); + _logger.LogVerbose("HttpOS Client: stream.FlushAsync"); await stream.FlushAsync(cancellationToken).ConfigureAwait(false); _logger.LogVerbose("HttpOS Client: Finished writing request"); }, cancellationToken); diff --git a/src/HttpOverStream.NamedPipe/NamedPipeHttpClientBuilder.cs b/src/HttpOverStream.NamedPipe/NamedPipeHttpClientBuilder.cs new file mode 100644 index 0000000..52bccf9 --- /dev/null +++ b/src/HttpOverStream.NamedPipe/NamedPipeHttpClientBuilder.cs @@ -0,0 +1,49 @@ +using System; +using System.Net.Http; +using HttpOverStream.Logging; + +namespace HttpOverStream.NamedPipe +{ + public class NamedPipeHttpClientBuilder + { + private string _pipeName; + private ILoggerHttpOverStream _logger; + private DelegatingHandler _outerHandler; + private TimeSpan? _perRequestTimeout; + private Version _httpVersion; + + public NamedPipeHttpClientBuilder(string pipeName) + { + _pipeName = pipeName; + } + + public NamedPipeHttpClientBuilder WithLogger(ILoggerHttpOverStream logger) + { + _logger = logger; + return this; + } + + public NamedPipeHttpClientBuilder WithDelegatingHandler(DelegatingHandler outerHandler) + { + _outerHandler = outerHandler; + return this; + } + + public NamedPipeHttpClientBuilder WithPerRequestTimeout(TimeSpan perRequestTimeout) + { + _perRequestTimeout = perRequestTimeout; + return this; + } + + public NamedPipeHttpClientBuilder WithHttpVersion(Version httpVersion) + { + _httpVersion = httpVersion; + return this; + } + + public HttpClient Build() + { + return NamedPipeHttpClientFactory.ForPipeName(_pipeName, _logger, _perRequestTimeout, _httpVersion, _outerHandler); + } + } +} diff --git a/src/HttpOverStream.NamedPipe/NamedPipeHttpClientFactory.cs b/src/HttpOverStream.NamedPipe/NamedPipeHttpClientFactory.cs index d149d79..231e674 100644 --- a/src/HttpOverStream.NamedPipe/NamedPipeHttpClientFactory.cs +++ b/src/HttpOverStream.NamedPipe/NamedPipeHttpClientFactory.cs @@ -7,13 +7,21 @@ namespace HttpOverStream.NamedPipe { public class NamedPipeHttpClientFactory { - public static HttpClient ForPipeName(string pipeName, ILoggerHttpOverStream logger = null, TimeSpan? perRequestTimeout = null, Version httpVersion = null) + public static HttpClient ForPipeName(string pipeName, ILoggerHttpOverStream logger = null, TimeSpan? perRequestTimeout = null, Version httpVersion = null, DelegatingHandler outerHandler = null ) { - var httpClient = new HttpClient(new DialMessageHandler(new NamedPipeDialer(pipeName), logger, httpVersion)) + var innerHandler = new DialMessageHandler(new NamedPipeDialer(pipeName), logger, httpVersion); + HttpClient httpClient; + if (outerHandler != null) { - BaseAddress = new Uri("http://localhost") - }; + outerHandler.InnerHandler = innerHandler; + httpClient = new HttpClient(outerHandler); + } + else + { + httpClient = new HttpClient(innerHandler); + } + httpClient.BaseAddress = new Uri("http://localhost"); if (perRequestTimeout != null) { httpClient.Timeout = perRequestTimeout.Value; diff --git a/src/HttpOverStream.Server.Owin.Tests/EndToEndTests.cs b/src/HttpOverStream.Server.Owin.Tests/EndToEndTests.cs index 46a76cf..c24563b 100644 --- a/src/HttpOverStream.Server.Owin.Tests/EndToEndTests.cs +++ b/src/HttpOverStream.Server.Owin.Tests/EndToEndTests.cs @@ -105,15 +105,17 @@ namespace HttpOverStream.Server.Owin.Tests { for (int i = 0; i < numberOfRequests; i++) { - var client = new HttpClient(new DialMessageHandler(new NamedPipeDialer(TestContext.TestName), null, httpVersion)); - client.Timeout = TimeSpan.FromSeconds(5); + var client = new NamedPipeHttpClientBuilder(TestContext.TestName) + .WithPerRequestTimeout(TimeSpan.FromSeconds(5)) + .WithHttpVersion(httpVersion) + .WithDelegatingHandler(new TestLoggingHandler()) + .Build(); var result = await client.GetAsync("http://localhost/api/e2e-tests/hello-world"); Assert.AreEqual("Hello World", await result.Content.ReadAsAsync()); } } } - [TestMethod] public async Task TestGetStressTest_ClientBeforeServer() { @@ -140,8 +142,9 @@ namespace HttpOverStream.Server.Owin.Tests { for (int i = 0; i < numberOfRequests; i++) { - var client2 = new HttpClient(new DialMessageHandler(new NamedPipeDialer(TestContext.TestName))); - client2.Timeout = TimeSpan.FromSeconds(5); + var client2 = new NamedPipeHttpClientBuilder(TestContext.TestName) + .WithPerRequestTimeout(TimeSpan.FromSeconds(5)) + .Build(); var result = await client2.GetAsync("http://localhost/api/e2e-tests/hello-world"); Assert.AreEqual("Hello World", await result.Content.ReadAsAsync()); } @@ -161,8 +164,9 @@ namespace HttpOverStream.Server.Owin.Tests { using (CustomListenerHost.Start(SetupDefaultAppBuilder, new NamedPipeListener(TestContext.TestName))) { - var client = new HttpClient(new DialMessageHandler(new NamedPipeDialer(TestContext.TestName))); - client.Timeout = TimeSpan.FromSeconds(1); + var client = new NamedPipeHttpClientBuilder(TestContext.TestName) + .WithPerRequestTimeout(TimeSpan.FromSeconds(1)) + .Build(); var badContent = new StringContent("{ ", Encoding.UTF8, "application/broken"); var result = await client.PostAsJsonAsync("http://localhost/api/e2e-tests/hello", badContent); var wlcMsg = await result.Content.ReadAsAsync(); @@ -189,7 +193,7 @@ namespace HttpOverStream.Server.Owin.Tests { using (CustomListenerHost.Start(SetupDefaultAppBuilder, new NamedPipeListener(TestContext.TestName))) { - var client = new HttpClient(new DialMessageHandler(new NamedPipeDialer(TestContext.TestName))); + var client = NamedPipeHttpClientFactory.ForPipeName(TestContext.TestName); try { var badContent = new StringContent("{ }"); @@ -273,7 +277,7 @@ namespace HttpOverStream.Server.Owin.Tests { using (CustomListenerHost.Start(SetupDefaultAppBuilder, new NamedPipeListener(TestContext.TestName))) { - var client = new HttpClient(new DialMessageHandler(new NamedPipeDialer(TestContext.TestName))); + var client = NamedPipeHttpClientFactory.ForPipeName(TestContext.TestName); var result = await client.PostAsJsonAsync("http://localhost/api/e2e-tests/hello", new PersonMessage { Name = "Test" }); var wlcMsg = await result.Content.ReadAsAsync(); Assert.AreEqual("Hello Test", wlcMsg.Text); @@ -283,7 +287,7 @@ namespace HttpOverStream.Server.Owin.Tests [TestMethod] public async Task TestPost_WhenNoServerListening_ThrowsTimeoutException() { - var client = new HttpClient(new DialMessageHandler(new NamedPipeDialer(TestContext.TestName))); + var client = NamedPipeHttpClientFactory.ForPipeName(TestContext.TestName); await Assert.ThrowsExceptionAsync(async () => await client.PostAsJsonAsync("http://localhost/api/e2e-tests/hello", new PersonMessage { Name = "Test" })); } @@ -334,8 +338,7 @@ namespace HttpOverStream.Server.Owin.Tests { using (CustomListenerHost.Start(SetupDefaultAppBuilder, new NamedPipeListener(TestContext.TestName))) { - var client = new HttpClient(new DialMessageHandler(new NamedPipeDialer(TestContext.TestName))); - client.Timeout = TimeSpan.FromMilliseconds(100); + var client = NamedPipeHttpClientFactory.ForPipeName(TestContext.TestName, null, TimeSpan.FromMilliseconds(100)); var sw = Stopwatch.StartNew(); await Assert.ThrowsExceptionAsync(async () => { diff --git a/src/HttpOverStream.Server.Owin.Tests/HttpOverStream.Server.Owin.Tests.csproj b/src/HttpOverStream.Server.Owin.Tests/HttpOverStream.Server.Owin.Tests.csproj index fe0cc48..22f1f0d 100644 --- a/src/HttpOverStream.Server.Owin.Tests/HttpOverStream.Server.Owin.Tests.csproj +++ b/src/HttpOverStream.Server.Owin.Tests/HttpOverStream.Server.Owin.Tests.csproj @@ -80,6 +80,7 @@ + diff --git a/src/HttpOverStream.Server.Owin.Tests/TestLoggingHandler.cs b/src/HttpOverStream.Server.Owin.Tests/TestLoggingHandler.cs new file mode 100644 index 0000000..28fd012 --- /dev/null +++ b/src/HttpOverStream.Server.Owin.Tests/TestLoggingHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Diagnostics; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace HttpOverStream.Server.Owin.Tests +{ + public class TestLoggingHandler : DelegatingHandler + { + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var correlationId = Guid.NewGuid(); + LogRequest(request, correlationId); + var stopwatch = Stopwatch.StartNew(); + var response = await base.SendAsync(request, cancellationToken); + stopwatch.Stop(); + LogResponse(request, response, stopwatch.Elapsed, correlationId); + return response; + } + + private void LogRequest(HttpRequestMessage request, Guid correlationId) + { + if (request == null) + { + Console.WriteLine("Null request"); + return; + } + + Console.WriteLine($"[{correlationId}] {request.Method?.Method} {request.RequestUri}"); + } + + private void LogResponse(HttpRequestMessage request, HttpResponseMessage response, TimeSpan stopwatchElapsed, Guid correlationId) + { + Console.WriteLine($"[{correlationId}] {request?.Method?.Method} {request?.RequestUri} -> {(int)response.StatusCode} {response.StatusCode} took {(int)stopwatchElapsed.TotalMilliseconds}ms"); + } + } +}