From 674fa89c3e9a84bf54ea99c6b861a9549772b063 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Thu, 9 Mar 2023 15:28:52 +0100 Subject: [PATCH] ProxySettings : Add logic to not save some requests depending on HttpMethods (#900) * Add ExcludedHttpMethods to ProxySettings * tst * fix * SaveMappingSettings * . --- src/WireMock.Net/Matchers/MatchScores.cs | 1 - src/WireMock.Net/Proxy/ProxyHelper.cs | 27 ++++++++++++- .../Serialization/ProxyMappingConverter.cs | 11 +++--- .../Settings/ProxyAndRecordSettings.cs | 21 +++++++++- .../Settings/ProxySaveMappingSetting.cs | 20 ++++++++++ .../Settings/ProxySaveMappingSettings.cs | 22 +++++++++++ .../Settings/WireMockServerSettingsParser.cs | 8 +++- .../ProxyMappingConverterTests.cs | 3 +- .../WireMockServer.Proxy.cs | 39 +++++++++++++++++++ 9 files changed, 140 insertions(+), 12 deletions(-) create mode 100644 src/WireMock.Net/Settings/ProxySaveMappingSetting.cs create mode 100644 src/WireMock.Net/Settings/ProxySaveMappingSettings.cs diff --git a/src/WireMock.Net/Matchers/MatchScores.cs b/src/WireMock.Net/Matchers/MatchScores.cs index cb79e712..82362a93 100644 --- a/src/WireMock.Net/Matchers/MatchScores.cs +++ b/src/WireMock.Net/Matchers/MatchScores.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Linq; namespace WireMock.Matchers; diff --git a/src/WireMock.Net/Proxy/ProxyHelper.cs b/src/WireMock.Net/Proxy/ProxyHelper.cs index 14f66b16..803194cd 100644 --- a/src/WireMock.Net/Proxy/ProxyHelper.cs +++ b/src/WireMock.Net/Proxy/ProxyHelper.cs @@ -1,8 +1,10 @@ using System; +using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Stef.Validation; using WireMock.Http; +using WireMock.Matchers; using WireMock.Serialization; using WireMock.Settings; using WireMock.Util; @@ -47,12 +49,33 @@ internal class ProxyHelper var responseMessage = await HttpResponseMessageHelper.CreateAsync(httpResponseMessage, requiredUri, originalUri, deserializeJson, decompressGzipAndDeflate).ConfigureAwait(false); IMapping? newMapping = null; - if (HttpStatusRangeParser.IsMatch(proxyAndRecordSettings.SaveMappingForStatusCodePattern, responseMessage.StatusCode) && - (proxyAndRecordSettings.SaveMapping || proxyAndRecordSettings.SaveMappingToFile)) + + var saveMappingSettings = proxyAndRecordSettings.SaveMappingSettings; + + bool save = true; + if (saveMappingSettings != null) + { + save &= Check(saveMappingSettings.StatusCodePattern, + () => HttpStatusRangeParser.IsMatch(saveMappingSettings.StatusCodePattern, responseMessage.StatusCode) + ); + + save &= Check(saveMappingSettings.HttpMethods, + () => saveMappingSettings.HttpMethods.Value.Contains(requestMessage.Method, StringComparer.OrdinalIgnoreCase) + ); + } + + if (save && (proxyAndRecordSettings.SaveMapping || proxyAndRecordSettings.SaveMappingToFile)) { newMapping = _proxyMappingConverter.ToMapping(mapping, proxyAndRecordSettings, requestMessage, responseMessage); } return (responseMessage, newMapping); } + + private static bool Check(ProxySaveMappingSetting? saveMappingSetting, Func action) + { + var isMatch = saveMappingSetting is null || action(); + var matchBehaviour = saveMappingSetting?.MatchBehaviour ?? MatchBehaviour.AcceptOnMatch; + return isMatch == (matchBehaviour == MatchBehaviour.AcceptOnMatch); + } } \ No newline at end of file diff --git a/src/WireMock.Net/Serialization/ProxyMappingConverter.cs b/src/WireMock.Net/Serialization/ProxyMappingConverter.cs index d84546aa..157e61a7 100644 --- a/src/WireMock.Net/Serialization/ProxyMappingConverter.cs +++ b/src/WireMock.Net/Serialization/ProxyMappingConverter.cs @@ -26,8 +26,12 @@ internal class ProxyMappingConverter _dateTimeUtils = Guard.NotNull(dateTimeUtils); } - public IMapping ToMapping(IMapping? mapping, ProxyAndRecordSettings proxyAndRecordSettings, IRequestMessage requestMessage, ResponseMessage responseMessage) + public IMapping? ToMapping(IMapping? mapping, ProxyAndRecordSettings proxyAndRecordSettings, IRequestMessage requestMessage, ResponseMessage responseMessage) { + var useDefinedRequestMatchers = proxyAndRecordSettings.UseDefinedRequestMatchers; + var excludedHeaders = new List(proxyAndRecordSettings.ExcludedHeaders ?? new string[] { }) { "Cookie" }; + var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[0]; + var request = (Request?)mapping?.RequestMatcher; var clientIPMatcher = request?.GetRequestMessageMatcher(); var pathMatcher = request?.GetRequestMessageMatcher(); @@ -37,11 +41,6 @@ internal class ProxyMappingConverter var methodMatcher = request?.GetRequestMessageMatcher(); var bodyMatcher = request?.GetRequestMessageMatcher(); - var useDefinedRequestMatchers = proxyAndRecordSettings.UseDefinedRequestMatchers; - - var excludedHeaders = new List(proxyAndRecordSettings.ExcludedHeaders ?? new string[] { }) { "Cookie" }; - var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[] { }; - var newRequest = Request.Create(); // ClientIP diff --git a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs index c9af7fbd..6227d162 100644 --- a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs +++ b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs @@ -28,9 +28,28 @@ public class ProxyAndRecordSettings : HttpClientSettings /// /// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.) /// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported. + /// + /// Deprecated : use SaveMappingSettings. /// [PublicAPI] - public string SaveMappingForStatusCodePattern { get; set; } = "*"; + public string SaveMappingForStatusCodePattern + { + set + { + if (SaveMappingSettings is null) + { + SaveMappingSettings = new ProxySaveMappingSettings(); + } + + SaveMappingSettings.StatusCodePattern = value; + } + } + + /// + /// Additional SaveMappingSettings. + /// + [PublicAPI] + public ProxySaveMappingSettings? SaveMappingSettings { get; set; } /// /// Defines a list from headers which will be excluded from the saved mappings. diff --git a/src/WireMock.Net/Settings/ProxySaveMappingSetting.cs b/src/WireMock.Net/Settings/ProxySaveMappingSetting.cs new file mode 100644 index 00000000..5f209eaa --- /dev/null +++ b/src/WireMock.Net/Settings/ProxySaveMappingSetting.cs @@ -0,0 +1,20 @@ +using WireMock.Matchers; + +namespace WireMock.Settings; + +public class ProxySaveMappingSetting +{ + public MatchBehaviour MatchBehaviour { get; } = MatchBehaviour.AcceptOnMatch; + + public T Value { get; private set; } + + public ProxySaveMappingSetting(T value, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) + { + Value = value; + MatchBehaviour = matchBehaviour; + } + + public static implicit operator ProxySaveMappingSetting(T value) => new ProxySaveMappingSetting(value); + + public static implicit operator T(ProxySaveMappingSetting @this) => @this.Value; +} \ No newline at end of file diff --git a/src/WireMock.Net/Settings/ProxySaveMappingSettings.cs b/src/WireMock.Net/Settings/ProxySaveMappingSettings.cs new file mode 100644 index 00000000..8c3a8f1d --- /dev/null +++ b/src/WireMock.Net/Settings/ProxySaveMappingSettings.cs @@ -0,0 +1,22 @@ +using JetBrains.Annotations; + +namespace WireMock.Settings; + +/// +/// ProxySaveMappingSettings +/// +public class ProxySaveMappingSettings +{ + /// + /// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.) + /// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported. + /// + [PublicAPI] + public ProxySaveMappingSetting? StatusCodePattern { get; set; } = "*"; + + /// + /// Only save these Http Methods. (Note that SaveMapping must also be set to true.) + /// + [PublicAPI] + public ProxySaveMappingSetting? HttpMethods { get; set; } +} \ No newline at end of file diff --git a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs index d07e9e25..49216dc1 100644 --- a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs +++ b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs @@ -4,6 +4,7 @@ using System.Linq; using JetBrains.Annotations; using Stef.Validation; using WireMock.Logging; +using WireMock.Matchers; using WireMock.Types; using WireMock.Util; @@ -111,7 +112,12 @@ public static class WireMockServerSettingsParser SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"), UseDefinedRequestMatchers = parser.GetBoolValue(nameof(ProxyAndRecordSettings.UseDefinedRequestMatchers)), AppendGuidToSavedMappingFile = parser.GetBoolValue(nameof(ProxyAndRecordSettings.AppendGuidToSavedMappingFile)), - Url = proxyUrl! + Url = proxyUrl!, + SaveMappingSettings = new ProxySaveMappingSettings + { + StatusCodePattern = parser.GetStringValue("SaveMappingForStatusCodePattern", "*"), + // HttpMethods = new ProxySaveMappingSetting(parser.GetValues("DoNotSaveMappingForHttpMethods", new string[0]), MatchBehaviour.RejectOnMatch) + } }; string? proxyAddress = parser.GetStringValue("WebProxyAddress"); diff --git a/test/WireMock.Net.Tests/Serialization/ProxyMappingConverterTests.cs b/test/WireMock.Net.Tests/Serialization/ProxyMappingConverterTests.cs index 58861f70..409b6871 100644 --- a/test/WireMock.Net.Tests/Serialization/ProxyMappingConverterTests.cs +++ b/test/WireMock.Net.Tests/Serialization/ProxyMappingConverterTests.cs @@ -1,6 +1,7 @@ #if !(NET452 || NET461 || NETCOREAPP3_1) using System; using System.Threading.Tasks; +using FluentAssertions; using Moq; using VerifyTests; using VerifyXunit; @@ -70,7 +71,7 @@ public class ProxyMappingConverterTests var responseMessage = new ResponseMessage(); // Act - var proxyMapping = _sut.ToMapping(mappingMock.Object, proxyAndRecordSettings, requestMessageMock.Object, responseMessage); + var proxyMapping = _sut.ToMapping(mappingMock.Object, proxyAndRecordSettings, requestMessageMock.Object, responseMessage)!; // Assert var model = _mappingConverter.ToMappingModel(proxyMapping); diff --git a/test/WireMock.Net.Tests/WireMockServer.Proxy.cs b/test/WireMock.Net.Tests/WireMockServer.Proxy.cs index ab43ed1b..1f670cf6 100644 --- a/test/WireMock.Net.Tests/WireMockServer.Proxy.cs +++ b/test/WireMock.Net.Tests/WireMockServer.Proxy.cs @@ -248,6 +248,45 @@ public class WireMockServerProxyTests fileSystemHandlerMock.Verify(f => f.WriteMappingFile(It.IsAny(), It.IsAny()), Times.Never); } + [Fact] + public async Task WireMockServer_Proxy_With_DoNotSaveMappingForHttpMethod_Should_Not_SaveMapping() + { + // Assign + var fileSystemHandlerMock = new Mock(); + fileSystemHandlerMock.Setup(f => f.GetMappingFolder()).Returns("m"); + + var settings = new WireMockServerSettings + { + ProxyAndRecordSettings = new ProxyAndRecordSettings + { + Url = "http://www.google.com", + SaveMapping = true, + SaveMappingToFile = true, + SaveMappingSettings = new ProxySaveMappingSettings + { + HttpMethods = new ProxySaveMappingSetting(new string[] { "GET" }, MatchBehaviour.RejectOnMatch) // To make sure that we don't want this mapping + } + }, + FileSystemHandler = fileSystemHandlerMock.Object + }; + var server = WireMockServer.Start(settings); + + // Act + var requestMessage = new HttpRequestMessage + { + Method = HttpMethod.Get, + RequestUri = new Uri(server.Urls[0]) + }; + var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; + await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false); + + // Assert + server.Mappings.Should().HaveCount(1); + + // Verify + fileSystemHandlerMock.Verify(f => f.WriteMappingFile(It.IsAny(), It.IsAny()), Times.Never); + } + [Fact] public async Task WireMockServer_Proxy_Should_log_proxied_requests() {