diff --git a/src/WireMock.Net.Abstractions/Admin/Settings/ProxyAndRecordSettingsModel.cs b/src/WireMock.Net.Abstractions/Admin/Settings/ProxyAndRecordSettingsModel.cs
index 1867dd9d..5bf9e95c 100644
--- a/src/WireMock.Net.Abstractions/Admin/Settings/ProxyAndRecordSettingsModel.cs
+++ b/src/WireMock.Net.Abstractions/Admin/Settings/ProxyAndRecordSettingsModel.cs
@@ -1,71 +1,76 @@
-namespace WireMock.Admin.Settings;
-
-[FluentBuilder.AutoGenerateBuilder]
-public class ProxyAndRecordSettingsModel
-{
- ///
- /// The clientCertificate thumbprint or subject name fragment to use.
- /// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
- ///
- public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
-
- ///
- /// Defines the WebProxySettings.
- ///
- public WebProxySettingsModel WebProxySettings { get; set; }
-
- ///
- /// Proxy requests should follow redirection (30x).
- ///
- public bool? AllowAutoRedirect { get; set; }
-
- ///
- /// The URL to proxy.
- ///
- public string Url { get; set; }
-
- ///
- /// Save the mapping for each request/response to the internal Mappings.
- ///
- public bool SaveMapping { get; set; }
-
- ///
- /// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
- ///
- public bool SaveMappingToFile { get; set; }
-
- ///
- /// 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.
- ///
- public string SaveMappingForStatusCodePattern { get; set; } = "*";
-
- ///
- /// Defines a list from headers which will be excluded from the saved mappings.
- ///
- public string[] ExcludedHeaders { get; set; }
-
- ///
- /// Defines a list of cookies which will be excluded from the saved mappings.
- ///
- public string[] ExcludedCookies { get; set; }
-
- ///
- /// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to true).
- ///
- // public bool PreferProxyMapping { get; set; }
-
- ///
- /// When SaveMapping is set to true, this setting can be used to control the behavior of the generated request matchers for the new mapping.
- /// - false, the default matchers will be used.
- /// - true, the defined mappings in the request wil be used for the new mapping.
- ///
- /// Default value is false.
- ///
- public bool UseDefinedRequestMatchers { get; set; }
-
- ///
- /// Append an unique GUID to the filename from the saved mapping file.
- ///
- public bool AppendGuidToSavedMappingFile { get; set; }
+namespace WireMock.Admin.Settings;
+
+[FluentBuilder.AutoGenerateBuilder]
+public class ProxyAndRecordSettingsModel
+{
+ ///
+ /// The clientCertificate thumbprint or subject name fragment to use.
+ /// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
+ ///
+ public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
+
+ ///
+ /// Defines the WebProxySettings.
+ ///
+ public WebProxySettingsModel WebProxySettings { get; set; }
+
+ ///
+ /// Proxy requests should follow redirection (30x).
+ ///
+ public bool? AllowAutoRedirect { get; set; }
+
+ ///
+ /// The URL to proxy.
+ ///
+ public string Url { get; set; }
+
+ ///
+ /// Save the mapping for each request/response to the internal Mappings.
+ ///
+ public bool SaveMapping { get; set; }
+
+ ///
+ /// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
+ ///
+ public bool SaveMappingToFile { get; set; }
+
+ ///
+ /// 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.
+ ///
+ public string SaveMappingForStatusCodePattern { get; set; } = "*";
+
+ ///
+ /// Defines a list from headers which will be excluded from the saved mappings.
+ ///
+ public string[] ExcludedHeaders { get; set; }
+
+ ///
+ /// Defines a list of cookies which will be excluded from the saved mappings.
+ ///
+ public string[] ExcludedCookies { get; set; }
+
+ ///
+ /// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to true).
+ ///
+ // public bool PreferProxyMapping { get; set; }
+
+ ///
+ /// When SaveMapping is set to true, this setting can be used to control the behavior of the generated request matchers for the new mapping.
+ /// - false, the default matchers will be used.
+ /// - true, the defined mappings in the request wil be used for the new mapping.
+ ///
+ /// Default value is false.
+ ///
+ public bool UseDefinedRequestMatchers { get; set; }
+
+ ///
+ /// Append an unique GUID to the filename from the saved mapping file.
+ ///
+ public bool AppendGuidToSavedMappingFile { get; set; }
+
+ ///
+ /// Defines the Replace Settings
+ ///
+ public ProxyUrlReplaceSettingsModel? ReplaceSettings { get; set; }
}
\ No newline at end of file
diff --git a/src/WireMock.Net.Abstractions/Admin/Settings/ProxyUrlReplaceSettingsModel.cs b/src/WireMock.Net.Abstractions/Admin/Settings/ProxyUrlReplaceSettingsModel.cs
new file mode 100644
index 00000000..2073acb5
--- /dev/null
+++ b/src/WireMock.Net.Abstractions/Admin/Settings/ProxyUrlReplaceSettingsModel.cs
@@ -0,0 +1,18 @@
+namespace WireMock.Admin.Settings;
+
+///
+/// Defines an old path param and a new path param to be replaced when proxying.
+///
+[FluentBuilder.AutoGenerateBuilder]
+public class ProxyUrlReplaceSettingsModel
+{
+ ///
+ /// The old path value to be replaced by the new path value
+ ///
+ public string OldValue { get; set; } = null!;
+
+ ///
+ /// The new path value to replace the old value with
+ ///
+ public string NewValue { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/Proxy/ProxyHelper.cs b/src/WireMock.Net/Proxy/ProxyHelper.cs
index 519a6056..cd0c279c 100644
--- a/src/WireMock.Net/Proxy/ProxyHelper.cs
+++ b/src/WireMock.Net/Proxy/ProxyHelper.cs
@@ -37,7 +37,9 @@ internal class ProxyHelper
var requiredUri = new Uri(url);
// Create HttpRequestMessage
- var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, url);
+ var replaceSettings = proxyAndRecordSettings.ReplaceSettings;
+ var proxyUrl = replaceSettings is not null ? url.Replace(replaceSettings.OldValue, replaceSettings.NewValue) : url;
+ var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, proxyUrl);
// Call the URL
var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead).ConfigureAwait(false);
diff --git a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs
index 719a04fc..2ca3fda5 100644
--- a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs
+++ b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs
@@ -1,91 +1,97 @@
-using JetBrains.Annotations;
-
-namespace WireMock.Settings;
-
-///
-/// ProxyAndRecordSettings
-///
-public class ProxyAndRecordSettings : HttpClientSettings
-{
- ///
- /// The URL to proxy.
- ///
- [PublicAPI]
- public string Url { get; set; } = null!;
-
- ///
- /// Save the mapping for each request/response to the internal Mappings.
- ///
- [PublicAPI]
- public bool SaveMapping { get; set; }
-
- ///
- /// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
- ///
- [PublicAPI]
- public bool SaveMappingToFile { get; set; }
-
- ///
- /// 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
- {
- 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.
- ///
- [PublicAPI]
- public string[]? ExcludedHeaders { get; set; }
-
- ///
- /// Defines a list of params which will be excluded from the saved mappings.
- ///
- [PublicAPI]
- public string[]? ExcludedParams { get; set; }
-
- ///
- /// Defines a list of cookies which will be excluded from the saved mappings.
- ///
- [PublicAPI]
- public string[]? ExcludedCookies { get; set; }
-
- ///
- /// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to true).
- ///
- //[PublicAPI]
- //public bool PreferProxyMapping { get; set; }
-
- ///
- /// When SaveMapping is set to true, this setting can be used to control the behavior of the generated request matchers for the new mapping.
- /// - false, the default matchers will be used.
- /// - true, the defined mappings in the request wil be used for the new mapping.
- ///
- /// Default value is false.
- ///
- public bool UseDefinedRequestMatchers { get; set; }
-
- ///
- /// Append an unique GUID to the filename from the saved mapping file.
- ///
- public bool AppendGuidToSavedMappingFile { get; set; }
+using JetBrains.Annotations;
+
+namespace WireMock.Settings;
+
+///
+/// ProxyAndRecordSettings
+///
+public class ProxyAndRecordSettings : HttpClientSettings
+{
+ ///
+ /// The URL to proxy.
+ ///
+ [PublicAPI]
+ public string Url { get; set; } = null!;
+
+ ///
+ /// Save the mapping for each request/response to the internal Mappings.
+ ///
+ [PublicAPI]
+ public bool SaveMapping { get; set; }
+
+ ///
+ /// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
+ ///
+ [PublicAPI]
+ public bool SaveMappingToFile { get; set; }
+
+ ///
+ /// 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
+ {
+ 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.
+ ///
+ [PublicAPI]
+ public string[]? ExcludedHeaders { get; set; }
+
+ ///
+ /// Defines a list of params which will be excluded from the saved mappings.
+ ///
+ [PublicAPI]
+ public string[]? ExcludedParams { get; set; }
+
+ ///
+ /// Defines a list of cookies which will be excluded from the saved mappings.
+ ///
+ [PublicAPI]
+ public string[]? ExcludedCookies { get; set; }
+
+ ///
+ /// Replace Settings
+ ///
+ [PublicAPI]
+ public ProxyUrlReplaceSettings? ReplaceSettings { get; set; }
+
+ ///
+ /// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to true).
+ ///
+ //[PublicAPI]
+ //public bool PreferProxyMapping { get; set; }
+
+ ///
+ /// When SaveMapping is set to true, this setting can be used to control the behavior of the generated request matchers for the new mapping.
+ /// - false, the default matchers will be used.
+ /// - true, the defined mappings in the request wil be used for the new mapping.
+ ///
+ /// Default value is false.
+ ///
+ public bool UseDefinedRequestMatchers { get; set; }
+
+ ///
+ /// Append an unique GUID to the filename from the saved mapping file.
+ ///
+ public bool AppendGuidToSavedMappingFile { get; set; }
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Settings/ProxyUrlReplaceSettings.cs b/src/WireMock.Net/Settings/ProxyUrlReplaceSettings.cs
new file mode 100644
index 00000000..11da3362
--- /dev/null
+++ b/src/WireMock.Net/Settings/ProxyUrlReplaceSettings.cs
@@ -0,0 +1,17 @@
+namespace WireMock.Settings;
+
+///
+/// Defines an old path param and a new path param to be replaced when proxying.
+///
+public class ProxyUrlReplaceSettings
+{
+ ///
+ /// The old path value to be replaced by the new path value
+ ///
+ public string OldValue { get; set; } = null!;
+
+ ///
+ /// The new path value to replace the old value with
+ ///
+ public string NewValue { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs
index f4c6c5be..b8caea0e 100644
--- a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs
+++ b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs
@@ -21,7 +21,8 @@ public static class WireMockServerSettingsParser
/// The logger (optional, can be null)
/// The parsed settings
[PublicAPI]
- public static bool TryParseArguments(string[] args, [NotNullWhen(true)] out WireMockServerSettings? settings, IWireMockLogger? logger = null)
+ public static bool TryParseArguments(string[] args, [NotNullWhen(true)] out WireMockServerSettings? settings,
+ IWireMockLogger? logger = null)
{
Guard.HasNoNulls(args);
@@ -70,6 +71,17 @@ public static class WireMockServerSettingsParser
settings.AcceptAnyClientCertificate = parser.GetBoolValue(nameof(WireMockServerSettings.AcceptAnyClientCertificate));
#endif
+ ParseLoggerSettings(settings, logger, parser);
+ ParsePortSettings(settings, parser);
+ ParseProxyAndRecordSettings(settings, parser);
+ ParseCertificateSettings(settings, parser);
+
+ return true;
+ }
+
+ private static void ParseLoggerSettings(WireMockServerSettings settings, IWireMockLogger? logger,
+ SimpleCommandLineParser parser)
+ {
var loggerType = parser.GetStringValue("WireMockLogger");
switch (loggerType)
{
@@ -86,22 +98,17 @@ public static class WireMockServerSettingsParser
{
settings.Logger = logger;
}
+
break;
}
+ }
- if (parser.Contains(nameof(WireMockServerSettings.Port)))
- {
- settings.Port = parser.GetIntValue(nameof(WireMockServerSettings.Port));
- }
- else if (settings.HostingScheme is null)
- {
- settings.Urls = parser.GetValues("Urls", new[] { "http://*:9091/" });
- }
-
+ private static void ParseProxyAndRecordSettings(WireMockServerSettings settings, SimpleCommandLineParser parser)
+ {
var proxyUrl = parser.GetStringValue("ProxyURL") ?? parser.GetStringValue("ProxyUrl");
if (!string.IsNullOrEmpty(proxyUrl))
{
- settings.ProxyAndRecordSettings = new ProxyAndRecordSettings
+ var proxyAndRecordSettings = new ProxyAndRecordSettings
{
AllowAutoRedirect = parser.GetBoolValue("AllowAutoRedirect"),
ClientX509Certificate2ThumbprintOrSubjectName = parser.GetStringValue("ClientX509Certificate2ThumbprintOrSubjectName"),
@@ -121,18 +128,27 @@ public static class WireMockServerSettingsParser
}
};
- string? proxyAddress = parser.GetStringValue("WebProxyAddress");
- if (!string.IsNullOrEmpty(proxyAddress))
- {
- settings.ProxyAndRecordSettings.WebProxySettings = new WebProxySettings
- {
- Address = proxyAddress!,
- UserName = parser.GetStringValue("WebProxyUserName"),
- Password = parser.GetStringValue("WebProxyPassword")
- };
- }
- }
+ ParseWebProxyAddressSettings(proxyAndRecordSettings, parser);
+ ParseProxyUrlReplaceSettings(proxyAndRecordSettings, parser);
+ settings.ProxyAndRecordSettings = proxyAndRecordSettings;
+ }
+ }
+
+ private static void ParsePortSettings(WireMockServerSettings settings, SimpleCommandLineParser parser)
+ {
+ if (parser.Contains(nameof(WireMockServerSettings.Port)))
+ {
+ settings.Port = parser.GetIntValue(nameof(WireMockServerSettings.Port));
+ }
+ else if (settings.HostingScheme is null)
+ {
+ settings.Urls = parser.GetValues("Urls", new[] { "http://*:9091/" });
+ }
+ }
+
+ private static void ParseCertificateSettings(WireMockServerSettings settings, SimpleCommandLineParser parser)
+ {
var certificateSettings = new WireMockCertificateSettings
{
X509StoreName = parser.GetStringValue("X509StoreName"),
@@ -145,7 +161,33 @@ public static class WireMockServerSettingsParser
{
settings.CertificateSettings = certificateSettings;
}
+ }
- return true;
+ private static void ParseWebProxyAddressSettings(ProxyAndRecordSettings settings, SimpleCommandLineParser parser)
+ {
+ string? proxyAddress = parser.GetStringValue("WebProxyAddress");
+ if (!string.IsNullOrEmpty(proxyAddress))
+ {
+ settings.WebProxySettings = new WebProxySettings
+ {
+ Address = proxyAddress!,
+ UserName = parser.GetStringValue("WebProxyUserName"),
+ Password = parser.GetStringValue("WebProxyPassword")
+ };
+ }
+ }
+
+ private static void ParseProxyUrlReplaceSettings(ProxyAndRecordSettings settings, SimpleCommandLineParser parser)
+ {
+ var proxyUrlReplaceOldValue = parser.GetStringValue("ProxyUrlReplaceOldValue");
+ var proxyUrlReplaceNewValue = parser.GetStringValue("ProxyUrlReplaceNewValue");
+ if (!string.IsNullOrEmpty(proxyUrlReplaceOldValue) && proxyUrlReplaceNewValue != null)
+ {
+ settings.ReplaceSettings = new ProxyUrlReplaceSettings
+ {
+ OldValue = proxyUrlReplaceOldValue!,
+ NewValue = proxyUrlReplaceNewValue!
+ };
+ }
}
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Util/TinyMapperUtils.cs b/src/WireMock.Net/Util/TinyMapperUtils.cs
index 3bc9fb6d..06de691a 100644
--- a/src/WireMock.Net/Util/TinyMapperUtils.cs
+++ b/src/WireMock.Net/Util/TinyMapperUtils.cs
@@ -1,29 +1,31 @@
-using Nelibur.ObjectMapper;
-using WireMock.Admin.Settings;
-using WireMock.Settings;
-
-namespace WireMock.Util;
-
-internal sealed class TinyMapperUtils
-{
- public static TinyMapperUtils Instance { get; } = new();
-
- private TinyMapperUtils()
- {
- TinyMapper.Bind();
- TinyMapper.Bind();
-
- TinyMapper.Bind();
- TinyMapper.Bind();
- }
-
- public ProxyAndRecordSettingsModel? Map(ProxyAndRecordSettings? instance)
- {
- return instance == null ? null : TinyMapper.Map(instance);
- }
-
- public ProxyAndRecordSettings? Map(ProxyAndRecordSettingsModel? model)
- {
- return model == null ? null : TinyMapper.Map(model);
- }
+using Nelibur.ObjectMapper;
+using WireMock.Admin.Settings;
+using WireMock.Settings;
+
+namespace WireMock.Util;
+
+internal sealed class TinyMapperUtils
+{
+ public static TinyMapperUtils Instance { get; } = new();
+
+ private TinyMapperUtils()
+ {
+ TinyMapper.Bind();
+ TinyMapper.Bind();
+ TinyMapper.Bind();
+
+ TinyMapper.Bind();
+ TinyMapper.Bind();
+ TinyMapper.Bind();
+ }
+
+ public ProxyAndRecordSettingsModel? Map(ProxyAndRecordSettings? instance)
+ {
+ return instance == null ? null : TinyMapper.Map(instance);
+ }
+
+ public ProxyAndRecordSettings? Map(ProxyAndRecordSettingsModel? model)
+ {
+ return model == null ? null : TinyMapper.Map(model);
+ }
}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/WireMockServer.Proxy.cs b/test/WireMock.Net.Tests/WireMockServer.Proxy.cs
index 25cc876c..8d4d7273 100644
--- a/test/WireMock.Net.Tests/WireMockServer.Proxy.cs
+++ b/test/WireMock.Net.Tests/WireMockServer.Proxy.cs
@@ -570,6 +570,52 @@ public class WireMockServerProxyTests
Check.That(matchers).Contains("name");
}
+ [Fact]
+ public async Task WireMockServer_Proxy_Should_replace_old_path_value_with_new_path_value_in_replace_settings()
+ {
+ // Assign
+ var replaceSettings = new ProxyUrlReplaceSettings
+ {
+ OldValue = "value-to-replace",
+ NewValue = "new-value"
+ };
+ string path = $"/prx_{Guid.NewGuid()}";
+ var serverForProxyForwarding = WireMockServer.Start();
+ serverForProxyForwarding
+ .Given(Request.Create().WithPath($"/{replaceSettings.NewValue}{path}"))
+ .RespondWith(Response.Create());
+
+ var settings = new WireMockServerSettings
+ {
+ ProxyAndRecordSettings = new ProxyAndRecordSettings
+ {
+ Url = serverForProxyForwarding.Urls[0],
+ SaveMapping = true,
+ SaveMappingToFile = false,
+ ReplaceSettings = replaceSettings
+ }
+ };
+ var server = WireMockServer.Start(settings);
+ var defaultMapping = server.Mappings.First();
+
+ // Act
+ var requestMessage = new HttpRequestMessage
+ {
+ Method = HttpMethod.Post,
+ RequestUri = new Uri($"{server.Urls[0]}/{replaceSettings.OldValue}{path}"),
+ Content = new StringContent("stringContent")
+ };
+
+ var handler = new HttpClientHandler();
+ await new HttpClient(handler).SendAsync(requestMessage).ConfigureAwait(false);
+
+ // Assert
+ var mapping = serverForProxyForwarding.Mappings.FirstOrDefault(m => m.Guid != defaultMapping.Guid);
+ var score = mapping.RequestMatcher.GetMatchingScore(serverForProxyForwarding.LogEntries.First().RequestMessage,
+ new RequestMatchResult());
+ Check.That(score).IsEqualTo(1.0);
+ }
+
[Fact]
public async Task WireMockServer_Proxy_Should_preserve_content_header_in_proxied_request_with_empty_content()
{
@@ -701,7 +747,7 @@ public class WireMockServerProxyTests
///
/// Send some binary content in a request through the proxy and check that the same content
/// arrived at the target. As example a JPEG/JIFF header is used, which is not representable
- /// in UTF8 and breaks if it is not treated as binary content.
+ /// in UTF8 and breaks if it is not treated as binary content.
///
[Fact]
public async Task WireMockServer_Proxy_Should_preserve_binary_request_content()