From eda71bd725929a869faf84508c33e1fdbd969be8 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Fri, 25 May 2018 21:04:58 +0200 Subject: [PATCH] Allow all headers to be set as Response headers (#142) * Allow all headers to be set as Response headers * RunTestDifferentPort * Fix Proxy_Should_change_absolute_location_header_in_proxied_response test * Fix OwinResponseMapper * Fix test * 1.0.3.18 --- .../Program.cs | 28 ++++- .../WireMock.Net.StandAlone.csproj | 2 +- src/WireMock.Net/Owin/AspNetCoreSelfHost.cs | 2 - src/WireMock.Net/Owin/OwinResponseMapper.cs | 102 ++++++++---------- src/WireMock.Net/Server/FluentMockServer.cs | 2 + src/WireMock.Net/WireMock.Net.csproj | 6 +- .../FluentMockServerTests.Proxy.cs | 16 +-- .../FluentMockServerTests.cs | 54 ++++------ test/WireMock.Net.Tests/ResponseTests.cs | 11 +- 9 files changed, 110 insertions(+), 113 deletions(-) diff --git a/examples/WireMock.Net.Console.Proxy.NETCoreApp2/Program.cs b/examples/WireMock.Net.Console.Proxy.NETCoreApp2/Program.cs index 96c67ee1..461f66a4 100644 --- a/examples/WireMock.Net.Console.Proxy.NETCoreApp2/Program.cs +++ b/examples/WireMock.Net.Console.Proxy.NETCoreApp2/Program.cs @@ -1,4 +1,10 @@ -using Newtonsoft.Json; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; using WireMock.Server; using WireMock.Settings; @@ -8,6 +14,9 @@ namespace WireMock.Net.Console.Proxy.NETCoreApp2 { static void Main(string[] args) { + RunTestDifferentPort().Wait(20000); // prints "1" + RunTestDifferentPort().Wait(20000); // prints "1" + var server = FluentMockServer.Start(new FluentMockServerSettings { Urls = new[] { "http://localhost:9091", "https://localhost:9443" }, @@ -32,5 +41,22 @@ namespace WireMock.Net.Console.Proxy.NETCoreApp2 System.Console.ReadKey(); server.Stop(); } + + private static async Task RunTestDifferentPort() + { + var server = FluentMockServer.Start(); + + server.Given(Request.Create().WithPath("/").UsingGet()) + .RespondWith(Response.Create().WithStatusCode(200).WithBody("Hello")); + + Thread.Sleep(1000); + + var response = await new HttpClient().GetAsync(server.Urls[0]); + response.EnsureSuccessStatusCode(); + + System.Console.WriteLine("RunTestDifferentPort - server.LogEntries.Count() = " + server.LogEntries.Count()); + + server.Stop(); + } } } diff --git a/src/WireMock.Net.StandAlone/WireMock.Net.StandAlone.csproj b/src/WireMock.Net.StandAlone/WireMock.Net.StandAlone.csproj index 0c462bc5..6b7c2235 100644 --- a/src/WireMock.Net.StandAlone/WireMock.Net.StandAlone.csproj +++ b/src/WireMock.Net.StandAlone/WireMock.Net.StandAlone.csproj @@ -3,7 +3,7 @@ Lightweight StandAlone Http Mocking Server for .Net. WireMock.Net.StandAlone - 1.0.3.17 + 1.0.3.18 Stef Heyenrath net452;net46;netstandard1.3;netstandard2.0 true diff --git a/src/WireMock.Net/Owin/AspNetCoreSelfHost.cs b/src/WireMock.Net/Owin/AspNetCoreSelfHost.cs index 917d76fc..a651f230 100644 --- a/src/WireMock.Net/Owin/AspNetCoreSelfHost.cs +++ b/src/WireMock.Net/Owin/AspNetCoreSelfHost.cs @@ -94,8 +94,6 @@ namespace WireMock.Owin #endif .Build(); - IsStarted = true; - return Task.Run(() => { StartServers(); diff --git a/src/WireMock.Net/Owin/OwinResponseMapper.cs b/src/WireMock.Net/Owin/OwinResponseMapper.cs index fd9a3660..85e3f627 100644 --- a/src/WireMock.Net/Owin/OwinResponseMapper.cs +++ b/src/WireMock.Net/Owin/OwinResponseMapper.cs @@ -24,29 +24,41 @@ namespace WireMock.Owin // https://msdn.microsoft.com/en-us/library/78h415ay(v=vs.110).aspx #if !NETSTANDARD - private static readonly IDictionary>> RestrictedResponseHeaders = new Dictionary>>(StringComparer.OrdinalIgnoreCase) { + private static readonly IDictionary>> ResponseHeadersToFix = new Dictionary>>(StringComparer.OrdinalIgnoreCase) { #else - private static readonly IDictionary>> RestrictedResponseHeaders = new Dictionary>>(StringComparer.OrdinalIgnoreCase) { + private static readonly IDictionary>> ResponseHeadersToFix = new Dictionary>>(StringComparer.OrdinalIgnoreCase) { #endif - { HttpKnownHeaderNames.Accept, null }, - { HttpKnownHeaderNames.Connection, null }, - { HttpKnownHeaderNames.ContentLength, null }, - { HttpKnownHeaderNames.ContentType, (r, v) => r.ContentType = v.FirstOrDefault() }, - { HttpKnownHeaderNames.Date, null }, - { HttpKnownHeaderNames.Expect, null }, - { HttpKnownHeaderNames.Host, null }, - { HttpKnownHeaderNames.IfModifiedSince, null }, - { HttpKnownHeaderNames.KeepAlive, null }, - { HttpKnownHeaderNames.Range, null }, - { HttpKnownHeaderNames.Referer, null }, - { HttpKnownHeaderNames.TransferEncoding, null }, - { HttpKnownHeaderNames.UserAgent, null }, - { HttpKnownHeaderNames.ProxyConnection, null }, - { HttpKnownHeaderNames.WWWAuthenticate, null } + { HttpKnownHeaderNames.ContentType, (r, v) => r.ContentType = v.FirstOrDefault() } }; + private void SetResponseHeaders(ResponseMessage responseMessage +#if !NETSTANDARD + , IOwinResponse response +#else + , HttpResponse response +#endif + ) + { + // Set headers + foreach (var pair in responseMessage.Headers) + { + if (ResponseHeadersToFix.ContainsKey(pair.Key)) + { + ResponseHeadersToFix[pair.Key]?.Invoke(response, pair.Value); + } + else + { +#if !NETSTANDARD + response.Headers.AppendValues(pair.Key, pair.Value.ToArray()); +#else + response.Headers.Append(pair.Key, pair.Value.ToArray()); +#endif + } + } + } + /// - /// MapAsync ResponseMessage to OwinResponse + /// Map ResponseMessage to OwinResponse/HttpResponse /// /// /// @@ -60,57 +72,31 @@ namespace WireMock.Owin { response.StatusCode = responseMessage.StatusCode; - // Set headers - foreach (var pair in responseMessage.Headers) - { - if (RestrictedResponseHeaders.ContainsKey(pair.Key)) - { - RestrictedResponseHeaders[pair.Key]?.Invoke(response, pair.Value); - } - else - { -#if !NETSTANDARD - response.Headers.AppendValues(pair.Key, pair.Value.ToArray()); -#else - response.Headers.Append(pair.Key, pair.Value.ToArray()); -#endif - } - } - - if (responseMessage.Body == null && responseMessage.BodyAsBytes == null && responseMessage.BodyAsFile == null && responseMessage.BodyAsJson == null) - { - return; - } - + byte[] bytes = null; if (responseMessage.BodyAsBytes != null) { - await response.Body.WriteAsync(responseMessage.BodyAsBytes, 0, responseMessage.BodyAsBytes.Length); - return; + bytes = responseMessage.BodyAsBytes; } - - if (responseMessage.BodyAsFile != null) + else if (responseMessage.BodyAsFile != null) { - byte[] bytes = File.ReadAllBytes(responseMessage.BodyAsFile); - - await response.Body.WriteAsync(bytes, 0, bytes.Length); - return; + bytes = File.ReadAllBytes(responseMessage.BodyAsFile); } - - if (responseMessage.BodyAsJson != null) + else if (responseMessage.BodyAsJson != null) { Formatting formatting = responseMessage.BodyAsJsonIndented == true ? Formatting.Indented : Formatting.None; string jsonBody = JsonConvert.SerializeObject(responseMessage.BodyAsJson, new JsonSerializerSettings { Formatting = formatting, NullValueHandling = NullValueHandling.Ignore }); - using (var writer = new StreamWriter(response.Body, responseMessage.BodyEncoding ?? _utf8NoBom)) - { - await writer.WriteAsync(jsonBody); - } - - return; + bytes = (responseMessage.BodyEncoding ?? _utf8NoBom).GetBytes(jsonBody); + } + else if (responseMessage.Body != null) + { + bytes = (responseMessage.BodyEncoding ?? _utf8NoBom).GetBytes(responseMessage.Body); } - using (var writer = new StreamWriter(response.Body, responseMessage.BodyEncoding ?? _utf8NoBom)) + SetResponseHeaders(responseMessage, response); + + if (bytes != null) { - await writer.WriteAsync(responseMessage.Body); + await response.Body.WriteAsync(bytes, 0, bytes.Length); } } } diff --git a/src/WireMock.Net/Server/FluentMockServer.cs b/src/WireMock.Net/Server/FluentMockServer.cs index cd414824..7804a434 100644 --- a/src/WireMock.Net/Server/FluentMockServer.cs +++ b/src/WireMock.Net/Server/FluentMockServer.cs @@ -195,11 +195,13 @@ namespace WireMock.Server { throw new Exception($"Service start failed with error: {_httpServer.RunningException.Message}", _httpServer.RunningException); } + // Respect start timeout setting by throwing TimeoutException if (ctsStartTimeout.IsCancellationRequested) { throw new TimeoutException($"Service start timed out after {TimeSpan.FromMilliseconds(settings.StartTimeout)}"); } + ctsStartTimeout.Token.WaitHandle.WaitOne(ServerStartDelay); } } diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index 00f63d1b..4f730cb1 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -3,7 +3,7 @@ Lightweight Http Mocking Server for .Net, inspired by WireMock from the Java landscape. WireMock.Net - 1.0.3.17 + 1.0.3.18 Stef Heyenrath net452;net46;netstandard1.3;netstandard2.0 true @@ -36,10 +36,6 @@ - - - - All diff --git a/test/WireMock.Net.Tests/FluentMockServerTests.Proxy.cs b/test/WireMock.Net.Tests/FluentMockServerTests.Proxy.cs index e031d388..a3a8b019 100644 --- a/test/WireMock.Net.Tests/FluentMockServerTests.Proxy.cs +++ b/test/WireMock.Net.Tests/FluentMockServerTests.Proxy.cs @@ -5,7 +5,6 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; using NFluent; -using WireMock.Matchers.Request; using WireMock.RequestBuilders; using WireMock.ResponseBuilders; using WireMock.Server; @@ -190,29 +189,30 @@ namespace WireMock.Net.Tests [Fact] public async Task FluentMockServer_Proxy_Should_change_absolute_location_header_in_proxied_response() { - // given - _serverForProxyForwarding = FluentMockServer.Start(); + // Assign + var settings = new FluentMockServerSettings { AllowPartialMapping = false }; + _serverForProxyForwarding = FluentMockServer.Start(settings); _serverForProxyForwarding .Given(Request.Create().WithPath("/*")) .RespondWith(Response.Create() .WithStatusCode(HttpStatusCode.Redirect) .WithHeader("Location", _serverForProxyForwarding.Urls[0] + "testpath")); - _server = FluentMockServer.Start(); + _server = FluentMockServer.Start(settings); _server - .Given(Request.Create().WithPath("/*")) + .Given(Request.Create().WithPath("/prx")) .RespondWith(Response.Create().WithProxy(_serverForProxyForwarding.Urls[0])); - // when + // Act var requestMessage = new HttpRequestMessage { Method = HttpMethod.Get, - RequestUri = new Uri(_server.Urls[0]) + RequestUri = new Uri(_server.Urls[0] + "/prx") }; var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; var response = await new HttpClient(httpClientHandler).SendAsync(requestMessage); - // then + // Assert Check.That(response.Headers.Contains("Location")).IsTrue(); Check.That(response.Headers.GetValues("Location")).ContainsExactly(_server.Urls[0] + "testpath"); } diff --git a/test/WireMock.Net.Tests/FluentMockServerTests.cs b/test/WireMock.Net.Tests/FluentMockServerTests.cs index 8c080da8..4092c8ad 100644 --- a/test/WireMock.Net.Tests/FluentMockServerTests.cs +++ b/test/WireMock.Net.Tests/FluentMockServerTests.cs @@ -351,8 +351,11 @@ namespace WireMock.Net.Tests Check.That(responseAsBytes).ContainsExactly(new byte[] { 48, 49 }); } - public static IEnumerable ValidMatchersForHelloServerJsonMessage => - new List + [Fact] + public async Task FluentMockServer_Should_respond_to_valid_matchers_when_sent_json() + { + // Assign + var validMatchersForHelloServerJsonMessage = new List { new object[] { new WildcardMatcher("*Hello server*"), "application/json" }, new object[] { new WildcardMatcher("*Hello server*"), "text/plain" }, @@ -364,24 +367,25 @@ namespace WireMock.Net.Tests new object[] { new JsonPathMatcher("$..[?(@.message == 'Hello server')]"), "text/plain" } }; - [Theory] - [MemberData(nameof(ValidMatchersForHelloServerJsonMessage))] - public async Task FluentMockServer_Should_respond_to_valid_matchers_when_sent_json(IMatcher matcher, string contentType) - { - // Assign _server = FluentMockServer.Start(); - _server - .Given(Request.Create().WithPath("/foo").WithBody(matcher)) - .RespondWith(Response.Create().WithBody("Hello client")); + foreach (var item in validMatchersForHelloServerJsonMessage) + { + _server + .Given(Request.Create().WithPath("/foo").WithBody((IMatcher)item[0])) + .RespondWith(Response.Create().WithBody("Hello client")); - // Act - var content = new StringContent(jsonRequestMessage, Encoding.UTF8, contentType); - var response = await new HttpClient().PostAsync("http://localhost:" + _server.Ports[0] + "/foo", content); + // Act + var content = new StringContent(jsonRequestMessage, Encoding.UTF8, (string)item[1]); + var response = await new HttpClient().PostAsync("http://localhost:" + _server.Ports[0] + "/foo", content); - // Assert - var responseString = await response.Content.ReadAsStringAsync(); - Check.That(responseString).Equals("Hello client"); + // Assert + var responseString = await response.Content.ReadAsStringAsync(); + Check.That(responseString).Equals("Hello client"); + + _server.ResetMappings(); + _server.ResetLogEntries(); + } } [Fact] @@ -581,24 +585,6 @@ namespace WireMock.Net.Tests Check.That(response).IsEqualTo("/fooBar"); } - [Fact] - public async Task FluentMockServer_Should_IgnoreRestrictedHeader() - { - // Assign - _server = FluentMockServer.Start(); - _server - .Given(Request.Create().WithPath("/head").UsingHead()) - .RespondWith(Response.Create().WithHeader("Content-Length", "1024")); - - var request = new HttpRequestMessage(HttpMethod.Head, "http://localhost:" + _server.Ports[0] + "/head"); - - // Act - var response = await new HttpClient().SendAsync(request); - - // Assert - Check.That(response.Content.Headers.GetValues("Content-Length")).ContainsExactly("0"); - } - public void Dispose() { _server?.Stop(); diff --git a/test/WireMock.Net.Tests/ResponseTests.cs b/test/WireMock.Net.Tests/ResponseTests.cs index 700ca009..fad94de3 100644 --- a/test/WireMock.Net.Tests/ResponseTests.cs +++ b/test/WireMock.Net.Tests/ResponseTests.cs @@ -9,18 +9,21 @@ namespace WireMock.Net.Tests { private const string ClientIp = "::1"; - [Fact] - public async void Response_Create_WithHeader_ContentLength() + [Theory] + [InlineData("Content-Length", "1024")] + [InlineData("Transfer-Encoding", "identity")] + [InlineData("Location", "http://test")] + public async void Response_Create_WithHeader(string headerName, string headerValue) { // Assign var requestMock = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp); - IResponseBuilder builder = Response.Create().WithHeader("Content-Length", "1024"); + IResponseBuilder builder = Response.Create().WithHeader(headerName, headerValue); // Act var response = await builder.ProvideResponseAsync(requestMock); // Assert - Check.That(response.Headers["Content-Length"].ToString()).Equals("1024"); + Check.That(response.Headers[headerName].ToString()).Equals(headerValue); } } } \ No newline at end of file