Create WireMock.Net.MimePart project (#1300)

* Create WireMock.Net.MimePart project

* .

* REFACTOR

* ILRepack

* --

* ...

* x

* x

* .

* fix

* public class MimePartMatcher

* shared

* min

* .

* <!--<DelaySign>true</DelaySign>-->

* Update README.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Stef Heyenrath
2025-05-24 12:17:42 +02:00
committed by GitHub
parent c15206ecd8
commit 96eca4262a
306 changed files with 9746 additions and 9285 deletions

View File

@@ -0,0 +1,50 @@
// Copyright © WireMock.Net
using HandlebarsDotNet.Helpers.Enums;
using JetBrains.Annotations;
using WireMock.Types;
namespace WireMock.Settings;
/// <summary>
/// HandlebarsSettings
/// </summary>
[PublicAPI]
public class HandlebarsSettings
{
internal static readonly Category[] DefaultAllowedHandlebarsHelpers =
[
Category.Boolean,
Category.Constants,
Category.DateTime,
Category.Enumerable,
Category.Humanizer,
Category.JsonPath,
Category.Math,
Category.Object,
Category.Random,
Category.Regex,
Category.String,
Category.Url,
Category.Xeger,
Category.XPath,
Category.Xslt
];
/// <summary>
/// Defines the allowed custom HandlebarsHelpers which can be used. Possible values are:
/// - <see cref="CustomHandlebarsHelpers.None"/> (Default)
/// - <see cref="CustomHandlebarsHelpers.File"/>
/// - <see cref="CustomHandlebarsHelpers.All"/>
/// </summary>
[PublicAPI]
public CustomHandlebarsHelpers AllowedCustomHandlebarsHelpers { get; set; } = CustomHandlebarsHelpers.None;
/// <summary>
/// Defines the allowed HandlebarHelpers which can be used.
///
/// By default, all categories except <see cref="Category.DynamicLinq"/> and <see cref="Category.Environment"/> are registered.
/// </summary>
[PublicAPI]
public Category[] AllowedHandlebarsHelpers { get; set; } = DefaultAllowedHandlebarsHelpers;
}

View File

@@ -0,0 +1,32 @@
// Copyright © WireMock.Net
using System.Security.Cryptography.X509Certificates;
namespace WireMock.Settings;
/// <summary>
/// HttpClientSettings
/// </summary>
public abstract class HttpClientSettings
{
/// <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 WebProxySettings? WebProxySettings { get; set; }
/// <summary>
/// Proxy requests should follow redirection (30x).
/// </summary>
public bool? AllowAutoRedirect { get; set; }
/// <summary>
/// The <see cref="X509Certificate2"/> to use.
/// </summary>
public X509Certificate2? Certificate { get; set; }
}

View File

@@ -0,0 +1,115 @@
// Copyright © WireMock.Net
using JetBrains.Annotations;
namespace WireMock.Settings;
/// <summary>
/// ProxyAndRecordSettings
/// </summary>
public class ProxyAndRecordSettings : HttpClientSettings
{
/// <summary>
/// Default prefix value for saved mapping file
/// </summary>
public const string DefaultPrefixForSavedMappingFile = "Proxy Mapping for ";
/// <summary>
/// The URL to proxy.
/// </summary>
[PublicAPI]
public string Url { get; set; } = null!;
/// <summary>
/// Save the mapping for each request/response to the internal Mappings.
/// </summary>
[PublicAPI]
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>
[PublicAPI]
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.
///
/// Deprecated : use SaveMappingSettings.
/// </summary>
[PublicAPI]
public string SaveMappingForStatusCodePattern
{
set
{
if (SaveMappingSettings is null)
{
SaveMappingSettings = new ProxySaveMappingSettings();
}
SaveMappingSettings.StatusCodePattern = value;
}
}
/// <summary>
/// Additional SaveMappingSettings.
/// </summary>
[PublicAPI]
public ProxySaveMappingSettings? SaveMappingSettings { get; set; }
/// <summary>
/// Defines a list from headers which will be excluded from the saved mappings.
/// </summary>
[PublicAPI]
public string[]? ExcludedHeaders { get; set; }
/// <summary>
/// Defines a list of params which will be excluded from the saved mappings.
/// </summary>
[PublicAPI]
public string[]? ExcludedParams { get; set; }
/// <summary>
/// Defines a list of cookies which will be excluded from the saved mappings.
/// </summary>
[PublicAPI]
public string[]? ExcludedCookies { get; set; }
/// <summary>
/// Replace Settings
/// </summary>
[PublicAPI]
public ProxyUrlReplaceSettings? ReplaceSettings { get; set; }
/// <summary>
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
/// </summary>
//[PublicAPI]
//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>
/// Append a unique GUID to the filename from the saved mapping file.
/// </summary>
public bool AppendGuidToSavedMappingFile { get; set; }
/// <summary>
/// Set prefix for saved mapping file.
/// </summary>
public string PrefixForSavedMappingFile { get; set; } = DefaultPrefixForSavedMappingFile;
/// <summary>
/// Proxy all Api calls, irrespective of any condition
/// </summary>
[PublicAPI]
public bool ProxyAll { get; set; }
}

View File

@@ -0,0 +1,50 @@
// Copyright © WireMock.Net
using System;
using Stef.Validation;
using WireMock.Matchers;
namespace WireMock.Settings;
/// <summary>
/// Represents settings for saving a proxy mapping with a non-nullable value and a specific match behaviour.
/// </summary>
/// <typeparam name="T">The non-nullable type of the value associated with the proxy save mapping setting.</typeparam>
public class ProxySaveMappingSetting<T> where T : notnull
{
/// <summary>
/// Gets the match behaviour for the proxy save mapping setting.
/// </summary>
/// <value>The match behaviour which determines how matches are evaluated.</value>
public MatchBehaviour MatchBehaviour { get; }
/// <summary>
/// Gets the non-nullable value associated with the proxy save mapping setting.
/// </summary>
/// <value>The value of type <typeparamref name="T"/>.</value>
public T Value { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ProxySaveMappingSetting{T}"/> class with specified non-nullable value and match behaviour.
/// </summary>
/// <param name="value">The non-nullable value of type <typeparamref name="T"/>.</param>
/// <param name="matchBehaviour">The match behaviour (optional, default is <c>MatchBehaviour.AcceptOnMatch</c>.</param>
/// <exception cref="ArgumentNullException">Thrown if the <paramref name="value"/> is null.</exception>
public ProxySaveMappingSetting(T value, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
Value = Guard.NotNull(value);
MatchBehaviour = matchBehaviour;
}
/// <summary>
/// Converts a non-nullable value of type <typeparamref name="T"/> to a <see cref="ProxySaveMappingSetting{T}"/> implicitly.
/// </summary>
/// <param name="value">The non-nullable value to convert.</param>
public static implicit operator ProxySaveMappingSetting<T>(T value) => new(value);
/// <summary>
/// Converts a <see cref="ProxySaveMappingSetting{T}"/> to its underlying non-nullable value of type <typeparamref name="T"/> implicitly.
/// </summary>
/// <param name="instance">The <see cref="ProxySaveMappingSetting{T}"/> to convert.</param>
public static implicit operator T(ProxySaveMappingSetting<T> instance) => instance.Value;
}

View File

@@ -0,0 +1,24 @@
// Copyright © WireMock.Net
using JetBrains.Annotations;
namespace WireMock.Settings;
/// <summary>
/// ProxySaveMappingSettings
/// </summary>
public class ProxySaveMappingSettings
{
/// <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>
[PublicAPI]
public ProxySaveMappingSetting<string>? StatusCodePattern { get; set; } = "*";
/// <summary>
/// Only save these Http Methods. (Note that SaveMapping must also be set to true.)
/// </summary>
[PublicAPI]
public ProxySaveMappingSetting<string[]>? HttpMethods { get; set; }
}

View File

@@ -0,0 +1,24 @@
// Copyright © WireMock.Net
namespace WireMock.Settings;
/// <summary>
/// Defines an old path param and a new path param to be replaced when proxying.
/// </summary>
public class ProxyUrlReplaceSettings
{
/// <summary>
/// The old path value to be replaced by the new path value
/// </summary>
public string OldValue { get; set; } = null!;
/// <summary>
/// The new path value to replace the old value with
/// </summary>
public string NewValue { get; set; } = null!;
/// <summary>
/// Defines if the case should be ignored when replacing.
/// </summary>
public bool IgnoreCase { get; set; }
}

View File

@@ -0,0 +1,187 @@
// Copyright © WireMock.Net
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using WireMock.Extensions;
using WireMock.Util;
namespace WireMock.Settings;
// Based on http://blog.gauffin.org/2014/12/simple-command-line-parser/
internal class SimpleSettingsParser
{
private const string Sigil = "--";
private const string Prefix = $"{nameof(WireMockServerSettings)}__";
private static readonly int PrefixLength = Prefix.Length;
private IDictionary<string, string[]> Arguments { get; } = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
public void Parse(string[] arguments, IDictionary? environment = null)
{
string currentName = string.Empty;
var values = new List<string>();
// Split a single argument on a space character to fix issue (e.g. Azure Service Fabric) when an argument is supplied like "--x abc" or '--x abc'
foreach (string arg in arguments.SelectMany(arg => arg.Split(' ')))
{
if (arg.StartsWith(Sigil))
{
if (!string.IsNullOrEmpty(currentName))
{
Arguments[currentName] = values.ToArray();
}
values.Clear();
currentName = arg.Substring(Sigil.Length);
}
else if (string.IsNullOrEmpty(currentName))
{
Arguments[arg] = [];
}
else
{
values.Add(arg);
}
}
if (!string.IsNullOrEmpty(currentName))
{
Arguments[currentName] = values.ToArray();
}
// Now also parse environment
if (environment != null)
{
foreach (string key in environment.Keys)
{
if (key.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase) && environment.TryGetStringValue(key, out var value))
{
Arguments[key.Substring(PrefixLength)] = value.Split(' ').ToArray();
}
}
}
}
public bool Contains(string name)
{
return Arguments.ContainsKey(name);
}
public bool ContainsAny(params string[] names)
{
return names.Any(Arguments.ContainsKey);
}
public string[] GetValues(string name, string[] defaultValue)
{
return Contains(name) ? Arguments[name] : defaultValue;
}
public string[]? GetValues(string name)
{
return Contains(name) ? Arguments[name] : default;
}
public T GetValue<T>(string name, Func<string[], T> func, T defaultValue)
{
return Contains(name) ? func(Arguments[name]) : defaultValue;
}
public T? GetValue<T>(string name, Func<string[], T> func)
{
return Contains(name) ? func(Arguments[name]) : default;
}
public bool GetBoolValue(string name, bool defaultValue = false)
{
return GetValue(name, values =>
{
var value = values.FirstOrDefault();
return !string.IsNullOrEmpty(value) ? bool.Parse(value) : defaultValue;
}, defaultValue);
}
public bool GetBoolSwitchValue(string name)
{
return Contains(name);
}
public int? GetIntValue(string name)
{
return GetValue<int?>(name, values =>
{
var value = values.FirstOrDefault();
return !string.IsNullOrEmpty(value) ? int.Parse(value) : null;
}, null);
}
public int GetIntValue(string name, int defaultValue)
{
return GetValue(name, values =>
{
var value = values.FirstOrDefault();
return !string.IsNullOrEmpty(value) ? int.Parse(value) : defaultValue;
}, defaultValue);
}
public TEnum? GetEnumValue<TEnum>(string name)
where TEnum : struct
{
return GetValue(name, values =>
{
var value = values.FirstOrDefault();
return Enum.TryParse<TEnum>(value, true, out var enumValue) ? enumValue : (TEnum?)null;
});
}
public TEnum GetEnumValue<TEnum>(string name, TEnum defaultValue)
where TEnum : struct
{
return GetValue(name, values =>
{
var value = values.FirstOrDefault();
return Enum.TryParse<TEnum>(value, true, out var enumValue) ? enumValue : defaultValue;
}, defaultValue);
}
public TEnum[] GetEnumValues<TEnum>(string name, TEnum[] defaultValues)
where TEnum : struct
{
var values = GetValues(name);
if (values == null)
{
return defaultValues;
}
var enums = new List<TEnum>();
foreach (var value in values)
{
if (Enum.TryParse<TEnum>(value, true, out var enumValue))
{
enums.Add(enumValue);
}
}
return enums.ToArray();
}
public string GetStringValue(string name, string defaultValue)
{
return GetValue(name, values => values.FirstOrDefault() ?? defaultValue, defaultValue);
}
public string? GetStringValue(string name)
{
return GetValue(name, values => values.FirstOrDefault());
}
public T? GetObjectValueFromJson<T>(string name)
{
var value = GetValue(name, values => values.FirstOrDefault());
return string.IsNullOrWhiteSpace(value) ? default : JsonUtils.DeserializeObject<T>(value);
}
}

View File

@@ -0,0 +1,29 @@
// Copyright © WireMock.Net
using JetBrains.Annotations;
namespace WireMock.Settings;
/// <summary>
/// WebProxySettings
/// </summary>
public class WebProxySettings
{
/// <summary>
/// A string instance that contains the address of the proxy server.
/// </summary>
[PublicAPI]
public string Address { get; set; } = null!;
/// <summary>
/// The username associated with the credentials.
/// </summary>
[PublicAPI]
public string? UserName { get; set; }
/// <summary>
/// The password for the username associated with the credentials.
/// </summary>
[PublicAPI]
public string? Password { get; set; }
}

View File

@@ -0,0 +1,24 @@
// Copyright © WireMock.Net
using System.Collections.Generic;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Settings;
/// <summary>
/// WebhookSettings
/// </summary>
public class WebhookSettings : HttpClientSettings
{
/// <summary>
/// Executes an action after the transformation of the request body.
/// </summary>
/// <param name="mapping">The mapping used for the request.</param>
/// <param name="requestUrl">The request Url.</param>
/// <param name="bodyData">The body data of the request. [Optional]</param>
/// <param name="headers">The headers of the request. [Optional]</param>
public virtual void PostTransform(IMapping mapping, string requestUrl, IBodyData? bodyData = null, IDictionary<string, WireMockList<string>>? headers = null)
{
}
}

View File

@@ -0,0 +1,55 @@
// Copyright © WireMock.Net
using JetBrains.Annotations;
namespace WireMock.Settings;
/// <summary>
/// If https is used, these settings can be used to configure the CertificateSettings in case a custom certificate instead the default .NET certificate should be used.
///
/// X509StoreName and X509StoreLocation should be defined
/// OR
/// X509CertificateFilePath and X509CertificatePassword should be defined
/// </summary>
public class WireMockCertificateSettings
{
/// <summary>
/// X509 StoreName (AddressBook, AuthRoot, CertificateAuthority, My, Root, TrustedPeople or TrustedPublisher)
/// </summary>
[PublicAPI]
public string? X509StoreName { get; set; }
/// <summary>
/// X509 StoreLocation (CurrentUser or LocalMachine)
/// </summary>
[PublicAPI]
public string? X509StoreLocation { get; set; }
/// <summary>
/// X509 Thumbprint or SubjectName (if not defined, the 'host' is used)
/// </summary>
[PublicAPI]
public string? X509StoreThumbprintOrSubjectName { get; set; }
/// <summary>
/// X509Certificate FilePath
/// </summary>
[PublicAPI]
public string? X509CertificateFilePath { get; set; }
/// <summary>
/// X509Certificate Password
/// </summary>
[PublicAPI]
public string? X509CertificatePassword { get; set; }
/// <summary>
/// X509StoreName and X509StoreLocation should be defined
/// OR
/// X509CertificateFilePath and X509CertificatePassword should be defined
/// </summary>
[PublicAPI]
public bool IsDefined =>
!string.IsNullOrEmpty(X509StoreName) && !string.IsNullOrEmpty(X509StoreLocation) ||
!string.IsNullOrEmpty(X509CertificateFilePath);
}

View File

@@ -0,0 +1,338 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using HandlebarsDotNet;
using JetBrains.Annotations;
using Newtonsoft.Json;
using WireMock.Admin.Mappings;
using WireMock.Handlers;
using WireMock.Logging;
using WireMock.Matchers;
using WireMock.RegularExpressions;
using WireMock.Types;
using System.Globalization;
using WireMock.Models;
#if USE_ASPNETCORE
using Microsoft.Extensions.DependencyInjection;
#endif
namespace WireMock.Settings;
/// <summary>
/// WireMockServerSettings
/// </summary>
public class WireMockServerSettings
{
internal const int DefaultStartTimeout = 10000;
/// <summary>
/// Gets or sets the http port.
/// </summary>
[PublicAPI]
public int? Port { get; set; }
/// <summary>
/// Gets or sets the use SSL.
/// </summary>
// ReSharper disable once InconsistentNaming
[PublicAPI]
public bool? UseSSL { get; set; }
/// <summary>
/// Defines on which scheme (http/https) to host. (This overrides the <c>UseSSL</c> value).
/// </summary>
[PublicAPI]
public HostingScheme? HostingScheme { get; set; }
/// <summary>
/// Gets or sets to use HTTP 2 (used for Grpc).
/// </summary>
[PublicAPI]
public bool? UseHttp2 { get; set; }
/// <summary>
/// Gets or sets whether to start admin interface.
/// </summary>
[PublicAPI]
public bool? StartAdminInterface { get; set; }
/// <summary>
/// Gets or sets if the static mappings should be read at startup.
/// </summary>
[PublicAPI]
public bool? ReadStaticMappings { get; set; }
/// <summary>
/// Watch the static mapping files + folder for changes when running.
/// </summary>
[PublicAPI]
public bool? WatchStaticMappings { get; set; }
/// <summary>
/// A value indicating whether subdirectories within the static mappings path should be monitored.
/// </summary>
[PublicAPI]
public bool? WatchStaticMappingsInSubdirectories { get; set; }
/// <summary>
/// Gets or sets if the proxy and record settings.
/// </summary>
[PublicAPI]
public ProxyAndRecordSettings? ProxyAndRecordSettings { get; set; }
/// <summary>
/// Gets or sets the urls.
/// </summary>
[PublicAPI]
public string[]? Urls { get; set; }
/// <summary>
/// StartTimeout
/// </summary>
[PublicAPI]
public int StartTimeout { get; set; } = DefaultStartTimeout;
/// <summary>
/// Allow Partial Mapping (default set to false).
/// </summary>
[PublicAPI]
public bool? AllowPartialMapping { get; set; }
/// <summary>
/// The username needed for __admin access.
/// </summary>
[PublicAPI]
public string? AdminUsername { get; set; }
/// <summary>
/// The password needed for __admin access.
/// </summary>
[PublicAPI]
public string? AdminPassword { get; set; }
/// <summary>
/// The AzureAD Tenant needed for __admin access.
/// </summary>
[PublicAPI]
public string? AdminAzureADTenant { get; set; }
/// <summary>
/// The AzureAD Audience / Resource for __admin access.
/// </summary>
[PublicAPI]
public string? AdminAzureADAudience { get; set; }
/// <summary>
/// The RequestLog expiration in hours (optional).
/// </summary>
[PublicAPI]
public int? RequestLogExpirationDuration { get; set; }
/// <summary>
/// The MaxRequestLog count (optional).
/// </summary>
[PublicAPI]
public int? MaxRequestLogCount { get; set; }
/// <summary>
/// Action which is called (with the IAppBuilder or IApplicationBuilder) before the internal WireMockMiddleware is initialized. [Optional]
/// </summary>
[PublicAPI]
[JsonIgnore]
public Action<object>? PreWireMockMiddlewareInit { get; set; }
/// <summary>
/// Action which is called (with the IAppBuilder or IApplicationBuilder) after the internal WireMockMiddleware is initialized. [Optional]
/// </summary>
[PublicAPI]
[JsonIgnore]
public Action<object>? PostWireMockMiddlewareInit { get; set; }
#if USE_ASPNETCORE
/// <summary>
/// Action which is called with IServiceCollection when ASP.NET Core DI is being configured. [Optional]
/// </summary>
[PublicAPI]
[JsonIgnore]
public Action<IServiceCollection>? AdditionalServiceRegistration { get; set; }
/// <summary>
/// Policies to use when using CORS. By default CORS is disabled. [Optional]
/// </summary>
[PublicAPI]
public CorsPolicyOptions? CorsPolicyOptions { get; set; }
#endif
/// <summary>
/// The IWireMockLogger which logs Debug, Info, Warning or Error
/// </summary>
[PublicAPI]
[JsonIgnore]
public IWireMockLogger Logger { get; set; } = null!;
/// <summary>
/// Handler to interact with the file system to read and write static mapping files.
/// </summary>
[PublicAPI]
[JsonIgnore]
public IFileSystemHandler FileSystemHandler { get; set; } = null!;
/// <summary>
/// Action which can be used to add additional Handlebars registrations. [Optional]
/// </summary>
[PublicAPI]
[JsonIgnore]
public Action<IHandlebars, IFileSystemHandler>? HandlebarsRegistrationCallback { get; set; }
/// <summary>
/// Allow the usage of CSharpCodeMatcher (default is not allowed).
/// </summary>
[PublicAPI]
public bool? AllowCSharpCodeMatcher { get; set; }
/// <summary>
/// Allow a Body for all HTTP Methods. (default set to <c>false</c>).
/// </summary>
[PublicAPI]
public bool? AllowBodyForAllHttpMethods { get; set; }
/// <summary>
/// Allow only a HttpStatus Code in the response which is defined. (default set to <c>false</c>).
/// - false : also null, 0, empty or invalid HttpStatus codes are allowed.
/// - true : only codes defined in <see cref="System.Net.HttpStatusCode"/> are allowed.
/// </summary>
[PublicAPI]
public bool? AllowOnlyDefinedHttpStatusCodeInResponse { get; set; }
/// <summary>
/// Set to true to disable Json deserialization when processing requests. (default set to <c>false</c>).
/// </summary>
[PublicAPI]
public bool? DisableJsonBodyParsing { get; set; }
/// <summary>
/// Disable support for GZip and Deflate request body decompression. (default set to <c>false</c>).
/// </summary>
[PublicAPI]
public bool? DisableRequestBodyDecompressing { get; set; }
/// <summary>
/// Set to true to disable FormUrlEncoded deserializing when processing requests. (default set to <c>false</c>).
/// </summary>
[PublicAPI]
public bool? DisableDeserializeFormUrlEncoded { get; set; }
/// <summary>
/// Handle all requests synchronously. (default set to <c>false</c>).
/// </summary>
[PublicAPI]
public bool? HandleRequestsSynchronously { get; set; }
/// <summary>
/// If https is used, these settings can be used to configure the CertificateSettings in case a custom certificate instead the default .NET certificate should be used.
///
/// X509StoreName and X509StoreLocation should be defined
/// OR
/// X509CertificateFilePath and X509CertificatePassword should be defined
/// </summary>
[PublicAPI]
public WireMockCertificateSettings? CertificateSettings { get; set; }
/// <summary>
/// Defines if custom CertificateSettings are defined
/// </summary>
[PublicAPI]
public bool CustomCertificateDefined => CertificateSettings?.IsDefined == true;
#if USE_ASPNETCORE
/// <summary>
/// Client certificate mode for the server
/// </summary>
[PublicAPI]
public ClientCertificateMode ClientCertificateMode { get; set; }
/// <summary>
/// Whether to accept any client certificate
/// </summary>
public bool AcceptAnyClientCertificate { get; set; }
#endif
/// <summary>
/// Defines the global IWebhookSettings to use.
/// </summary>
[PublicAPI]
public WebhookSettings? WebhookSettings { get; set; }
/// <summary>
/// Use the <see cref="RegexExtended"/> instead of the default <see cref="Regex"/> (default set to <c>true</c>).
/// </summary>
[PublicAPI]
public bool? UseRegexExtended { get; set; } = true;
/// <summary>
/// Save unmatched requests to a file using the <see cref="IFileSystemHandler"/> (default set to <c>false</c>).
/// </summary>
[PublicAPI]
public bool? SaveUnmatchedRequests { get; set; }
/// <summary>
/// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to <c>false</c>).
/// </summary>
[PublicAPI]
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
/// <summary>
/// See <seealso cref="QueryParameterMultipleValueSupport"/>.
///
/// Default value = "All".
/// </summary>
[PublicAPI]
public QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
/// <summary>
/// Custom matcher mappings for static mappings
/// </summary>
[PublicAPI, JsonIgnore]
public IDictionary<string, Func<MatcherModel, IMatcher>>? CustomMatcherMappings { get; set; }
/// <summary>
/// The <see cref="JsonSerializerSettings"/> used when the JSON response is generated.
/// </summary>
[PublicAPI, JsonIgnore]
public JsonSerializerSettings? JsonSerializerSettings { get; set; }
/// <summary>
/// The Culture to use.
/// Currently used for:
/// - Handlebars Transformer
/// </summary>
[JsonIgnore]
public CultureInfo Culture { get; set; } = CultureInfo.CurrentCulture;
/// <summary>
/// A list of Grpc ProtoDefinitions which can be used.
/// </summary>
[PublicAPI]
public Dictionary<string, string[]>? ProtoDefinitions { get; set; }
/// <summary>
/// A list of GraphQL Schemas which can be used.
/// </summary>
[PublicAPI]
public Dictionary<string, GraphQLSchemaDetails>? GraphQLSchemas { get; set; }
/// <summary>
/// The admin path to use for accessing the Admin REST interface.
/// If not set <c>__/admin</c> is used.
/// </summary>
[PublicAPI]
public string? AdminPath { get; set; }
/// <summary>
/// Defines the additional Handlebars Settings.
/// </summary>
[PublicAPI]
public HandlebarsSettings? HandlebarsSettings { get; set; }
}

View File

@@ -0,0 +1,216 @@
// Copyright © WireMock.Net
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using JetBrains.Annotations;
using Stef.Validation;
using WireMock.Logging;
using WireMock.Models;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Settings;
/// <summary>
/// A static helper class to parse commandline arguments into WireMockServerSettings.
/// </summary>
public static class WireMockServerSettingsParser
{
/// <summary>
/// Parse commandline arguments into WireMockServerSettings.
/// </summary>
/// <param name="args">The commandline arguments</param>
/// <param name="environment">The environment settings (optional)</param>
/// <param name="logger">The logger (optional, can be null)</param>
/// <param name="settings">The parsed settings</param>
[PublicAPI]
public static bool TryParseArguments(string[] args, IDictionary? environment, [NotNullWhen(true)] out WireMockServerSettings? settings, IWireMockLogger? logger = null)
{
Guard.HasNoNulls(args);
var parser = new SimpleSettingsParser();
parser.Parse(args, environment);
if (parser.GetBoolSwitchValue("help"))
{
(logger ?? new WireMockConsoleLogger()).Info("See https://github.com/wiremock/WireMock.Net/wiki/WireMock-commandline-parameters for details on all commandline options.");
settings = null;
return false;
}
settings = new WireMockServerSettings
{
AdminAzureADAudience = parser.GetStringValue(nameof(WireMockServerSettings.AdminAzureADAudience)),
AdminAzureADTenant = parser.GetStringValue(nameof(WireMockServerSettings.AdminAzureADTenant)),
AdminPassword = parser.GetStringValue(nameof(WireMockServerSettings.AdminPassword)),
AdminUsername = parser.GetStringValue(nameof(WireMockServerSettings.AdminUsername)),
AdminPath = parser.GetStringValue(nameof(WireMockServerSettings.AdminPath), "/__admin"),
AllowBodyForAllHttpMethods = parser.GetBoolValue(nameof(WireMockServerSettings.AllowBodyForAllHttpMethods)),
AllowCSharpCodeMatcher = parser.GetBoolValue(nameof(WireMockServerSettings.AllowCSharpCodeMatcher)),
AllowOnlyDefinedHttpStatusCodeInResponse = parser.GetBoolValue(nameof(WireMockServerSettings.AllowOnlyDefinedHttpStatusCodeInResponse)),
AllowPartialMapping = parser.GetBoolValue(nameof(WireMockServerSettings.AllowPartialMapping)),
Culture = parser.GetValue(nameof(WireMockServerSettings.Culture), strings => CultureInfoUtils.Parse(strings.FirstOrDefault()), CultureInfo.CurrentCulture),
DisableJsonBodyParsing = parser.GetBoolValue(nameof(WireMockServerSettings.DisableJsonBodyParsing)),
DisableRequestBodyDecompressing = parser.GetBoolValue(nameof(WireMockServerSettings.DisableRequestBodyDecompressing)),
DisableDeserializeFormUrlEncoded = parser.GetBoolValue(nameof(WireMockServerSettings.DisableDeserializeFormUrlEncoded)),
DoNotSaveDynamicResponseInLogEntry = parser.GetBoolValue(nameof(WireMockServerSettings.DoNotSaveDynamicResponseInLogEntry)),
GraphQLSchemas = parser.GetObjectValueFromJson<Dictionary<string, GraphQLSchemaDetails>>(nameof(settings.GraphQLSchemas)),
HandleRequestsSynchronously = parser.GetBoolValue(nameof(WireMockServerSettings.HandleRequestsSynchronously)),
HostingScheme = parser.GetEnumValue<HostingScheme>(nameof(WireMockServerSettings.HostingScheme)),
MaxRequestLogCount = parser.GetIntValue(nameof(WireMockServerSettings.MaxRequestLogCount)),
ProtoDefinitions = parser.GetObjectValueFromJson<Dictionary<string, string[]>>(nameof(settings.ProtoDefinitions)),
QueryParameterMultipleValueSupport = parser.GetEnumValue<QueryParameterMultipleValueSupport>(nameof(WireMockServerSettings.QueryParameterMultipleValueSupport)),
ReadStaticMappings = parser.GetBoolValue(nameof(WireMockServerSettings.ReadStaticMappings)),
RequestLogExpirationDuration = parser.GetIntValue(nameof(WireMockServerSettings.RequestLogExpirationDuration)),
SaveUnmatchedRequests = parser.GetBoolValue(nameof(WireMockServerSettings.SaveUnmatchedRequests)),
StartAdminInterface = parser.GetBoolValue(nameof(WireMockServerSettings.StartAdminInterface), true),
StartTimeout = parser.GetIntValue(nameof(WireMockServerSettings.StartTimeout), WireMockServerSettings.DefaultStartTimeout),
UseHttp2 = parser.GetBoolValue(nameof(WireMockServerSettings.UseHttp2)),
UseRegexExtended = parser.GetBoolValue(nameof(WireMockServerSettings.UseRegexExtended), true),
WatchStaticMappings = parser.GetBoolValue(nameof(WireMockServerSettings.WatchStaticMappings)),
WatchStaticMappingsInSubdirectories = parser.GetBoolValue(nameof(WireMockServerSettings.WatchStaticMappingsInSubdirectories)),
};
#if USE_ASPNETCORE
settings.CorsPolicyOptions = parser.GetEnumValue(nameof(WireMockServerSettings.CorsPolicyOptions), CorsPolicyOptions.None);
settings.ClientCertificateMode = parser.GetEnumValue(nameof(WireMockServerSettings.ClientCertificateMode), ClientCertificateMode.NoCertificate);
settings.AcceptAnyClientCertificate = parser.GetBoolValue(nameof(WireMockServerSettings.AcceptAnyClientCertificate));
#endif
ParseLoggerSettings(settings, logger, parser);
ParsePortSettings(settings, parser);
ParseProxyAndRecordSettings(settings, parser);
ParseCertificateSettings(settings, parser);
ParseHandlebarsSettings(settings, parser);
return true;
}
private static void ParseLoggerSettings(WireMockServerSettings settings, IWireMockLogger? logger, SimpleSettingsParser parser)
{
var loggerType = parser.GetStringValue("WireMockLogger");
switch (loggerType)
{
case nameof(WireMockConsoleLogger):
settings.Logger = new WireMockConsoleLogger();
break;
case nameof(WireMockNullLogger):
settings.Logger = new WireMockNullLogger();
break;
default:
if (logger != null)
{
settings.Logger = logger;
}
break;
}
}
private static void ParseProxyAndRecordSettings(WireMockServerSettings settings, SimpleSettingsParser parser)
{
var proxyUrl = parser.GetStringValue("ProxyURL") ?? parser.GetStringValue("ProxyUrl");
if (!string.IsNullOrEmpty(proxyUrl))
{
var proxyAndRecordSettings = new ProxyAndRecordSettings
{
AllowAutoRedirect = parser.GetBoolValue(nameof(ProxyAndRecordSettings.AllowAutoRedirect)),
ClientX509Certificate2ThumbprintOrSubjectName = parser.GetStringValue(nameof(ProxyAndRecordSettings.ClientX509Certificate2ThumbprintOrSubjectName)),
ExcludedCookies = parser.GetValues(nameof(ProxyAndRecordSettings.ExcludedCookies)),
ExcludedHeaders = parser.GetValues(nameof(ProxyAndRecordSettings.ExcludedHeaders)),
// PreferProxyMapping = parser.GetBoolValue(nameof(ProxyAndRecordSettings.PreferProxyMapping)),
SaveMapping = parser.GetBoolValue(nameof(ProxyAndRecordSettings.SaveMapping)),
SaveMappingForStatusCodePattern = parser.GetStringValue(nameof(ProxyAndRecordSettings.SaveMappingForStatusCodePattern), "*"),
SaveMappingToFile = parser.GetBoolValue(nameof(ProxyAndRecordSettings.SaveMappingToFile)),
UseDefinedRequestMatchers = parser.GetBoolValue(nameof(ProxyAndRecordSettings.UseDefinedRequestMatchers)),
AppendGuidToSavedMappingFile = parser.GetBoolValue(nameof(ProxyAndRecordSettings.AppendGuidToSavedMappingFile)),
PrefixForSavedMappingFile = parser.GetStringValue(nameof(ProxyAndRecordSettings.PrefixForSavedMappingFile), ProxyAndRecordSettings.DefaultPrefixForSavedMappingFile),
Url = proxyUrl!,
SaveMappingSettings = new ProxySaveMappingSettings
{
StatusCodePattern = parser.GetStringValue(nameof(ProxyAndRecordSettings.SaveMappingForStatusCodePattern), "*"),
// HttpMethods = new ProxySaveMappingSetting<string[]>(parser.GetValues("DoNotSaveMappingForHttpMethods", new string[0]), MatchBehaviour.RejectOnMatch)
},
ProxyAll = parser.GetBoolValue(nameof(ProxyAndRecordSettings.ProxyAll))
};
ParseWebProxyAddressSettings(proxyAndRecordSettings, parser);
ParseProxyUrlReplaceSettings(proxyAndRecordSettings, parser);
settings.ProxyAndRecordSettings = proxyAndRecordSettings;
}
}
private static void ParsePortSettings(WireMockServerSettings settings, SimpleSettingsParser 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", ["http://*:9091/"]);
}
}
private static void ParseCertificateSettings(WireMockServerSettings settings, SimpleSettingsParser parser)
{
var certificateSettings = new WireMockCertificateSettings
{
X509StoreName = parser.GetStringValue("X509StoreName"),
X509StoreLocation = parser.GetStringValue("X509StoreLocation"),
X509StoreThumbprintOrSubjectName = parser.GetStringValue("X509StoreThumbprintOrSubjectName"),
X509CertificateFilePath = parser.GetStringValue("X509CertificateFilePath"),
X509CertificatePassword = parser.GetStringValue("X509CertificatePassword")
};
if (certificateSettings.IsDefined)
{
settings.CertificateSettings = certificateSettings;
}
}
private static void ParseHandlebarsSettings(WireMockServerSettings settings, SimpleSettingsParser parser)
{
if (parser.ContainsAny(nameof(HandlebarsSettings.AllowedCustomHandlebarsHelpers), nameof(HandlebarsSettings.AllowedHandlebarsHelpers)))
{
settings.HandlebarsSettings = new HandlebarsSettings
{
AllowedCustomHandlebarsHelpers = parser.GetEnumValue(nameof(HandlebarsSettings.AllowedCustomHandlebarsHelpers), CustomHandlebarsHelpers.None),
AllowedHandlebarsHelpers = parser.GetEnumValues(nameof(HandlebarsSettings.AllowedHandlebarsHelpers), HandlebarsSettings.DefaultAllowedHandlebarsHelpers)
};
}
}
private static void ParseWebProxyAddressSettings(ProxyAndRecordSettings settings, SimpleSettingsParser 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, SimpleSettingsParser 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
};
}
}
}