From 4a2d512f83cc09b4288ad7c6d70cf507b8be0927 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Thu, 10 Oct 2019 08:18:58 +0200 Subject: [PATCH] Add Proxy Setting for: SaveMappingForStatusCodePattern to only save the mapping when the status code matches the pattern (#357) * proxy * HttpStatusRangeParserTests * test --- .../Server/FluentMockServer.Admin.cs | 3 +- .../Settings/IProxyAndRecordSettings.cs | 13 +++- .../Settings/ProxyAndRecordSettings.cs | 4 + .../Util/HttpStatusRangeParser.cs | 71 ++++++++++++++++++ .../Util/HttpStatusRangeParserTests.cs | 75 +++++++++++++++++++ 5 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 src/WireMock.Net/Util/HttpStatusRangeParser.cs create mode 100644 test/WireMock.Net.Tests/Util/HttpStatusRangeParserTests.cs diff --git a/src/WireMock.Net/Server/FluentMockServer.Admin.cs b/src/WireMock.Net/Server/FluentMockServer.Admin.cs index 8ee0a65b..c5d034aa 100644 --- a/src/WireMock.Net/Server/FluentMockServer.Admin.cs +++ b/src/WireMock.Net/Server/FluentMockServer.Admin.cs @@ -267,7 +267,8 @@ namespace WireMock.Server var responseMessage = await HttpClientHelper.SendAsync(_httpClientForProxy, requestMessage, proxyUriWithRequestPathAndQuery.AbsoluteUri); - if (settings.ProxyAndRecordSettings.SaveMapping || settings.ProxyAndRecordSettings.SaveMappingToFile) + if (HttpStatusRangeParser.IsMatch(settings.ProxyAndRecordSettings.SaveMappingForStatusCodePattern, responseMessage.StatusCode) && + (settings.ProxyAndRecordSettings.SaveMapping || settings.ProxyAndRecordSettings.SaveMappingToFile)) { var mapping = ToMapping(requestMessage, responseMessage, settings.ProxyAndRecordSettings.BlackListedHeaders ?? new string[] { }, settings.ProxyAndRecordSettings.BlackListedCookies ?? new string[] { }); diff --git a/src/WireMock.Net/Settings/IProxyAndRecordSettings.cs b/src/WireMock.Net/Settings/IProxyAndRecordSettings.cs index 74848bca..2e173553 100644 --- a/src/WireMock.Net/Settings/IProxyAndRecordSettings.cs +++ b/src/WireMock.Net/Settings/IProxyAndRecordSettings.cs @@ -1,4 +1,6 @@ -namespace WireMock.Settings +using JetBrains.Annotations; + +namespace WireMock.Settings { /// /// IProxyAndRecordSettings @@ -16,7 +18,14 @@ bool SaveMapping { get; set; } /// - /// Save the mapping for each request/response to also file. (Note that SaveMapping must also be set to true.) + /// 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. + /// + [CanBeNull] + string SaveMappingForStatusCodePattern { get; set; } + + /// + /// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.) /// bool SaveMappingToFile { get; set; } diff --git a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs index b124db53..09c7a48e 100644 --- a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs +++ b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs @@ -15,6 +15,10 @@ namespace WireMock.Settings [PublicAPI] public bool SaveMapping { get; set; } = true; + /// + [PublicAPI] + public string SaveMappingForStatusCodePattern { get; set; } = "*"; + /// [PublicAPI] public bool SaveMappingToFile { get; set; } = true; diff --git a/src/WireMock.Net/Util/HttpStatusRangeParser.cs b/src/WireMock.Net/Util/HttpStatusRangeParser.cs new file mode 100644 index 00000000..d838ea48 --- /dev/null +++ b/src/WireMock.Net/Util/HttpStatusRangeParser.cs @@ -0,0 +1,71 @@ +using System; +using System.Linq; +using System.Net; +using System.Text.RegularExpressions; + +namespace WireMock.Util +{ + /// + /// Based on https://github.com/tmenier/Flurl/blob/129565361e135e639f1d44a35a78aea4302ac6ca/src/Flurl.Http/HttpStatusRangeParser.cs + /// + internal static class HttpStatusRangeParser + { + /// + /// Determines whether the specified pattern is match. + /// + /// The pattern. (Can be null, in that case it's allowed.) + /// The value. + /// is invalid. + public static bool IsMatch(string pattern, HttpStatusCode httpStatusCode) + { + return IsMatch(pattern, (int)httpStatusCode); + } + + /// + /// Determines whether the specified pattern is match. + /// + /// The pattern. (Can be null, in that case it's allowed.) + /// The value. + /// is invalid. + public static bool IsMatch(string pattern, int httpStatusCode) + { + if (pattern == null) + { + return true; + } + + foreach (var range in pattern.Split(',').Select(p => p.Trim())) + { + switch (range) + { + case "": + continue; + + case "*": + return true; // special case - allow everything + } + + string[] bounds = range.Split('-'); + int lower = 0; + int upper = 0; + + bool valid = + bounds.Length <= 2 && + int.TryParse(Regex.Replace(bounds.First().Trim(), "[*xX]", "0"), out lower) && + int.TryParse(Regex.Replace(bounds.Last().Trim(), "[*xX]", "9"), out upper); + + if (!valid) + { + throw new ArgumentException($"Invalid range pattern: \"{pattern}\". Examples of allowed patterns: \"400\", \"4xx\", \"300,400-403\", \"*\"."); + } + + if (httpStatusCode >= lower && httpStatusCode <= upper) + { + return true; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Util/HttpStatusRangeParserTests.cs b/test/WireMock.Net.Tests/Util/HttpStatusRangeParserTests.cs new file mode 100644 index 00000000..0eed338a --- /dev/null +++ b/test/WireMock.Net.Tests/Util/HttpStatusRangeParserTests.cs @@ -0,0 +1,75 @@ +using FluentAssertions; +using System; +using System.Net; +using WireMock.Util; +using Xunit; + +namespace WireMock.Net.Tests.Util +{ + /// + /// Based on https://raw.githubusercontent.com/tmenier/Flurl/129565361e135e639f1d44a35a78aea4302ac6ca/Test/Flurl.Test/Http/HttpStatusRangeParserTests.cs + /// + public class HttpStatusRangeParserTests + { + [Theory] + [InlineData("4**", 399, false)] + [InlineData("4**", 400, true)] + [InlineData("4**", 499, true)] + [InlineData("4**", 500, false)] + + [InlineData("4xx", 399, false)] + [InlineData("4xx", 400, true)] + [InlineData("4xx", 499, true)] + [InlineData("4xx", 500, false)] + + [InlineData("4XX", 399, false)] + [InlineData("4XX", 400, true)] + [InlineData("4XX", 499, true)] + [InlineData("4XX", 500, false)] + + [InlineData("400-499", 399, false)] + [InlineData("400-499", 400, true)] + [InlineData("400-499", 499, true)] + [InlineData("400-499", 500, false)] + + [InlineData("100,3xx,600", 100, true)] + [InlineData("100,3xx,600", 101, false)] + [InlineData("100,3xx,600", 300, true)] + [InlineData("100,3xx,600", 399, true)] + [InlineData("100,3xx,600", 400, false)] + [InlineData("100,3xx,600", 600, true)] + + [InlineData("400-409,490-499", 399, false)] + [InlineData("400-409,490-499", 405, true)] + [InlineData("400-409,490-499", 450, false)] + [InlineData("400-409,490-499", 495, true)] + [InlineData("400-409,490-499", 500, false)] + + [InlineData("*", 0, true)] + [InlineData(",,,*", 9999, true)] + + [InlineData("", 0, false)] + [InlineData(",,,", 9999, false)] + + [InlineData(null, 399, true)] + public void HttpStatusRangeParser_ValidPattern_IsMatch(string pattern, int value, bool expectedResult) + { + HttpStatusRangeParser.IsMatch(pattern, value).Should().Be(expectedResult); + } + + [Fact] + public void HttpStatusRangeParser_ValidPattern_HttpStatusCode_IsMatch() + { + HttpStatusRangeParser.IsMatch("4xx", HttpStatusCode.BadRequest).Should().BeTrue(); + } + + [Theory] + [InlineData("-100")] + [InlineData("100-")] + [InlineData("1yy")] + public void HttpStatusRangeParser_InvalidPattern_ThrowsException(string pattern) + { + Assert.Throws(() => HttpStatusRangeParser.IsMatch(pattern, 100)); + } + } +} \ No newline at end of file