diff --git a/WireMock.Net Solution.sln.DotSettings b/WireMock.Net Solution.sln.DotSettings index b54c5d82d..e6147e2f8 100644 --- a/WireMock.Net Solution.sln.DotSettings +++ b/WireMock.Net Solution.sln.DotSettings @@ -7,6 +7,7 @@ SSL TE TSV + TTL WWW XMS XUA diff --git a/src/WireMock.Net.Abstractions/Models/ITimeSettings.cs b/src/WireMock.Net.Abstractions/Models/ITimeSettings.cs index f9c8dc121..fb4c44f8d 100644 --- a/src/WireMock.Net.Abstractions/Models/ITimeSettings.cs +++ b/src/WireMock.Net.Abstractions/Models/ITimeSettings.cs @@ -1,25 +1,24 @@ using System; -namespace WireMock.Models +namespace WireMock.Models; + +/// +/// TimeSettings: Start, End and TTL +/// +public interface ITimeSettings { /// - /// 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). /// - public interface ITimeSettings - { - /// - /// Gets or sets the DateTime from which this mapping should be used. In case this is not defined, it's used (default behavior). - /// - DateTime? Start { get; set; } + DateTime? Start { get; set; } - /// - /// Gets or sets the DateTime from until this mapping should be used. In case this is not defined, it's used forever (default behavior). - /// - DateTime? End { get; set; } + /// + /// Gets or sets the DateTime from until this mapping should be used. In case this is not defined, it's used forever (default behavior). + /// + DateTime? End { get; set; } - /// - /// Gets or sets the TTL (Time To Live) in seconds for this mapping. In case this is not defined, it's used (default behavior). - /// - int? TTL { get; set; } - } + /// + /// Gets or sets the TTL (Time To Live) in seconds for this mapping. In case this is not defined, it's used (default behavior). + /// + int? TTL { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net/Extensions/TimeSettingsExtensions.cs b/src/WireMock.Net/Extensions/TimeSettingsExtensions.cs index 138ffe1d1..250aeca21 100644 --- a/src/WireMock.Net/Extensions/TimeSettingsExtensions.cs +++ b/src/WireMock.Net/Extensions/TimeSettingsExtensions.cs @@ -1,35 +1,34 @@ using System; -using JetBrains.Annotations; 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; - } + var now = DateTime.Now; + var start = settings.Start ?? now; + DateTime end; - return now >= start && now <= 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; } } \ No newline at end of file diff --git a/src/WireMock.Net/IMapping.cs b/src/WireMock.Net/IMapping.cs index b49dfa934..0b00a8888 100644 --- a/src/WireMock.Net/IMapping.cs +++ b/src/WireMock.Net/IMapping.cs @@ -20,22 +20,22 @@ public interface IMapping /// /// Gets the TimeSettings (Start, End and TTL). /// - ITimeSettings TimeSettings { get; } + ITimeSettings? TimeSettings { get; } /// /// Gets the unique title. /// - string Title { get; } + string? Title { get; } /// /// Gets the description. /// - string Description { get; } + string? Description { get; } /// /// The full filename path for this mapping (only defined for static mappings). /// - string Path { get; set; } + string? Path { get; set; } /// /// Gets the priority. (A low value means higher priority.) diff --git a/src/WireMock.Net/Mapping.cs b/src/WireMock.Net/Mapping.cs index e3fd18c62..108c1244a 100644 --- a/src/WireMock.Net/Mapping.cs +++ b/src/WireMock.Net/Mapping.cs @@ -1,149 +1,147 @@ using System; using System.Threading.Tasks; -using JetBrains.Annotations; using WireMock.Matchers.Request; using WireMock.Models; using WireMock.ResponseProviders; using WireMock.Settings; -namespace WireMock +namespace WireMock; + +/// +/// The Mapping. +/// +public class Mapping : IMapping { + /// + public Guid Guid { get; } + + /// + public string? Title { get; } + + /// + public string? Description { get; } + + /// + public string? Path { get; set; } + + /// + public int Priority { get; } + + /// + public string? Scenario { get; } + + /// + public string? ExecutionConditionState { get; } + + /// + public string? NextState { get; } + + /// + public int? StateTimes { get; } + + /// + public IRequestMatcher RequestMatcher { get; } + + /// + public IResponseProvider Provider { get; } + + /// + public WireMockServerSettings Settings { get; } + + /// + public bool IsStartState => Scenario == null || Scenario != null && NextState != null && ExecutionConditionState == null; + + /// + public bool IsAdminInterface => Provider is DynamicResponseProvider or DynamicAsyncResponseProvider or ProxyAsyncResponseProvider; + + /// + public bool IsProxy => Provider is ProxyAsyncResponseProvider; + + /// + public bool LogMapping => Provider is not (DynamicResponseProvider or DynamicAsyncResponseProvider); + + /// + public IWebhook[]? Webhooks { get; } + + /// + public ITimeSettings? TimeSettings { get; } + /// - /// The Mapping. + /// Initializes a new instance of the class. /// - public class Mapping : IMapping + /// The unique identifier. + /// The unique title (can be null). + /// The description (can be null). + /// The full file path from this mapping title (can be null). + /// The WireMockServerSettings. + /// The request matcher. + /// The provider. + /// The priority for this mapping. + /// The scenario. [Optional] + /// State in which the current mapping can occur. [Optional] + /// The next state which will occur after the current mapping execution. [Optional] + /// Only when the current state is executed this number, the next state which will occur. [Optional] + /// The Webhooks. [Optional] + /// The TimeSettings. [Optional] + 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) { - /// - public Guid Guid { get; } - - /// - public string Title { get; } - - /// - public string Description { get; } - - /// - public string Path { get; set; } - - /// - public int Priority { get; } - - /// - public string Scenario { get; } - - /// - public string ExecutionConditionState { get; } - - /// - public string NextState { get; } - - /// - public int? StateTimes { get; } - - /// - public IRequestMatcher RequestMatcher { get; } - - /// - public IResponseProvider Provider { get; } - - /// - public WireMockServerSettings Settings { get; } - - /// - public bool IsStartState => Scenario == null || Scenario != null && NextState != null && ExecutionConditionState == null; - - /// - public bool IsAdminInterface => Provider is DynamicResponseProvider || Provider is DynamicAsyncResponseProvider || Provider is ProxyAsyncResponseProvider; - - /// - public bool IsProxy => Provider is ProxyAsyncResponseProvider; - - /// - public bool LogMapping => !(Provider is DynamicResponseProvider || Provider is DynamicAsyncResponseProvider); - - /// - public IWebhook[] Webhooks { get; } - - /// - public ITimeSettings TimeSettings { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The unique identifier. - /// The unique title (can be null). - /// The description (can be null). - /// The full file path from this mapping title (can be null). - /// The WireMockServerSettings. - /// The request matcher. - /// The provider. - /// The priority for this mapping. - /// The scenario. [Optional] - /// State in which the current mapping can occur. [Optional] - /// The next state which will occur after the current mapping execution. [Optional] - /// Only when the current state is executed this number, the next state which will occur. [Optional] - /// The Webhooks. [Optional] - /// The TimeSettings. [Optional] - 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; - 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; - } + Guid = guid; + 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; + } - /// - public Task<(IResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(IRequestMessage requestMessage) - { - return Provider.ProvideResponseAsync(requestMessage, Settings); - } + /// + public Task<(IResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(IRequestMessage requestMessage) + { + return Provider.ProvideResponseAsync(requestMessage, Settings); + } - /// - public IRequestMatchResult GetRequestMatchResult(IRequestMessage requestMessage, string nextState) + /// + 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 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; + 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; } } \ No newline at end of file diff --git a/src/WireMock.Net/Matchers/Request/RequestMessageScenarioAndStateMatcher.cs b/src/WireMock.Net/Matchers/Request/RequestMessageScenarioAndStateMatcher.cs index cb374019c..00bb01ee4 100644 --- a/src/WireMock.Net/Matchers/Request/RequestMessageScenarioAndStateMatcher.cs +++ b/src/WireMock.Net/Matchers/Request/RequestMessageScenarioAndStateMatcher.cs @@ -1,46 +1,41 @@ -using JetBrains.Annotations; +namespace WireMock.Matchers.Request; -namespace WireMock.Matchers.Request +/// +/// The scenario and state matcher. +/// +internal class RequestMessageScenarioAndStateMatcher : IRequestMatcher { /// - /// The scenario and state matcher. + /// Execution state condition for the current mapping. /// - internal class RequestMessageScenarioAndStateMatcher : IRequestMatcher - { - /// - /// Execution state condition for the current mapping. - /// - [CanBeNull] - private readonly string _executionConditionState; + private readonly string? _executionConditionState; - /// - /// The next state which will be signaled after the current mapping execution. - /// In case the value is null state will not be changed. - /// - [CanBeNull] - private readonly string _nextState; + /// + /// The next state which will be signaled after the current mapping execution. + /// In case the value is null state will not be changed. + /// + private readonly string? _nextState; - /// - /// Initializes a new instance of the class. - /// - /// The next state. - /// Execution state condition for the current mapping. - public RequestMessageScenarioAndStateMatcher([CanBeNull] string nextState, [CanBeNull] string executionConditionState) - { - _nextState = nextState; - _executionConditionState = executionConditionState; - } + /// + /// Initializes a new instance of the class. + /// + /// The next state. + /// Execution state condition for the current mapping. + public RequestMessageScenarioAndStateMatcher(string? nextState, string? executionConditionState) + { + _nextState = nextState; + _executionConditionState = executionConditionState; + } - /// - public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult) - { - double score = IsMatch(); - return requestMatchResult.AddScore(GetType(), score); - } + /// + 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; - } + private double IsMatch() + { + return Equals(_executionConditionState, _nextState) ? MatchScores.Perfect : MatchScores.Mismatch; } } \ No newline at end of file diff --git a/src/WireMock.Net/Owin/MappingMatcher.cs b/src/WireMock.Net/Owin/MappingMatcher.cs index 303a83489..de78afc63 100644 --- a/src/WireMock.Net/Owin/MappingMatcher.cs +++ b/src/WireMock.Net/Owin/MappingMatcher.cs @@ -25,7 +25,7 @@ public MappingMatcher(IWireMockMiddlewareOptions options) { try { - string nextState = GetNextState(mapping); + var nextState = GetNextState(mapping); mappings.Add(new MappingMatcherResult { @@ -59,7 +59,7 @@ public MappingMatcher(IWireMockMiddlewareOptions options) return (match, partialMatch); } - private string GetNextState(IMapping mapping) + 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. diff --git a/src/WireMock.Net/Proxy/ProxyHelper.cs b/src/WireMock.Net/Proxy/ProxyHelper.cs index a76c655b5..482d049b0 100644 --- a/src/WireMock.Net/Proxy/ProxyHelper.cs +++ b/src/WireMock.Net/Proxy/ProxyHelper.cs @@ -4,6 +4,7 @@ using System.Net.Http; using System.Threading.Tasks; using Stef.Validation; +using WireMock.Constants; using WireMock.Http; using WireMock.Matchers; using WireMock.RequestBuilders; @@ -85,7 +86,7 @@ private IMapping ToMapping(ProxyAndRecordSettings proxyAndRecordSettings, IReque } }); - bool throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true; + var throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true; switch (requestMessage.BodyData?.DetectedBodyType) { case BodyType.Json: @@ -103,6 +104,22 @@ private IMapping ToMapping(ProxyAndRecordSettings proxyAndRecordSettings, IReque 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, // was 0 + scenario: null, + executionConditionState: null, + nextState: null, + stateTimes: null, + webhooks: null, + timeSettings: null + ); } } \ No newline at end of file diff --git a/src/WireMock.Net/ResponseMessageBuilder.cs b/src/WireMock.Net/ResponseMessageBuilder.cs index 1b353c0dc..dac1a3602 100644 --- a/src/WireMock.Net/ResponseMessageBuilder.cs +++ b/src/WireMock.Net/ResponseMessageBuilder.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Net; using WireMock.Admin.Mappings; using WireMock.Constants; using WireMock.Http; @@ -15,6 +16,11 @@ internal static class ResponseMessageBuilder { HttpKnownHeaderNames.ContentType, new WireMockList { 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) { var response = new ResponseMessage diff --git a/src/WireMock.Net/Serialization/TimeSettingsMapper.cs b/src/WireMock.Net/Serialization/TimeSettingsMapper.cs index 6528a78b5..7e78764c2 100644 --- a/src/WireMock.Net/Serialization/TimeSettingsMapper.cs +++ b/src/WireMock.Net/Serialization/TimeSettingsMapper.cs @@ -1,27 +1,26 @@ using WireMock.Models; -namespace WireMock.Serialization +namespace WireMock.Serialization; + +internal static class TimeSettingsMapper { - internal static class TimeSettingsMapper + public static TimeSettingsModel? Map(ITimeSettings? settings) { - public static TimeSettingsModel Map(ITimeSettings settings) + return settings != null ? new TimeSettingsModel { - return settings != null ? new TimeSettingsModel - { - Start = settings.Start, - End = settings.End, - TTL = settings.TTL - } : null; - } + Start = settings.Start, + End = settings.End, + TTL = settings.TTL + } : null; + } - public static ITimeSettings Map(TimeSettingsModel settings) + public static ITimeSettings? Map(TimeSettingsModel? settings) + { + return settings != null ? new TimeSettings { - return settings != null ? new TimeSettings - { - Start = settings.Start, - End = settings.End, - TTL = settings.TTL - } : null; - } + Start = settings.Start, + End = settings.End, + TTL = settings.TTL + } : null; } } \ No newline at end of file diff --git a/src/WireMock.Net/Server/WireMockServer.Admin.cs b/src/WireMock.Net/Server/WireMockServer.Admin.cs index c98b33167..6d8fb11a9 100644 --- a/src/WireMock.Net/Server/WireMockServer.Admin.cs +++ b/src/WireMock.Net/Server/WireMockServer.Admin.cs @@ -20,7 +20,6 @@ using WireMock.Matchers.Request; using WireMock.Proxy; using WireMock.RequestBuilders; -using WireMock.ResponseBuilders; using WireMock.ResponseProviders; using WireMock.Serialization; using WireMock.Settings; @@ -43,10 +42,10 @@ public partial class WireMockServer private const string AdminScenarios = "/__admin/scenarios"; private const string QueryParamReloadStaticMappings = "reloadStaticMappings"; - private readonly Guid _proxyMappingGuid = new("e59914fd-782e-428e-91c1-4810ffb86567"); - private 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 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 Guid ProxyMappingGuid = new("e59914fd-782e-428e-91c1-4810ffb86567"); + private static readonly RegexMatcher AdminRequestContentTypeJson = new ContentTypeMatcher(WireMockConstants.ContentTypeJson, true); + 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 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; @@ -55,21 +54,21 @@ private void InitAdmin() { // __admin/settings 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 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(AdminMappingsWireMockOrg).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, _adminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPostWireMockOrg)); + 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(AdminMappings).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsDelete)); // __admin/mappings/reset Given(Request.Create().WithPath(AdminMappings + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsReset)); // __admin/mappings/{guid} - 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).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDelete)); + 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).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDelete)); // __admin/mappings/save Given(Request.Create().WithPath($"{AdminMappings}/save").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsSave)); @@ -85,8 +84,8 @@ private void InitAdmin() Given(Request.Create().WithPath(AdminRequests + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestsDelete)); // __admin/request/{guid} - 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).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestGet)); + Given(Request.Create().WithPath(AdminRequestsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestDelete)); // __admin/requests/find Given(Request.Create().WithPath(AdminRequests + "/find").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(RequestsFind)); @@ -216,13 +215,13 @@ private void InitProxyAndRecord(WireMockServerSettings settings) if (settings.ProxyAndRecordSettings == null) { _httpClientForProxy = null; - DeleteMapping(_proxyMappingGuid); + DeleteMapping(ProxyMappingGuid); return; } _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) { proxyRespondProvider.AtPriority(WireMockConstants.ProxyPriority); @@ -359,7 +358,7 @@ private IResponseMessage MappingPut(IRequestMessage requestMessage) var mappingModel = DeserializeObject(requestMessage); 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) @@ -368,13 +367,13 @@ private IResponseMessage MappingDelete(IRequestMessage requestMessage) 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)); } diff --git a/src/WireMock.Net/Server/WireMockServer.cs b/src/WireMock.Net/Server/WireMockServer.cs index 9cf9d64cb..c44a5ff0c 100644 --- a/src/WireMock.Net/Server/WireMockServer.cs +++ b/src/WireMock.Net/Server/WireMockServer.cs @@ -80,7 +80,7 @@ public partial class WireMockServer : IWireMockServer /// Gets the scenarios. /// [PublicAPI] - public ConcurrentDictionary Scenarios => new ConcurrentDictionary(_options.Scenarios); + public ConcurrentDictionary Scenarios => new(_options.Scenarios); #region IDisposable Members /// @@ -414,10 +414,10 @@ public void AllowPartialMapping(bool allow = true) /// [PublicAPI] - public void SetAzureADAuthentication([NotNull] string tenant, [NotNull] string audience) + public void SetAzureADAuthentication(string tenant, string audience) { - Guard.NotNull(tenant, nameof(tenant)); - Guard.NotNull(audience, nameof(audience)); + Guard.NotNull(tenant); + Guard.NotNull(audience); #if NETSTANDARD1_3 throw new NotSupportedException("AzureADAuthentication is not supported for NETStandard 1.3"); @@ -445,14 +445,14 @@ public void RemoveAuthentication() /// [PublicAPI] - public void SetMaxRequestLogCount([CanBeNull] int? maxRequestLogCount) + public void SetMaxRequestLogCount(int? maxRequestLogCount) { _options.MaxRequestLogCount = maxRequestLogCount; } /// [PublicAPI] - public void SetRequestLogExpirationDuration([CanBeNull] int? requestLogExpirationDuration) + public void SetRequestLogExpirationDuration(int? requestLogExpirationDuration) { _options.RequestLogExpirationDuration = requestLogExpirationDuration; } @@ -542,12 +542,12 @@ private void InitSettings(WireMockServerSettings settings) { 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)) { - SetAzureADAuthentication(settings.AdminAzureADTenant, settings.AdminAzureADAudience); + SetAzureADAuthentication(settings.AdminAzureADTenant!, settings.AdminAzureADAudience!); } InitAdmin(); diff --git a/test/WireMock.Net.Tests/WireMockServer.Proxy.cs b/test/WireMock.Net.Tests/WireMockServer.Proxy.cs index 57df7716f..58b317c32 100644 --- a/test/WireMock.Net.Tests/WireMockServer.Proxy.cs +++ b/test/WireMock.Net.Tests/WireMockServer.Proxy.cs @@ -18,688 +18,723 @@ using WireMock.Util; using Xunit; -namespace WireMock.Net.Tests +namespace WireMock.Net.Tests; + +public class WireMockServerProxyTests { - public class WireMockServerProxyTests + [Fact(Skip = "Fails in Linux CI")] + public async Task WireMockServer_ProxySSL_Should_log_proxied_requests() { - [Fact(Skip = "Fails in Linux CI")] - public async Task WireMockServer_ProxySSL_Should_log_proxied_requests() + // Assign + var settings = new WireMockServerSettings { - // Assign - var settings = new WireMockServerSettings + UseSSL = true, + ProxyAndRecordSettings = new ProxyAndRecordSettings { - UseSSL = true, - ProxyAndRecordSettings = new ProxyAndRecordSettings - { - Url = "https://www.google.com", - SaveMapping = true, - SaveMappingToFile = false - } + Url = "https://www.google.com", + SaveMapping = true, + SaveMappingToFile = false + } - }; - var server = WireMockServer.Start(settings); + }; + var server = WireMockServer.Start(settings); - // Act - var requestMessage = new HttpRequestMessage - { - Method = HttpMethod.Get, - RequestUri = new Uri(server.Urls[0]) - }; - var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; - await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false); - - // Assert - Check.That(server.Mappings).HasSize(2); - Check.That(server.LogEntries).HasSize(1); - } + // Act + var requestMessage = new HttpRequestMessage + { + Method = HttpMethod.Get, + RequestUri = new Uri(server.Urls[0]) + }; + var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; + await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false); + + // Assert + Check.That(server.Mappings).HasSize(2); + Check.That(server.LogEntries).HasSize(1); + } - [Fact] - public async Task WireMockServer_Proxy_With_SaveMapping_Is_True_And_SaveMappingToFile_Is_False_Should_AddInternalMappingOnly() + [Fact] + public async Task WireMockServer_Proxy_AdminFalse_With_SaveMapping_Is_True_And_SaveMappingToFile_Is_False_Should_AddInternalMappingOnly() + { + // Assign + var settings = new WireMockServerSettings { - // Assign - var settings = new WireMockServerSettings + ProxyAndRecordSettings = new ProxyAndRecordSettings { - ProxyAndRecordSettings = new ProxyAndRecordSettings - { - Url = "http://www.google.com", - SaveMapping = true, - SaveMappingToFile = false - } - }; - var server = WireMockServer.Start(settings); - - // Act + Url = "http://www.google.com", + SaveMapping = true, + SaveMappingToFile = false + } + }; + var server = WireMockServer.Start(settings); + + // Act + var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; + var client = new HttpClient(httpClientHandler); + for (int i = 0; i < 5; i++) + { var requestMessage = new HttpRequestMessage { Method = HttpMethod.Get, - RequestUri = new Uri(server.Urls[0]) + RequestUri = new Uri(server.Url!) }; - var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; - await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false); - - // Assert - server.Mappings.Should().HaveCount(2); + await client.SendAsync(requestMessage).ConfigureAwait(false); } - [Fact] - public async Task WireMockServer_Proxy_With_SaveMapping_Is_False_And_SaveMappingToFile_Is_True_ShouldSaveMappingToFile() - { - // Assign - var fileSystemHandlerMock = new Mock(); - fileSystemHandlerMock.Setup(f => f.GetMappingFolder()).Returns("m"); + // Assert + server.Mappings.Should().HaveCountGreaterOrEqualTo(2); // For .NET 4.5.2 and 4.6.x this is 3? + } - var settings = new WireMockServerSettings + [Fact] + public async Task WireMockServer_Proxy_AdminTrue_With_SaveMapping_Is_True_And_SaveMappingToFile_Is_False_Should_AddInternalMappingOnly() + { + // Assign + var settings = new WireMockServerSettings + { + ProxyAndRecordSettings = new ProxyAndRecordSettings { - ProxyAndRecordSettings = new ProxyAndRecordSettings - { - Url = "http://www.google.com", - SaveMapping = false, - SaveMappingToFile = true - }, - FileSystemHandler = fileSystemHandlerMock.Object - }; - var server = WireMockServer.Start(settings); - - // Act + Url = "http://www.google.com", + SaveMapping = true, + SaveMappingToFile = false + }, + StartAdminInterface = true + }; + var server = WireMockServer.Start(settings); + + // Act + for (int i = 0; i < 5; i++) + { var requestMessage = new HttpRequestMessage { Method = HttpMethod.Get, - RequestUri = new Uri(server.Urls[0]) + RequestUri = new Uri(server.Url!) }; var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false); + } - // Assert - server.Mappings.Should().HaveCount(1); + // Assert + server.Mappings.Should().HaveCountGreaterOrEqualTo(28); // For .NET 4.5.2 and 4.6.x this is 29? + } - // Verify - fileSystemHandlerMock.Verify(f => f.WriteMappingFile(It.IsAny(), It.IsAny()), Times.Once); - } + [Fact] + public async Task WireMockServer_Proxy_With_SaveMapping_Is_False_And_SaveMappingToFile_Is_True_ShouldSaveMappingToFile() + { + // Assign + var fileSystemHandlerMock = new Mock(); + fileSystemHandlerMock.Setup(f => f.GetMappingFolder()).Returns("m"); - [Fact] - public async Task WireMockServer_Proxy_With_SaveMappingForStatusCodePattern_Is_False_Should_Not_SaveMapping() + var settings = new WireMockServerSettings { - // Assign - var fileSystemHandlerMock = new Mock(); - fileSystemHandlerMock.Setup(f => f.GetMappingFolder()).Returns("m"); - - var settings = new WireMockServerSettings + ProxyAndRecordSettings = new ProxyAndRecordSettings { - ProxyAndRecordSettings = new ProxyAndRecordSettings - { - Url = "http://www.google.com", - SaveMapping = true, - SaveMappingToFile = true, - SaveMappingForStatusCodePattern = "999" // Just make sure that we don't want this mapping - }, - FileSystemHandler = fileSystemHandlerMock.Object - }; - var server = WireMockServer.Start(settings); + Url = "http://www.google.com", + SaveMapping = false, + SaveMappingToFile = true + }, + FileSystemHandler = fileSystemHandlerMock.Object + }; + var server = WireMockServer.Start(settings); + + // Act + var requestMessage = new HttpRequestMessage + { + Method = HttpMethod.Get, + RequestUri = new Uri(server.Urls[0]) + }; + var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; + await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false); - // Act - var requestMessage = new HttpRequestMessage - { - Method = HttpMethod.Get, - RequestUri = new Uri(server.Urls[0]) - }; - var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; - await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false); + // Assert + server.Mappings.Should().HaveCount(1); - // Assert - server.Mappings.Should().HaveCount(1); + // Verify + fileSystemHandlerMock.Verify(f => f.WriteMappingFile(It.IsAny(), It.IsAny()), Times.Once); + } - // Verify - fileSystemHandlerMock.Verify(f => f.WriteMappingFile(It.IsAny(), It.IsAny()), Times.Never); - } + [Fact] + public async Task WireMockServer_Proxy_With_SaveMappingForStatusCodePattern_Is_False_Should_Not_SaveMapping() + { + // Assign + var fileSystemHandlerMock = new Mock(); + fileSystemHandlerMock.Setup(f => f.GetMappingFolder()).Returns("m"); - [Fact] - public async Task WireMockServer_Proxy_Should_log_proxied_requests() + var settings = new WireMockServerSettings { - // Assign - var settings = new WireMockServerSettings + ProxyAndRecordSettings = new ProxyAndRecordSettings { - ProxyAndRecordSettings = new ProxyAndRecordSettings - { - Url = "http://www.google.com", - SaveMapping = true, - SaveMappingToFile = false - } - }; - var server = WireMockServer.Start(settings); - - // Act - var requestMessage = new HttpRequestMessage - { - Method = HttpMethod.Get, - RequestUri = new Uri(server.Urls[0]) - }; - var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; - await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false); - - // Assert - server.Mappings.Should().HaveCount(2); - server.LogEntries.Should().HaveCount(1); - } - - [Fact] - public async Task WireMockServer_Proxy_Should_proxy_responses() + Url = "http://www.google.com", + SaveMapping = true, + SaveMappingToFile = true, + SaveMappingForStatusCodePattern = "999" // Just make sure that we don't want this mapping + }, + FileSystemHandler = fileSystemHandlerMock.Object + }; + var server = WireMockServer.Start(settings); + + // Act + var requestMessage = new HttpRequestMessage { - // Assign - string path = $"/prx_{Guid.NewGuid()}"; - var server = WireMockServer.Start(); - server - .Given(Request.Create().WithPath(path)) - .RespondWith(Response.Create().WithProxy("http://www.google.com")); - - // Act - var requestMessage = new HttpRequestMessage - { - Method = HttpMethod.Get, - RequestUri = new Uri($"{server.Urls[0]}{path}") - }; - var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; - var response = await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false); - string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + Method = HttpMethod.Get, + RequestUri = new Uri(server.Urls[0]) + }; + var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; + await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false); - // Assert - Check.That(server.Mappings).HasSize(1); - Check.That(server.LogEntries).HasSize(1); - Check.That(content).Contains("google"); + // Assert + server.Mappings.Should().HaveCount(1); - server.Stop(); - } + // Verify + fileSystemHandlerMock.Verify(f => f.WriteMappingFile(It.IsAny(), It.IsAny()), Times.Never); + } - [Fact] - public async Task WireMockServer_Proxy_Should_preserve_content_header_in_proxied_request() + [Fact] + public async Task WireMockServer_Proxy_Should_log_proxied_requests() + { + // Assign + var settings = new WireMockServerSettings { - // Assign - string path = $"/prx_{Guid.NewGuid()}"; - var serverForProxyForwarding = WireMockServer.Start(); - serverForProxyForwarding - .Given(Request.Create().WithPath(path)) - .RespondWith(Response.Create()); - - var settings = new WireMockServerSettings + ProxyAndRecordSettings = new ProxyAndRecordSettings { - ProxyAndRecordSettings = new ProxyAndRecordSettings - { - Url = serverForProxyForwarding.Urls[0], - SaveMapping = true, - SaveMappingToFile = false - } - }; - var server = WireMockServer.Start(settings); - - // Act - var requestMessage = new HttpRequestMessage - { - Method = HttpMethod.Post, - RequestUri = new Uri($"{server.Urls[0]}{path}"), - Content = new StringContent("stringContent", Encoding.ASCII) - }; - requestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain"); - requestMessage.Content.Headers.Add("bbb", "test"); - await new HttpClient().SendAsync(requestMessage).ConfigureAwait(false); - - // Assert - var receivedRequest = serverForProxyForwarding.LogEntries.First().RequestMessage; - Check.That(receivedRequest.BodyData.BodyAsString).IsEqualTo("stringContent"); - Check.That(receivedRequest.Headers).ContainsKey("Content-Type"); - Check.That(receivedRequest.Headers["Content-Type"].First()).Contains("text/plain"); - Check.That(receivedRequest.Headers).ContainsKey("bbb"); - - // check that new proxied mapping is added - Check.That(server.Mappings).HasSize(2); - } - - [Fact] - public async Task WireMockServer_Proxy_Should_preserve_Authorization_header_in_proxied_request() + Url = "http://www.google.com", + SaveMapping = true, + SaveMappingToFile = false + } + }; + var server = WireMockServer.Start(settings); + + // Act + var requestMessage = new HttpRequestMessage { - // Assign - string path = $"/prx_{Guid.NewGuid()}"; - var serverForProxyForwarding = WireMockServer.Start(); - serverForProxyForwarding - .Given(Request.Create().WithPath(path)) - .RespondWith(Response.Create().WithCallback(x => new ResponseMessage - { - BodyData = new BodyData - { - BodyAsString = x.Headers["Authorization"].ToString(), - DetectedBodyType = Types.BodyType.String - } - })); - - var settings = new WireMockServerSettings - { - ProxyAndRecordSettings = new ProxyAndRecordSettings - { - Url = serverForProxyForwarding.Urls[0], - SaveMapping = true, - SaveMappingToFile = false - } - }; - var server = WireMockServer.Start(settings); - - // Act - var requestMessage = new HttpRequestMessage - { - Method = HttpMethod.Post, - RequestUri = new Uri($"{server.Urls[0]}{path}"), - Content = new StringContent("stringContent", Encoding.ASCII) - }; - requestMessage.Headers.Authorization = new AuthenticationHeaderValue("BASIC", "test-A"); - var result = await new HttpClient().SendAsync(requestMessage).ConfigureAwait(false); - - // Assert - (await result.Content.ReadAsStringAsync().ConfigureAwait(false)).Should().Be("BASIC test-A"); - - var receivedRequest = serverForProxyForwarding.LogEntries.First().RequestMessage; - var authorizationHeader = receivedRequest.Headers["Authorization"].ToString().Should().Be("BASIC test-A"); - - server.Mappings.Should().HaveCount(2); - var authorizationRequestMessageHeaderMatcher = ((Request)server.Mappings.Single(m => !m.IsAdminInterface).RequestMatcher) - .GetRequestMessageMatcher(x => x.Matchers.Any(m => m.GetPatterns().Contains("BASIC test-A"))); - authorizationRequestMessageHeaderMatcher.Should().NotBeNull(); - } + Method = HttpMethod.Get, + RequestUri = new Uri(server.Urls[0]) + }; + var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; + await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false); + + // Assert + server.Mappings.Should().HaveCount(2); + server.LogEntries.Should().HaveCount(1); + } - [Fact] - public async Task WireMockServer_Proxy_Should_exclude_ExcludedHeaders_in_mapping() + [Fact] + public async Task WireMockServer_Proxy_Should_proxy_responses() + { + // Assign + string path = $"/prx_{Guid.NewGuid()}"; + var server = WireMockServer.Start(); + server + .Given(Request.Create().WithPath(path)) + .RespondWith(Response.Create().WithProxy("http://www.google.com")); + + // Act + var requestMessage = new HttpRequestMessage { - // Assign - string path = $"/prx_{Guid.NewGuid()}"; - var serverForProxyForwarding = WireMockServer.Start(); - serverForProxyForwarding - .Given(Request.Create().WithPath(path)) - .RespondWith(Response.Create()); - - var settings = new WireMockServerSettings - { - ProxyAndRecordSettings = new ProxyAndRecordSettings - { - Url = serverForProxyForwarding.Urls[0], - SaveMapping = true, - SaveMappingToFile = false, - ExcludedHeaders = new[] { "excluded-header-X" } - } - }; - var server = WireMockServer.Start(settings); - var defaultMapping = server.Mappings.First(); + Method = HttpMethod.Get, + RequestUri = new Uri($"{server.Urls[0]}{path}") + }; + var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; + var response = await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false); + string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + + // Assert + Check.That(server.Mappings).HasSize(1); + Check.That(server.LogEntries).HasSize(1); + Check.That(content).Contains("google"); + + server.Stop(); + } - // Act - var requestMessage = new HttpRequestMessage + [Fact] + public async Task WireMockServer_Proxy_Should_preserve_content_header_in_proxied_request() + { + // Assign + string path = $"/prx_{Guid.NewGuid()}"; + var serverForProxyForwarding = WireMockServer.Start(); + serverForProxyForwarding + .Given(Request.Create().WithPath(path)) + .RespondWith(Response.Create()); + + var settings = new WireMockServerSettings + { + ProxyAndRecordSettings = new ProxyAndRecordSettings { - Method = HttpMethod.Post, - RequestUri = new Uri($"{server.Urls[0]}{path}"), - Content = new StringContent("stringContent") - }; - requestMessage.Headers.Add("foobar", "exact_match"); - requestMessage.Headers.Add("ok", "ok-value"); - await new HttpClient().SendAsync(requestMessage).ConfigureAwait(false); - - // Assert - var mapping = server.Mappings.FirstOrDefault(m => m.Guid != defaultMapping.Guid); - Check.That(mapping).IsNotNull(); - var matchers = ((Request)mapping.RequestMatcher).GetRequestMessageMatchers().Select(m => m.Name).ToList(); - Check.That(matchers).Not.Contains("excluded-header-X"); - Check.That(matchers).Contains("ok"); - } - - [Fact] - public async Task WireMockServer_Proxy_Should_exclude_ExcludedCookies_in_mapping() + Url = serverForProxyForwarding.Urls[0], + SaveMapping = true, + SaveMappingToFile = false + } + }; + var server = WireMockServer.Start(settings); + + // Act + var requestMessage = new HttpRequestMessage { - // Assign - string path = $"/prx_{Guid.NewGuid()}"; - var serverForProxyForwarding = WireMockServer.Start(); - serverForProxyForwarding - .Given(Request.Create().WithPath(path)) - .RespondWith(Response.Create()); + Method = HttpMethod.Post, + RequestUri = new Uri($"{server.Urls[0]}{path}"), + Content = new StringContent("stringContent", Encoding.ASCII) + }; + requestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain"); + requestMessage.Content.Headers.Add("bbb", "test"); + await new HttpClient().SendAsync(requestMessage).ConfigureAwait(false); + + // Assert + var receivedRequest = serverForProxyForwarding.LogEntries.First().RequestMessage; + Check.That(receivedRequest.BodyData.BodyAsString).IsEqualTo("stringContent"); + Check.That(receivedRequest.Headers).ContainsKey("Content-Type"); + Check.That(receivedRequest.Headers["Content-Type"].First()).Contains("text/plain"); + Check.That(receivedRequest.Headers).ContainsKey("bbb"); + + // check that new proxied mapping is added + Check.That(server.Mappings).HasSize(2); + } - var settings = new WireMockServerSettings + [Fact] + public async Task WireMockServer_Proxy_Should_preserve_Authorization_header_in_proxied_request() + { + // Assign + string path = $"/prx_{Guid.NewGuid()}"; + var serverForProxyForwarding = WireMockServer.Start(); + serverForProxyForwarding + .Given(Request.Create().WithPath(path)) + .RespondWith(Response.Create().WithCallback(x => new ResponseMessage { - ProxyAndRecordSettings = new ProxyAndRecordSettings + BodyData = new BodyData { - Url = serverForProxyForwarding.Urls[0], - SaveMapping = true, - SaveMappingToFile = false, - ExcludedCookies = new[] { "ASP.NET_SessionId" } + BodyAsString = x.Headers!["Authorization"].ToString(), + DetectedBodyType = Types.BodyType.String } - }; - var server = WireMockServer.Start(settings); - var defaultMapping = server.Mappings.First(); + })); - // Act - var requestMessage = new HttpRequestMessage + var settings = new WireMockServerSettings + { + ProxyAndRecordSettings = new ProxyAndRecordSettings { - Method = HttpMethod.Post, - RequestUri = new Uri($"{server.Urls[0]}{path}"), - Content = new StringContent("stringContent") - }; - - var cookieContainer = new CookieContainer(3); - cookieContainer.Add(new Uri("http://localhost"), new Cookie("ASP.NET_SessionId", "exact_match")); - cookieContainer.Add(new Uri("http://localhost"), new Cookie("AsP.NeT_SessIonID", "case_mismatch")); - cookieContainer.Add(new Uri("http://localhost"), new Cookie("GoodCookie", "I_should_pass")); - - var handler = new HttpClientHandler { CookieContainer = cookieContainer }; - await new HttpClient(handler).SendAsync(requestMessage).ConfigureAwait(false); - - // Assert - var mapping = server.Mappings.FirstOrDefault(m => m.Guid != defaultMapping.Guid); - Check.That(mapping).IsNotNull(); - - var matchers = ((Request)mapping.RequestMatcher).GetRequestMessageMatchers().Select(m => m.Name).ToList(); - Check.That(matchers).Not.Contains("ASP.NET_SessionId"); - Check.That(matchers).Not.Contains("AsP.NeT_SessIonID"); - Check.That(matchers).Contains("GoodCookie"); - } - - [Fact] - public async Task WireMockServer_Proxy_Should_preserve_content_header_in_proxied_request_with_empty_content() + Url = serverForProxyForwarding.Urls[0], + SaveMapping = true, + SaveMappingToFile = false + } + }; + var server = WireMockServer.Start(settings); + + // Act + var requestMessage = new HttpRequestMessage { - // Assign - string path = $"/prx_{Guid.NewGuid()}"; - var serverForProxyForwarding = WireMockServer.Start(); - serverForProxyForwarding - .Given(Request.Create().WithPath(path)) - .RespondWith(Response.Create()); - - var server = WireMockServer.Start(); - server - .Given(Request.Create().WithPath("/*")) - .RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0])); + Method = HttpMethod.Post, + RequestUri = new Uri($"{server.Urls[0]}{path}"), + Content = new StringContent("stringContent", Encoding.ASCII) + }; + requestMessage.Headers.Authorization = new AuthenticationHeaderValue("BASIC", "test-A"); + var result = await new HttpClient().SendAsync(requestMessage).ConfigureAwait(false); + + // Assert + (await result.Content.ReadAsStringAsync().ConfigureAwait(false)).Should().Be("BASIC test-A"); + + var receivedRequest = serverForProxyForwarding.LogEntries.First().RequestMessage; + receivedRequest.Headers!["Authorization"].ToString().Should().Be("BASIC test-A"); + + server.Mappings.Should().HaveCount(2); + var authorizationRequestMessageHeaderMatcher = ((Request)server.Mappings.Single(m => !m.IsAdminInterface).RequestMatcher) + .GetRequestMessageMatcher(x => x.Matchers.Any(m => m.GetPatterns().Contains("BASIC test-A"))); + authorizationRequestMessageHeaderMatcher.Should().NotBeNull(); + } - // Act - var requestMessage = new HttpRequestMessage + [Fact] + public async Task WireMockServer_Proxy_Should_exclude_ExcludedHeaders_in_mapping() + { + // Assign + string path = $"/prx_{Guid.NewGuid()}"; + var serverForProxyForwarding = WireMockServer.Start(); + serverForProxyForwarding + .Given(Request.Create().WithPath(path)) + .RespondWith(Response.Create()); + + var settings = new WireMockServerSettings + { + ProxyAndRecordSettings = new ProxyAndRecordSettings { - Method = HttpMethod.Post, - RequestUri = new Uri($"{server.Urls[0]}{path}"), - Content = new StringContent("") - }; - requestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain"); - await new HttpClient().SendAsync(requestMessage).ConfigureAwait(false); - - // Assert - var receivedRequest = serverForProxyForwarding.LogEntries.First().RequestMessage; - Check.That(receivedRequest.BodyData.BodyAsString).IsEqualTo(""); - Check.That(receivedRequest.Headers).ContainsKey("Content-Type"); - Check.That(receivedRequest.Headers["Content-Type"].First()).Contains("text/plain"); - } - - [Fact] - public async Task WireMockServer_Proxy_Should_preserve_content_header_in_proxied_response() + Url = serverForProxyForwarding.Urls[0], + SaveMapping = true, + SaveMappingToFile = false, + ExcludedHeaders = new[] { "excluded-header-X" } + } + }; + var server = WireMockServer.Start(settings); + var defaultMapping = server.Mappings.First(); + + // Act + var requestMessage = new HttpRequestMessage { - // Assign - string path = $"/prx_{Guid.NewGuid()}"; - var serverForProxyForwarding = WireMockServer.Start(); - serverForProxyForwarding - .Given(Request.Create().WithPath(path)) - .RespondWith(Response.Create() - .WithBody("body") - .WithHeader("Content-Type", "text/plain")); - - var server = WireMockServer.Start(); - server - .Given(Request.Create().WithPath(path)) - .RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0])); + Method = HttpMethod.Post, + RequestUri = new Uri($"{server.Urls[0]}{path}"), + Content = new StringContent("stringContent") + }; + requestMessage.Headers.Add("foobar", "exact_match"); + requestMessage.Headers.Add("ok", "ok-value"); + await new HttpClient().SendAsync(requestMessage).ConfigureAwait(false); + + // Assert + var mapping = server.Mappings.FirstOrDefault(m => m.Guid != defaultMapping.Guid); + Check.That(mapping).IsNotNull(); + var matchers = ((Request)mapping.RequestMatcher).GetRequestMessageMatchers().Select(m => m.Name).ToList(); + Check.That(matchers).Not.Contains("excluded-header-X"); + Check.That(matchers).Contains("ok"); + } - // Act - var requestMessage = new HttpRequestMessage + [Fact] + public async Task WireMockServer_Proxy_Should_exclude_ExcludedCookies_in_mapping() + { + // Assign + string path = $"/prx_{Guid.NewGuid()}"; + var serverForProxyForwarding = WireMockServer.Start(); + serverForProxyForwarding + .Given(Request.Create().WithPath(path)) + .RespondWith(Response.Create()); + + var settings = new WireMockServerSettings + { + ProxyAndRecordSettings = new ProxyAndRecordSettings { - Method = HttpMethod.Get, - RequestUri = new Uri($"{server.Urls[0]}{path}") - }; - var response = await new HttpClient().SendAsync(requestMessage).ConfigureAwait(false); - - // Assert - Check.That(await response.Content.ReadAsStringAsync().ConfigureAwait(false)).IsEqualTo("body"); - Check.That(response.Content.Headers.Contains("Content-Type")).IsTrue(); - Check.That(response.Content.Headers.GetValues("Content-Type")).ContainsExactly("text/plain"); - } - - [Fact] - public async Task WireMockServer_Proxy_Should_change_absolute_location_header_in_proxied_response() + Url = serverForProxyForwarding.Urls[0], + SaveMapping = true, + SaveMappingToFile = false, + ExcludedCookies = new[] { "ASP.NET_SessionId" } + } + }; + var server = WireMockServer.Start(settings); + var defaultMapping = server.Mappings.First(); + + // Act + var requestMessage = new HttpRequestMessage { - // Assign - string path = $"/prx_{Guid.NewGuid()}"; - var settings = new WireMockServerSettings { AllowPartialMapping = false }; - - var serverForProxyForwarding = WireMockServer.Start(settings); - serverForProxyForwarding - .Given(Request.Create().WithPath(path)) - .RespondWith(Response.Create() - .WithStatusCode(HttpStatusCode.Redirect) - .WithHeader("Location", "/testpath")); + Method = HttpMethod.Post, + RequestUri = new Uri($"{server.Urls[0]}{path}"), + Content = new StringContent("stringContent") + }; + + var cookieContainer = new CookieContainer(3); + cookieContainer.Add(new Uri("http://localhost"), new Cookie("ASP.NET_SessionId", "exact_match")); + cookieContainer.Add(new Uri("http://localhost"), new Cookie("AsP.NeT_SessIonID", "case_mismatch")); + cookieContainer.Add(new Uri("http://localhost"), new Cookie("GoodCookie", "I_should_pass")); + + var handler = new HttpClientHandler { CookieContainer = cookieContainer }; + await new HttpClient(handler).SendAsync(requestMessage).ConfigureAwait(false); + + // Assert + var mapping = server.Mappings.FirstOrDefault(m => m.Guid != defaultMapping.Guid); + Check.That(mapping).IsNotNull(); + + var matchers = ((Request)mapping.RequestMatcher).GetRequestMessageMatchers().Select(m => m.Name).ToList(); + Check.That(matchers).Not.Contains("ASP.NET_SessionId"); + Check.That(matchers).Not.Contains("AsP.NeT_SessIonID"); + Check.That(matchers).Contains("GoodCookie"); + } - var server = WireMockServer.Start(settings); - server - .Given(Request.Create().WithPath(path).UsingAnyMethod()) - .RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0])); + [Fact] + public async Task WireMockServer_Proxy_Should_preserve_content_header_in_proxied_request_with_empty_content() + { + // Assign + string path = $"/prx_{Guid.NewGuid()}"; + var serverForProxyForwarding = WireMockServer.Start(); + serverForProxyForwarding + .Given(Request.Create().WithPath(path)) + .RespondWith(Response.Create()); + + var server = WireMockServer.Start(); + server + .Given(Request.Create().WithPath("/*")) + .RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0])); + + // Act + var requestMessage = new HttpRequestMessage + { + Method = HttpMethod.Post, + RequestUri = new Uri($"{server.Urls[0]}{path}"), + Content = new StringContent("") + }; + requestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain"); + await new HttpClient().SendAsync(requestMessage).ConfigureAwait(false); + + // Assert + var receivedRequest = serverForProxyForwarding.LogEntries.First().RequestMessage; + Check.That(receivedRequest.BodyData.BodyAsString).IsEqualTo(""); + Check.That(receivedRequest.Headers).ContainsKey("Content-Type"); + Check.That(receivedRequest.Headers["Content-Type"].First()).Contains("text/plain"); + } - // Act - var requestMessage = new HttpRequestMessage - { - Method = HttpMethod.Get, - RequestUri = new Uri($"{server.Urls[0]}{path}") - }; - var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; - var response = await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false); + [Fact] + public async Task WireMockServer_Proxy_Should_preserve_content_header_in_proxied_response() + { + // Assign + string path = $"/prx_{Guid.NewGuid()}"; + var serverForProxyForwarding = WireMockServer.Start(); + serverForProxyForwarding + .Given(Request.Create().WithPath(path)) + .RespondWith(Response.Create() + .WithBody("body") + .WithHeader("Content-Type", "text/plain")); + + var server = WireMockServer.Start(); + server + .Given(Request.Create().WithPath(path)) + .RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0])); + + // Act + var requestMessage = new HttpRequestMessage + { + Method = HttpMethod.Get, + RequestUri = new Uri($"{server.Urls[0]}{path}") + }; + var response = await new HttpClient().SendAsync(requestMessage).ConfigureAwait(false); + + // Assert + Check.That(await response.Content.ReadAsStringAsync().ConfigureAwait(false)).IsEqualTo("body"); + Check.That(response.Content.Headers.Contains("Content-Type")).IsTrue(); + Check.That(response.Content.Headers.GetValues("Content-Type")).ContainsExactly("text/plain"); + } - // Assert - Check.That(response.Headers.Contains("Location")).IsTrue(); - Check.That(response.Headers.GetValues("Location")).ContainsExactly("/testpath"); - } + [Fact] + public async Task WireMockServer_Proxy_Should_change_absolute_location_header_in_proxied_response() + { + // Assign + string path = $"/prx_{Guid.NewGuid()}"; + var settings = new WireMockServerSettings { AllowPartialMapping = false }; + + var serverForProxyForwarding = WireMockServer.Start(settings); + serverForProxyForwarding + .Given(Request.Create().WithPath(path)) + .RespondWith(Response.Create() + .WithStatusCode(HttpStatusCode.Redirect) + .WithHeader("Location", "/testpath")); + + var server = WireMockServer.Start(settings); + server + .Given(Request.Create().WithPath(path).UsingAnyMethod()) + .RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0])); + + // Act + var requestMessage = new HttpRequestMessage + { + Method = HttpMethod.Get, + RequestUri = new Uri($"{server.Urls[0]}{path}") + }; + var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; + var response = await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false); + + // Assert + Check.That(response.Headers.Contains("Location")).IsTrue(); + Check.That(response.Headers.GetValues("Location")).ContainsExactly("/testpath"); + } - [Fact] - public async Task WireMockServer_Proxy_Should_preserve_cookie_header_in_proxied_request() + [Fact] + public async Task WireMockServer_Proxy_Should_preserve_cookie_header_in_proxied_request() + { + // Assign + string path = $"/prx_{Guid.NewGuid()}"; + var serverForProxyForwarding = WireMockServer.Start(); + serverForProxyForwarding + .Given(Request.Create().WithPath(path)) + .RespondWith(Response.Create()); + + var server = WireMockServer.Start(); + server + .Given(Request.Create().WithPath(path)) + .RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0])); + + // Act + var requestUri = new Uri($"{server.Urls[0]}{path}"); + var requestMessage = new HttpRequestMessage { - // Assign - string path = $"/prx_{Guid.NewGuid()}"; - var serverForProxyForwarding = WireMockServer.Start(); - serverForProxyForwarding - .Given(Request.Create().WithPath(path)) - .RespondWith(Response.Create()); + Method = HttpMethod.Get, + RequestUri = requestUri + }; + var clientHandler = new HttpClientHandler(); + clientHandler.CookieContainer.Add(requestUri, new Cookie("name", "value")); + await new HttpClient(clientHandler).SendAsync(requestMessage).ConfigureAwait(false); + + // then + var receivedRequest = serverForProxyForwarding.LogEntries.First().RequestMessage; + Check.That(receivedRequest.Cookies).IsNotNull(); + Check.That(receivedRequest.Cookies).ContainsPair("name", "value"); + } - var server = WireMockServer.Start(); - server - .Given(Request.Create().WithPath(path)) - .RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0])); + /// + /// Send some binary content in a request through the proxy and check that the same content + /// arrived at the target. As example a JPEG/JIFF header is used, which is not representable + /// in UTF8 and breaks if it is not treated as binary content. + /// + [Fact] + public async Task WireMockServer_Proxy_Should_preserve_binary_request_content() + { + // arrange + var jpegHeader = new byte[] { 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00 }; + var brokenJpegHeader = new byte[] + {0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00}; - // Act - var requestUri = new Uri($"{server.Urls[0]}{path}"); - var requestMessage = new HttpRequestMessage - { - Method = HttpMethod.Get, - RequestUri = requestUri - }; - var clientHandler = new HttpClientHandler(); - clientHandler.CookieContainer.Add(requestUri, new Cookie("name", "value")); - await new HttpClient(clientHandler).SendAsync(requestMessage).ConfigureAwait(false); - - // then - var receivedRequest = serverForProxyForwarding.LogEntries.First().RequestMessage; - Check.That(receivedRequest.Cookies).IsNotNull(); - Check.That(receivedRequest.Cookies).ContainsPair("name", "value"); - } + bool HasCorrectHeader(byte[] bytes) => bytes.SequenceEqual(jpegHeader); + bool HasBrokenHeader(byte[] bytes) => bytes.SequenceEqual(brokenJpegHeader); - /// - /// Send some binary content in a request through the proxy and check that the same content - /// arrived at the target. As example a JPEG/JIFF header is used, which is not representable - /// in UTF8 and breaks if it is not treated as binary content. - /// - [Fact] - public async Task WireMockServer_Proxy_Should_preserve_binary_request_content() - { - // arrange - var jpegHeader = new byte[] { 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00 }; - var brokenJpegHeader = new byte[] - {0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00}; - - bool HasCorrectHeader(byte[] bytes) => bytes.SequenceEqual(jpegHeader); - bool HasBrokenHeader(byte[] bytes) => bytes.SequenceEqual(brokenJpegHeader); - - var serverForProxyForwarding = WireMockServer.Start(); - serverForProxyForwarding - .Given(Request.Create().WithBody(HasCorrectHeader)) - .RespondWith(Response.Create().WithSuccess()); - - serverForProxyForwarding - .Given(Request.Create().WithBody(HasBrokenHeader)) - .RespondWith(Response.Create().WithStatusCode(HttpStatusCode.InternalServerError)); - - var server = WireMockServer.Start(); - server - .Given(Request.Create()) - .RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0])); - - // act - var response = await new HttpClient().PostAsync(server.Urls[0], new ByteArrayContent(jpegHeader)).ConfigureAwait(false); - - // assert - Check.That(response.StatusCode).IsEqualTo(HttpStatusCode.OK); - } + var serverForProxyForwarding = WireMockServer.Start(); + serverForProxyForwarding + .Given(Request.Create().WithBody(HasCorrectHeader)) + .RespondWith(Response.Create().WithSuccess()); - [Fact] - public async Task WireMockServer_Proxy_Should_set_BodyAsJson_in_proxied_response() - { - // Assign - string path = $"/prx_{Guid.NewGuid()}"; - var serverForProxyForwarding = WireMockServer.Start(); - serverForProxyForwarding - .Given(Request.Create().WithPath(path)) - .RespondWith(Response.Create() - .WithBodyAsJson(new { i = 42 }) - .WithHeader("Content-Type", "application/json; charset=utf-8")); + serverForProxyForwarding + .Given(Request.Create().WithBody(HasBrokenHeader)) + .RespondWith(Response.Create().WithStatusCode(HttpStatusCode.InternalServerError)); - var server = WireMockServer.Start(); - server - .Given(Request.Create().WithPath(path)) - .RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0])); + var server = WireMockServer.Start(); + server + .Given(Request.Create()) + .RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0])); - // Act - var requestMessage = new HttpRequestMessage - { - Method = HttpMethod.Get, - RequestUri = new Uri($"{server.Urls[0]}{path}") - }; - var response = await new HttpClient().SendAsync(requestMessage).ConfigureAwait(false); + // act + var response = await new HttpClient().PostAsync(server.Urls[0], new ByteArrayContent(jpegHeader)).ConfigureAwait(false); - // Assert - string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - Check.That(content).IsEqualTo("{\"i\":42}"); - Check.That(response.Content.Headers.GetValues("Content-Type")).ContainsExactly("application/json; charset=utf-8"); - } + // assert + Check.That(response.StatusCode).IsEqualTo(HttpStatusCode.OK); + } - [Fact] - public async Task WireMockServer_Proxy_Should_set_Body_in_multipart_proxied_response() + [Fact] + public async Task WireMockServer_Proxy_Should_set_BodyAsJson_in_proxied_response() + { + // Assign + string path = $"/prx_{Guid.NewGuid()}"; + var serverForProxyForwarding = WireMockServer.Start(); + serverForProxyForwarding + .Given(Request.Create().WithPath(path)) + .RespondWith(Response.Create() + .WithBodyAsJson(new { i = 42 }) + .WithHeader("Content-Type", "application/json; charset=utf-8")); + + var server = WireMockServer.Start(); + server + .Given(Request.Create().WithPath(path)) + .RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0])); + + // Act + var requestMessage = new HttpRequestMessage { - // Assign - string path = $"/prx_{Guid.NewGuid()}"; - var serverForProxyForwarding = WireMockServer.Start(); - serverForProxyForwarding - .Given(Request.Create().WithPath(path)) - .RespondWith(Response.Create() - .WithBodyAsJson(new { i = 42 }) - ); - - var server = WireMockServer.Start(); - server - .Given(Request.Create().WithPath(path)) - .RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0])); + Method = HttpMethod.Get, + RequestUri = new Uri($"{server.Urls[0]}{path}") + }; + var response = await new HttpClient().SendAsync(requestMessage).ConfigureAwait(false); + + // Assert + string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + Check.That(content).IsEqualTo("{\"i\":42}"); + Check.That(response.Content.Headers.GetValues("Content-Type")).ContainsExactly("application/json; charset=utf-8"); + } - // Act - var uri = new Uri($"{server.Urls[0]}{path}"); - var form = new MultipartFormDataContent - { - { new StringContent("data"), "test", "test.txt" } - }; - var response = await new HttpClient().PostAsync(uri, form).ConfigureAwait(false); + [Fact] + public async Task WireMockServer_Proxy_Should_set_Body_in_multipart_proxied_response() + { + // Assign + string path = $"/prx_{Guid.NewGuid()}"; + var serverForProxyForwarding = WireMockServer.Start(); + serverForProxyForwarding + .Given(Request.Create().WithPath(path)) + .RespondWith(Response.Create() + .WithBodyAsJson(new { i = 42 }) + ); - // Assert - string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - Check.That(content).IsEqualTo("{\"i\":42}"); - } + var server = WireMockServer.Start(); + server + .Given(Request.Create().WithPath(path)) + .RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0])); - [Fact] - public async Task WireMockServer_Proxy_Should_Not_overrule_AdminMappings() + // Act + var uri = new Uri($"{server.Urls[0]}{path}"); + var form = new MultipartFormDataContent { - // Assign - string path = $"/prx_{Guid.NewGuid()}"; - var serverForProxyForwarding = WireMockServer.Start(); - serverForProxyForwarding - .Given(Request.Create().WithPath(path)) - .RespondWith(Response.Create().WithBody("ok")); + { new StringContent("data"), "test", "test.txt" } + }; + var response = await new HttpClient().PostAsync(uri, form).ConfigureAwait(false); - var server = WireMockServer.Start(new WireMockServerSettings - { - StartAdminInterface = true, - ReadStaticMappings = false, - ProxyAndRecordSettings = new ProxyAndRecordSettings - { - Url = serverForProxyForwarding.Urls[0], - SaveMapping = false, - SaveMappingToFile = false - } - }); + // Assert + string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + Check.That(content).IsEqualTo("{\"i\":42}"); + } - // Act 1 - var requestMessage1 = new HttpRequestMessage + [Fact] + public async Task WireMockServer_Proxy_Should_Not_overrule_AdminMappings() + { + // Assign + string path = $"/prx_{Guid.NewGuid()}"; + var serverForProxyForwarding = WireMockServer.Start(); + serverForProxyForwarding + .Given(Request.Create().WithPath(path)) + .RespondWith(Response.Create().WithBody("ok")); + + var server = WireMockServer.Start(new WireMockServerSettings + { + StartAdminInterface = true, + ReadStaticMappings = false, + ProxyAndRecordSettings = new ProxyAndRecordSettings { - Method = HttpMethod.Get, - RequestUri = new Uri($"{server.Urls[0]}{path}") - }; - var response1 = await new HttpClient().SendAsync(requestMessage1).ConfigureAwait(false); - - // Assert 1 - string content1 = await response1.Content.ReadAsStringAsync().ConfigureAwait(false); - Check.That(content1).IsEqualTo("ok"); + Url = serverForProxyForwarding.Urls[0], + SaveMapping = false, + SaveMappingToFile = false + } + }); + + // Act 1 + var requestMessage1 = new HttpRequestMessage + { + Method = HttpMethod.Get, + RequestUri = new Uri($"{server.Urls[0]}{path}") + }; + var response1 = await new HttpClient().SendAsync(requestMessage1).ConfigureAwait(false); - // Act 2 - var requestMessage2 = new HttpRequestMessage - { - Method = HttpMethod.Get, - RequestUri = new Uri($"{server.Urls[0]}/__admin/mappings") - }; - var response2 = await new HttpClient().SendAsync(requestMessage2).ConfigureAwait(false); + // Assert 1 + string content1 = await response1.Content.ReadAsStringAsync().ConfigureAwait(false); + Check.That(content1).IsEqualTo("ok"); - // Assert 2 - string content2 = await response2.Content.ReadAsStringAsync().ConfigureAwait(false); - Check.That(content2).IsEqualTo("[]"); - } + // Act 2 + var requestMessage2 = new HttpRequestMessage + { + Method = HttpMethod.Get, + RequestUri = new Uri($"{server.Urls[0]}/__admin/mappings") + }; + var response2 = await new HttpClient().SendAsync(requestMessage2).ConfigureAwait(false); + + // Assert 2 + string content2 = await response2.Content.ReadAsStringAsync().ConfigureAwait(false); + Check.That(content2).IsEqualTo("[]"); + } - // On Ubuntu latest it's : "Resource temporarily unavailable" - // On Windows-2019 it's : "No such host is known." - [Fact] - public async Task WireMockServer_Proxy_WhenTargetIsNotAvailable_Should_Return_CorrectResponse() + // On Ubuntu latest it's : "Resource temporarily unavailable" + // On Windows-2019 it's : "No such host is known." + [Fact] + public async Task WireMockServer_Proxy_WhenTargetIsNotAvailable_Should_Return_CorrectResponse() + { + // Assign + var settings = new WireMockServerSettings { - // Assign - var settings = new WireMockServerSettings + ProxyAndRecordSettings = new ProxyAndRecordSettings { - ProxyAndRecordSettings = new ProxyAndRecordSettings - { - Url = $"http://error{Guid.NewGuid()}:12345" - } - }; - var server = WireMockServer.Start(settings); + Url = $"http://error{Guid.NewGuid()}:12345" + } + }; + var server = WireMockServer.Start(settings); - // Act - var requestMessage = new HttpRequestMessage - { - Method = HttpMethod.Get, - RequestUri = new Uri(server.Urls[0]) - }; - var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; - var result = await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false); + // Act + var requestMessage = new HttpRequestMessage + { + Method = HttpMethod.Get, + RequestUri = new Uri(server.Urls[0]) + }; + var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false }; + var result = await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false); - // Assert - result.StatusCode.Should().Be(HttpStatusCode.InternalServerError); + // Assert + result.StatusCode.Should().Be(HttpStatusCode.InternalServerError); - var content = await result.Content.ReadAsStringAsync().ConfigureAwait(false); - content.Should().NotBeEmpty(); + var content = await result.Content.ReadAsStringAsync().ConfigureAwait(false); + content.Should().NotBeEmpty(); - server.LogEntries.Should().HaveCount(1); - var status = ((StatusModel)server.LogEntries.First().ResponseMessage.BodyData.BodyAsJson).Status; + server.LogEntries.Should().HaveCount(1); + var status = ((StatusModel)server.LogEntries.First().ResponseMessage.BodyData.BodyAsJson).Status; - server.Stop(); - } + server.Stop(); } } \ No newline at end of file