diff --git a/src/WireMock.Net/Matchers/RegexMatcher.cs b/src/WireMock.Net/Matchers/RegexMatcher.cs index f4f5dc74..8ee94cae 100644 --- a/src/WireMock.Net/Matchers/RegexMatcher.cs +++ b/src/WireMock.Net/Matchers/RegexMatcher.cs @@ -18,7 +18,7 @@ namespace WireMock.Matchers public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher { private readonly AnyOf[] _patterns; - private readonly RegexExtended[] _expressions; + private readonly Regex[] _expressions; /// public MatchBehaviour MatchBehaviour { get; } @@ -31,7 +31,10 @@ namespace WireMock.Matchers /// /// The pattern. /// Ignore the case from the pattern. - public RegexMatcher([NotNull, RegexPattern] AnyOf pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase) + /// Throw an exception when the internal matching fails because of invalid input. + /// Use RegexExtended (default = true). + public RegexMatcher([NotNull, RegexPattern] AnyOf pattern, bool ignoreCase = false, bool throwException = false, bool useRegexExtended = true) : + this(MatchBehaviour.AcceptOnMatch, new[] { pattern }, ignoreCase, throwException, useRegexExtended) { } @@ -41,16 +44,10 @@ namespace WireMock.Matchers /// The match behaviour. /// The pattern. /// Ignore the case from the pattern. - public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] AnyOf pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The patterns. - /// Ignore the case from the pattern. - public RegexMatcher([NotNull, RegexPattern] AnyOf[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase) + /// Throw an exception when the internal matching fails because of invalid input. + /// Use RegexExtended (default = true). + public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] AnyOf pattern, bool ignoreCase = false, bool throwException = false, bool useRegexExtended = true) : + this(matchBehaviour, new[] { pattern }, ignoreCase, throwException, useRegexExtended) { } @@ -61,7 +58,8 @@ namespace WireMock.Matchers /// The patterns. /// Ignore the case from the pattern. /// Throw an exception when the internal matching fails because of invalid input. - public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] AnyOf[] patterns, bool ignoreCase = false, bool throwException = false) + /// Use RegexExtended (default = true). + public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] AnyOf[] patterns, bool ignoreCase = false, bool throwException = false, bool useRegexExtended = true) { Check.NotNull(patterns, nameof(patterns)); @@ -77,7 +75,7 @@ namespace WireMock.Matchers options |= RegexOptions.IgnoreCase; } - _expressions = patterns.Select(p => new RegexExtended(p.GetPattern(), options)).ToArray(); + _expressions = patterns.Select(p => useRegexExtended ? new RegexExtended(p.GetPattern(), options) : new Regex(p.GetPattern(), options)).ToArray(); } /// diff --git a/src/WireMock.Net/RegularExpressions/RegexExtended.cs b/src/WireMock.Net/RegularExpressions/RegexExtended.cs index 661459d3..887a4a7c 100644 --- a/src/WireMock.Net/RegularExpressions/RegexExtended.cs +++ b/src/WireMock.Net/RegularExpressions/RegexExtended.cs @@ -16,16 +16,13 @@ namespace WireMock.RegularExpressions } /// - public RegexExtended(string pattern, - RegexOptions options) + public RegexExtended(string pattern, RegexOptions options) : this(pattern, options, Regex.InfiniteMatchTimeout) { } /// - public RegexExtended(string pattern, - RegexOptions options, - TimeSpan matchTimeout) + public RegexExtended(string pattern, RegexOptions options, TimeSpan matchTimeout) : base(ReplaceGuidPattern(pattern), options, matchTimeout) { } @@ -35,22 +32,31 @@ namespace WireMock.RegularExpressions { // Lower case format `B` Guid pattern { @"\guidb", @"(\{[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}\})" }, + // Upper case format `B` Guid pattern { @"\GUIDB", @"(\{[A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12}\})" }, + // Lower case format `D` Guid pattern { @"\guidd", "([a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12})" }, + // Upper case format `D` Guid pattern { @"\GUIDD", "([A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12})" }, + // Lower case format `N` Guid pattern { @"\guidn", "([a-z0-9]{32})" }, + // Upper case format `N` Guid pattern { @"\GUIDN", "([A-Z0-9]{32})" }, + // Lower case format `P` Guid pattern { @"\guidp", @"(\([a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}\))" }, + // Upper case format `P` Guid pattern { @"\GUIDP", @"(\([A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12}\))" }, + // Lower case format `X` Guid pattern { @"\guidx", @"(\{0x[a-f0-9]{8},0x[a-f0-9]{4},0x[a-f0-9]{4},\{(0x[a-f0-9]{2},){7}(0x[a-f0-9]{2})\}\})" }, + // Upper case format `X` Guid pattern { @"\GUIDX", @"(\{0x[A-F0-9]{8},0x[A-F0-9]{4},0x[A-F0-9]{4},\{(0x[A-F0-9]{2},){7}(0x[A-F0-9]{2})\}\})" }, }; @@ -62,11 +68,13 @@ namespace WireMock.RegularExpressions private static string ReplaceGuidPattern(string pattern) { Check.NotNull(pattern, nameof(pattern)); + foreach (var tokenPattern in GuidTokenPatterns) { pattern = pattern.Replace(tokenPattern.Key, tokenPattern.Value); } + return pattern; } } -} +} \ No newline at end of file diff --git a/src/WireMock.Net/Serialization/MatcherMapper.cs b/src/WireMock.Net/Serialization/MatcherMapper.cs index 027d5684..a92daaa8 100644 --- a/src/WireMock.Net/Serialization/MatcherMapper.cs +++ b/src/WireMock.Net/Serialization/MatcherMapper.cs @@ -41,6 +41,7 @@ namespace WireMock.Serialization var matchBehaviour = matcher.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch; bool ignoreCase = matcher.IgnoreCase == true; bool throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true; + bool useRegexExtended = _settings.UseRegexExtended == true; switch (matcherName) { @@ -65,7 +66,7 @@ namespace WireMock.Serialization return CreateExactObjectMatcher(matchBehaviour, stringPatterns[0], throwExceptionWhenMatcherFails); case nameof(RegexMatcher): - return new RegexMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails); + return new RegexMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails, useRegexExtended); case nameof(JsonMatcher): object valueForJsonMatcher = matcher.Pattern ?? matcher.Patterns; diff --git a/src/WireMock.Net/Server/WireMockServer.AdminFiles.cs b/src/WireMock.Net/Server/WireMockServer.AdminFiles.cs index fb0548aa..e9db1a4a 100644 --- a/src/WireMock.Net/Server/WireMockServer.AdminFiles.cs +++ b/src/WireMock.Net/Server/WireMockServer.AdminFiles.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using System.Linq; using System.Text; using WireMock.Matchers; @@ -9,7 +9,7 @@ namespace WireMock.Server { public partial class WireMockServer { - private readonly RegexMatcher _adminFilesFilenamePathMatcher = new RegexMatcher(MatchBehaviour.AcceptOnMatch, @"^\/__admin\/files\/.*$"); + private readonly RegexMatcher _adminFilesFilenamePathMatcher = new RegexMatcher(@"^\/__admin\/files\/.*$"); private static readonly Encoding[] FileBodyIsString = { Encoding.UTF8, Encoding.ASCII }; #region Files/{filename} diff --git a/src/WireMock.Net/Settings/IWireMockServerSettings.cs b/src/WireMock.Net/Settings/IWireMockServerSettings.cs index ea3060ca..b5a93ea3 100644 --- a/src/WireMock.Net/Settings/IWireMockServerSettings.cs +++ b/src/WireMock.Net/Settings/IWireMockServerSettings.cs @@ -1,9 +1,11 @@ using System; +using System.Text.RegularExpressions; using HandlebarsDotNet; using JetBrains.Annotations; using WireMock.Handlers; using WireMock.Logging; using WireMock.Matchers; +using WireMock.RegularExpressions; #if USE_ASPNETCORE using Microsoft.Extensions.DependencyInjection; #endif @@ -215,5 +217,11 @@ namespace WireMock.Settings /// [PublicAPI] IWebhookSettings WebhookSettings { get; set; } + + /// + /// Use the instead of the default . + /// + [PublicAPI] + bool? UseRegexExtended { get; } } } \ No newline at end of file diff --git a/src/WireMock.Net/Settings/WireMockServerSettings.cs b/src/WireMock.Net/Settings/WireMockServerSettings.cs index 8f963073..eb345bc4 100644 --- a/src/WireMock.Net/Settings/WireMockServerSettings.cs +++ b/src/WireMock.Net/Settings/WireMockServerSettings.cs @@ -151,5 +151,9 @@ namespace WireMock.Settings /// [PublicAPI] public IWebhookSettings WebhookSettings { get; set; } + + /// + [PublicAPI] + public bool? UseRegexExtended { get; set; } = true; } } \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Matchers/RegexMatcherTests.cs b/test/WireMock.Net.Tests/Matchers/RegexMatcherTests.cs index 5271ccd4..f39fea64 100644 --- a/test/WireMock.Net.Tests/Matchers/RegexMatcherTests.cs +++ b/test/WireMock.Net.Tests/Matchers/RegexMatcherTests.cs @@ -1,3 +1,5 @@ +using System; +using FluentAssertions; using NFluent; using WireMock.Matchers; using Xunit; @@ -70,6 +72,32 @@ namespace WireMock.Net.Tests.Matchers Check.That(result).IsEqualTo(0.0d); } + [Fact] + public void RegexMatcher_IsMatch_RegexExtended_Guid() + { + // Assign + var matcher = new RegexMatcher(@"\GUIDB", true); + + // Act + double result = matcher.IsMatch(Guid.NewGuid().ToString("B")); + + // Assert + result.Should().Be(1.0); + } + + [Fact] + public void RegexMatcher_IsMatch_Regex_Guid() + { + // Assign + var matcher = new RegexMatcher(@"\GUIDB", true, false, false); + + // Act + double result = matcher.IsMatch(Guid.NewGuid().ToString("B")); + + // Assert + result.Should().Be(0); + } + [Fact] public void RegexMatcher_IsMatch_IgnoreCase() { diff --git a/test/WireMock.Net.Tests/RegularExpressions/RegexExtendedTests.cs b/test/WireMock.Net.Tests/RegularExpressions/RegexExtendedTests.cs index 8f6b7a98..fdeca434 100644 --- a/test/WireMock.Net.Tests/RegularExpressions/RegexExtendedTests.cs +++ b/test/WireMock.Net.Tests/RegularExpressions/RegexExtendedTests.cs @@ -10,7 +10,7 @@ namespace WireMock.Net.Tests.RegularExpressions /// /// Input guid used for testing /// - public Guid InputGuid { get; } = Guid.NewGuid(); + public Guid InputGuid => Guid.NewGuid(); [Fact] public void RegexExtended_GuidB_Pattern()