mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-03-12 05:22:23 +01:00
Add option to ProxySettings to append guid to mapping file (#838)
* Add option to ProxySettings to append guid to mapping file * . * . * .
This commit is contained in:
@@ -1,67 +1,71 @@
|
||||
namespace WireMock.Admin.Settings
|
||||
namespace WireMock.Admin.Settings;
|
||||
|
||||
[FluentBuilder.AutoGenerateBuilder]
|
||||
public class ProxyAndRecordSettingsModel
|
||||
{
|
||||
[FluentBuilder.AutoGenerateBuilder]
|
||||
public class ProxyAndRecordSettingsModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The clientCertificate thumbprint or subject name fragment to use.
|
||||
/// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
|
||||
/// </summary>
|
||||
public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
|
||||
/// <summary>
|
||||
/// The clientCertificate thumbprint or subject name fragment to use.
|
||||
/// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
|
||||
/// </summary>
|
||||
public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines the WebProxySettings.
|
||||
/// </summary>
|
||||
public WebProxySettingsModel WebProxySettings { get; set; }
|
||||
/// <summary>
|
||||
/// Defines the WebProxySettings.
|
||||
/// </summary>
|
||||
public WebProxySettingsModel WebProxySettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Proxy requests should follow redirection (30x).
|
||||
/// </summary>
|
||||
public bool? AllowAutoRedirect { get; set; }
|
||||
/// <summary>
|
||||
/// Proxy requests should follow redirection (30x).
|
||||
/// </summary>
|
||||
public bool? AllowAutoRedirect { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The URL to proxy.
|
||||
/// </summary>
|
||||
public string Url { get; set; }
|
||||
/// <summary>
|
||||
/// The URL to proxy.
|
||||
/// </summary>
|
||||
public string Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Save the mapping for each request/response to the internal Mappings.
|
||||
/// </summary>
|
||||
public bool SaveMapping { get; set; }
|
||||
/// <summary>
|
||||
/// Save the mapping for each request/response to the internal Mappings.
|
||||
/// </summary>
|
||||
public bool SaveMapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
|
||||
/// </summary>
|
||||
public bool SaveMappingToFile { get; set; }
|
||||
/// <summary>
|
||||
/// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
|
||||
/// </summary>
|
||||
public bool SaveMappingToFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public string SaveMappingForStatusCodePattern { get; set; } = "*";
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public string SaveMappingForStatusCodePattern { get; set; } = "*";
|
||||
|
||||
/// <summary>
|
||||
/// Defines a list from headers which will be excluded from the saved mappings.
|
||||
/// </summary>
|
||||
public string[] ExcludedHeaders { get; set; }
|
||||
/// <summary>
|
||||
/// Defines a list from headers which will be excluded from the saved mappings.
|
||||
/// </summary>
|
||||
public string[] ExcludedHeaders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines a list of cookies which will be excluded from the saved mappings.
|
||||
/// </summary>
|
||||
public string[] ExcludedCookies { get; set; }
|
||||
/// <summary>
|
||||
/// Defines a list of cookies which will be excluded from the saved mappings.
|
||||
/// </summary>
|
||||
public string[] ExcludedCookies { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
|
||||
/// </summary>
|
||||
// public bool PreferProxyMapping { get; set; }
|
||||
/// <summary>
|
||||
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
|
||||
/// </summary>
|
||||
// public bool PreferProxyMapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When SaveMapping is set to <c>true</c>, this setting can be used to control the behavior of the generated request matchers for the new mapping.
|
||||
/// - <c>false</c>, the default matchers will be used.
|
||||
/// - <c>true</c>, the defined mappings in the request wil be used for the new mapping.
|
||||
///
|
||||
/// Default value is false.
|
||||
/// </summary>
|
||||
public bool UseDefinedRequestMatchers { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// When SaveMapping is set to <c>true</c>, this setting can be used to control the behavior of the generated request matchers for the new mapping.
|
||||
/// - <c>false</c>, the default matchers will be used.
|
||||
/// - <c>true</c>, the defined mappings in the request wil be used for the new mapping.
|
||||
///
|
||||
/// Default value is false.
|
||||
/// </summary>
|
||||
public bool UseDefinedRequestMatchers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Append an unique GUID to the filename from the saved mapping file.
|
||||
/// </summary>
|
||||
public bool AppendGuidToSavedMappingFile { get; set; }
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Util;
|
||||
@@ -35,15 +36,18 @@ internal static class HttpResponseMessageHelper
|
||||
contentEncodingHeader = headers.First(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentEncoding, StringComparison.OrdinalIgnoreCase)).Value;
|
||||
}
|
||||
|
||||
var bodyParserSettings = new BodyParserSettings
|
||||
if (httpResponseMessage.StatusCode != HttpStatusCode.NoContent) // A body is not allowed for 204.
|
||||
{
|
||||
Stream = stream,
|
||||
ContentType = contentTypeHeader?.FirstOrDefault(),
|
||||
DeserializeJson = deserializeJson,
|
||||
ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
|
||||
DecompressGZipAndDeflate = decompressGzipAndDeflate
|
||||
};
|
||||
responseMessage.BodyData = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
|
||||
var bodyParserSettings = new BodyParserSettings
|
||||
{
|
||||
Stream = stream,
|
||||
ContentType = contentTypeHeader?.FirstOrDefault(),
|
||||
DeserializeJson = deserializeJson,
|
||||
ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
|
||||
DecompressGZipAndDeflate = decompressGzipAndDeflate
|
||||
};
|
||||
responseMessage.BodyData = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var header in headers)
|
||||
|
||||
@@ -99,7 +99,14 @@ namespace WireMock.Owin.Mappers
|
||||
|
||||
if (bytes != null)
|
||||
{
|
||||
await response.Body.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await response.Body.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_options.Logger.Warn("Error writing response body. Exception : {0}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,8 @@ internal class MappingToFileSaver
|
||||
|
||||
public MappingToFileSaver(WireMockServerSettings settings, MappingConverter mappingConverter)
|
||||
{
|
||||
Guard.NotNull(settings);
|
||||
Guard.NotNull(mappingConverter);
|
||||
|
||||
_settings = settings;
|
||||
_mappingConverter = mappingConverter;
|
||||
_settings = Guard.NotNull(settings);
|
||||
_mappingConverter = Guard.NotNull(mappingConverter);
|
||||
}
|
||||
|
||||
public void SaveMappingToFile(IMapping mapping, string? folder = null)
|
||||
@@ -30,17 +27,31 @@ internal class MappingToFileSaver
|
||||
}
|
||||
|
||||
var model = _mappingConverter.ToMappingModel(mapping);
|
||||
string filename = (!string.IsNullOrEmpty(mapping.Title) ? SanitizeFileName(mapping.Title) : mapping.Guid.ToString()) + ".json";
|
||||
|
||||
string path = Path.Combine(folder, filename);
|
||||
var filename = BuildSanitizedFileName(mapping);
|
||||
var path = Path.Combine(folder, filename);
|
||||
|
||||
_settings.Logger.Info("Saving Mapping file {0}", filename);
|
||||
_settings.Logger.Info("Saving Mapping file {0}", path);
|
||||
|
||||
_settings.FileSystemHandler.WriteMappingFile(path, JsonConvert.SerializeObject(model, JsonSerializationConstants.JsonSerializerSettingsDefault));
|
||||
}
|
||||
|
||||
private static string SanitizeFileName(string name, char replaceChar = '_')
|
||||
private string BuildSanitizedFileName(IMapping mapping, char replaceChar = '_')
|
||||
{
|
||||
return Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, replaceChar));
|
||||
string name;
|
||||
if (!string.IsNullOrEmpty(mapping.Title))
|
||||
{
|
||||
name = mapping.Title!;
|
||||
if (_settings.ProxyAndRecordSettings?.AppendGuidToSavedMappingFile == true)
|
||||
{
|
||||
name += $"{replaceChar}{mapping.Guid}";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
name = mapping.Guid.ToString();
|
||||
}
|
||||
|
||||
return $"{Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, replaceChar))}.json";
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -18,11 +16,9 @@ using WireMock.Http;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Proxy;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseProviders;
|
||||
using WireMock.Serialization;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
@@ -208,62 +204,6 @@ public partial class WireMockServer
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Proxy and Record
|
||||
private HttpClient? _httpClientForProxy;
|
||||
|
||||
private void InitProxyAndRecord(WireMockServerSettings settings)
|
||||
{
|
||||
if (settings.ProxyAndRecordSettings == null)
|
||||
{
|
||||
_httpClientForProxy = null;
|
||||
DeleteMapping(ProxyMappingGuid);
|
||||
return;
|
||||
}
|
||||
|
||||
_httpClientForProxy = HttpClientBuilder.Build(settings.ProxyAndRecordSettings);
|
||||
|
||||
var proxyRespondProvider = Given(Request.Create().WithPath("/*").UsingAnyMethod()).WithGuid(ProxyMappingGuid).WithTitle("Default Proxy Mapping on /*");
|
||||
if (settings.StartAdminInterface == true)
|
||||
{
|
||||
proxyRespondProvider.AtPriority(WireMockConstants.ProxyPriority);
|
||||
}
|
||||
|
||||
proxyRespondProvider.RespondWith(new ProxyAsyncResponseProvider(ProxyAndRecordAsync, settings));
|
||||
}
|
||||
|
||||
private async Task<IResponseMessage> ProxyAndRecordAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
|
||||
{
|
||||
var requestUri = new Uri(requestMessage.Url);
|
||||
var proxyUri = new Uri(settings.ProxyAndRecordSettings!.Url);
|
||||
var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery);
|
||||
|
||||
var proxyHelper = new ProxyHelper(settings);
|
||||
|
||||
var (responseMessage, mapping) = await proxyHelper.SendAsync(
|
||||
null,
|
||||
_settings.ProxyAndRecordSettings!,
|
||||
_httpClientForProxy!,
|
||||
requestMessage,
|
||||
proxyUriWithRequestPathAndQuery.AbsoluteUri
|
||||
).ConfigureAwait(false);
|
||||
|
||||
if (mapping != null)
|
||||
{
|
||||
if (settings.ProxyAndRecordSettings.SaveMapping)
|
||||
{
|
||||
_options.Mappings.TryAdd(mapping.Guid, mapping);
|
||||
}
|
||||
|
||||
if (settings.ProxyAndRecordSettings.SaveMappingToFile)
|
||||
{
|
||||
_mappingToFileSaver.SaveMappingToFile(mapping);
|
||||
}
|
||||
}
|
||||
|
||||
return responseMessage;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Settings
|
||||
private IResponseMessage SettingsGet(IRequestMessage requestMessage)
|
||||
{
|
||||
|
||||
68
src/WireMock.Net/Server/WireMockServer.Proxy.cs
Normal file
68
src/WireMock.Net/Server/WireMockServer.Proxy.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Constants;
|
||||
using WireMock.Http;
|
||||
using WireMock.Proxy;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseProviders;
|
||||
using WireMock.Settings;
|
||||
|
||||
namespace WireMock.Server;
|
||||
|
||||
public partial class WireMockServer
|
||||
{
|
||||
private HttpClient? _httpClientForProxy;
|
||||
|
||||
private void InitProxyAndRecord(WireMockServerSettings settings)
|
||||
{
|
||||
if (settings.ProxyAndRecordSettings == null)
|
||||
{
|
||||
_httpClientForProxy = null;
|
||||
DeleteMapping(ProxyMappingGuid);
|
||||
return;
|
||||
}
|
||||
|
||||
_httpClientForProxy = HttpClientBuilder.Build(settings.ProxyAndRecordSettings);
|
||||
|
||||
var proxyRespondProvider = Given(Request.Create().WithPath("/*").UsingAnyMethod()).WithGuid(ProxyMappingGuid).WithTitle("Default Proxy Mapping on /*");
|
||||
if (settings.StartAdminInterface == true)
|
||||
{
|
||||
proxyRespondProvider.AtPriority(WireMockConstants.ProxyPriority);
|
||||
}
|
||||
|
||||
proxyRespondProvider.RespondWith(new ProxyAsyncResponseProvider(ProxyAndRecordAsync, settings));
|
||||
}
|
||||
|
||||
private async Task<IResponseMessage> ProxyAndRecordAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
|
||||
{
|
||||
var requestUri = new Uri(requestMessage.Url);
|
||||
var proxyUri = new Uri(settings.ProxyAndRecordSettings!.Url);
|
||||
var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery);
|
||||
|
||||
var proxyHelper = new ProxyHelper(settings);
|
||||
|
||||
var (responseMessage, mapping) = await proxyHelper.SendAsync(
|
||||
null,
|
||||
_settings.ProxyAndRecordSettings!,
|
||||
_httpClientForProxy!,
|
||||
requestMessage,
|
||||
proxyUriWithRequestPathAndQuery.AbsoluteUri
|
||||
).ConfigureAwait(false);
|
||||
|
||||
if (mapping != null)
|
||||
{
|
||||
if (settings.ProxyAndRecordSettings.SaveMapping)
|
||||
{
|
||||
_options.Mappings.TryAdd(mapping.Guid, mapping);
|
||||
}
|
||||
|
||||
if (settings.ProxyAndRecordSettings.SaveMappingToFile)
|
||||
{
|
||||
_mappingToFileSaver.SaveMappingToFile(mapping);
|
||||
}
|
||||
}
|
||||
|
||||
return responseMessage;
|
||||
}
|
||||
}
|
||||
@@ -58,4 +58,9 @@ public class ProxyAndRecordSettings : HttpClientSettings
|
||||
/// Default value is false.
|
||||
/// </summary>
|
||||
public bool UseDefinedRequestMatchers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Append an unique GUID to the filename from the saved mapping file.
|
||||
/// </summary>
|
||||
public bool AppendGuidToSavedMappingFile { get; set; }
|
||||
}
|
||||
@@ -94,6 +94,7 @@ public static class WireMockServerSettingsParser
|
||||
SaveMappingForStatusCodePattern = parser.GetStringValue("SaveMappingForStatusCodePattern", "*"),
|
||||
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
|
||||
UseDefinedRequestMatchers = parser.GetBoolValue(nameof(ProxyAndRecordSettings.UseDefinedRequestMatchers)),
|
||||
AppendGuidToSavedMappingFile = parser.GetBoolValue(nameof(ProxyAndRecordSettings.AppendGuidToSavedMappingFile)),
|
||||
Url = proxyUrl!
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user