mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-24 01:08:28 +02:00
Fix Proxying when StartAdminInterface=true (#778)
* ProxyHelper fixes * . * more reformat * .
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SSL/@EntryIndexedValue">SSL</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SSL/@EntryIndexedValue">SSL</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=TE/@EntryIndexedValue">TE</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=TE/@EntryIndexedValue">TE</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=TSV/@EntryIndexedValue">TSV</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=TSV/@EntryIndexedValue">TSV</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=TTL/@EntryIndexedValue">TTL</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=WWW/@EntryIndexedValue">WWW</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=WWW/@EntryIndexedValue">WWW</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XMS/@EntryIndexedValue">XMS</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XMS/@EntryIndexedValue">XMS</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XUA/@EntryIndexedValue">XUA</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XUA/@EntryIndexedValue">XUA</s:String>
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ public class LogEntryModel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The mapping unique title.
|
/// The mapping unique title.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string MappingTitle { get; set; }
|
public string? MappingTitle { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The request match result.
|
/// The request match result.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public LogRequestMatchModel RequestMatchResult { get; set; }
|
public LogRequestMatchModel? RequestMatchResult { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The partial mapping unique identifier.
|
/// The partial mapping unique identifier.
|
||||||
@@ -45,10 +45,10 @@ public class LogEntryModel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The partial mapping unique title.
|
/// The partial mapping unique title.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string PartialMappingTitle { get; set; }
|
public string? PartialMappingTitle { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The partial request match result.
|
/// The partial request match result.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public LogRequestMatchModel PartialRequestMatchResult { get; set; }
|
public LogRequestMatchModel? PartialRequestMatchResult { get; set; }
|
||||||
}
|
}
|
||||||
@@ -63,12 +63,12 @@ public interface IRequestMessage
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the headers.
|
/// Gets the headers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IDictionary<string, WireMockList<string>>? Headers { get; }
|
IDictionary<string, WireMockList<string>> Headers { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the cookies.
|
/// Gets the cookies.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IDictionary<string, string>? Cookies { get; }
|
IDictionary<string, string> Cookies { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the query.
|
/// Gets the query.
|
||||||
|
|||||||
@@ -1,25 +1,24 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace WireMock.Models
|
namespace WireMock.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// TimeSettings: Start, End and TTL
|
||||||
|
/// </summary>
|
||||||
|
public interface ITimeSettings
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// TimeSettings: Start, End and TTL
|
/// Gets or sets the DateTime from which this mapping should be used. In case this is not defined, it's used (default behavior).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ITimeSettings
|
DateTime? Start { get; set; }
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the DateTime from which this mapping should be used. In case this is not defined, it's used (default behavior).
|
|
||||||
/// </summary>
|
|
||||||
DateTime? Start { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the DateTime from until this mapping should be used. In case this is not defined, it's used forever (default behavior).
|
/// Gets or sets the DateTime from until this mapping should be used. In case this is not defined, it's used forever (default behavior).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
DateTime? End { get; set; }
|
DateTime? End { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the TTL (Time To Live) in seconds for this mapping. In case this is not defined, it's used (default behavior).
|
/// Gets or sets the TTL (Time To Live) in seconds for this mapping. In case this is not defined, it's used (default behavior).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int? TTL { get; set; }
|
int? TTL { get; set; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,35 +1,34 @@
|
|||||||
using System;
|
using System;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
|
|
||||||
namespace WireMock.Extensions
|
namespace WireMock.Extensions;
|
||||||
|
|
||||||
|
internal static class TimeSettingsExtensions
|
||||||
{
|
{
|
||||||
internal static class TimeSettingsExtensions
|
public static bool IsValid(this ITimeSettings? settings)
|
||||||
{
|
{
|
||||||
public static bool IsValid([CanBeNull] this ITimeSettings settings)
|
if (settings == null)
|
||||||
{
|
{
|
||||||
if (settings == null)
|
return true;
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var now = DateTime.Now;
|
|
||||||
var start = settings.Start != null ? settings.Start.Value : now;
|
|
||||||
DateTime end;
|
|
||||||
if (settings.End != null)
|
|
||||||
{
|
|
||||||
end = settings.End.Value;
|
|
||||||
}
|
|
||||||
else if (settings.TTL != null)
|
|
||||||
{
|
|
||||||
end = start.AddSeconds(settings.TTL.Value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
end = DateTime.MaxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return now >= start && now <= end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var now = DateTime.Now;
|
||||||
|
var start = settings.Start ?? now;
|
||||||
|
DateTime end;
|
||||||
|
|
||||||
|
if (settings.End != null)
|
||||||
|
{
|
||||||
|
end = settings.End.Value;
|
||||||
|
}
|
||||||
|
else if (settings.TTL != null)
|
||||||
|
{
|
||||||
|
end = start.AddSeconds(settings.TTL.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
end = DateTime.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return now >= start && now <= end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,22 +20,22 @@ public interface IMapping
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the TimeSettings (Start, End and TTL).
|
/// Gets the TimeSettings (Start, End and TTL).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ITimeSettings TimeSettings { get; }
|
ITimeSettings? TimeSettings { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the unique title.
|
/// Gets the unique title.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string Title { get; }
|
string? Title { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the description.
|
/// Gets the description.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string Description { get; }
|
string? Description { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The full filename path for this mapping (only defined for static mappings).
|
/// The full filename path for this mapping (only defined for static mappings).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string Path { get; set; }
|
string? Path { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the priority. (A low value means higher priority.)
|
/// Gets the priority. (A low value means higher priority.)
|
||||||
|
|||||||
@@ -1,149 +1,147 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
using WireMock.ResponseProviders;
|
using WireMock.ResponseProviders;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
|
|
||||||
namespace WireMock
|
namespace WireMock;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Mapping.
|
||||||
|
/// </summary>
|
||||||
|
public class Mapping : IMapping
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Guid Guid { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string? Title { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string? Description { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string? Path { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int Priority { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string? Scenario { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string? ExecutionConditionState { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string? NextState { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int? StateTimes { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IRequestMatcher RequestMatcher { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IResponseProvider Provider { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public WireMockServerSettings Settings { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsStartState => Scenario == null || Scenario != null && NextState != null && ExecutionConditionState == null;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsAdminInterface => Provider is DynamicResponseProvider or DynamicAsyncResponseProvider or ProxyAsyncResponseProvider;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsProxy => Provider is ProxyAsyncResponseProvider;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool LogMapping => Provider is not (DynamicResponseProvider or DynamicAsyncResponseProvider);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IWebhook[]? Webhooks { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ITimeSettings? TimeSettings { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Mapping.
|
/// Initializes a new instance of the <see cref="Mapping"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Mapping : IMapping
|
/// <param name="guid">The unique identifier.</param>
|
||||||
|
/// <param name="title">The unique title (can be null).</param>
|
||||||
|
/// <param name="description">The description (can be null).</param>
|
||||||
|
/// <param name="path">The full file path from this mapping title (can be null).</param>
|
||||||
|
/// <param name="settings">The WireMockServerSettings.</param>
|
||||||
|
/// <param name="requestMatcher">The request matcher.</param>
|
||||||
|
/// <param name="provider">The provider.</param>
|
||||||
|
/// <param name="priority">The priority for this mapping.</param>
|
||||||
|
/// <param name="scenario">The scenario. [Optional]</param>
|
||||||
|
/// <param name="executionConditionState">State in which the current mapping can occur. [Optional]</param>
|
||||||
|
/// <param name="nextState">The next state which will occur after the current mapping execution. [Optional]</param>
|
||||||
|
/// <param name="stateTimes">Only when the current state is executed this number, the next state which will occur. [Optional]</param>
|
||||||
|
/// <param name="webhooks">The Webhooks. [Optional]</param>
|
||||||
|
/// <param name="timeSettings">The TimeSettings. [Optional]</param>
|
||||||
|
public Mapping(
|
||||||
|
Guid guid,
|
||||||
|
string? title,
|
||||||
|
string? description,
|
||||||
|
string? path,
|
||||||
|
WireMockServerSettings settings,
|
||||||
|
IRequestMatcher requestMatcher,
|
||||||
|
IResponseProvider provider,
|
||||||
|
int priority,
|
||||||
|
string? scenario,
|
||||||
|
string? executionConditionState,
|
||||||
|
string? nextState,
|
||||||
|
int? stateTimes,
|
||||||
|
IWebhook[]? webhooks,
|
||||||
|
ITimeSettings? timeSettings)
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
Guid = guid;
|
||||||
public Guid Guid { get; }
|
Title = title;
|
||||||
|
Description = description;
|
||||||
|
Path = path;
|
||||||
|
Settings = settings;
|
||||||
|
RequestMatcher = requestMatcher;
|
||||||
|
Provider = provider;
|
||||||
|
Priority = priority;
|
||||||
|
Scenario = scenario;
|
||||||
|
ExecutionConditionState = executionConditionState;
|
||||||
|
NextState = nextState;
|
||||||
|
StateTimes = stateTimes;
|
||||||
|
Webhooks = webhooks;
|
||||||
|
TimeSettings = timeSettings;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />
|
||||||
public string Title { get; }
|
public Task<(IResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(IRequestMessage requestMessage)
|
||||||
|
{
|
||||||
|
return Provider.ProvideResponseAsync(requestMessage, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc cref="IMapping.GetRequestMatchResult" />
|
||||||
public string Description { get; }
|
public IRequestMatchResult GetRequestMatchResult(IRequestMessage requestMessage, string? nextState)
|
||||||
|
{
|
||||||
|
var result = new RequestMatchResult();
|
||||||
|
|
||||||
/// <inheritdoc />
|
RequestMatcher.GetMatchingScore(requestMessage, result);
|
||||||
public string Path { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
// Only check state if Scenario is defined
|
||||||
public int Priority { get; }
|
if (Scenario != null)
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Scenario { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string ExecutionConditionState { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string NextState { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public int? StateTimes { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IRequestMatcher RequestMatcher { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IResponseProvider Provider { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public WireMockServerSettings Settings { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool IsStartState => Scenario == null || Scenario != null && NextState != null && ExecutionConditionState == null;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool IsAdminInterface => Provider is DynamicResponseProvider || Provider is DynamicAsyncResponseProvider || Provider is ProxyAsyncResponseProvider;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool IsProxy => Provider is ProxyAsyncResponseProvider;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool LogMapping => !(Provider is DynamicResponseProvider || Provider is DynamicAsyncResponseProvider);
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IWebhook[] Webhooks { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public ITimeSettings TimeSettings { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Mapping"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="guid">The unique identifier.</param>
|
|
||||||
/// <param name="title">The unique title (can be null).</param>
|
|
||||||
/// <param name="description">The description (can be null).</param>
|
|
||||||
/// <param name="path">The full file path from this mapping title (can be null).</param>
|
|
||||||
/// <param name="settings">The WireMockServerSettings.</param>
|
|
||||||
/// <param name="requestMatcher">The request matcher.</param>
|
|
||||||
/// <param name="provider">The provider.</param>
|
|
||||||
/// <param name="priority">The priority for this mapping.</param>
|
|
||||||
/// <param name="scenario">The scenario. [Optional]</param>
|
|
||||||
/// <param name="executionConditionState">State in which the current mapping can occur. [Optional]</param>
|
|
||||||
/// <param name="nextState">The next state which will occur after the current mapping execution. [Optional]</param>
|
|
||||||
/// <param name="stateTimes">Only when the current state is executed this number, the next state which will occur. [Optional]</param>
|
|
||||||
/// <param name="webhooks">The Webhooks. [Optional]</param>
|
|
||||||
/// <param name="timeSettings">The TimeSettings. [Optional]</param>
|
|
||||||
public Mapping(
|
|
||||||
Guid guid,
|
|
||||||
string? title,
|
|
||||||
string? description,
|
|
||||||
string? path,
|
|
||||||
WireMockServerSettings settings,
|
|
||||||
IRequestMatcher requestMatcher,
|
|
||||||
IResponseProvider provider,
|
|
||||||
int priority,
|
|
||||||
string? scenario,
|
|
||||||
string? executionConditionState,
|
|
||||||
string? nextState,
|
|
||||||
int? stateTimes,
|
|
||||||
IWebhook[]? webhooks,
|
|
||||||
ITimeSettings? timeSettings)
|
|
||||||
{
|
{
|
||||||
Guid = guid;
|
var matcher = new RequestMessageScenarioAndStateMatcher(nextState, ExecutionConditionState);
|
||||||
Title = title;
|
matcher.GetMatchingScore(requestMessage, result);
|
||||||
Description = description;
|
//// If ExecutionConditionState is null, this means that request is the start from a scenario. So just return.
|
||||||
Path = path;
|
//if (ExecutionConditionState != null)
|
||||||
Settings = settings;
|
//{
|
||||||
RequestMatcher = requestMatcher;
|
// // ExecutionConditionState is not null, so get score for matching with the nextState.
|
||||||
Provider = provider;
|
// var matcher = new RequestMessageScenarioAndStateMatcher(nextState, ExecutionConditionState);
|
||||||
Priority = priority;
|
// matcher.GetMatchingScore(requestMessage, result);
|
||||||
Scenario = scenario;
|
//}
|
||||||
ExecutionConditionState = executionConditionState;
|
|
||||||
NextState = nextState;
|
|
||||||
StateTimes = stateTimes;
|
|
||||||
Webhooks = webhooks;
|
|
||||||
TimeSettings = timeSettings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />
|
return result;
|
||||||
public Task<(IResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(IRequestMessage requestMessage)
|
|
||||||
{
|
|
||||||
return Provider.ProvideResponseAsync(requestMessage, Settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IMapping.GetRequestMatchResult" />
|
|
||||||
public IRequestMatchResult GetRequestMatchResult(IRequestMessage requestMessage, string nextState)
|
|
||||||
{
|
|
||||||
var result = new RequestMatchResult();
|
|
||||||
|
|
||||||
RequestMatcher.GetMatchingScore(requestMessage, result);
|
|
||||||
|
|
||||||
// Only check state if Scenario is defined
|
|
||||||
if (Scenario != null)
|
|
||||||
{
|
|
||||||
var matcher = new RequestMessageScenarioAndStateMatcher(nextState, ExecutionConditionState);
|
|
||||||
matcher.GetMatchingScore(requestMessage, result);
|
|
||||||
//// If ExecutionConditionState is null, this means that request is the start from a scenario. So just return.
|
|
||||||
//if (ExecutionConditionState != null)
|
|
||||||
//{
|
|
||||||
// // ExecutionConditionState is not null, so get score for matching with the nextState.
|
|
||||||
// var matcher = new RequestMessageScenarioAndStateMatcher(nextState, ExecutionConditionState);
|
|
||||||
// matcher.GetMatchingScore(requestMessage, result);
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,46 +1,41 @@
|
|||||||
using JetBrains.Annotations;
|
namespace WireMock.Matchers.Request;
|
||||||
|
|
||||||
namespace WireMock.Matchers.Request
|
/// <summary>
|
||||||
|
/// The scenario and state matcher.
|
||||||
|
/// </summary>
|
||||||
|
internal class RequestMessageScenarioAndStateMatcher : IRequestMatcher
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The scenario and state matcher.
|
/// Execution state condition for the current mapping.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class RequestMessageScenarioAndStateMatcher : IRequestMatcher
|
private readonly string? _executionConditionState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The next state which will be signaled after the current mapping execution.
|
||||||
|
/// In case the value is null state will not be changed.
|
||||||
|
/// </summary>
|
||||||
|
private readonly string? _nextState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RequestMessageScenarioAndStateMatcher"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nextState">The next state.</param>
|
||||||
|
/// <param name="executionConditionState">Execution state condition for the current mapping.</param>
|
||||||
|
public RequestMessageScenarioAndStateMatcher(string? nextState, string? executionConditionState)
|
||||||
{
|
{
|
||||||
/// <summary>
|
_nextState = nextState;
|
||||||
/// Execution state condition for the current mapping.
|
_executionConditionState = executionConditionState;
|
||||||
/// </summary>
|
}
|
||||||
[CanBeNull]
|
|
||||||
private readonly string _executionConditionState;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// The next state which will be signaled after the current mapping execution.
|
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||||
/// In case the value is null state will not be changed.
|
{
|
||||||
/// </summary>
|
double score = IsMatch();
|
||||||
[CanBeNull]
|
return requestMatchResult.AddScore(GetType(), score);
|
||||||
private readonly string _nextState;
|
}
|
||||||
|
|
||||||
/// <summary>
|
private double IsMatch()
|
||||||
/// Initializes a new instance of the <see cref="RequestMessageScenarioAndStateMatcher"/> class.
|
{
|
||||||
/// </summary>
|
return Equals(_executionConditionState, _nextState) ? MatchScores.Perfect : MatchScores.Mismatch;
|
||||||
/// <param name="nextState">The next state.</param>
|
|
||||||
/// <param name="executionConditionState">Execution state condition for the current mapping.</param>
|
|
||||||
public RequestMessageScenarioAndStateMatcher([CanBeNull] string nextState, [CanBeNull] string executionConditionState)
|
|
||||||
{
|
|
||||||
_nextState = nextState;
|
|
||||||
_executionConditionState = executionConditionState;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
|
||||||
{
|
|
||||||
double score = IsMatch();
|
|
||||||
return requestMatchResult.AddScore(GetType(), score);
|
|
||||||
}
|
|
||||||
|
|
||||||
private double IsMatch()
|
|
||||||
{
|
|
||||||
return Equals(_executionConditionState, _nextState) ? MatchScores.Perfect : MatchScores.Mismatch;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,45 +3,44 @@ using Microsoft.AspNetCore.Builder;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
|
|
||||||
namespace WireMock.Owin
|
namespace WireMock.Owin;
|
||||||
|
|
||||||
|
internal partial class AspNetCoreSelfHost
|
||||||
{
|
{
|
||||||
internal partial class AspNetCoreSelfHost
|
public void AddCors(IServiceCollection services)
|
||||||
{
|
{
|
||||||
public void AddCors(IServiceCollection services)
|
if (_wireMockMiddlewareOptions.CorsPolicyOptions > CorsPolicyOptions.None)
|
||||||
{
|
{
|
||||||
if (_wireMockMiddlewareOptions.CorsPolicyOptions > CorsPolicyOptions.None)
|
/* https://stackoverflow.com/questions/31942037/how-to-enable-cors-in-asp-net-core */
|
||||||
{
|
/* Enable Cors */
|
||||||
/* https://stackoverflow.com/questions/31942037/how-to-enable-cors-in-asp-net-core */
|
services.AddCors(corsOptions => corsOptions
|
||||||
/* Enable Cors */
|
.AddPolicy(CorsPolicyName,
|
||||||
services.AddCors(corsOptions => corsOptions
|
corsPolicyBuilder =>
|
||||||
.AddPolicy(CorsPolicyName,
|
{
|
||||||
corsPolicyBuilder =>
|
if (_wireMockMiddlewareOptions.CorsPolicyOptions.Value.HasFlag(CorsPolicyOptions.AllowAnyHeader))
|
||||||
{
|
{
|
||||||
if (_wireMockMiddlewareOptions.CorsPolicyOptions.Value.HasFlag(CorsPolicyOptions.AllowAnyHeader))
|
corsPolicyBuilder.AllowAnyHeader();
|
||||||
{
|
}
|
||||||
corsPolicyBuilder.AllowAnyHeader();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_wireMockMiddlewareOptions.CorsPolicyOptions.Value.HasFlag(CorsPolicyOptions.AllowAnyMethod))
|
if (_wireMockMiddlewareOptions.CorsPolicyOptions.Value.HasFlag(CorsPolicyOptions.AllowAnyMethod))
|
||||||
{
|
{
|
||||||
corsPolicyBuilder.AllowAnyMethod();
|
corsPolicyBuilder.AllowAnyMethod();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_wireMockMiddlewareOptions.CorsPolicyOptions.Value.HasFlag(CorsPolicyOptions.AllowAnyOrigin))
|
if (_wireMockMiddlewareOptions.CorsPolicyOptions.Value.HasFlag(CorsPolicyOptions.AllowAnyOrigin))
|
||||||
{
|
{
|
||||||
corsPolicyBuilder.AllowAnyOrigin();
|
corsPolicyBuilder.AllowAnyOrigin();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void UseCors(IApplicationBuilder appBuilder)
|
public void UseCors(IApplicationBuilder appBuilder)
|
||||||
|
{
|
||||||
|
if (_wireMockMiddlewareOptions.CorsPolicyOptions > CorsPolicyOptions.None)
|
||||||
{
|
{
|
||||||
if (_wireMockMiddlewareOptions.CorsPolicyOptions > CorsPolicyOptions.None)
|
/* Use Cors */
|
||||||
{
|
appBuilder.UseCors(CorsPolicyName);
|
||||||
/* Use Cors */
|
|
||||||
appBuilder.UseCors(CorsPolicyName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,10 +36,10 @@ namespace WireMock.Owin
|
|||||||
|
|
||||||
public Exception RunningException => _runningException;
|
public Exception RunningException => _runningException;
|
||||||
|
|
||||||
public AspNetCoreSelfHost([NotNull] IWireMockMiddlewareOptions wireMockMiddlewareOptions, [NotNull] HostUrlOptions urlOptions)
|
public AspNetCoreSelfHost(IWireMockMiddlewareOptions wireMockMiddlewareOptions, HostUrlOptions urlOptions)
|
||||||
{
|
{
|
||||||
Guard.NotNull(wireMockMiddlewareOptions, nameof(wireMockMiddlewareOptions));
|
Guard.NotNull(wireMockMiddlewareOptions);
|
||||||
Guard.NotNull(urlOptions, nameof(urlOptions));
|
Guard.NotNull(urlOptions);
|
||||||
|
|
||||||
_logger = wireMockMiddlewareOptions.Logger ?? new WireMockConsoleLogger();
|
_logger = wireMockMiddlewareOptions.Logger ?? new WireMockConsoleLogger();
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ namespace WireMock.Owin
|
|||||||
{
|
{
|
||||||
Urls.Add(address.Replace("0.0.0.0", "localhost").Replace("[::]", "localhost"));
|
Urls.Add(address.Replace("0.0.0.0", "localhost").Replace("[::]", "localhost"));
|
||||||
|
|
||||||
PortUtils.TryExtract(address, out bool isHttps, out string protocol, out string host, out int port);
|
PortUtils.TryExtract(address, out _, out _, out _, out int port);
|
||||||
Ports.Add(port);
|
Ports.Add(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
namespace WireMock.Owin
|
namespace WireMock.Owin;
|
||||||
|
|
||||||
|
internal struct HostUrlDetails
|
||||||
{
|
{
|
||||||
internal class HostUrlDetails
|
public bool IsHttps { get; set; }
|
||||||
{
|
|
||||||
public bool IsHttps { get; set; }
|
|
||||||
|
|
||||||
public string Url { get; set; }
|
public string Url { get; set; }
|
||||||
|
|
||||||
public string Protocol { get; set; }
|
public string Protocol { get; set; }
|
||||||
|
|
||||||
public string Host { get; set; }
|
public string Host { get; set; }
|
||||||
|
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,46 +1,47 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
|
|
||||||
namespace WireMock.Owin
|
namespace WireMock.Owin;
|
||||||
|
|
||||||
|
internal class HostUrlOptions
|
||||||
{
|
{
|
||||||
internal class HostUrlOptions
|
private const string LOCALHOST = "localhost";
|
||||||
|
|
||||||
|
public ICollection<string>? Urls { get; set; }
|
||||||
|
|
||||||
|
public int? Port { get; set; }
|
||||||
|
|
||||||
|
public bool UseSSL { get; set; }
|
||||||
|
|
||||||
|
public ICollection<HostUrlDetails> GetDetails()
|
||||||
{
|
{
|
||||||
private const string LOCALHOST = "localhost";
|
var list = new List<HostUrlDetails>();
|
||||||
|
if (Urls == null)
|
||||||
public ICollection<string> Urls { get; set; }
|
|
||||||
|
|
||||||
public int? Port { get; set; }
|
|
||||||
|
|
||||||
public bool UseSSL { get; set; }
|
|
||||||
|
|
||||||
public ICollection<HostUrlDetails> GetDetails()
|
|
||||||
{
|
{
|
||||||
var list = new List<HostUrlDetails>();
|
int port = Port > 0 ? Port.Value : FindFreeTcpPort();
|
||||||
if (Urls == null)
|
string protocol = UseSSL ? "https" : "http";
|
||||||
|
list.Add(new HostUrlDetails { IsHttps = UseSSL, Url = $"{protocol}://{LOCALHOST}:{port}", Protocol = protocol, Host = LOCALHOST, Port = port });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (string url in Urls)
|
||||||
{
|
{
|
||||||
int port = Port > 0 ? Port.Value : FindFreeTcpPort();
|
if (PortUtils.TryExtract(url, out bool isHttps, out var protocol, out var host, out int port))
|
||||||
string protocol = UseSSL ? "https" : "http";
|
|
||||||
list.Add(new HostUrlDetails { IsHttps = UseSSL, Url = $"{protocol}://{LOCALHOST}:{port}", Protocol = protocol, Host = LOCALHOST, Port = port });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (string url in Urls)
|
|
||||||
{
|
{
|
||||||
PortUtils.TryExtract(url, out bool isHttps, out string protocol, out string host, out int port);
|
|
||||||
list.Add(new HostUrlDetails { IsHttps = isHttps, Url = url, Protocol = protocol, Host = host, Port = port });
|
list.Add(new HostUrlDetails { IsHttps = isHttps, Url = url, Protocol = protocol, Host = host, Port = port });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int FindFreeTcpPort()
|
return list;
|
||||||
{
|
}
|
||||||
|
|
||||||
|
private static int FindFreeTcpPort()
|
||||||
|
{
|
||||||
#if USE_ASPNETCORE || NETSTANDARD2_0 || NETSTANDARD2_1
|
#if USE_ASPNETCORE || NETSTANDARD2_0 || NETSTANDARD2_1
|
||||||
return 0;
|
return 0;
|
||||||
#else
|
#else
|
||||||
return PortUtils.FindFreeTcpPort();
|
return PortUtils.FindFreeTcpPort();
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
namespace WireMock.Owin
|
namespace WireMock.Owin;
|
||||||
|
|
||||||
|
internal interface IMappingMatcher
|
||||||
{
|
{
|
||||||
internal interface IMappingMatcher
|
(MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request);
|
||||||
{
|
|
||||||
(MappingMatcherResult Match, MappingMatcherResult Partial) FindBestMatch(RequestMessage request);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,36 +1,35 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace WireMock.Owin
|
namespace WireMock.Owin;
|
||||||
|
|
||||||
|
interface IOwinSelfHost
|
||||||
{
|
{
|
||||||
interface IOwinSelfHost
|
/// <summary>
|
||||||
{
|
/// Gets a value indicating whether this server is started.
|
||||||
/// <summary>
|
/// </summary>
|
||||||
/// Gets a value indicating whether this server is started.
|
/// <value>
|
||||||
/// </summary>
|
/// <c>true</c> if this server is started; otherwise, <c>false</c>.
|
||||||
/// <value>
|
/// </value>
|
||||||
/// <c>true</c> if this server is started; otherwise, <c>false</c>.
|
bool IsStarted { get; }
|
||||||
/// </value>
|
|
||||||
bool IsStarted { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the urls.
|
/// Gets the urls.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
List<string> Urls { get; }
|
List<string> Urls { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the ports.
|
/// Gets the ports.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
List<int> Ports { get; }
|
List<int> Ports { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The exception occurred when the host is running
|
/// The exception occurred when the host is running.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Exception RunningException { get; }
|
Exception? RunningException { get; }
|
||||||
|
|
||||||
Task StartAsync();
|
Task StartAsync();
|
||||||
|
|
||||||
Task StopAsync();
|
Task StopAsync();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ namespace WireMock.Owin.Mappers
|
|||||||
|
|
||||||
string method = request.Method;
|
string method = request.Method;
|
||||||
|
|
||||||
Dictionary<string, string[]>? headers = null;
|
var headers = new Dictionary<string, string[]>();
|
||||||
IEnumerable<string>? contentEncodingHeader = null;
|
IEnumerable<string>? contentEncodingHeader = null;
|
||||||
if (request.Headers.Any())
|
if (request.Headers.Any())
|
||||||
{
|
{
|
||||||
@@ -43,7 +43,7 @@ namespace WireMock.Owin.Mappers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IDictionary<string, string>? cookies = null;
|
var cookies = new Dictionary<string, string>();
|
||||||
if (request.Cookies.Any())
|
if (request.Cookies.Any())
|
||||||
{
|
{
|
||||||
cookies = new Dictionary<string, string>();
|
cookies = new Dictionary<string, string>();
|
||||||
@@ -75,13 +75,24 @@ namespace WireMock.Owin.Mappers
|
|||||||
{
|
{
|
||||||
#if !USE_ASPNETCORE
|
#if !USE_ASPNETCORE
|
||||||
var urlDetails = UrlUtils.Parse(request.Uri, request.PathBase);
|
var urlDetails = UrlUtils.Parse(request.Uri, request.PathBase);
|
||||||
string clientIP = request.RemoteIpAddress;
|
var clientIP = request.RemoteIpAddress;
|
||||||
#else
|
#else
|
||||||
var urlDetails = UrlUtils.Parse(new Uri(request.GetEncodedUrl()), request.PathBase);
|
var urlDetails = UrlUtils.Parse(new Uri(request.GetEncodedUrl()), request.PathBase);
|
||||||
|
|
||||||
var connection = request.HttpContext.Connection;
|
var connection = request.HttpContext.Connection;
|
||||||
string clientIP = connection.RemoteIpAddress.IsIPv4MappedToIPv6
|
string clientIP;
|
||||||
? connection.RemoteIpAddress.MapToIPv4().ToString()
|
if (connection.RemoteIpAddress is null)
|
||||||
: connection.RemoteIpAddress.ToString();
|
{
|
||||||
|
clientIP = string.Empty;
|
||||||
|
}
|
||||||
|
else if (connection.RemoteIpAddress.IsIPv4MappedToIPv6)
|
||||||
|
{
|
||||||
|
clientIP = connection.RemoteIpAddress.MapToIPv4().ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clientIP = connection.RemoteIpAddress.ToString();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return (urlDetails, clientIP);
|
return (urlDetails, clientIP);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,72 +4,71 @@ using System.Linq;
|
|||||||
using WireMock.Extensions;
|
using WireMock.Extensions;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.Owin
|
namespace WireMock.Owin;
|
||||||
|
|
||||||
|
internal class MappingMatcher : IMappingMatcher
|
||||||
{
|
{
|
||||||
internal class MappingMatcher : IMappingMatcher
|
private readonly IWireMockMiddlewareOptions _options;
|
||||||
|
|
||||||
|
public MappingMatcher(IWireMockMiddlewareOptions options)
|
||||||
{
|
{
|
||||||
private readonly IWireMockMiddlewareOptions _options;
|
Guard.NotNull(options, nameof(options));
|
||||||
|
|
||||||
public MappingMatcher(IWireMockMiddlewareOptions options)
|
_options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request)
|
||||||
|
{
|
||||||
|
var possibleMappings = new List<MappingMatcherResult>();
|
||||||
|
|
||||||
|
foreach (var mapping in _options.Mappings.Values.Where(m => m.TimeSettings.IsValid()))
|
||||||
{
|
{
|
||||||
Guard.NotNull(options, nameof(options));
|
try
|
||||||
|
|
||||||
_options = options;
|
|
||||||
}
|
|
||||||
|
|
||||||
public (MappingMatcherResult Match, MappingMatcherResult Partial) FindBestMatch(RequestMessage request)
|
|
||||||
{
|
|
||||||
var mappings = new List<MappingMatcherResult>();
|
|
||||||
|
|
||||||
foreach (var mapping in _options.Mappings.Values.Where(m => m.TimeSettings.IsValid()))
|
|
||||||
{
|
{
|
||||||
try
|
var nextState = GetNextState(mapping);
|
||||||
|
|
||||||
|
possibleMappings.Add(new MappingMatcherResult
|
||||||
{
|
{
|
||||||
string nextState = GetNextState(mapping);
|
Mapping = mapping,
|
||||||
|
RequestMatchResult = mapping.GetRequestMatchResult(request, nextState)
|
||||||
mappings.Add(new MappingMatcherResult
|
});
|
||||||
{
|
|
||||||
Mapping = mapping,
|
|
||||||
RequestMatchResult = mapping.GetRequestMatchResult(request, nextState)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_options.Logger.Error($"Getting a Request MatchResult for Mapping '{mapping.Guid}' failed. This mapping will not be evaluated. Exception: {ex}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
var partialMappings = mappings
|
|
||||||
.Where(pm => (pm.Mapping.IsAdminInterface && pm.RequestMatchResult.IsPerfectMatch) || !pm.Mapping.IsAdminInterface)
|
|
||||||
.OrderBy(m => m.RequestMatchResult)
|
|
||||||
.ThenBy(m => m.Mapping.Priority)
|
|
||||||
.ToList();
|
|
||||||
var partialMatch = partialMappings.FirstOrDefault(pm => pm.RequestMatchResult.AverageTotalScore > 0.0);
|
|
||||||
|
|
||||||
if (_options.AllowPartialMapping == true)
|
|
||||||
{
|
{
|
||||||
return (partialMatch, partialMatch);
|
_options.Logger.Error($"Getting a Request MatchResult for Mapping '{mapping.Guid}' failed. This mapping will not be evaluated. Exception: {ex}");
|
||||||
}
|
}
|
||||||
|
|
||||||
var match = mappings
|
|
||||||
.Where(m => m.RequestMatchResult.IsPerfectMatch)
|
|
||||||
.OrderBy(m => m.Mapping.Priority).ThenBy(m => m.RequestMatchResult)
|
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
return (match, partialMatch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetNextState(IMapping mapping)
|
var partialMappings = possibleMappings
|
||||||
|
.Where(pm => (pm.Mapping.IsAdminInterface && pm.RequestMatchResult.IsPerfectMatch) || !pm.Mapping.IsAdminInterface)
|
||||||
|
.OrderBy(m => m.RequestMatchResult)
|
||||||
|
.ThenBy(m => m.Mapping.Priority)
|
||||||
|
.ToList();
|
||||||
|
var partialMatch = partialMappings.FirstOrDefault(pm => pm.RequestMatchResult.AverageTotalScore > 0.0);
|
||||||
|
|
||||||
|
if (_options.AllowPartialMapping == true)
|
||||||
{
|
{
|
||||||
// If the mapping does not have a scenario or _options.Scenarios does not contain this scenario from the mapping,
|
return (partialMatch, partialMatch);
|
||||||
// just return null to indicate that there is no next state.
|
|
||||||
if (mapping.Scenario == null || !_options.Scenarios.ContainsKey(mapping.Scenario))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Else just return the next state
|
|
||||||
return _options.Scenarios[mapping.Scenario].NextState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var match = possibleMappings
|
||||||
|
.Where(m => m.RequestMatchResult.IsPerfectMatch)
|
||||||
|
.OrderBy(m => m.Mapping.Priority).ThenBy(m => m.RequestMatchResult)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
return (match, partialMatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? GetNextState(IMapping mapping)
|
||||||
|
{
|
||||||
|
// If the mapping does not have a scenario or _options.Scenarios does not contain this scenario from the mapping,
|
||||||
|
// just return null to indicate that there is no next state.
|
||||||
|
if (mapping.Scenario == null || !_options.Scenarios.ContainsKey(mapping.Scenario))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else just return the next state
|
||||||
|
return _options.Scenarios[mapping.Scenario].NextState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
|
|
||||||
namespace WireMock.Owin
|
namespace WireMock.Owin;
|
||||||
{
|
|
||||||
internal class MappingMatcherResult
|
|
||||||
{
|
|
||||||
public IMapping Mapping { get; set; }
|
|
||||||
|
|
||||||
public IRequestMatchResult RequestMatchResult { get; set; }
|
internal class MappingMatcherResult
|
||||||
}
|
{
|
||||||
|
public IMapping Mapping { get; set; }
|
||||||
|
|
||||||
|
public IRequestMatchResult RequestMatchResult { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,110 +1,109 @@
|
|||||||
#if !USE_ASPNETCORE
|
#if !USE_ASPNETCORE
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Microsoft.Owin.Hosting;
|
using Microsoft.Owin.Hosting;
|
||||||
using Owin;
|
using Owin;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using WireMock.Logging;
|
using WireMock.Logging;
|
||||||
using WireMock.Owin.Mappers;
|
using WireMock.Owin.Mappers;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.Owin
|
namespace WireMock.Owin;
|
||||||
|
|
||||||
|
internal class OwinSelfHost : IOwinSelfHost
|
||||||
{
|
{
|
||||||
internal class OwinSelfHost : IOwinSelfHost
|
private readonly IWireMockMiddlewareOptions _options;
|
||||||
|
private readonly CancellationTokenSource _cts = new();
|
||||||
|
private readonly IWireMockLogger _logger;
|
||||||
|
|
||||||
|
private Exception? _runningException;
|
||||||
|
|
||||||
|
public OwinSelfHost(IWireMockMiddlewareOptions options, HostUrlOptions urlOptions)
|
||||||
{
|
{
|
||||||
private readonly IWireMockMiddlewareOptions _options;
|
Guard.NotNull(options, nameof(options));
|
||||||
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
|
Guard.NotNull(urlOptions, nameof(urlOptions));
|
||||||
private readonly IWireMockLogger _logger;
|
|
||||||
|
|
||||||
private Exception _runningException;
|
_options = options;
|
||||||
|
_logger = options.Logger ?? new WireMockConsoleLogger();
|
||||||
|
|
||||||
public OwinSelfHost([NotNull] IWireMockMiddlewareOptions options, [NotNull] HostUrlOptions urlOptions)
|
foreach (var detail in urlOptions.GetDetails())
|
||||||
{
|
{
|
||||||
Guard.NotNull(options, nameof(options));
|
Urls.Add(detail.Url);
|
||||||
Guard.NotNull(urlOptions, nameof(urlOptions));
|
Ports.Add(detail.Port);
|
||||||
|
|
||||||
_options = options;
|
|
||||||
_logger = options.Logger ?? new WireMockConsoleLogger();
|
|
||||||
|
|
||||||
foreach (var detail in urlOptions.GetDetails())
|
|
||||||
{
|
|
||||||
Urls.Add(detail.Url);
|
|
||||||
Ports.Add(detail.Port);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsStarted { get; private set; }
|
public bool IsStarted { get; private set; }
|
||||||
|
|
||||||
public List<string> Urls { get; } = new List<string>();
|
public List<string> Urls { get; } = new();
|
||||||
|
|
||||||
public List<int> Ports { get; } = new List<int>();
|
public List<int> Ports { get; } = new();
|
||||||
|
|
||||||
public Exception RunningException => _runningException;
|
public Exception? RunningException => _runningException;
|
||||||
|
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public Task StartAsync()
|
public Task StartAsync()
|
||||||
{
|
{
|
||||||
return Task.Run(StartServers, _cts.Token);
|
return Task.Run(StartServers, _cts.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public Task StopAsync()
|
public Task StopAsync()
|
||||||
{
|
{
|
||||||
_cts.Cancel();
|
_cts.Cancel();
|
||||||
|
|
||||||
return Task.FromResult(true);
|
return Task.FromResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartServers()
|
private void StartServers()
|
||||||
{
|
{
|
||||||
#if NET46
|
#if NET46
|
||||||
_logger.Info("Server using .net 4.6.1 or higher");
|
_logger.Info("Server using .net 4.6.1 or higher");
|
||||||
#else
|
#else
|
||||||
_logger.Info("Server using .net 4.5.x");
|
_logger.Info("Server using .net 4.5.x");
|
||||||
#endif
|
#endif
|
||||||
var servers = new List<IDisposable>();
|
var servers = new List<IDisposable>();
|
||||||
|
|
||||||
try
|
try
|
||||||
|
{
|
||||||
|
var requestMapper = new OwinRequestMapper();
|
||||||
|
var responseMapper = new OwinResponseMapper(_options);
|
||||||
|
var matcher = new MappingMatcher(_options);
|
||||||
|
|
||||||
|
Action<IAppBuilder> startup = app =>
|
||||||
{
|
{
|
||||||
var requestMapper = new OwinRequestMapper();
|
app.Use<GlobalExceptionMiddleware>(_options, responseMapper);
|
||||||
var responseMapper = new OwinResponseMapper(_options);
|
_options.PreWireMockMiddlewareInit?.Invoke(app);
|
||||||
var matcher = new MappingMatcher(_options);
|
app.Use<WireMockMiddleware>(_options, requestMapper, responseMapper, matcher);
|
||||||
|
_options.PostWireMockMiddlewareInit?.Invoke(app);
|
||||||
|
};
|
||||||
|
|
||||||
Action<IAppBuilder> startup = app =>
|
foreach (var url in Urls)
|
||||||
{
|
|
||||||
app.Use<GlobalExceptionMiddleware>(_options, responseMapper);
|
|
||||||
_options.PreWireMockMiddlewareInit?.Invoke(app);
|
|
||||||
app.Use<WireMockMiddleware>(_options, requestMapper, responseMapper, matcher);
|
|
||||||
_options.PostWireMockMiddlewareInit?.Invoke(app);
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var url in Urls)
|
|
||||||
{
|
|
||||||
servers.Add(WebApp.Start(url, startup));
|
|
||||||
}
|
|
||||||
|
|
||||||
IsStarted = true;
|
|
||||||
|
|
||||||
// WaitHandle is signaled when the token is cancelled,
|
|
||||||
// which will be more efficient than Thread.Sleep in while loop
|
|
||||||
_cts.Token.WaitHandle.WaitOne();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
{
|
||||||
// Expose exception of starting host, otherwise it's hard to be troubleshooting if keeping quiet
|
servers.Add(WebApp.Start(url, startup));
|
||||||
// For example, WebApp.Start will fail with System.MissingMemberException if Microsoft.Owin.Host.HttpListener.dll is being located
|
|
||||||
// https://stackoverflow.com/questions/25090211/owin-httplistener-not-located/31369857
|
|
||||||
_runningException = e;
|
|
||||||
_logger.Error(e.ToString());
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
IsStarted = false;
|
|
||||||
// Dispose all servers in finally block to make sure clean up allocated resource on error happening
|
|
||||||
servers.ForEach(s => s.Dispose());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IsStarted = true;
|
||||||
|
|
||||||
|
// WaitHandle is signaled when the token is cancelled,
|
||||||
|
// which will be more efficient than Thread.Sleep in while loop
|
||||||
|
_cts.Token.WaitHandle.WaitOne();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// Expose exception of starting host, otherwise it's hard to be troubleshooting if keeping quiet
|
||||||
|
// For example, WebApp.Start will fail with System.MissingMemberException if Microsoft.Owin.Host.HttpListener.dll is being located
|
||||||
|
// https://stackoverflow.com/questions/25090211/owin-httplistener-not-located/31369857
|
||||||
|
_runningException = e;
|
||||||
|
_logger.Error(e.ToString());
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsStarted = false;
|
||||||
|
// Dispose all servers in finally block to make sure clean up allocated resource on error happening
|
||||||
|
servers.ForEach(s => s.Dispose());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Logging;
|
using WireMock.Logging;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
@@ -74,10 +75,16 @@ namespace WireMock.Owin
|
|||||||
var logRequest = false;
|
var logRequest = false;
|
||||||
IResponseMessage? response = null;
|
IResponseMessage? response = null;
|
||||||
(MappingMatcherResult? Match, MappingMatcherResult? Partial) result = (null, null);
|
(MappingMatcherResult? Match, MappingMatcherResult? Partial) result = (null, null);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (var mapping in _options.Mappings.Values.Where(m => m?.Scenario != null))
|
foreach (var mapping in _options.Mappings.Values)
|
||||||
{
|
{
|
||||||
|
if (mapping.Scenario is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Set scenario start
|
// Set scenario start
|
||||||
if (!_options.Scenarios.ContainsKey(mapping.Scenario) && mapping.IsStartState)
|
if (!_options.Scenarios.ContainsKey(mapping.Scenario) && mapping.IsStartState)
|
||||||
{
|
{
|
||||||
@@ -107,7 +114,7 @@ namespace WireMock.Owin
|
|||||||
if (!present || _options.AuthenticationMatcher.IsMatch(authorization.ToString()) < MatchScores.Perfect)
|
if (!present || _options.AuthenticationMatcher.IsMatch(authorization.ToString()) < MatchScores.Perfect)
|
||||||
{
|
{
|
||||||
_options.Logger.Error("HttpStatusCode set to 401");
|
_options.Logger.Error("HttpStatusCode set to 401");
|
||||||
response = ResponseMessageBuilder.Create(null, 401);
|
response = ResponseMessageBuilder.Create(null, HttpStatusCode.Unauthorized);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,7 +201,7 @@ namespace WireMock.Owin
|
|||||||
|
|
||||||
private async Task SendToWebhooksAsync(IMapping mapping, IRequestMessage request, IResponseMessage response)
|
private async Task SendToWebhooksAsync(IMapping mapping, IRequestMessage request, IResponseMessage response)
|
||||||
{
|
{
|
||||||
for (int index = 0; index < mapping.Webhooks.Length; index++)
|
for (int index = 0; index < mapping.Webhooks?.Length; index++)
|
||||||
{
|
{
|
||||||
var httpClientForWebhook = HttpClientBuilder.Build(mapping.Settings.WebhookSettings ?? new WebhookSettings());
|
var httpClientForWebhook = HttpClientBuilder.Build(mapping.Settings.WebhookSettings ?? new WebhookSettings());
|
||||||
var webhookSender = new WebhookSender(mapping.Settings);
|
var webhookSender = new WebhookSender(mapping.Settings);
|
||||||
@@ -212,7 +219,7 @@ namespace WireMock.Owin
|
|||||||
|
|
||||||
private void UpdateScenarioState(IMapping mapping)
|
private void UpdateScenarioState(IMapping mapping)
|
||||||
{
|
{
|
||||||
var scenario = _options.Scenarios[mapping.Scenario];
|
var scenario = _options.Scenarios[mapping.Scenario!];
|
||||||
|
|
||||||
// Increase the number of times this state has been executed
|
// Increase the number of times this state has been executed
|
||||||
scenario.Counter++;
|
scenario.Counter++;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Linq;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
using WireMock.Constants;
|
||||||
using WireMock.Http;
|
using WireMock.Http;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.RequestBuilders;
|
using WireMock.RequestBuilders;
|
||||||
@@ -29,9 +30,9 @@ internal class ProxyHelper
|
|||||||
IRequestMessage requestMessage,
|
IRequestMessage requestMessage,
|
||||||
string url)
|
string url)
|
||||||
{
|
{
|
||||||
Guard.NotNull(client, nameof(client));
|
Guard.NotNull(client);
|
||||||
Guard.NotNull(requestMessage, nameof(requestMessage));
|
Guard.NotNull(requestMessage);
|
||||||
Guard.NotNull(url, nameof(url));
|
Guard.NotNull(url);
|
||||||
|
|
||||||
var originalUri = new Uri(requestMessage.Url);
|
var originalUri = new Uri(requestMessage.Url);
|
||||||
var requiredUri = new Uri(url);
|
var requiredUri = new Uri(url);
|
||||||
@@ -103,6 +104,22 @@ internal class ProxyHelper
|
|||||||
|
|
||||||
var response = Response.Create(responseMessage);
|
var response = Response.Create(responseMessage);
|
||||||
|
|
||||||
return new Mapping(Guid.NewGuid(), string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, null, null);
|
return new Mapping
|
||||||
|
(
|
||||||
|
guid: Guid.NewGuid(),
|
||||||
|
title: $"Proxy Mapping for {requestMessage.Method} {requestMessage.Path}",
|
||||||
|
description: string.Empty,
|
||||||
|
path: null,
|
||||||
|
settings: _settings,
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
priority: WireMockConstants.ProxyPriority, // This was 0
|
||||||
|
scenario: null,
|
||||||
|
executionConditionState: null,
|
||||||
|
nextState: null,
|
||||||
|
stateTimes: null,
|
||||||
|
webhooks: null,
|
||||||
|
timeSettings: null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,10 +47,10 @@ public class RequestMessage : IRequestMessage
|
|||||||
public string Method { get; }
|
public string Method { get; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IRequestMessage.Headers" />
|
/// <inheritdoc cref="IRequestMessage.Headers" />
|
||||||
public IDictionary<string, WireMockList<string>>? Headers { get; }
|
public IDictionary<string, WireMockList<string>> Headers { get; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IRequestMessage.Cookies" />
|
/// <inheritdoc cref="IRequestMessage.Cookies" />
|
||||||
public IDictionary<string, string>? Cookies { get; }
|
public IDictionary<string, string> Cookies { get; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IRequestMessage.Query" />
|
/// <inheritdoc cref="IRequestMessage.Query" />
|
||||||
public IDictionary<string, WireMockList<string>>? Query { get; }
|
public IDictionary<string, WireMockList<string>>? Query { get; }
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
using WireMock.Constants;
|
using WireMock.Constants;
|
||||||
using WireMock.Http;
|
using WireMock.Http;
|
||||||
@@ -15,6 +16,11 @@ internal static class ResponseMessageBuilder
|
|||||||
{ HttpKnownHeaderNames.ContentType, new WireMockList<string> { WireMockConstants.ContentTypeJson } }
|
{ HttpKnownHeaderNames.ContentType, new WireMockList<string> { WireMockConstants.ContentTypeJson } }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
internal static ResponseMessage Create(string? message, HttpStatusCode statusCode, Guid? guid = null)
|
||||||
|
{
|
||||||
|
return Create(message, (int)statusCode, guid);
|
||||||
|
}
|
||||||
|
|
||||||
internal static ResponseMessage Create(string? message, int statusCode = 200, Guid? guid = null)
|
internal static ResponseMessage Create(string? message, int statusCode = 200, Guid? guid = null)
|
||||||
{
|
{
|
||||||
var response = new ResponseMessage
|
var response = new ResponseMessage
|
||||||
|
|||||||
@@ -6,143 +6,142 @@ using WireMock.Matchers.Request;
|
|||||||
using WireMock.ResponseBuilders;
|
using WireMock.ResponseBuilders;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
|
|
||||||
namespace WireMock.Serialization
|
namespace WireMock.Serialization;
|
||||||
|
|
||||||
|
internal static class LogEntryMapper
|
||||||
{
|
{
|
||||||
internal static class LogEntryMapper
|
public static LogEntryModel Map(ILogEntry logEntry)
|
||||||
{
|
{
|
||||||
public static LogEntryModel Map(ILogEntry logEntry)
|
var logRequestModel = new LogRequestModel
|
||||||
{
|
{
|
||||||
var logRequestModel = new LogRequestModel
|
DateTime = logEntry.RequestMessage.DateTime,
|
||||||
{
|
ClientIP = logEntry.RequestMessage.ClientIP,
|
||||||
DateTime = logEntry.RequestMessage.DateTime,
|
Path = logEntry.RequestMessage.Path,
|
||||||
ClientIP = logEntry.RequestMessage.ClientIP,
|
AbsolutePath = logEntry.RequestMessage.AbsolutePath,
|
||||||
Path = logEntry.RequestMessage.Path,
|
Url = logEntry.RequestMessage.Url,
|
||||||
AbsolutePath = logEntry.RequestMessage.AbsolutePath,
|
AbsoluteUrl = logEntry.RequestMessage.AbsoluteUrl,
|
||||||
Url = logEntry.RequestMessage.Url,
|
ProxyUrl = logEntry.RequestMessage.ProxyUrl,
|
||||||
AbsoluteUrl = logEntry.RequestMessage.AbsoluteUrl,
|
Query = logEntry.RequestMessage.Query,
|
||||||
ProxyUrl = logEntry.RequestMessage.ProxyUrl,
|
Method = logEntry.RequestMessage.Method,
|
||||||
Query = logEntry.RequestMessage.Query,
|
Headers = logEntry.RequestMessage.Headers,
|
||||||
Method = logEntry.RequestMessage.Method,
|
Cookies = logEntry.RequestMessage.Cookies
|
||||||
Headers = logEntry.RequestMessage.Headers,
|
};
|
||||||
Cookies = logEntry.RequestMessage.Cookies
|
|
||||||
};
|
|
||||||
|
|
||||||
if (logEntry.RequestMessage.BodyData != null)
|
if (logEntry.RequestMessage.BodyData != null)
|
||||||
{
|
{
|
||||||
logRequestModel.DetectedBodyType = logEntry.RequestMessage.BodyData.DetectedBodyType?.ToString();
|
logRequestModel.DetectedBodyType = logEntry.RequestMessage.BodyData.DetectedBodyType?.ToString();
|
||||||
logRequestModel.DetectedBodyTypeFromContentType = logEntry.RequestMessage.BodyData.DetectedBodyTypeFromContentType?.ToString();
|
logRequestModel.DetectedBodyTypeFromContentType = logEntry.RequestMessage.BodyData.DetectedBodyTypeFromContentType?.ToString();
|
||||||
|
|
||||||
switch (logEntry.RequestMessage.BodyData.DetectedBodyType)
|
switch (logEntry.RequestMessage.BodyData.DetectedBodyType)
|
||||||
|
{
|
||||||
|
case BodyType.String:
|
||||||
|
logRequestModel.Body = logEntry.RequestMessage.BodyData.BodyAsString;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BodyType.Json:
|
||||||
|
logRequestModel.Body = logEntry.RequestMessage.BodyData.BodyAsString; // In case of Json, do also save the Body as string (backwards compatible)
|
||||||
|
logRequestModel.BodyAsJson = logEntry.RequestMessage.BodyData.BodyAsJson;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BodyType.Bytes:
|
||||||
|
logRequestModel.BodyAsBytes = logEntry.RequestMessage.BodyData.BodyAsBytes;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
logRequestModel.BodyEncoding = logEntry.RequestMessage.BodyData.Encoding != null
|
||||||
|
? new EncodingModel
|
||||||
{
|
{
|
||||||
case BodyType.String:
|
EncodingName = logEntry.RequestMessage.BodyData.Encoding.EncodingName,
|
||||||
logRequestModel.Body = logEntry.RequestMessage.BodyData.BodyAsString;
|
CodePage = logEntry.RequestMessage.BodyData.Encoding.CodePage,
|
||||||
break;
|
WebName = logEntry.RequestMessage.BodyData.Encoding.WebName
|
||||||
|
|
||||||
case BodyType.Json:
|
|
||||||
logRequestModel.Body = logEntry.RequestMessage.BodyData.BodyAsString; // In case of Json, do also save the Body as string (backwards compatible)
|
|
||||||
logRequestModel.BodyAsJson = logEntry.RequestMessage.BodyData.BodyAsJson;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BodyType.Bytes:
|
|
||||||
logRequestModel.BodyAsBytes = logEntry.RequestMessage.BodyData.BodyAsBytes;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
: null;
|
||||||
logRequestModel.BodyEncoding = logEntry.RequestMessage.BodyData.Encoding != null
|
|
||||||
? new EncodingModel
|
|
||||||
{
|
|
||||||
EncodingName = logEntry.RequestMessage.BodyData.Encoding.EncodingName,
|
|
||||||
CodePage = logEntry.RequestMessage.BodyData.Encoding.CodePage,
|
|
||||||
WebName = logEntry.RequestMessage.BodyData.Encoding.WebName
|
|
||||||
}
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var logResponseModel = new LogResponseModel
|
|
||||||
{
|
|
||||||
StatusCode = logEntry.ResponseMessage.StatusCode,
|
|
||||||
Headers = logEntry.ResponseMessage.Headers
|
|
||||||
};
|
|
||||||
|
|
||||||
if (logEntry.ResponseMessage.FaultType != FaultType.NONE)
|
|
||||||
{
|
|
||||||
logResponseModel.FaultType = logEntry.ResponseMessage.FaultType.ToString();
|
|
||||||
logResponseModel.FaultPercentage = logEntry.ResponseMessage.FaultPercentage;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logEntry.ResponseMessage.BodyData != null)
|
|
||||||
{
|
|
||||||
logResponseModel.BodyOriginal = logEntry.ResponseMessage.BodyOriginal;
|
|
||||||
logResponseModel.BodyDestination = logEntry.ResponseMessage.BodyDestination;
|
|
||||||
|
|
||||||
logResponseModel.DetectedBodyType = logEntry.ResponseMessage.BodyData.DetectedBodyType;
|
|
||||||
logResponseModel.DetectedBodyTypeFromContentType = logEntry.ResponseMessage.BodyData.DetectedBodyTypeFromContentType;
|
|
||||||
|
|
||||||
switch (logEntry.ResponseMessage.BodyData.DetectedBodyType)
|
|
||||||
{
|
|
||||||
case BodyType.String:
|
|
||||||
logResponseModel.Body = logEntry.ResponseMessage.BodyData.BodyAsString;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BodyType.Json:
|
|
||||||
logResponseModel.BodyAsJson = logEntry.ResponseMessage.BodyData.BodyAsJson;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BodyType.Bytes:
|
|
||||||
logResponseModel.BodyAsBytes = logEntry.ResponseMessage.BodyData.BodyAsBytes;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BodyType.File:
|
|
||||||
logResponseModel.BodyAsFile = logEntry.ResponseMessage.BodyData.BodyAsFile;
|
|
||||||
logResponseModel.BodyAsFileIsCached = logEntry.ResponseMessage.BodyData.BodyAsFileIsCached;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
logResponseModel.BodyEncoding = logEntry.ResponseMessage.BodyData.Encoding != null
|
|
||||||
? new EncodingModel
|
|
||||||
{
|
|
||||||
EncodingName = logEntry.ResponseMessage.BodyData.Encoding.EncodingName,
|
|
||||||
CodePage = logEntry.ResponseMessage.BodyData.Encoding.CodePage,
|
|
||||||
WebName = logEntry.ResponseMessage.BodyData.Encoding.WebName
|
|
||||||
}
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new LogEntryModel
|
|
||||||
{
|
|
||||||
Guid = logEntry.Guid,
|
|
||||||
Request = logRequestModel,
|
|
||||||
Response = logResponseModel,
|
|
||||||
|
|
||||||
MappingGuid = logEntry.MappingGuid,
|
|
||||||
MappingTitle = logEntry.MappingTitle,
|
|
||||||
RequestMatchResult = Map(logEntry.RequestMatchResult),
|
|
||||||
|
|
||||||
PartialMappingGuid = logEntry.PartialMappingGuid,
|
|
||||||
PartialMappingTitle = logEntry.PartialMappingTitle,
|
|
||||||
PartialRequestMatchResult = Map(logEntry.PartialMatchResult)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LogRequestMatchModel Map(IRequestMatchResult matchResult)
|
var logResponseModel = new LogResponseModel
|
||||||
{
|
{
|
||||||
if (matchResult == null)
|
StatusCode = logEntry.ResponseMessage.StatusCode,
|
||||||
|
Headers = logEntry.ResponseMessage.Headers
|
||||||
|
};
|
||||||
|
|
||||||
|
if (logEntry.ResponseMessage.FaultType != FaultType.NONE)
|
||||||
|
{
|
||||||
|
logResponseModel.FaultType = logEntry.ResponseMessage.FaultType.ToString();
|
||||||
|
logResponseModel.FaultPercentage = logEntry.ResponseMessage.FaultPercentage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logEntry.ResponseMessage.BodyData != null)
|
||||||
|
{
|
||||||
|
logResponseModel.BodyOriginal = logEntry.ResponseMessage.BodyOriginal;
|
||||||
|
logResponseModel.BodyDestination = logEntry.ResponseMessage.BodyDestination;
|
||||||
|
|
||||||
|
logResponseModel.DetectedBodyType = logEntry.ResponseMessage.BodyData.DetectedBodyType;
|
||||||
|
logResponseModel.DetectedBodyTypeFromContentType = logEntry.ResponseMessage.BodyData.DetectedBodyTypeFromContentType;
|
||||||
|
|
||||||
|
switch (logEntry.ResponseMessage.BodyData.DetectedBodyType)
|
||||||
{
|
{
|
||||||
return null;
|
case BodyType.String:
|
||||||
|
logResponseModel.Body = logEntry.ResponseMessage.BodyData.BodyAsString;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BodyType.Json:
|
||||||
|
logResponseModel.BodyAsJson = logEntry.ResponseMessage.BodyData.BodyAsJson;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BodyType.Bytes:
|
||||||
|
logResponseModel.BodyAsBytes = logEntry.ResponseMessage.BodyData.BodyAsBytes;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BodyType.File:
|
||||||
|
logResponseModel.BodyAsFile = logEntry.ResponseMessage.BodyData.BodyAsFile;
|
||||||
|
logResponseModel.BodyAsFileIsCached = logEntry.ResponseMessage.BodyData.BodyAsFileIsCached;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LogRequestMatchModel
|
logResponseModel.BodyEncoding = logEntry.ResponseMessage.BodyData.Encoding != null
|
||||||
{
|
? new EncodingModel
|
||||||
IsPerfectMatch = matchResult.IsPerfectMatch,
|
|
||||||
TotalScore = matchResult.TotalScore,
|
|
||||||
TotalNumber = matchResult.TotalNumber,
|
|
||||||
AverageTotalScore = matchResult.AverageTotalScore,
|
|
||||||
MatchDetails = matchResult.MatchDetails.Select(md => new
|
|
||||||
{
|
{
|
||||||
Name = md.MatcherType.Name.Replace("RequestMessage", string.Empty),
|
EncodingName = logEntry.ResponseMessage.BodyData.Encoding.EncodingName,
|
||||||
Score = md.Score
|
CodePage = logEntry.ResponseMessage.BodyData.Encoding.CodePage,
|
||||||
} as object).ToList()
|
WebName = logEntry.ResponseMessage.BodyData.Encoding.WebName
|
||||||
};
|
}
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new LogEntryModel
|
||||||
|
{
|
||||||
|
Guid = logEntry.Guid,
|
||||||
|
Request = logRequestModel,
|
||||||
|
Response = logResponseModel,
|
||||||
|
|
||||||
|
MappingGuid = logEntry.MappingGuid,
|
||||||
|
MappingTitle = logEntry.MappingTitle,
|
||||||
|
RequestMatchResult = Map(logEntry.RequestMatchResult),
|
||||||
|
|
||||||
|
PartialMappingGuid = logEntry.PartialMappingGuid,
|
||||||
|
PartialMappingTitle = logEntry.PartialMappingTitle,
|
||||||
|
PartialRequestMatchResult = Map(logEntry.PartialMatchResult)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LogRequestMatchModel? Map(IRequestMatchResult? matchResult)
|
||||||
|
{
|
||||||
|
if (matchResult == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LogRequestMatchModel
|
||||||
|
{
|
||||||
|
IsPerfectMatch = matchResult.IsPerfectMatch,
|
||||||
|
TotalScore = matchResult.TotalScore,
|
||||||
|
TotalNumber = matchResult.TotalNumber,
|
||||||
|
AverageTotalScore = matchResult.AverageTotalScore,
|
||||||
|
MatchDetails = matchResult.MatchDetails.Select(md => new
|
||||||
|
{
|
||||||
|
Name = md.MatcherType.Name.Replace("RequestMessage", string.Empty),
|
||||||
|
Score = md.Score
|
||||||
|
} as object).ToList()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,27 +1,26 @@
|
|||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
|
|
||||||
namespace WireMock.Serialization
|
namespace WireMock.Serialization;
|
||||||
{
|
|
||||||
internal static class TimeSettingsMapper
|
|
||||||
{
|
|
||||||
public static TimeSettingsModel Map(ITimeSettings settings)
|
|
||||||
{
|
|
||||||
return settings != null ? new TimeSettingsModel
|
|
||||||
{
|
|
||||||
Start = settings.Start,
|
|
||||||
End = settings.End,
|
|
||||||
TTL = settings.TTL
|
|
||||||
} : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ITimeSettings Map(TimeSettingsModel settings)
|
internal static class TimeSettingsMapper
|
||||||
|
{
|
||||||
|
public static TimeSettingsModel? Map(ITimeSettings? settings)
|
||||||
|
{
|
||||||
|
return settings != null ? new TimeSettingsModel
|
||||||
{
|
{
|
||||||
return settings != null ? new TimeSettings
|
Start = settings.Start,
|
||||||
{
|
End = settings.End,
|
||||||
Start = settings.Start,
|
TTL = settings.TTL
|
||||||
End = settings.End,
|
} : null;
|
||||||
TTL = settings.TTL
|
}
|
||||||
} : null;
|
|
||||||
}
|
public static ITimeSettings? Map(TimeSettingsModel? settings)
|
||||||
|
{
|
||||||
|
return settings != null ? new TimeSettings
|
||||||
|
{
|
||||||
|
Start = settings.Start,
|
||||||
|
End = settings.End,
|
||||||
|
TTL = settings.TTL
|
||||||
|
} : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,6 @@ using WireMock.Matchers;
|
|||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
using WireMock.Proxy;
|
using WireMock.Proxy;
|
||||||
using WireMock.RequestBuilders;
|
using WireMock.RequestBuilders;
|
||||||
using WireMock.ResponseBuilders;
|
|
||||||
using WireMock.ResponseProviders;
|
using WireMock.ResponseProviders;
|
||||||
using WireMock.Serialization;
|
using WireMock.Serialization;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
@@ -43,10 +42,10 @@ public partial class WireMockServer
|
|||||||
private const string AdminScenarios = "/__admin/scenarios";
|
private const string AdminScenarios = "/__admin/scenarios";
|
||||||
private const string QueryParamReloadStaticMappings = "reloadStaticMappings";
|
private const string QueryParamReloadStaticMappings = "reloadStaticMappings";
|
||||||
|
|
||||||
private readonly Guid _proxyMappingGuid = new("e59914fd-782e-428e-91c1-4810ffb86567");
|
private static readonly Guid ProxyMappingGuid = new("e59914fd-782e-428e-91c1-4810ffb86567");
|
||||||
private readonly RegexMatcher _adminRequestContentTypeJson = new ContentTypeMatcher(WireMockConstants.ContentTypeJson, true);
|
private static readonly RegexMatcher AdminRequestContentTypeJson = new ContentTypeMatcher(WireMockConstants.ContentTypeJson, true);
|
||||||
private readonly RegexMatcher _adminMappingsGuidPathMatcher = new(@"^\/__admin\/mappings\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
|
private static readonly RegexMatcher AdminMappingsGuidPathMatcher = new(@"^\/__admin\/mappings\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
|
||||||
private readonly RegexMatcher _adminRequestsGuidPathMatcher = new(@"^\/__admin\/requests\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
|
private static readonly RegexMatcher AdminRequestsGuidPathMatcher = new(@"^\/__admin\/requests\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
|
||||||
|
|
||||||
private EnhancedFileSystemWatcher? _enhancedFileSystemWatcher;
|
private EnhancedFileSystemWatcher? _enhancedFileSystemWatcher;
|
||||||
|
|
||||||
@@ -55,21 +54,21 @@ public partial class WireMockServer
|
|||||||
{
|
{
|
||||||
// __admin/settings
|
// __admin/settings
|
||||||
Given(Request.Create().WithPath(AdminSettings).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(SettingsGet));
|
Given(Request.Create().WithPath(AdminSettings).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(SettingsGet));
|
||||||
Given(Request.Create().WithPath(AdminSettings).UsingMethod("PUT", "POST").WithHeader(HttpKnownHeaderNames.ContentType, _adminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(SettingsUpdate));
|
Given(Request.Create().WithPath(AdminSettings).UsingMethod("PUT", "POST").WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(SettingsUpdate));
|
||||||
|
|
||||||
// __admin/mappings
|
// __admin/mappings
|
||||||
Given(Request.Create().WithPath(AdminMappings).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsGet));
|
Given(Request.Create().WithPath(AdminMappings).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsGet));
|
||||||
Given(Request.Create().WithPath(AdminMappings).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, _adminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPost));
|
Given(Request.Create().WithPath(AdminMappings).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPost));
|
||||||
Given(Request.Create().WithPath(AdminMappingsWireMockOrg).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, _adminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPostWireMockOrg));
|
Given(Request.Create().WithPath(AdminMappingsWireMockOrg).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPostWireMockOrg));
|
||||||
Given(Request.Create().WithPath(AdminMappings).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsDelete));
|
Given(Request.Create().WithPath(AdminMappings).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsDelete));
|
||||||
|
|
||||||
// __admin/mappings/reset
|
// __admin/mappings/reset
|
||||||
Given(Request.Create().WithPath(AdminMappings + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsReset));
|
Given(Request.Create().WithPath(AdminMappings + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsReset));
|
||||||
|
|
||||||
// __admin/mappings/{guid}
|
// __admin/mappings/{guid}
|
||||||
Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingGet));
|
Given(Request.Create().WithPath(AdminMappingsGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingGet));
|
||||||
Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingPut().WithHeader(HttpKnownHeaderNames.ContentType, _adminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingPut));
|
Given(Request.Create().WithPath(AdminMappingsGuidPathMatcher).UsingPut().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingPut));
|
||||||
Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDelete));
|
Given(Request.Create().WithPath(AdminMappingsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDelete));
|
||||||
|
|
||||||
// __admin/mappings/save
|
// __admin/mappings/save
|
||||||
Given(Request.Create().WithPath($"{AdminMappings}/save").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsSave));
|
Given(Request.Create().WithPath($"{AdminMappings}/save").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsSave));
|
||||||
@@ -85,8 +84,8 @@ public partial class WireMockServer
|
|||||||
Given(Request.Create().WithPath(AdminRequests + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestsDelete));
|
Given(Request.Create().WithPath(AdminRequests + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestsDelete));
|
||||||
|
|
||||||
// __admin/request/{guid}
|
// __admin/request/{guid}
|
||||||
Given(Request.Create().WithPath(_adminRequestsGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestGet));
|
Given(Request.Create().WithPath(AdminRequestsGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestGet));
|
||||||
Given(Request.Create().WithPath(_adminRequestsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestDelete));
|
Given(Request.Create().WithPath(AdminRequestsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestDelete));
|
||||||
|
|
||||||
// __admin/requests/find
|
// __admin/requests/find
|
||||||
Given(Request.Create().WithPath(AdminRequests + "/find").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestsFind));
|
Given(Request.Create().WithPath(AdminRequests + "/find").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestsFind));
|
||||||
@@ -186,7 +185,7 @@ public partial class WireMockServer
|
|||||||
|
|
||||||
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(path);
|
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(path);
|
||||||
|
|
||||||
if (FileHelper.TryReadMappingFileWithRetryAndDelay(_settings.FileSystemHandler, path, out string value))
|
if (FileHelper.TryReadMappingFileWithRetryAndDelay(_settings.FileSystemHandler, path, out var value))
|
||||||
{
|
{
|
||||||
var mappingModels = DeserializeJsonToArray<MappingModel>(value);
|
var mappingModels = DeserializeJsonToArray<MappingModel>(value);
|
||||||
foreach (var mappingModel in mappingModels)
|
foreach (var mappingModel in mappingModels)
|
||||||
@@ -216,13 +215,13 @@ public partial class WireMockServer
|
|||||||
if (settings.ProxyAndRecordSettings == null)
|
if (settings.ProxyAndRecordSettings == null)
|
||||||
{
|
{
|
||||||
_httpClientForProxy = null;
|
_httpClientForProxy = null;
|
||||||
DeleteMapping(_proxyMappingGuid);
|
DeleteMapping(ProxyMappingGuid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_httpClientForProxy = HttpClientBuilder.Build(settings.ProxyAndRecordSettings);
|
_httpClientForProxy = HttpClientBuilder.Build(settings.ProxyAndRecordSettings);
|
||||||
|
|
||||||
var proxyRespondProvider = Given(Request.Create().WithPath("/*").UsingAnyMethod()).WithGuid(_proxyMappingGuid);
|
var proxyRespondProvider = Given(Request.Create().WithPath("/*").UsingAnyMethod()).WithGuid(ProxyMappingGuid).WithTitle("Default Proxy Mapping on /*");
|
||||||
if (settings.StartAdminInterface == true)
|
if (settings.StartAdminInterface == true)
|
||||||
{
|
{
|
||||||
proxyRespondProvider.AtPriority(WireMockConstants.ProxyPriority);
|
proxyRespondProvider.AtPriority(WireMockConstants.ProxyPriority);
|
||||||
@@ -359,7 +358,7 @@ public partial class WireMockServer
|
|||||||
var mappingModel = DeserializeObject<MappingModel>(requestMessage);
|
var mappingModel = DeserializeObject<MappingModel>(requestMessage);
|
||||||
Guid? guidFromPut = ConvertMappingAndRegisterAsRespondProvider(mappingModel, guid);
|
Guid? guidFromPut = ConvertMappingAndRegisterAsRespondProvider(mappingModel, guid);
|
||||||
|
|
||||||
return ResponseMessageBuilder.Create("Mapping added or updated", 200, guidFromPut);
|
return ResponseMessageBuilder.Create("Mapping added or updated", HttpStatusCode.OK, guidFromPut);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IResponseMessage MappingDelete(IRequestMessage requestMessage)
|
private IResponseMessage MappingDelete(IRequestMessage requestMessage)
|
||||||
@@ -368,13 +367,13 @@ public partial class WireMockServer
|
|||||||
|
|
||||||
if (DeleteMapping(guid))
|
if (DeleteMapping(guid))
|
||||||
{
|
{
|
||||||
return ResponseMessageBuilder.Create("Mapping removed", 200, guid);
|
return ResponseMessageBuilder.Create("Mapping removed", HttpStatusCode.OK, guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseMessageBuilder.Create("Mapping not found", 404);
|
return ResponseMessageBuilder.Create("Mapping not found", HttpStatusCode.NotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Guid ParseGuidFromRequestMessage(IRequestMessage requestMessage)
|
private static Guid ParseGuidFromRequestMessage(IRequestMessage requestMessage)
|
||||||
{
|
{
|
||||||
return Guid.Parse(requestMessage.Path.Substring(AdminMappings.Length + 1));
|
return Guid.Parse(requestMessage.Path.Substring(AdminMappings.Length + 1));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public partial class WireMockServer
|
|||||||
|
|
||||||
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(path);
|
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(path);
|
||||||
|
|
||||||
if (FileHelper.TryReadMappingFileWithRetryAndDelay(_settings.FileSystemHandler, path, out string value))
|
if (FileHelper.TryReadMappingFileWithRetryAndDelay(_settings.FileSystemHandler, path, out var value))
|
||||||
{
|
{
|
||||||
var mappings = DeserializeJsonToArray<OrgMappings>(value);
|
var mappings = DeserializeJsonToArray<OrgMappings>(value);
|
||||||
foreach (var mapping in mappings)
|
foreach (var mapping in mappings)
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ public partial class WireMockServer : IWireMockServer
|
|||||||
/// Gets the scenarios.
|
/// Gets the scenarios.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public ConcurrentDictionary<string, ScenarioState> Scenarios => new ConcurrentDictionary<string, ScenarioState>(_options.Scenarios);
|
public ConcurrentDictionary<string, ScenarioState> Scenarios => new(_options.Scenarios);
|
||||||
|
|
||||||
#region IDisposable Members
|
#region IDisposable Members
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -414,10 +414,10 @@ public partial class WireMockServer : IWireMockServer
|
|||||||
|
|
||||||
/// <inheritdoc cref="IWireMockServer.SetAzureADAuthentication(string, string)" />
|
/// <inheritdoc cref="IWireMockServer.SetAzureADAuthentication(string, string)" />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public void SetAzureADAuthentication([NotNull] string tenant, [NotNull] string audience)
|
public void SetAzureADAuthentication(string tenant, string audience)
|
||||||
{
|
{
|
||||||
Guard.NotNull(tenant, nameof(tenant));
|
Guard.NotNull(tenant);
|
||||||
Guard.NotNull(audience, nameof(audience));
|
Guard.NotNull(audience);
|
||||||
|
|
||||||
#if NETSTANDARD1_3
|
#if NETSTANDARD1_3
|
||||||
throw new NotSupportedException("AzureADAuthentication is not supported for NETStandard 1.3");
|
throw new NotSupportedException("AzureADAuthentication is not supported for NETStandard 1.3");
|
||||||
@@ -445,14 +445,14 @@ public partial class WireMockServer : IWireMockServer
|
|||||||
|
|
||||||
/// <inheritdoc cref="IWireMockServer.SetMaxRequestLogCount" />
|
/// <inheritdoc cref="IWireMockServer.SetMaxRequestLogCount" />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public void SetMaxRequestLogCount([CanBeNull] int? maxRequestLogCount)
|
public void SetMaxRequestLogCount(int? maxRequestLogCount)
|
||||||
{
|
{
|
||||||
_options.MaxRequestLogCount = maxRequestLogCount;
|
_options.MaxRequestLogCount = maxRequestLogCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockServer.SetRequestLogExpirationDuration" />
|
/// <inheritdoc cref="IWireMockServer.SetRequestLogExpirationDuration" />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public void SetRequestLogExpirationDuration([CanBeNull] int? requestLogExpirationDuration)
|
public void SetRequestLogExpirationDuration(int? requestLogExpirationDuration)
|
||||||
{
|
{
|
||||||
_options.RequestLogExpirationDuration = requestLogExpirationDuration;
|
_options.RequestLogExpirationDuration = requestLogExpirationDuration;
|
||||||
}
|
}
|
||||||
@@ -542,12 +542,12 @@ public partial class WireMockServer : IWireMockServer
|
|||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(settings.AdminUsername) && !string.IsNullOrEmpty(settings.AdminPassword))
|
if (!string.IsNullOrEmpty(settings.AdminUsername) && !string.IsNullOrEmpty(settings.AdminPassword))
|
||||||
{
|
{
|
||||||
SetBasicAuthentication(settings.AdminUsername, settings.AdminPassword);
|
SetBasicAuthentication(settings.AdminUsername!, settings.AdminPassword!);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(settings.AdminAzureADTenant) && !string.IsNullOrEmpty(settings.AdminAzureADAudience))
|
if (!string.IsNullOrEmpty(settings.AdminAzureADTenant) && !string.IsNullOrEmpty(settings.AdminAzureADAudience))
|
||||||
{
|
{
|
||||||
SetAzureADAuthentication(settings.AdminAzureADTenant, settings.AdminAzureADAudience);
|
SetAzureADAuthentication(settings.AdminAzureADTenant!, settings.AdminAzureADAudience!);
|
||||||
}
|
}
|
||||||
|
|
||||||
InitAdmin();
|
InitAdmin();
|
||||||
|
|||||||
@@ -1,230 +1,230 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace WireMock.Util
|
namespace WireMock.Util;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Based on:
|
||||||
|
/// http://utf8checker.codeplex.com
|
||||||
|
/// https://github.com/0x53A/Mvvm/blob/master/src/Mvvm/src/Utf8Checker.cs
|
||||||
|
///
|
||||||
|
/// References:
|
||||||
|
/// http://anubis.dkuug.dk/JTC1/SC2/WG2/docs/n1335
|
||||||
|
/// http://www.cl.cam.ac.uk/~mgk25/ucs/ISO-10646-UTF-8.html
|
||||||
|
/// http://www.unicode.org/versions/corrigendum1.html
|
||||||
|
/// http://www.ietf.org/rfc/rfc2279.txt
|
||||||
|
/// </summary>
|
||||||
|
public static class BytesEncodingUtils
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Based on:
|
/// Tries the get the Encoding from an array of bytes.
|
||||||
/// http://utf8checker.codeplex.com
|
|
||||||
/// https://github.com/0x53A/Mvvm/blob/master/src/Mvvm/src/Utf8Checker.cs
|
|
||||||
///
|
|
||||||
/// References:
|
|
||||||
/// http://anubis.dkuug.dk/JTC1/SC2/WG2/docs/n1335
|
|
||||||
/// http://www.cl.cam.ac.uk/~mgk25/ucs/ISO-10646-UTF-8.html
|
|
||||||
/// http://www.unicode.org/versions/corrigendum1.html
|
|
||||||
/// http://www.ietf.org/rfc/rfc2279.txt
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class BytesEncodingUtils
|
/// <param name="bytes">The bytes.</param>
|
||||||
|
/// <param name="encoding">The output encoding.</param>
|
||||||
|
public static bool TryGetEncoding(byte[] bytes, [NotNullWhen(true)] out Encoding? encoding)
|
||||||
{
|
{
|
||||||
/// <summary>
|
encoding = null;
|
||||||
/// Tries the get the Encoding from an array of bytes.
|
if (bytes.All(b => b < 80))
|
||||||
/// </summary>
|
|
||||||
/// <param name="bytes">The bytes.</param>
|
|
||||||
/// <param name="encoding">The output encoding.</param>
|
|
||||||
public static bool TryGetEncoding(byte[] bytes, out Encoding encoding)
|
|
||||||
{
|
{
|
||||||
encoding = null;
|
encoding = Encoding.ASCII;
|
||||||
if (bytes.All(b => b < 80))
|
|
||||||
{
|
|
||||||
encoding = Encoding.ASCII;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StartsWith(bytes, new byte[] { 0xff, 0xfe, 0x00, 0x00 }))
|
|
||||||
{
|
|
||||||
encoding = Encoding.UTF32;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StartsWith(bytes, new byte[] { 0xfe, 0xff }))
|
|
||||||
{
|
|
||||||
encoding = Encoding.BigEndianUnicode;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StartsWith(bytes, new byte[] { 0xff, 0xfe }))
|
|
||||||
{
|
|
||||||
encoding = Encoding.Unicode;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StartsWith(bytes, new byte[] { 0xef, 0xbb, 0xbf }))
|
|
||||||
{
|
|
||||||
encoding = Encoding.UTF8;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsUtf8(bytes, bytes.Length))
|
|
||||||
{
|
|
||||||
encoding = new UTF8Encoding(false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool StartsWith(IEnumerable<byte> data, IReadOnlyCollection<byte> other)
|
|
||||||
{
|
|
||||||
byte[] arraySelf = data.Take(other.Count).ToArray();
|
|
||||||
return other.SequenceEqual(arraySelf);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsUtf8(IReadOnlyList<byte> buffer, int length)
|
|
||||||
{
|
|
||||||
int position = 0;
|
|
||||||
int bytes = 0;
|
|
||||||
while (position < length)
|
|
||||||
{
|
|
||||||
if (!IsValid(buffer, position, length, ref bytes))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
position += bytes;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable S3776 // Cognitive Complexity of methods should not be too high
|
if (StartsWith(bytes, new byte[] { 0xff, 0xfe, 0x00, 0x00 }))
|
||||||
private static bool IsValid(IReadOnlyList<byte> buffer, int position, int length, ref int bytes)
|
|
||||||
{
|
{
|
||||||
if (length > buffer.Count)
|
encoding = Encoding.UTF32;
|
||||||
{
|
return true;
|
||||||
throw new ArgumentException("Invalid length");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (position > length - 1)
|
if (StartsWith(bytes, new byte[] { 0xfe, 0xff }))
|
||||||
|
{
|
||||||
|
encoding = Encoding.BigEndianUnicode;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StartsWith(bytes, new byte[] { 0xff, 0xfe }))
|
||||||
|
{
|
||||||
|
encoding = Encoding.Unicode;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StartsWith(bytes, new byte[] { 0xef, 0xbb, 0xbf }))
|
||||||
|
{
|
||||||
|
encoding = Encoding.UTF8;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsUtf8(bytes, bytes.Length))
|
||||||
|
{
|
||||||
|
encoding = new UTF8Encoding(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool StartsWith(IEnumerable<byte> data, IReadOnlyCollection<byte> other)
|
||||||
|
{
|
||||||
|
byte[] arraySelf = data.Take(other.Count).ToArray();
|
||||||
|
return other.SequenceEqual(arraySelf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsUtf8(IReadOnlyList<byte> buffer, int length)
|
||||||
|
{
|
||||||
|
int position = 0;
|
||||||
|
int bytes = 0;
|
||||||
|
while (position < length)
|
||||||
|
{
|
||||||
|
if (!IsValid(buffer, position, length, ref bytes))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
position += bytes;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning disable S3776 // Cognitive Complexity of methods should not be too high
|
||||||
|
private static bool IsValid(IReadOnlyList<byte> buffer, int position, int length, ref int bytes)
|
||||||
|
{
|
||||||
|
if (length > buffer.Count)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid length");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position > length - 1)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte ch = buffer[position];
|
||||||
|
if (ch <= 0x7F)
|
||||||
|
{
|
||||||
|
bytes = 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch >= 0xc2 && ch <= 0xdf)
|
||||||
|
{
|
||||||
|
if (position >= length - 2)
|
||||||
{
|
{
|
||||||
bytes = 0;
|
bytes = 0;
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte ch = buffer[position];
|
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0xbf)
|
||||||
if (ch <= 0x7F)
|
|
||||||
{
|
{
|
||||||
bytes = 1;
|
bytes = 0;
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ch >= 0xc2 && ch <= 0xdf)
|
bytes = 2;
|
||||||
{
|
return true;
|
||||||
if (position >= length - 2)
|
|
||||||
{
|
|
||||||
bytes = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0xbf)
|
|
||||||
{
|
|
||||||
bytes = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes = 2;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch == 0xe0)
|
|
||||||
{
|
|
||||||
if (position >= length - 3)
|
|
||||||
{
|
|
||||||
bytes = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer[position + 1] < 0xa0 || buffer[position + 1] > 0xbf ||
|
|
||||||
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf)
|
|
||||||
{
|
|
||||||
bytes = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes = 3;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch >= 0xe1 && ch <= 0xef)
|
|
||||||
{
|
|
||||||
if (position >= length - 3)
|
|
||||||
{
|
|
||||||
bytes = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0xbf ||
|
|
||||||
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf)
|
|
||||||
{
|
|
||||||
bytes = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes = 3;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch == 0xf0)
|
|
||||||
{
|
|
||||||
if (position >= length - 4)
|
|
||||||
{
|
|
||||||
bytes = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer[position + 1] < 0x90 || buffer[position + 1] > 0xbf ||
|
|
||||||
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf ||
|
|
||||||
buffer[position + 3] < 0x80 || buffer[position + 3] > 0xbf)
|
|
||||||
{
|
|
||||||
bytes = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes = 4;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch == 0xf4)
|
|
||||||
{
|
|
||||||
if (position >= length - 4)
|
|
||||||
{
|
|
||||||
bytes = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0x8f ||
|
|
||||||
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf ||
|
|
||||||
buffer[position + 3] < 0x80 || buffer[position + 3] > 0xbf)
|
|
||||||
{
|
|
||||||
bytes = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes = 4;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch >= 0xf1 && ch <= 0xf3)
|
|
||||||
{
|
|
||||||
if (position >= length - 4)
|
|
||||||
{
|
|
||||||
bytes = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0xbf ||
|
|
||||||
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf ||
|
|
||||||
buffer[position + 3] < 0x80 || buffer[position + 3] > 0xbf)
|
|
||||||
{
|
|
||||||
bytes = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes = 4;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ch == 0xe0)
|
||||||
|
{
|
||||||
|
if (position >= length - 3)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer[position + 1] < 0xa0 || buffer[position + 1] > 0xbf ||
|
||||||
|
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = 3;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch >= 0xe1 && ch <= 0xef)
|
||||||
|
{
|
||||||
|
if (position >= length - 3)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0xbf ||
|
||||||
|
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = 3;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == 0xf0)
|
||||||
|
{
|
||||||
|
if (position >= length - 4)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer[position + 1] < 0x90 || buffer[position + 1] > 0xbf ||
|
||||||
|
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf ||
|
||||||
|
buffer[position + 3] < 0x80 || buffer[position + 3] > 0xbf)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = 4;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == 0xf4)
|
||||||
|
{
|
||||||
|
if (position >= length - 4)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0x8f ||
|
||||||
|
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf ||
|
||||||
|
buffer[position + 3] < 0x80 || buffer[position + 3] > 0xbf)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = 4;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch >= 0xf1 && ch <= 0xf3)
|
||||||
|
{
|
||||||
|
if (position >= length - 4)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0xbf ||
|
||||||
|
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf ||
|
||||||
|
buffer[position + 3] < 0x80 || buffer[position + 3] > 0xbf)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = 4;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
#pragma warning restore S3776 // Cognitive Complexity of methods should not be too high
|
}
|
||||||
}
|
#pragma warning restore S3776 // Cognitive Complexity of methods should not be too high
|
||||||
@@ -1,49 +1,39 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
|
|
||||||
namespace WireMock.Util
|
namespace WireMock.Util;
|
||||||
|
|
||||||
|
internal static class CompressionUtils
|
||||||
{
|
{
|
||||||
internal static class CompressionUtils
|
public static byte[] Compress(string contentEncoding, byte[] data)
|
||||||
{
|
{
|
||||||
public static byte[] Compress(string contentEncoding, byte[] data)
|
using var compressedStream = new MemoryStream();
|
||||||
{
|
using var zipStream = Create(contentEncoding, compressedStream, CompressionMode.Compress);
|
||||||
using (var compressedStream = new MemoryStream())
|
zipStream.Write(data, 0, data.Length);
|
||||||
using (var zipStream = Create(contentEncoding, compressedStream, CompressionMode.Compress))
|
|
||||||
{
|
|
||||||
zipStream.Write(data, 0, data.Length);
|
|
||||||
|
|
||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3
|
||||||
zipStream.Close();
|
zipStream.Close();
|
||||||
#endif
|
#endif
|
||||||
return compressedStream.ToArray();
|
return compressedStream.ToArray();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] Decompress(string contentEncoding, byte[] data)
|
public static byte[] Decompress(string contentEncoding, byte[] data)
|
||||||
|
{
|
||||||
|
using var compressedStream = new MemoryStream(data);
|
||||||
|
using var zipStream = Create(contentEncoding, compressedStream, CompressionMode.Decompress);
|
||||||
|
using var resultStream = new MemoryStream();
|
||||||
|
zipStream.CopyTo(resultStream);
|
||||||
|
return resultStream.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream Create(string contentEncoding, Stream stream, CompressionMode mode)
|
||||||
|
{
|
||||||
|
return contentEncoding switch
|
||||||
{
|
{
|
||||||
using (var compressedStream = new MemoryStream(data))
|
"gzip" => new GZipStream(stream, mode),
|
||||||
using (var zipStream = Create(contentEncoding, compressedStream, CompressionMode.Decompress))
|
"deflate" => new DeflateStream(stream, mode),
|
||||||
using (var resultStream = new MemoryStream())
|
_ => throw new NotSupportedException($"ContentEncoding '{contentEncoding}' is not supported.")
|
||||||
{
|
};
|
||||||
zipStream.CopyTo(resultStream);
|
|
||||||
return resultStream.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Stream Create(string contentEncoding, Stream stream, CompressionMode mode)
|
|
||||||
{
|
|
||||||
switch (contentEncoding)
|
|
||||||
{
|
|
||||||
case "gzip":
|
|
||||||
return new GZipStream(stream, mode);
|
|
||||||
|
|
||||||
case "deflate":
|
|
||||||
return new DeflateStream(stream, mode);
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new NotSupportedException($"ContentEncoding '{contentEncoding}' is not supported.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,32 +1,30 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.Util
|
namespace WireMock.Util;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Some IDictionary Extensions
|
||||||
|
/// </summary>
|
||||||
|
public static class DictionaryExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Some IDictionary Extensions
|
/// Loops the dictionary and executes the specified action.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class DictionaryExtensions
|
/// <typeparam name="TKey">The type of the key.</typeparam>
|
||||||
|
/// <typeparam name="TValue">The type of the value.</typeparam>
|
||||||
|
/// <param name="dictionary">The dictionary to loop (can be null).</param>
|
||||||
|
/// <param name="action">The action.</param>
|
||||||
|
public static void Loop<TKey, TValue>(this IDictionary<TKey, TValue>? dictionary, Action<TKey, TValue> action)
|
||||||
{
|
{
|
||||||
/// <summary>
|
Guard.NotNull(action);
|
||||||
/// Loops the dictionary and executes the specified action.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TKey">The type of the key.</typeparam>
|
|
||||||
/// <typeparam name="TValue">The type of the value.</typeparam>
|
|
||||||
/// <param name="dictionary">The dictionary to loop (can be null).</param>
|
|
||||||
/// <param name="action">The action.</param>
|
|
||||||
public static void Loop<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, [NotNull] Action<TKey, TValue> action)
|
|
||||||
{
|
|
||||||
Guard.NotNull(action, nameof(action));
|
|
||||||
|
|
||||||
if (dictionary != null)
|
if (dictionary != null)
|
||||||
|
{
|
||||||
|
foreach (var entry in dictionary)
|
||||||
{
|
{
|
||||||
foreach (var entry in dictionary)
|
action(entry.Key, entry.Value);
|
||||||
{
|
|
||||||
action(entry.Key, entry.Value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,35 @@
|
|||||||
using JetBrains.Annotations;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using WireMock.Handlers;
|
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
using WireMock.Handlers;
|
||||||
|
|
||||||
namespace WireMock.Util
|
namespace WireMock.Util;
|
||||||
|
|
||||||
|
internal static class FileHelper
|
||||||
{
|
{
|
||||||
internal static class FileHelper
|
private const int NumberOfRetries = 3;
|
||||||
|
private const int DelayOnRetry = 500;
|
||||||
|
|
||||||
|
public static bool TryReadMappingFileWithRetryAndDelay(IFileSystemHandler handler, string path, [NotNullWhen(true)] out string? value)
|
||||||
{
|
{
|
||||||
private const int NumberOfRetries = 3;
|
Guard.NotNull(handler);
|
||||||
private const int DelayOnRetry = 500;
|
Guard.NotNullOrEmpty(path);
|
||||||
|
|
||||||
public static bool TryReadMappingFileWithRetryAndDelay([NotNull] IFileSystemHandler handler, [NotNull] string path, out string value)
|
value = null;
|
||||||
|
|
||||||
|
for (int i = 1; i <= NumberOfRetries; ++i)
|
||||||
{
|
{
|
||||||
Guard.NotNull(handler, nameof(handler));
|
try
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
|
||||||
|
|
||||||
value = null;
|
|
||||||
|
|
||||||
for (int i = 1; i <= NumberOfRetries; ++i)
|
|
||||||
{
|
{
|
||||||
try
|
value = handler.ReadMappingFile(path);
|
||||||
{
|
return true;
|
||||||
value = handler.ReadMappingFile(path);
|
}
|
||||||
return true;
|
catch
|
||||||
}
|
{
|
||||||
catch
|
Thread.Sleep(DelayOnRetry);
|
||||||
{
|
|
||||||
Thread.Sleep(DelayOnRetry);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,91 +1,90 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace WireMock.Util
|
namespace WireMock.Util;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Based on https://github.com/tmenier/Flurl/blob/129565361e135e639f1d44a35a78aea4302ac6ca/src/Flurl.Http/HttpStatusRangeParser.cs
|
||||||
|
/// </summary>
|
||||||
|
internal static class HttpStatusRangeParser
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Based on https://github.com/tmenier/Flurl/blob/129565361e135e639f1d44a35a78aea4302ac6ca/src/Flurl.Http/HttpStatusRangeParser.cs
|
/// Determines whether the specified pattern is match.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class HttpStatusRangeParser
|
/// <param name="pattern">The pattern. (Can be null, in that case it's allowed.)</param>
|
||||||
|
/// <param name="httpStatusCode">The value.</param>
|
||||||
|
/// <exception cref="ArgumentException"><paramref name="pattern"/> is invalid.</exception>
|
||||||
|
public static bool IsMatch(string pattern, object httpStatusCode)
|
||||||
{
|
{
|
||||||
/// <summary>
|
switch (httpStatusCode)
|
||||||
/// Determines whether the specified pattern is match.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pattern">The pattern. (Can be null, in that case it's allowed.)</param>
|
|
||||||
/// <param name="httpStatusCode">The value.</param>
|
|
||||||
/// <exception cref="ArgumentException"><paramref name="pattern"/> is invalid.</exception>
|
|
||||||
public static bool IsMatch(string pattern, object httpStatusCode)
|
|
||||||
{
|
{
|
||||||
switch (httpStatusCode)
|
case int statusCodeAsInteger:
|
||||||
{
|
return IsMatch(pattern, statusCodeAsInteger);
|
||||||
case int statusCodeAsInteger:
|
|
||||||
return IsMatch(pattern, statusCodeAsInteger);
|
|
||||||
|
|
||||||
case string statusCodeAsString:
|
case string statusCodeAsString:
|
||||||
return IsMatch(pattern, int.Parse(statusCodeAsString));
|
return IsMatch(pattern, int.Parse(statusCodeAsString));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified pattern is match.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pattern">The pattern. (Can be null, in that case it's allowed.)</param>
|
||||||
|
/// <param name="httpStatusCode">The value.</param>
|
||||||
|
/// <exception cref="ArgumentException"><paramref name="pattern"/> is invalid.</exception>
|
||||||
|
public static bool IsMatch(string pattern, HttpStatusCode httpStatusCode)
|
||||||
|
{
|
||||||
|
return IsMatch(pattern, (int)httpStatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified pattern is match.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pattern">The pattern. (Can be null, in that case it's allowed.)</param>
|
||||||
|
/// <param name="httpStatusCode">The value.</param>
|
||||||
|
/// <exception cref="ArgumentException"><paramref name="pattern"/> is invalid.</exception>
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
string[] bounds = range.Split('-');
|
||||||
}
|
int lower = 0;
|
||||||
|
int upper = 0;
|
||||||
|
|
||||||
/// <summary>
|
bool valid =
|
||||||
/// Determines whether the specified pattern is match.
|
bounds.Length <= 2 &&
|
||||||
/// </summary>
|
int.TryParse(Regex.Replace(bounds.First().Trim(), "[*xX]", "0"), out lower) &&
|
||||||
/// <param name="pattern">The pattern. (Can be null, in that case it's allowed.)</param>
|
int.TryParse(Regex.Replace(bounds.Last().Trim(), "[*xX]", "9"), out upper);
|
||||||
/// <param name="httpStatusCode">The value.</param>
|
|
||||||
/// <exception cref="ArgumentException"><paramref name="pattern"/> is invalid.</exception>
|
|
||||||
public static bool IsMatch(string pattern, HttpStatusCode httpStatusCode)
|
|
||||||
{
|
|
||||||
return IsMatch(pattern, (int)httpStatusCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
if (!valid)
|
||||||
/// Determines whether the specified pattern is match.
|
{
|
||||||
/// </summary>
|
throw new ArgumentException($"Invalid range pattern: \"{pattern}\". Examples of allowed patterns: \"400\", \"4xx\", \"300,400-403\", \"*\".");
|
||||||
/// <param name="pattern">The pattern. (Can be null, in that case it's allowed.)</param>
|
}
|
||||||
/// <param name="httpStatusCode">The value.</param>
|
|
||||||
/// <exception cref="ArgumentException"><paramref name="pattern"/> is invalid.</exception>
|
if (httpStatusCode >= lower && httpStatusCode <= upper)
|
||||||
public static bool IsMatch(string pattern, int httpStatusCode)
|
|
||||||
{
|
|
||||||
if (pattern == null)
|
|
||||||
{
|
{
|
||||||
return true;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,58 +1,58 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace WireMock.Util
|
namespace WireMock.Util;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Port Utility class
|
||||||
|
/// </summary>
|
||||||
|
public static class PortUtils
|
||||||
{
|
{
|
||||||
|
private static readonly Regex UrlDetailsRegex = new(@"^((?<proto>\w+)://)(?<host>[^/]+?):(?<port>\d+)\/?$", RegexOptions.Compiled);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Port Utility class
|
/// Finds a free TCP port.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class PortUtils
|
/// <remarks>see http://stackoverflow.com/questions/138043/find-the-next-tcp-port-in-net.</remarks>
|
||||||
|
public static int FindFreeTcpPort()
|
||||||
{
|
{
|
||||||
private static readonly Regex UrlDetailsRegex = new Regex(@"^((?<proto>\w+)://)(?<host>[^/]+?):(?<port>\d+)\/?$", RegexOptions.Compiled);
|
TcpListener? tcpListener = null;
|
||||||
|
try
|
||||||
/// <summary>
|
|
||||||
/// Finds a free TCP port.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>see http://stackoverflow.com/questions/138043/find-the-next-tcp-port-in-net.</remarks>
|
|
||||||
public static int FindFreeTcpPort()
|
|
||||||
{
|
{
|
||||||
TcpListener tcpListener = null;
|
tcpListener = new TcpListener(IPAddress.Loopback, 0);
|
||||||
try
|
tcpListener.Start();
|
||||||
{
|
|
||||||
tcpListener = new TcpListener(IPAddress.Loopback, 0);
|
|
||||||
tcpListener.Start();
|
|
||||||
|
|
||||||
return ((IPEndPoint)tcpListener.LocalEndpoint).Port;
|
return ((IPEndPoint)tcpListener.LocalEndpoint).Port;
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
tcpListener?.Stop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
/// <summary>
|
|
||||||
/// Extract the if-isHttps, protocol, host and port from a URL.
|
|
||||||
/// </summary>
|
|
||||||
public static bool TryExtract(string url, out bool isHttps, out string protocol, out string host, out int port)
|
|
||||||
{
|
{
|
||||||
isHttps = false;
|
tcpListener?.Stop();
|
||||||
protocol = null;
|
|
||||||
host = null;
|
|
||||||
port = default;
|
|
||||||
|
|
||||||
var match = UrlDetailsRegex.Match(url);
|
|
||||||
if (match.Success)
|
|
||||||
{
|
|
||||||
protocol = match.Groups["proto"].Value;
|
|
||||||
isHttps = protocol.StartsWith("https", StringComparison.OrdinalIgnoreCase);
|
|
||||||
host = match.Groups["host"].Value;
|
|
||||||
|
|
||||||
return int.TryParse(match.Groups["port"].Value, out port);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extract the if-isHttps, protocol, host and port from a URL.
|
||||||
|
/// </summary>
|
||||||
|
public static bool TryExtract(string url, out bool isHttps, [NotNullWhen(true)] out string? protocol, [NotNullWhen(true)] out string? host, out int port)
|
||||||
|
{
|
||||||
|
isHttps = false;
|
||||||
|
protocol = null;
|
||||||
|
host = null;
|
||||||
|
port = default;
|
||||||
|
|
||||||
|
var match = UrlDetailsRegex.Match(url);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
protocol = match.Groups["proto"].Value;
|
||||||
|
isHttps = protocol.StartsWith("https", StringComparison.OrdinalIgnoreCase);
|
||||||
|
host = match.Groups["host"].Value;
|
||||||
|
|
||||||
|
return int.TryParse(match.Groups["port"].Value, out port);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,38 +1,37 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
|
|
||||||
namespace WireMock.Util
|
namespace WireMock.Util;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Based on https://stackoverflow.com/questions/659887/get-url-parameters-from-a-string-in-net
|
||||||
|
/// </summary>
|
||||||
|
internal static class QueryStringParser
|
||||||
{
|
{
|
||||||
/// <summary>
|
public static IDictionary<string, WireMockList<string>> Parse(string queryString)
|
||||||
/// Based on https://stackoverflow.com/questions/659887/get-url-parameters-from-a-string-in-net
|
|
||||||
/// </summary>
|
|
||||||
internal static class QueryStringParser
|
|
||||||
{
|
{
|
||||||
public static IDictionary<string, WireMockList<string>> Parse(string queryString)
|
if (string.IsNullOrEmpty(queryString))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(queryString))
|
return new Dictionary<string, WireMockList<string>>();
|
||||||
{
|
|
||||||
return new Dictionary<string, WireMockList<string>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
string[] JoinParts(string[] parts)
|
|
||||||
{
|
|
||||||
if (parts.Length > 1)
|
|
||||||
{
|
|
||||||
return parts[1].Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); // support "?key=1,2"
|
|
||||||
}
|
|
||||||
|
|
||||||
return new string[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return queryString.TrimStart('?')
|
|
||||||
.Split(new[] { '&', ';' }, StringSplitOptions.RemoveEmptyEntries) // Support "?key=value;key=anotherValue" and "?key=value&key=anotherValue"
|
|
||||||
.Select(parameter => parameter.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries))
|
|
||||||
.GroupBy(parts => parts[0], JoinParts)
|
|
||||||
.ToDictionary(grouping => grouping.Key, grouping => new WireMockList<string>(grouping.SelectMany(x => x).Select(WebUtility.UrlDecode)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string[] JoinParts(string[] parts)
|
||||||
|
{
|
||||||
|
if (parts.Length > 1)
|
||||||
|
{
|
||||||
|
return parts[1].Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); // support "?key=1,2"
|
||||||
|
}
|
||||||
|
|
||||||
|
return new string[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryString.TrimStart('?')
|
||||||
|
.Split(new[] { '&', ';' }, StringSplitOptions.RemoveEmptyEntries) // Support "?key=value;key=anotherValue" and "?key=value&key=anotherValue"
|
||||||
|
.Select(parameter => parameter.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
.GroupBy(parts => parts[0], JoinParts)
|
||||||
|
.ToDictionary(grouping => grouping.Key, grouping => new WireMockList<string>(grouping.SelectMany(x => x).Select(WebUtility.UrlDecode)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,8 +14,8 @@ internal static class TypeBuilderUtils
|
|||||||
{
|
{
|
||||||
private static readonly ConcurrentDictionary<IDictionary<string, Type>, Type> Types = new();
|
private static readonly ConcurrentDictionary<IDictionary<string, Type>, Type> Types = new();
|
||||||
|
|
||||||
private static readonly ModuleBuilder ModuleBuilder =
|
private static readonly ModuleBuilder ModuleBuilder = AssemblyBuilder
|
||||||
AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("WireMock.Net.Reflection"), AssemblyBuilderAccess.Run)
|
.DefineDynamicAssembly(new AssemblyName("WireMock.Net.Reflection"), AssemblyBuilderAccess.Run)
|
||||||
.DefineDynamicModule("WireMock.Net.Reflection.Module");
|
.DefineDynamicModule("WireMock.Net.Reflection.Module");
|
||||||
|
|
||||||
public static Type BuildType(IDictionary<string, Type> properties, string? name = null)
|
public static Type BuildType(IDictionary<string, Type> properties, string? name = null)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
#if !USE_ASPNETCORE
|
#if !USE_ASPNETCORE
|
||||||
@@ -8,34 +7,33 @@ using Microsoft.Owin;
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace WireMock.Util
|
namespace WireMock.Util;
|
||||||
|
|
||||||
|
internal static class UrlUtils
|
||||||
{
|
{
|
||||||
internal static class UrlUtils
|
public static UrlDetails Parse(Uri uri, PathString pathBase)
|
||||||
{
|
{
|
||||||
public static UrlDetails Parse([NotNull] Uri uri, PathString pathBase)
|
Guard.NotNull(uri);
|
||||||
|
|
||||||
|
if (!pathBase.HasValue)
|
||||||
{
|
{
|
||||||
Guard.NotNull(uri, nameof(uri));
|
return new UrlDetails(uri, uri);
|
||||||
|
|
||||||
if (!pathBase.HasValue)
|
|
||||||
{
|
|
||||||
return new UrlDetails(uri, uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
var builder = new UriBuilder(uri);
|
|
||||||
builder.Path = RemoveFirst(builder.Path, pathBase.Value);
|
|
||||||
|
|
||||||
return new UrlDetails(uri, builder.Uri);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string RemoveFirst(string text, string search)
|
var builder = new UriBuilder(uri);
|
||||||
{
|
builder.Path = RemoveFirst(builder.Path, pathBase.Value);
|
||||||
int pos = text.IndexOf(search, StringComparison.Ordinal);
|
|
||||||
if (pos < 0)
|
|
||||||
{
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
return text.Substring(0, pos) + text.Substring(pos + search.Length);
|
return new UrlDetails(uri, builder.Uri);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private static string RemoveFirst(string text, string search)
|
||||||
|
{
|
||||||
|
int pos = text.IndexOf(search, StringComparison.Ordinal);
|
||||||
|
if (pos < 0)
|
||||||
|
{
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
return text.Substring(0, pos) + text.Substring(pos + search.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Moq;
|
using Moq;
|
||||||
@@ -9,183 +9,195 @@ using WireMock.Owin;
|
|||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace WireMock.Net.Tests.Owin
|
namespace WireMock.Net.Tests.Owin;
|
||||||
|
|
||||||
|
public class MappingMatcherTests
|
||||||
{
|
{
|
||||||
public class MappingMatcherTests
|
private readonly Mock<IWireMockMiddlewareOptions> _optionsMock;
|
||||||
|
private readonly MappingMatcher _sut;
|
||||||
|
|
||||||
|
public MappingMatcherTests()
|
||||||
{
|
{
|
||||||
private readonly Mock<IWireMockMiddlewareOptions> _optionsMock;
|
_optionsMock = new Mock<IWireMockMiddlewareOptions>();
|
||||||
private readonly MappingMatcher _sut;
|
_optionsMock.SetupAllProperties();
|
||||||
|
_optionsMock.Setup(o => o.Mappings).Returns(new ConcurrentDictionary<Guid, IMapping>());
|
||||||
|
_optionsMock.Setup(o => o.LogEntries).Returns(new ConcurrentObservableCollection<LogEntry>());
|
||||||
|
_optionsMock.Setup(o => o.Scenarios).Returns(new ConcurrentDictionary<string, ScenarioState>());
|
||||||
|
|
||||||
public MappingMatcherTests()
|
var loggerMock = new Mock<IWireMockLogger>();
|
||||||
|
loggerMock.SetupAllProperties();
|
||||||
|
loggerMock.Setup(l => l.Error(It.IsAny<string>()));
|
||||||
|
_optionsMock.Setup(o => o.Logger).Returns(loggerMock.Object);
|
||||||
|
|
||||||
|
_sut = new MappingMatcher(_optionsMock.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingMatcher_FindBestMatch_WhenNoMappingsDefined_ShouldReturnNull()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.FindBestMatch(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Match.Should().BeNull();
|
||||||
|
result.Partial.Should().BeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingMatcher_FindBestMatch_WhenMappingThrowsException_ShouldReturnNull()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var mappingMock = new Mock<IMapping>();
|
||||||
|
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Throws<Exception>();
|
||||||
|
|
||||||
|
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
||||||
|
mappings.TryAdd(Guid.NewGuid(), mappingMock.Object);
|
||||||
|
|
||||||
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.FindBestMatch(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Match.Should().BeNull();
|
||||||
|
result.Partial.Should().BeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsFalse_ShouldReturnExactMatch()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||||
|
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
|
var mappings = InitMappings
|
||||||
|
(
|
||||||
|
(guid1, new[] { 0.1 }),
|
||||||
|
(guid2, new[] { 1.0 })
|
||||||
|
);
|
||||||
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.FindBestMatch(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Match.Should().NotBeNull();
|
||||||
|
result.Match!.Mapping.Guid.Should().Be(guid2);
|
||||||
|
result.Match.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
||||||
|
|
||||||
|
result.Partial.Should().NotBeNull();
|
||||||
|
result.Partial!.Mapping.Guid.Should().Be(guid2);
|
||||||
|
result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsFalse_AndNoExactMatch_ShouldReturnNullExactMatch_And_PartialMatch()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||||
|
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
|
var mappings = InitMappings
|
||||||
|
(
|
||||||
|
(guid1, new[] { 0.1 }),
|
||||||
|
(guid2, new[] { 0.9 })
|
||||||
|
);
|
||||||
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.FindBestMatch(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Match.Should().BeNull();
|
||||||
|
|
||||||
|
result.Partial.Should().NotBeNull();
|
||||||
|
result.Partial!.Mapping.Guid.Should().Be(guid2);
|
||||||
|
result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsTrue_ShouldReturnAnyMatch()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||||
|
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
|
|
||||||
|
_optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true);
|
||||||
|
var mappings = InitMappings(
|
||||||
|
(guid1, new[] { 0.1 }),
|
||||||
|
(guid2, new[] { 0.9 })
|
||||||
|
);
|
||||||
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.FindBestMatch(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Match.Should().NotBeNull();
|
||||||
|
result.Match!.Mapping.Guid.Should().Be(guid2);
|
||||||
|
result.Match.RequestMatchResult.AverageTotalScore.Should().Be(0.9);
|
||||||
|
|
||||||
|
result.Partial.Should().NotBeNull();
|
||||||
|
result.Partial!.Mapping.Guid.Should().Be(guid2);
|
||||||
|
result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsFalse_And_WithSameAverageScoreButMoreMatchers_ReturnsMatchWithMoreMatchers()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||||
|
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
|
var mappings = InitMappings(
|
||||||
|
(guid1, new[] { 1.0 }),
|
||||||
|
(guid2, new[] { 1.0, 1.0 })
|
||||||
|
);
|
||||||
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.FindBestMatch(request);
|
||||||
|
|
||||||
|
// Assert and Verify
|
||||||
|
result.Match.Should().NotBeNull();
|
||||||
|
result.Match!.Mapping.Guid.Should().Be(guid2);
|
||||||
|
result.Match.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
||||||
|
|
||||||
|
result.Partial.Should().NotBeNull();
|
||||||
|
result.Partial!.Mapping.Guid.Should().Be(guid2);
|
||||||
|
result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double[] scores)[] matches)
|
||||||
|
{
|
||||||
|
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
||||||
|
|
||||||
|
foreach (var match in matches)
|
||||||
{
|
{
|
||||||
_optionsMock = new Mock<IWireMockMiddlewareOptions>();
|
|
||||||
_optionsMock.SetupAllProperties();
|
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(new ConcurrentDictionary<Guid, IMapping>());
|
|
||||||
_optionsMock.Setup(o => o.LogEntries).Returns(new ConcurrentObservableCollection<LogEntry>());
|
|
||||||
_optionsMock.Setup(o => o.Scenarios).Returns(new ConcurrentDictionary<string, ScenarioState>());
|
|
||||||
|
|
||||||
var loggerMock = new Mock<IWireMockLogger>();
|
|
||||||
loggerMock.SetupAllProperties();
|
|
||||||
loggerMock.Setup(l => l.Error(It.IsAny<string>()));
|
|
||||||
_optionsMock.Setup(o => o.Logger).Returns(loggerMock.Object);
|
|
||||||
|
|
||||||
_sut = new MappingMatcher(_optionsMock.Object);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MappingMatcher_FindBestMatch_WhenNoMappingsDefined_ShouldReturnNull()
|
|
||||||
{
|
|
||||||
// Assign
|
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = _sut.FindBestMatch(request);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.Match.Should().BeNull();
|
|
||||||
result.Partial.Should().BeNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MappingMatcher_FindBestMatch_WhenMappingThrowsException_ShouldReturnNull()
|
|
||||||
{
|
|
||||||
// Assign
|
|
||||||
var mappingMock = new Mock<IMapping>();
|
var mappingMock = new Mock<IMapping>();
|
||||||
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Throws<Exception>();
|
mappingMock.SetupGet(m => m.Guid).Returns(match.guid);
|
||||||
|
|
||||||
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
var requestMatchResult = new RequestMatchResult();
|
||||||
mappings.TryAdd(Guid.NewGuid(), mappingMock.Object);
|
foreach (var score in match.scores)
|
||||||
|
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
|
||||||
|
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = _sut.FindBestMatch(request);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.Match.Should().BeNull();
|
|
||||||
result.Partial.Should().BeNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsFalse_ShouldReturnExactMatch()
|
|
||||||
{
|
|
||||||
// Assign
|
|
||||||
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
|
||||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
|
||||||
var mappings = InitMappings(
|
|
||||||
(guid1, new[] { 0.1 }),
|
|
||||||
(guid2, new[] { 1.0 })
|
|
||||||
);
|
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
|
||||||
|
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = _sut.FindBestMatch(request);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.Match.Mapping.Guid.Should().Be(guid2);
|
|
||||||
result.Match.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
|
||||||
result.Partial.Mapping.Guid.Should().Be(guid2);
|
|
||||||
result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsFalse_AndNoExactmatch_ShouldReturnNullExactMatch_And_PartialMatch()
|
|
||||||
{
|
|
||||||
// Assign
|
|
||||||
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
|
||||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
|
||||||
var mappings = InitMappings(
|
|
||||||
(guid1, new[] { 0.1 }),
|
|
||||||
(guid2, new[] { 0.9 })
|
|
||||||
);
|
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
|
||||||
|
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = _sut.FindBestMatch(request);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.Match.Should().BeNull();
|
|
||||||
result.Partial.Mapping.Guid.Should().Be(guid2);
|
|
||||||
result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(0.9);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsTrue_ShouldReturnAnyMatch()
|
|
||||||
{
|
|
||||||
// Assign
|
|
||||||
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
|
||||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
|
||||||
|
|
||||||
_optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true);
|
|
||||||
var mappings = InitMappings(
|
|
||||||
(guid1, new[] { 0.1 }),
|
|
||||||
(guid2, new[] { 0.9 })
|
|
||||||
);
|
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
|
||||||
|
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = _sut.FindBestMatch(request);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.Match.Mapping.Guid.Should().Be(guid2);
|
|
||||||
result.Match.RequestMatchResult.AverageTotalScore.Should().Be(0.9);
|
|
||||||
result.Partial.Mapping.Guid.Should().Be(guid2);
|
|
||||||
result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(0.9);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void MappingMatcher_FindBestMatch_WhenAllowPartialMappingIsFalse_And_WithSameAverageScoreButMoreMatchers_ReturnsMatchWithMoreMatchers()
|
|
||||||
{
|
|
||||||
// Assign
|
|
||||||
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
|
||||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
|
||||||
var mappings = InitMappings(
|
|
||||||
(guid1, new[] { 1.0 }),
|
|
||||||
(guid2, new[] { 1.0, 1.0 })
|
|
||||||
);
|
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
|
||||||
|
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = _sut.FindBestMatch(request);
|
|
||||||
|
|
||||||
// Assert and Verify
|
|
||||||
result.Match.Mapping.Guid.Should().Be(guid2);
|
|
||||||
result.Match.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
|
||||||
result.Partial.Mapping.Guid.Should().Be(guid2);
|
|
||||||
result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double[] scores)[] matches)
|
|
||||||
{
|
|
||||||
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
|
||||||
|
|
||||||
foreach (var match in matches)
|
|
||||||
{
|
{
|
||||||
var mappingMock = new Mock<IMapping>();
|
requestMatchResult.AddScore(typeof(object), score);
|
||||||
mappingMock.SetupGet(m => m.Guid).Returns(match.guid);
|
|
||||||
|
|
||||||
var requestMatchResult = new RequestMatchResult();
|
|
||||||
foreach (var score in match.scores)
|
|
||||||
{
|
|
||||||
requestMatchResult.AddScore(typeof(object), score);
|
|
||||||
}
|
|
||||||
|
|
||||||
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Returns(requestMatchResult);
|
|
||||||
|
|
||||||
mappings.TryAdd(match.guid, mappingMock.Object);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return mappings;
|
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Returns(requestMatchResult);
|
||||||
|
|
||||||
|
mappings.TryAdd(match.guid, mappingMock.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return mappings;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,48 +1,47 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Moq;
|
using Moq;
|
||||||
using System;
|
using System;
|
||||||
using WireMock.Handlers;
|
using WireMock.Handlers;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace WireMock.Net.Tests.Util
|
namespace WireMock.Net.Tests.Util;
|
||||||
|
|
||||||
|
public class FileHelperTests
|
||||||
{
|
{
|
||||||
public class FileHelperTests
|
[Fact]
|
||||||
|
public void TryReadMappingFileWithRetryAndDelay_WithIFileSystemHandlerOk_ReturnsTrue()
|
||||||
{
|
{
|
||||||
[Fact]
|
// Assign
|
||||||
public void TryReadMappingFileWithRetryAndDelay_WithIFileSystemHandlerOk_ReturnsTrue()
|
var staticMappingHandlerMock = new Mock<IFileSystemHandler>();
|
||||||
{
|
staticMappingHandlerMock.Setup(m => m.ReadMappingFile(It.IsAny<string>())).Returns("text");
|
||||||
// Assign
|
|
||||||
var staticMappingHandlerMock = new Mock<IFileSystemHandler>();
|
|
||||||
staticMappingHandlerMock.Setup(m => m.ReadMappingFile(It.IsAny<string>())).Returns("text");
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
bool result = FileHelper.TryReadMappingFileWithRetryAndDelay(staticMappingHandlerMock.Object, @"c:\temp", out string value);
|
bool result = FileHelper.TryReadMappingFileWithRetryAndDelay(staticMappingHandlerMock.Object, @"c:\temp", out var value);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().BeTrue();
|
result.Should().BeTrue();
|
||||||
value.Should().Be("text");
|
value.Should().Be("text");
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
staticMappingHandlerMock.Verify(m => m.ReadMappingFile(@"c:\temp"), Times.Once);
|
staticMappingHandlerMock.Verify(m => m.ReadMappingFile(@"c:\temp"), Times.Once);
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TryReadMappingFileWithRetryAndDelay_WithIFileSystemHandlerThrows_ReturnsFalse()
|
|
||||||
{
|
|
||||||
// Assign
|
|
||||||
var staticMappingHandlerMock = new Mock<IFileSystemHandler>();
|
|
||||||
staticMappingHandlerMock.Setup(m => m.ReadMappingFile(It.IsAny<string>())).Throws<NotSupportedException>();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
bool result = FileHelper.TryReadMappingFileWithRetryAndDelay(staticMappingHandlerMock.Object, @"c:\temp", out string value);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
result.Should().BeFalse();
|
|
||||||
value.Should().BeNull();
|
|
||||||
|
|
||||||
// Verify
|
|
||||||
staticMappingHandlerMock.Verify(m => m.ReadMappingFile(@"c:\temp"), Times.Exactly(3));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
[Fact]
|
||||||
|
public void TryReadMappingFileWithRetryAndDelay_WithIFileSystemHandlerThrows_ReturnsFalse()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var staticMappingHandlerMock = new Mock<IFileSystemHandler>();
|
||||||
|
staticMappingHandlerMock.Setup(m => m.ReadMappingFile(It.IsAny<string>())).Throws<NotSupportedException>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
bool result = FileHelper.TryReadMappingFileWithRetryAndDelay(staticMappingHandlerMock.Object, @"c:\temp", out var value);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().BeFalse();
|
||||||
|
value.Should().BeNull();
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
staticMappingHandlerMock.Verify(m => m.ReadMappingFile(@"c:\temp"), Times.Exactly(3));
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user