Compare commits

...

22 Commits
1.5.2 ... 1.5.7

Author SHA1 Message Date
Stef Heyenrath
14dd619763 1.5.7 2022-10-11 09:57:11 +02:00
Stef Heyenrath
430c01a461 Add implicit operators to WireMockList (#823)
* Add implicit operators to WireMockList

* .
2022-10-01 10:50:18 +02:00
Stef Heyenrath
f7b04f3234 Add UseDefinedRequestMatchers to ProxyAndRecordSettings (#821)
* .

* UseDefinedRequestMatchers

* ok

* .

* ClientIP

* t

* fix ut

* .

* cf

* cf2
2022-09-30 11:25:11 +02:00
Stef Heyenrath
c0b18631a3 Add option to run the server on http & https (#818)
* HostingProtocol.HttpAndHttps

* .

* .

* .

* HostingScheme
2022-09-26 14:24:45 +02:00
Stef Heyenrath
fd996ab4ed 1.5.6 2022-09-12 20:45:26 +02:00
Stef Heyenrath
a57626c63a 1.5.6 2022-09-12 20:38:45 +02:00
Stef Heyenrath
98a0f2fa28 WebHook : UseFireAndForget + Delay (#803)
* UseFireAndForget

* ...

* delay

* async

* updated code accorsing to proposal

* Change nuget to package reference for WireMock.Net.Console.Net472.Classic, move the new FireAndForget into the main mapping, out of individual webhook mappings making it all or nothing, update tests, change Middleware to await or not the firing of all webhooks. Update models as needed. (#804)

Co-authored-by: Matt Philmon <Matt_Philmon@carmax.com>

* small update

* Tweak middleware and fix bug in example (#806)

Co-authored-by: Matt Philmon <Matt_Philmon@carmax.com>

* .ConfigureAwait(false)

Co-authored-by: mattisking <mattisking@gmail.com>
Co-authored-by: Matt Philmon <Matt_Philmon@carmax.com>
2022-09-12 20:30:40 +02:00
Stef Heyenrath
13a06b9b38 1.5.5 2022-09-03 08:57:30 +02:00
Stef Heyenrath
74480c8ba9 Add support to use 'mapping' object in in reponse templating (#798)
* mapping

* .

* .
2022-09-03 08:52:05 +02:00
Rafael Moreira Fonseca
862c04e722 Add assertions for request methods (#802)
* Add assertions for request methods

* Fix assertions tests when using connect method

* Remove unnecessary clear and fix assert condition
2022-09-03 08:49:39 +02:00
dependabot[bot]
cd93422554 Bump Microsoft.Owin from 4.1.1 to 4.2.2 in /src/WireMock.Net (#800)
Bumps [Microsoft.Owin](https://github.com/aspnet/AspNetKatana) from 4.1.1 to 4.2.2.
- [Release notes](https://github.com/aspnet/AspNetKatana/releases)
- [Commits](https://github.com/aspnet/AspNetKatana/compare/v4.1.1...v4.2.2)

---
updated-dependencies:
- dependency-name: Microsoft.Owin
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-31 21:19:37 +02:00
Stef Heyenrath
775c4fb2e3 1.5.4 2022-08-24 08:34:20 +02:00
Stef Heyenrath
2d4f513753 Update some NuGet packages (#781) 2022-08-24 08:26:35 +02:00
Stef Heyenrath
3d29d7fb2f Add Response.WithBody with IJsonConverter (#790)
* Response_ProvideResponse_WithBody_IJsonConverter_SystemTextJson

* Guard.NotNull(converter);

* .

* 0.1.0

* j
2022-08-23 15:49:54 +02:00
Florian
f704de65d8 Fixes header match handling using RejectOnMatch behavior (#797)
Co-authored-by: flts <>
2022-08-23 07:52:45 +02:00
Stef Heyenrath
f0d6ed26bc Add check for duplicate Guids when posting multiple mappings in one request (#795)
* Add check for DuplicateGuids

* Add check for duplicate Guids when posting mapping(s)

* mappingModels

* fix ut
2022-08-22 20:05:52 +02:00
Stef Heyenrath
330559b9fd Add support for PEM certificates (#787)
* Support PEM

* net5

* 31

* txt

* FILE

* new

* Fixed

* .

* .

* RSA
2022-08-16 13:26:00 +02:00
Stef Heyenrath
e2bd56531d Add support for Matcher.Pattern in Pact Body mapping (#789)
* Add support for Matcher.Pattern in Pact Body mapping

* SavePact_Get_Request_And_Response_WithNullBody
2022-08-15 09:28:16 +02:00
Stef Heyenrath
d2a1d0f069 Fix WithBody when using Pact and added more nullable annotations (#783)
* More nullable annotations

* .

* .

* FIX

* pact

* .

* p

* xxx

* ...

* auth

* array

* ...
2022-08-11 10:57:33 +02:00
Stef Heyenrath
b1af37f044 Fix Proxying when StartAdminInterface=true (#778)
* ProxyHelper fixes

* .

* more reformat

* .
2022-08-09 19:41:45 +02:00
Stef Heyenrath
be4b0addca 1.5.3 2022-07-29 13:21:22 +02:00
Stef Heyenrath
ae91ed2a79 Update Scriban.Signed to version 5.5.0 (#777) 2022-07-29 13:18:23 +02:00
200 changed files with 9983 additions and 8278 deletions

View File

@@ -1,3 +1,40 @@
# 1.5.7 (11 October 2022)
- [#818](https://github.com/WireMock-Net/WireMock.Net/pull/818) - Add option to run the server on http &amp; https [feature] contributed by [StefH](https://github.com/StefH)
- [#821](https://github.com/WireMock-Net/WireMock.Net/pull/821) - Add UseDefinedRequestMatchers to ProxyAndRecordSettings [feature] contributed by [StefH](https://github.com/StefH)
- [#823](https://github.com/WireMock-Net/WireMock.Net/pull/823) - Add implicit operators to WireMockList contributed by [StefH](https://github.com/StefH)
- [#819](https://github.com/WireMock-Net/WireMock.Net/issues/819) - Can I preserve Mapping title and matchers for proxy response? [feature]
# 1.5.6 (12 September 2022)
- [#803](https://github.com/WireMock-Net/WireMock.Net/pull/803) - WebHook : UseFireAndForget + Delay [feature] contributed by [StefH](https://github.com/StefH)
- [#804](https://github.com/WireMock-Net/WireMock.Net/pull/804) - Change nuget to package reference for WireMock.Net.Console.Net472.Cla&#8230; [feature] contributed by [mattisking](https://github.com/mattisking)
- [#806](https://github.com/WireMock-Net/WireMock.Net/pull/806) - Tweak middleware and fix bug in example [feature] contributed by [mattisking](https://github.com/mattisking)
- [#801](https://github.com/WireMock-Net/WireMock.Net/issues/801) - Webhook Delays [feature]
# 1.5.5 (03 September 2022)
- [#798](https://github.com/WireMock-Net/WireMock.Net/pull/798) - Add support to use 'mapping' object in in reponse templating [feature] contributed by [StefH](https://github.com/StefH)
- [#800](https://github.com/WireMock-Net/WireMock.Net/pull/800) - Bump Microsoft.Owin from 4.1.1 to 4.2.2 in /src/WireMock.Net (net46) [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
- [#802](https://github.com/WireMock-Net/WireMock.Net/pull/802) - Add assertions for request methods contributed by [rafaelmfonseca](https://github.com/rafaelmfonseca)
- [#772](https://github.com/WireMock-Net/WireMock.Net/issues/772) - How to get matched mapping by HttpRequest or HttpRequestMessage [feature]
# 1.5.4 (24 August 2022)
- [#778](https://github.com/WireMock-Net/WireMock.Net/pull/778) - Fix Proxying when StartAdminInterface=true [bug] contributed by [StefH](https://github.com/StefH)
- [#781](https://github.com/WireMock-Net/WireMock.Net/pull/781) - Update some NuGet packages [feature] contributed by [StefH](https://github.com/StefH)
- [#783](https://github.com/WireMock-Net/WireMock.Net/pull/783) - Fix WithBody when using Pact and added more nullable annotations [feature] contributed by [StefH](https://github.com/StefH)
- [#787](https://github.com/WireMock-Net/WireMock.Net/pull/787) - Add support for PEM certificates contributed by [StefH](https://github.com/StefH)
- [#789](https://github.com/WireMock-Net/WireMock.Net/pull/789) - Add support for Matcher.Pattern in Pact Body mapping [feature] contributed by [StefH](https://github.com/StefH)
- [#790](https://github.com/WireMock-Net/WireMock.Net/pull/790) - Add Response.WithBody with IJsonConverter [feature] contributed by [StefH](https://github.com/StefH)
- [#795](https://github.com/WireMock-Net/WireMock.Net/pull/795) - Add check for duplicate Guids when posting multiple mappings in one request contributed by [StefH](https://github.com/StefH)
- [#797](https://github.com/WireMock-Net/WireMock.Net/pull/797) - Fix WithHeader when using RejectOnMatch [bug] contributed by [flts](https://github.com/flts)
- [#775](https://github.com/WireMock-Net/WireMock.Net/issues/775) - When &quot;StartAdminInterface&quot; is true then each time is generated new mapping from the proxy [bug]
- [#784](https://github.com/WireMock-Net/WireMock.Net/issues/784) - Response body is missing in generated pact file when IBodyResponseBuilder.WithBody is used [bug]
- [#785](https://github.com/WireMock-Net/WireMock.Net/issues/785) - Support for PEM certificates when using ssl [feature]
- [#788](https://github.com/WireMock-Net/WireMock.Net/issues/788) - Request body is missing in generated pact file for requests that include matching on request body [bug]
- [#796](https://github.com/WireMock-Net/WireMock.Net/issues/796) - RequestMessageHeaderMatcher with MatchBehaviour.RejectOnMatch reverses match results twice [bug]
# 1.5.3 (29 July 2022)
- [#777](https://github.com/WireMock-Net/WireMock.Net/pull/777) - Update Scriban.Signed to version 5.5.0 [feature] contributed by [StefH](https://github.com/StefH)
- [#776](https://github.com/WireMock-Net/WireMock.Net/issues/776) - Update Scriban.Signed to support more functions, e.g math.random [feature]
# 1.5.2 (24 July 2022)
- [#769](https://github.com/WireMock-Net/WireMock.Net/pull/769) - Bump Microsoft.AspNetCore.Server.Kestrel.Core from 2.1.3 to 2.1.7 in /examples/WireMock.Net.StandAlone.Net461 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
- [#770](https://github.com/WireMock-Net/WireMock.Net/pull/770) - Added some more tests for JsonMatcher + refactored some code to use nullable [test] contributed by [StefH](https://github.com/StefH)

View File

@@ -4,7 +4,7 @@
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>1.5.2</VersionPrefix>
<VersionPrefix>1.5.7</VersionPrefix>
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
@@ -25,10 +25,6 @@
<!--<None Include="../../PackageReadme.md" Pack="true" PackagePath=""/>-->
</ItemGroup>
<PropertyGroup Condition="'$(TargetFramework)' != 'net45'">
<!--<Nullable>enable</Nullable>-->
</PropertyGroup>
<Choose>
<!-- The environment variable `Prerelease` is set in the azure-pipelines.yml file. -->
<When Condition=" '$(Prerelease)' != '' ">

View File

@@ -1,6 +1,6 @@
rem https://github.com/StefH/GitHubReleaseNotes
SET version=1.5.2
SET version=1.5.7
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate --version %version% --token %GH_TOKEN%

View File

@@ -1,6 +1,7 @@
# 1.5.2 (24 July 2022)
- #769 Bump Microsoft.AspNetCore.Server.Kestrel.Core from 2.1.3 to 2.1.7 in /examples/WireMock.Net.StandAlone.Net461 [dependencies]
- #770 Added some more tests for JsonMatcher + refactored some code to use nullable [test]
- #771 JsonPartialMatcher - support Regex [feature]
# 1.5.7 (11 October 2022)
- #818 Add option to run the server on http &amp; https [feature]
- #821 Add UseDefinedRequestMatchers to ProxyAndRecordSettings [feature]
- #823 Add implicit operators to WireMockList
- #819 Can I preserve Mapping title and matchers for proxy response? [feature]
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md

View File

@@ -103,6 +103,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.Proxy.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.xUnit", "src\WireMock.Net.xUnit\WireMock.Net.xUnit.csproj", "{0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.NET6.WithCertificate", "examples\WireMock.Net.Console.NET6.WithCertificate\WireMock.Net.Console.NET6.WithCertificate.csproj", "{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -245,6 +247,10 @@ Global
{0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0DE0954F-8C00-4E8D-B94A-4361FC1CBE44}.Release|Any CPU.Build.0 = Release|Any CPU
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -286,6 +292,7 @@ Global
{3F7AA023-6833-4856-A08A-4B5717B592B8} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{670C7562-C154-442E-A249-7D26849BCD13} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{0DE0954F-8C00-4E8D-B94A-4361FC1CBE44} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}

View File

@@ -1,18 +1,22 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AD/@EntryIndexedValue">AD</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CS/@EntryIndexedValue">CS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EC/@EntryIndexedValue">EC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ID/@EntryIndexedValue">ID</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IP/@EntryIndexedValue">IP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MD/@EntryIndexedValue">MD5</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RSA/@EntryIndexedValue">RSA</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/=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/=XMS/@EntryIndexedValue">XMS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XUA/@EntryIndexedValue">XUA</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Flurl/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=funcs/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=guidb/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Guids/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Heyenrath/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Jmes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pacticipant/@EntryIndexedValue">True</s:Boolean>
@@ -24,6 +28,8 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Victoor/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Webhook/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Webhooks/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=wiremock/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=wiremockserver/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Xeger/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=xunit/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary>

View File

@@ -0,0 +1,16 @@
https://www.scottbrady91.com/openssl/creating-elliptical-curve-keys-using-openssl
# find your curve
openssl ecparam -list_curves
# generate a private key for a curve
openssl ecparam -name prime256v1 -genkey -noout -out private-key.pem
# generate corresponding public key
openssl ec -in private-key.pem -pubout -out public-key.pem
# optional: create a self-signed certificate
openssl req -new -x509 -key private-key.pem -out cert.pem -days 360
# optional: convert pem to pfx
openssl pkcs12 -export -inkey private-key.pem -in cert.pem -out cert.pfx

View File

@@ -0,0 +1,52 @@
using System.IO;
using WireMock.Logging;
using WireMock.Server;
using WireMock.Settings;
namespace WireMock.Net.Console.NET6.WithCertificate;
class Program
{
static void Main(string[] args)
{
var serverEC = WireMockServer.Start(new WireMockServerSettings
{
Urls = new[] { "https://localhost:8433/" },
StartAdminInterface = true,
Logger = new WireMockConsoleLogger(),
CertificateSettings = new WireMockCertificateSettings
{
// https://www.scottbrady91.com/c-sharp/pem-loading-in-dotnet-core-and-dotnet
// https://www.scottbrady91.com/openssl/creating-elliptical-curve-keys-using-openssl
X509CertificateFilePath = "cert.pem",
X509CertificatePassword = @"
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIJZTv6ujGrEwxW+ab1+CtZouRd8PK7PsklVMvJwm1uDmoAoGCCqGSM49
AwEHoUQDQgAE39VoI268uDuIeKmRzr9e9jgMSGeuJTvTG7+cSXmeDymrVgIGXQgm
qKA8TDXpJNrRhWMd/fpsnWu1JwJUjBmspQ==
-----END EC PRIVATE KEY-----"
}
});
System.Console.WriteLine("WireMockServer listening at {0}", serverEC.Url);
var serverRSA = WireMockServer.Start(new WireMockServerSettings
{
Urls = new[] { "https://localhost:8434/" },
StartAdminInterface = true,
Logger = new WireMockConsoleLogger(),
CertificateSettings = new WireMockCertificateSettings
{
// https://www.scottbrady91.com/c-sharp/pem-loading-in-dotnet-core-and-dotnet
// https://www.scottbrady91.com/openssl/creating-rsa-keys-using-openssl
X509CertificateFilePath = "cert-rsa.pem",
X509CertificatePassword = File.ReadAllText("private-key-rsa.pem")
}
});
System.Console.WriteLine("WireMockServer listening at {0}", serverRSA.Url);
System.Console.WriteLine("Press any key to stop the server(s)");
System.Console.ReadKey();
serverEC.Stop();
serverRSA.Stop();
}
}

View File

@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<None Update="*.pem">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="*.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="nlog.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIErzCCAxegAwIBAgIUeVJ5l3LJPakcwhBGXNfa7UawgPcwDQYJKoZIhvcNAQEL
BQAwZzELMAkGA1UEBhMCTkwxEzARBgNVBAgMClNvbWUtU3RhdGUxFTATBgNVBAoM
DFdpcmVNb2NrLk5ldDEVMBMGA1UECwwMV2lyZU1vY2suTmV0MRUwEwYDVQQDDAxX
aXJlTW9jay5OZXQwHhcNMjIwODEyMTQzMjUxWhcNMzIwNjIwMTQzMjUxWjBnMQsw
CQYDVQQGEwJOTDETMBEGA1UECAwKU29tZS1TdGF0ZTEVMBMGA1UECgwMV2lyZU1v
Y2suTmV0MRUwEwYDVQQLDAxXaXJlTW9jay5OZXQxFTATBgNVBAMMDFdpcmVNb2Nr
Lk5ldDCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBALyF2sKsueDaZ78r
fQ5IyqWJLYXnYRT94xVfiPoRQNex7JMYwdIt+xEPcfhIlyODxYRxzYTSuXi/cBOM
/svTewIdBmDDyyCDboZ+P8THlzdwCLNHUPONQqJtc0msLVfwPuvDeZIwhIn9CDwC
1EXstWLEePxu1i2/PAfUudYeQunjrP10DE06fyW+ZH7sMSPOSyY4BO4Rt0dk3Cws
U/CEl+bSN5Kx2WkyPxbOvZLa66JmQEaeSZ4ypkhujWE1LuIIQE94P28BzFpHtDWO
1+42K8mqjdnO8eH1/IfAMmOE/o3rKoI4C5aUPyJpDOaz5KFCqdCSBHlb2uo3lqgd
yYOCrVOLIsljp8H4ncfs1AUo+tExNW/5jWYegAZGXLArrWktHEbwa4f+9ZMa7+VS
lKpx6hLI7eMSHBCsQJ7yH8QyLhr0HMjoDw38isGV+mK/N1u47s4oaTQyG+HXH+e/
Shj5OSKnLWBV/eX7YSAJ1hqHgmAbwl/BnuGI3SBXSK4mcjcNvQIDAQABo1MwUTAd
BgNVHQ4EFgQUYhrgWXNdcFpnyz0mifPqYM+kyK4wHwYDVR0jBBgwFoAUYhrgWXNd
cFpnyz0mifPqYM+kyK4wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
AYEAmfJZJ019pO7ZXDQ85nEC99+x9+MUnSZ6UuMIiy2VMcS/oea8ugQ4NBYVPpQS
O/xiYENpw36+H4+ctv1tdAbZELGGLBahkkzZedLyiFObxDALu0PP7jQYr4nUjfQj
B/fWuTcCrVmuH3yiutw/sALd1su63VjjZiOCwxMB8LV4T0ojBTHA2D4rqLTiSRgs
sP3CRoWDI2JIr2afM/K8SMczRbo/5ovv0YF9kFcwG9WDEa/oQ67Yu05GFBnItYmT
QP+Br5WiPTLOON9TKm1ZgxLwrhJNHfD2+u9uudkVF5uWyg9ZvB8sYmIw5wSvUFl5
SDj1Pxy4olim54BL5wIwlMMQu+fKp6T89iShgN/NEot3JKW3zDpa/t93IjJXGOlh
O4ECoUzXCtDTnc6aais2SoYjbveP2wduS9MHAQinjTiepzYbhJbySURKqCBu1mN2
EFsU9Pzd1+MA3ZbRfhvl8Jvwdp5GqaFyCqgqmP5oPOcd9ncJowyfobfdqzYMXPGl
bu9i
-----END CERTIFICATE-----

View File

@@ -0,0 +1,13 @@
-----BEGIN CERTIFICATE-----
MIIB9TCCAZugAwIBAgIUYH7UM/DAXzosxsT+ea2jdYvhqqMwCgYIKoZIzj0EAwIw
UDELMAkGA1UEBhMCTkwxEzARBgNVBAgMClNvbWUtU3RhdGUxFTATBgNVBAoMDFdp
cmVNb2NrLk5ldDEVMBMGA1UEAwwMV2lyZU1vY2suTmV0MB4XDTIyMDgxMTE2MjE0
NFoXDTMyMDYxOTE2MjE0NFowUDELMAkGA1UEBhMCTkwxEzARBgNVBAgMClNvbWUt
U3RhdGUxFTATBgNVBAoMDFdpcmVNb2NrLk5ldDEVMBMGA1UEAwwMV2lyZU1vY2su
TmV0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE39VoI268uDuIeKmRzr9e9jgM
SGeuJTvTG7+cSXmeDymrVgIGXQgmqKA8TDXpJNrRhWMd/fpsnWu1JwJUjBmspaNT
MFEwHQYDVR0OBBYEFILL8V+fAtMnccWKGAdkx2Dh/v/TMB8GA1UdIwQYMBaAFILL
8V+fAtMnccWKGAdkx2Dh/v/TMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwID
SAAwRQIgKDLAG8OWK6GF5HV4kmWz3kp2V3yVsNK2V9Lw3dSE+YsCIQCK1EEBvuqc
0ncZV4ETVnOY23PWFOMk1VwN2aoTi5n++Q==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,39 @@
-----BEGIN RSA PRIVATE KEY-----
MIIG5AIBAAKCAYEAvIXawqy54Npnvyt9DkjKpYkthedhFP3jFV+I+hFA17HskxjB
0i37EQ9x+EiXI4PFhHHNhNK5eL9wE4z+y9N7Ah0GYMPLIINuhn4/xMeXN3AIs0dQ
841Com1zSawtV/A+68N5kjCEif0IPALURey1YsR4/G7WLb88B9S51h5C6eOs/XQM
TTp/Jb5kfuwxI85LJjgE7hG3R2TcLCxT8ISX5tI3krHZaTI/Fs69ktrromZARp5J
njKmSG6NYTUu4ghAT3g/bwHMWke0NY7X7jYryaqN2c7x4fX8h8AyY4T+jesqgjgL
lpQ/ImkM5rPkoUKp0JIEeVva6jeWqB3Jg4KtU4siyWOnwfidx+zUBSj60TE1b/mN
Zh6ABkZcsCutaS0cRvBrh/71kxrv5VKUqnHqEsjt4xIcEKxAnvIfxDIuGvQcyOgP
DfyKwZX6Yr83W7juzihpNDIb4dcf579KGPk5IqctYFX95fthIAnWGoeCYBvCX8Ge
4YjdIFdIriZyNw29AgMBAAECggGBAJcMsNDWUECG/iVAFP0C+ctUdDMbxr9pBS+0
0i168XdhSeo6JeHfkZCDzY9fqil8hR+vhznrFUxYJtajW+u4UJDK7LdPaUttw3rj
YPir6s8yZuYuOABMqJ04EO1wlQwmpGOGxbuKQEfHg3eB1M8J7/No9H9d1yHkXZbw
rM2QhZCdKZgSCWE/g2ycdizz1hOYUMIYlGqjqzP67iY+hirqMkNxH7Hb6hTNe5ss
ntwxqCcAwnNSlC2661yRp5nBYQUeEfgXAx/cZZ6cILjX6JanDsML/PRY1hOm5hCZ
8/8satOGtd37rgr1cP/kvNMf7uLI8hIeC4JSCymUh6lU6ERpWRoQV8DUE60ztFbi
bQbGU/rseznBN8O1cM6fjduno/n8d4q5wGLjAbIai+xxxTSksbnlvkthRAUfK9/Y
rUdMxqgkDnecCAHof8UPz/Vg3n9J9wl6waFrKa+Kseda1wEB/jf51fb/t0sJP2Dq
n2kcp239zwalUr2XnXfENfeL9IDBiQKBwQDsogaTFy7E5P+66B1ZRDP2X6WgFedH
tjMCVXG4K4VOWp6xMfhieq2d+amMoYi/J3cxS8qDIM64q55caTaB3KTodGrzmvax
avqG58Mpyv9VdEDzMI29D7Xtx5ykoAAWOmQaByH/4J+3IBX6efRDGmSvmoqPoa0V
ChMO0Gw6O9GsH/kVEy2nRaj9dJpOqo05jhh9LLye2stPycCHzYybYqjs0Nt1uboY
3mbiCJBWJ8jZk9KeFhrqCkYB1zZJIJVybJ8CgcEAy/PRm0NZDUx6IJKnwDXBriE0
Qih1ZcvdVi907nhYAlBpAPWisGls6EghBQsfvQ4ZPwHAFBlDu/72JrqGJhkP61mP
D/xT9d59xSU5N90doiInrTHAOoyZEdpul9QvCXxKuFFl3RZnxn0RwPgc23sZUcny
aM4DIWk1541Z84Lxv3tFLXoG5uvqpCkSwEBd9iu0EMHlmoPFkEkY41Q3/zTs46FY
fnWLSdRahKDFHrZTgyu4i/3clbbp9m1cpWLwzUwjAoHAVpSWCT2jPCF5vD5vdpjw
1kV6yU8aV2+/zCvNNxCdbuTTSYw6EHZIjhOqSK1V5nMfNmc/yqi3WnRYtgE9E1jS
8cae11Es0A+PaMrl6qW+tNqbZR+vzKwx6bVuiAGO5pMoyyku9HuQlKVlxUbX67F9
g47tAc6rEJamEHaMEuaOOgdc0Kw6uQhQ46PFTeEzWQq3xR0YSptNZn0wN8AqoTQB
ENz+X128TJsbU7rEbPGTmKBwoKz/3gAySzwePbVxWPOLAoHAAsqehtKAKIdwcHux
YhcaRIjdzz4AhVkp+WEC57Sr97QkC8hQ5rs6q185XHlPgOXtgIhEmcHSxILz2Yna
BjF3n1AFfkGE4Kuf6w/cXaBgJHT1OBCjQenkunLT6q4TyrxxxV4P19vTpcrWcF60
/mgL66uo7rhLIKzw+O9dWNDlACruwnWWHJkECCUrxYfcAV+NwmD1BI1jKdtmRM5F
Se/ughsWO/zd4C/Q4VnV+Nqj//qcNwZNe5saTq4mg3j8NMMjAoHBANRR+WLpgr3f
Kh9vNA+v1gnBUFImheWrJ+Eu75q1sGKrz3gZ8glhewf37YbJVuSKFMEBAg1qobWG
/2B26beXia/NprVuV5vqJNuts29W1/WuDKE7BsNya/quDA1WxTQAzhSA54DYtWXw
UgVTPtEQXL9Wbk2wF98tWiUio4VjV++geVql4qKQUet1IMo1RgRgbQL5hn/rDH0g
ZOX552VdK1xivWQRVA4486eKHlW/lbKJjX0Os6qhNIF57qVquIRQPw==
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIJZTv6ujGrEwxW+ab1+CtZouRd8PK7PsklVMvJwm1uDmoAoGCCqGSM49
AwEHoUQDQgAE39VoI268uDuIeKmRzr9e9jgMSGeuJTvTG7+cSXmeDymrVgIGXQgm
qKA8TDXpJNrRhWMd/fpsnWu1JwJUjBmspQ==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,11 @@
-----BEGIN PUBLIC KEY-----
MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAvIXawqy54Npnvyt9DkjK
pYkthedhFP3jFV+I+hFA17HskxjB0i37EQ9x+EiXI4PFhHHNhNK5eL9wE4z+y9N7
Ah0GYMPLIINuhn4/xMeXN3AIs0dQ841Com1zSawtV/A+68N5kjCEif0IPALURey1
YsR4/G7WLb88B9S51h5C6eOs/XQMTTp/Jb5kfuwxI85LJjgE7hG3R2TcLCxT8ISX
5tI3krHZaTI/Fs69ktrromZARp5JnjKmSG6NYTUu4ghAT3g/bwHMWke0NY7X7jYr
yaqN2c7x4fX8h8AyY4T+jesqgjgLlpQ/ImkM5rPkoUKp0JIEeVva6jeWqB3Jg4Kt
U4siyWOnwfidx+zUBSj60TE1b/mNZh6ABkZcsCutaS0cRvBrh/71kxrv5VKUqnHq
Esjt4xIcEKxAnvIfxDIuGvQcyOgPDfyKwZX6Yr83W7juzihpNDIb4dcf579KGPk5
IqctYFX95fthIAnWGoeCYBvCX8Ge4YjdIFdIriZyNw29AgMBAAE=
-----END PUBLIC KEY-----

View File

@@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE39VoI268uDuIeKmRzr9e9jgMSGeu
JTvTG7+cSXmeDymrVgIGXQgmqKA8TDXpJNrRhWMd/fpsnWu1JwJUjBmspQ==
-----END PUBLIC KEY-----

View File

@@ -1,22 +1,21 @@
using System.IO;
using System.IO;
using System.Reflection;
using log4net;
using log4net.Config;
using log4net.Repository;
using WireMock.Net.ConsoleApplication;
namespace WireMock.Net.Console.NETCoreApp
namespace WireMock.Net.Console.NETCoreApp;
static class Program
{
static class Program
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
static void Main(params string[] args)
{
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
static void Main(params string[] args)
{
XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
MainApp.Run();
}
MainApp.Run();
}
}

View File

@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Newtonsoft.Json;
using WireMock.Logging;
using WireMock.Matchers;
using WireMock.Models;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
@@ -39,6 +40,19 @@ namespace WireMock.Net.ConsoleApplication
var s = WireMockServer.Start();
s.Stop();
var httpAndHttpsWithPort = WireMockServer.Start(new WireMockServerSettings
{
HostingScheme = HostingScheme.HttpAndHttps,
Port = 12399
});
httpAndHttpsWithPort.Stop();
var httpAndHttpsFree = WireMockServer.Start(new WireMockServerSettings
{
HostingScheme = HostingScheme.HttpAndHttps
});
httpAndHttpsFree.Stop();
string url1 = "http://localhost:9091/";
string url2 = "http://localhost:9092/";
string url3 = "https://localhost:9443/";
@@ -576,6 +590,40 @@ namespace WireMock.Net.ConsoleApplication
};
}));
server.Given(Request.Create().WithPath(new WildcardMatcher("/multi-webhook", true)).UsingPost())
.WithWebhook(new[] {
new Webhook()
{
Request = new WebhookRequest
{
Url = "http://localhost:12345/foo1",
Method = "post",
BodyData = new BodyData
{
BodyAsString = "OK 1!", DetectedBodyType = BodyType.String
},
Delay = 1000
}
},
new Webhook()
{
Request = new WebhookRequest
{
Url = "http://localhost:12345/foo2",
Method = "post",
BodyData = new BodyData
{
BodyAsString = "OK 2!",
DetectedBodyType = BodyType.String
},
MinimumRandomDelay = 3000,
MaximumRandomDelay = 7000
}
}
})
.WithWebhookFireAndForget(true)
.RespondWith(Response.Create().WithBody("a-response"));
System.Console.WriteLine(JsonConvert.SerializeObject(server.MappingModels, Formatting.Indented));
System.Console.WriteLine("Press any key to stop the server");

View File

@@ -1,15 +1,14 @@
using System.IO;
using System.IO;
using log4net.Config;
namespace WireMock.Net.ConsoleApplication
{
static class Program
{
static void Main(params string[] args)
{
XmlConfigurator.Configure(new FileInfo("log4net.config"));
namespace WireMock.Net.ConsoleApplication;
MainApp.Run();
}
static class Program
{
static void Main(params string[] args)
{
XmlConfigurator.Configure(new FileInfo("log4net.config"));
MainApp.Run();
}
}

View File

@@ -80,7 +80,6 @@
<None Include="App.config">
<SubType>Designer</SubType>
</None>
<None Include="libman.json" />
<None Include="log4net.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

View File

@@ -43,7 +43,7 @@
<HintPath>..\..\packages\AnyOf.0.3.0\lib\net45\AnyOf.dll</HintPath>
</Reference>
<Reference Include="Fare, Version=2.2.0.0, Culture=neutral, PublicKeyToken=ea68d375bf33a7c8, processorArchitecture=MSIL">
<HintPath>..\..\packages\Fare.2.2.0\lib\net35\Fare.dll</HintPath>
<HintPath>..\..\packages\Fare.2.2.1\lib\net35\Fare.dll</HintPath>
</Reference>
<Reference Include="Handlebars, Version=2.1.2.0, Culture=neutral, PublicKeyToken=22225d0bf33cd661, processorArchitecture=MSIL">
<HintPath>..\..\packages\Handlebars.Net.2.1.2\lib\net46\Handlebars.dll</HintPath>
@@ -262,7 +262,7 @@
<HintPath>..\..\packages\NSwag.Core.13.15.10\lib\net45\NSwag.Core.dll</HintPath>
</Reference>
<Reference Include="RandomDataGenerator, Version=1.0.15.0, Culture=neutral, PublicKeyToken=ae5c571d29a3b8d9, processorArchitecture=MSIL">
<HintPath>..\..\packages\RandomDataGenerator.Net.1.0.15\lib\net45\RandomDataGenerator.dll</HintPath>
<HintPath>..\..\packages\RandomDataGenerator.Net.1.0.16\lib\net45\RandomDataGenerator.dll</HintPath>
</Reference>
<Reference Include="Scriban.Signed, Version=2.1.4.0, Culture=neutral, PublicKeyToken=5675fb69b15f2433, processorArchitecture=MSIL">
<HintPath>..\..\packages\Scriban.Signed.2.1.4\lib\net45\Scriban.Signed.dll</HintPath>
@@ -342,15 +342,6 @@
<Reference Include="TinyMapper, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\TinyMapper.3.0.3\lib\net40\TinyMapper.dll</HintPath>
</Reference>
<Reference Include="WireMock.Net, Version=1.5.1.0, Culture=neutral, PublicKeyToken=c8d65537854e1f03, processorArchitecture=MSIL">
<HintPath>..\..\packages\WireMock.Net.1.5.1\lib\net461\WireMock.Net.dll</HintPath>
</Reference>
<Reference Include="WireMock.Net.Abstractions, Version=1.5.1.0, Culture=neutral, PublicKeyToken=c8d65537854e1f03, processorArchitecture=MSIL">
<HintPath>..\..\packages\WireMock.Net.Abstractions.1.5.1\lib\net451\WireMock.Net.Abstractions.dll</HintPath>
</Reference>
<Reference Include="WireMock.Org.Abstractions, Version=1.5.1.0, Culture=neutral, PublicKeyToken=c8d65537854e1f03, processorArchitecture=MSIL">
<HintPath>..\..\packages\WireMock.Org.Abstractions.1.5.1\lib\net45\WireMock.Org.Abstractions.dll</HintPath>
</Reference>
<Reference Include="XPath2, Version=1.1.3.0, Culture=neutral, PublicKeyToken=463c6d7fb740c7e5, processorArchitecture=MSIL">
<HintPath>..\..\packages\XPath2.1.1.3\lib\net452\XPath2.dll</HintPath>
</Reference>
@@ -388,6 +379,16 @@
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj">
<Project>{b6269aac-170a-4346-8b9a-579ded3d9a94}</Project>
<Name>WireMock.Net.Abstractions</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj">
<Project>{d3804228-91f4-4502-9595-39584e5a01ad}</Project>
<Name>WireMock.Net</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AnyOf" version="0.3.0" targetFramework="net472" />
<package id="Fare" version="2.2.0" targetFramework="net472" />
<package id="Fare" version="2.2.1" targetFramework="net472" />
<package id="Handlebars.Net" version="2.1.2" targetFramework="net472" />
<package id="Handlebars.Net.Helpers" version="2.3.5" targetFramework="net472" />
<package id="Handlebars.Net.Helpers.Core" version="2.3.5" targetFramework="net472" />
@@ -126,7 +126,7 @@
<package id="NJsonSchema.Extensions" version="0.1.0" targetFramework="net472" />
<package id="NSwag.Core" version="13.15.10" targetFramework="net472" />
<package id="Nullable" version="1.3.0" targetFramework="net472" developmentDependency="true" />
<package id="RandomDataGenerator.Net" version="1.0.15" targetFramework="net472" />
<package id="RandomDataGenerator.Net" version="1.0.16" targetFramework="net472" />
<package id="Scriban.Signed" version="2.1.4" targetFramework="net472" />
<package id="SimMetrics.Net" version="1.0.5" targetFramework="net461" />
<package id="Stef.Validation" version="0.1.0" targetFramework="net472" />
@@ -147,9 +147,6 @@
<package id="System.Threading.Tasks.Extensions" version="4.5.1" targetFramework="net472" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net472" />
<package id="TinyMapper" version="3.0.3" targetFramework="net472" />
<package id="WireMock.Net" version="1.5.1" targetFramework="net472" />
<package id="WireMock.Net.Abstractions" version="1.5.1" targetFramework="net472" />
<package id="WireMock.Org.Abstractions" version="1.5.1" targetFramework="net472" />
<package id="XPath2" version="1.1.3" targetFramework="net472" />
<package id="XPath2.Extensions" version="1.1.3" targetFramework="net472" />
</packages>

View File

@@ -1,8 +1,16 @@
{
"profiles": {
"WireMock.Net.StandAlone.NETCoreApp": {
"commandName": "Project",
"commandLineArgs": "--Urls http://localhost:9091 --CorsPolicyOptions AllowAll --WireMockLogger WireMockConsoleLogger"
"profiles": {
"WireMock.Net.StandAlone.NETCoreApp": {
"commandName": "Project",
"commandLineArgs": "--Urls http://localhost:9091 --CorsPolicyOptions AllowAll --WireMockLogger WireMockConsoleLogger"
},
"WireMock.Net.StandAlone.NETCoreAppNoPort": {
"commandName": "Project",
"commandLineArgs": "--CorsPolicyOptions AllowAll --WireMockLogger WireMockConsoleLogger"
},
"WireMock.Net.StandAlone.NETCoreAppWithHostingProtocol": {
"commandName": "Project",
"commandLineArgs": "--HostingProtocol HttpAndHttps --CorsPolicyOptions AllowAll --WireMockLogger WireMockConsoleLogger"
}
}
}
}

View File

@@ -1,27 +1,26 @@
using System;
using System.IO;
using System.Linq;
using log4net.Config;
using WireMock.Server;
using WireMock.Settings;
namespace WireMock.Net.StandAlone.Net452
{
public class Program
{
static void Main(params string[] args)
{
XmlConfigurator.Configure(new FileInfo("log4net.config"));
if (WireMockServerSettingsParser.TryParseArguments(args, out var settings))
{
settings.Logger.Debug("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));
using System;
using System.IO;
using System.Linq;
using log4net.Config;
using WireMock.Server;
using WireMock.Settings;
WireMockServer.Start(settings);
namespace WireMock.Net.StandAlone.Net452;
Console.WriteLine("Press any key to stop the server");
Console.ReadKey();
}
}
}
public class Program
{
static void Main(params string[] args)
{
XmlConfigurator.Configure(new FileInfo("log4net.config"));
if (WireMockServerSettingsParser.TryParseArguments(args, out var settings))
{
Console.WriteLine("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));
WireMockServer.Start(settings);
Console.WriteLine("Press any key to stop the server");
Console.ReadKey();
}
}
}

View File

@@ -1,23 +1,22 @@
using System;
using System.Linq;
using WireMock.Server;
using WireMock.Settings;
namespace WireMock.Net.StandAlone.Net461
{
static class Program
{
static void Main(string[] args)
{
if (WireMockServerSettingsParser.TryParseArguments(args, out var settings))
{
settings.Logger.Debug("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));
using System;
using System.Linq;
using WireMock.Server;
using WireMock.Settings;
WireMockServer.Start(settings);
namespace WireMock.Net.StandAlone.Net461;
Console.WriteLine("Press any key to stop the server");
Console.ReadKey();
}
}
}
static class Program
{
static void Main(string[] args)
{
if (WireMockServerSettingsParser.TryParseArguments(args, out var settings))
{
Console.WriteLine("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));
WireMockServer.Start(settings);
Console.WriteLine("Press any key to stop the server");
Console.ReadKey();
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,31 +1,39 @@
using System.Collections.Generic;
namespace WireMock.Admin.Mappings
namespace WireMock.Admin.Mappings;
/// <summary>
/// Cookie Model
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class CookieModel
{
/// <summary>
/// Cookie Model
/// Gets or sets the name.
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class CookieModel
{
/// <summary>
/// Gets or sets the name.
/// </summary>
public string Name { get; set; } = null!;
public string Name { get; set; } = null!;
/// <summary>
/// Gets or sets the matchers.
/// </summary>
public IList<MatcherModel>? Matchers { get; set; }
/// <summary>
/// Gets or sets the matchers.
/// </summary>
public IList<MatcherModel>? Matchers { get; set; }
/// <summary>
/// Gets or sets the ignore case.
/// </summary>
public bool? IgnoreCase { get; set; }
/// <summary>
/// Gets or sets the ignore case.
/// </summary>
public bool? IgnoreCase { get; set; }
/// <summary>
/// Reject on match.
/// </summary>
public bool? RejectOnMatch { get; set; }
}
/// <summary>
/// Reject on match.
/// </summary>
public bool? RejectOnMatch { get; set; }
/// <summary>
/// The Operator to use when matchers are defined. [Optional]
/// - null = Same as "or".
/// - "or" = Only one pattern should match.
/// - "and" = All patterns should match.
/// - "average" = The average value from all patterns.
/// </summary>
public string? MatchOperator { get; set; }
}

View File

@@ -74,4 +74,9 @@ public class MappingModel
/// The Webhooks.
/// </summary>
public WebhookModel[]? Webhooks { get; set; }
/// <summary>
/// Fire and forget for webhooks.
/// </summary>
public bool? UseWebhooksFireAndForget { get; set; }
}

View File

@@ -1,24 +1,23 @@
namespace WireMock.Admin.Mappings
namespace WireMock.Admin.Mappings;
/// <summary>
/// WebProxy settings
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class WebProxyModel
{
/// <summary>
/// WebProxy settings
/// A string instance that contains the address of the proxy server.
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class WebProxyModel
{
/// <summary>
/// A string instance that contains the address of the proxy server.
/// </summary>
public string Address { get; set; }
public string Address { get; set; } = null!;
/// <summary>
/// The user name associated with the credentials.
/// </summary>
public string UserName { get; set; }
/// <summary>
/// The user name associated with the credentials. [optional]
/// </summary>
public string? UserName { get; set; }
/// <summary>
/// The password for the user name associated with the credentials.
/// </summary>
public string Password { get; set; }
}
/// <summary>
/// The password for the user name associated with the credentials. [optional]
/// </summary>
public string? Password { get; set; }
}

View File

@@ -1,14 +1,13 @@
namespace WireMock.Admin.Mappings
namespace WireMock.Admin.Mappings;
/// <summary>
/// The Webhook
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class WebhookModel
{
/// <summary>
/// The Webhook
/// The Webhook Request.
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class WebhookModel
{
/// <summary>
/// The Webhook Request.
/// </summary>
public WebhookRequestModel Request { get; set; }
}
public WebhookRequestModel Request { get; set; } = null!;
}

View File

@@ -1,52 +1,65 @@
using System.Collections.Generic;
using WireMock.Types;
namespace WireMock.Admin.Mappings
namespace WireMock.Admin.Mappings;
/// <summary>
/// RequestModel
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class WebhookRequestModel
{
/// <summary>
/// RequestModel
/// Gets or sets the Url.
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class WebhookRequestModel
{
/// <summary>
/// Gets or sets the Url.
/// </summary>
public string Url { get; set; }
public string Url { get; set; } = null!;
/// <summary>
/// The method
/// </summary>
public string Method { get; set; }
/// <summary>
/// The method
/// </summary>
public string Method { get; set; } = null!;
/// <summary>
/// Gets or sets the headers.
/// </summary>
public IDictionary<string, string>? Headers { get; set; }
/// <summary>
/// Gets or sets the headers.
/// </summary>
public IDictionary<string, string>? Headers { get; set; }
/// <summary>
/// Gets or sets the body.
/// </summary>
public string? Body { get; set; }
/// <summary>
/// Gets or sets the body.
/// </summary>
public string? Body { get; set; }
/// <summary>
/// Gets or sets the body (as JSON object).
/// </summary>
public object? BodyAsJson { get; set; }
/// <summary>
/// Gets or sets the body (as JSON object).
/// </summary>
public object? BodyAsJson { get; set; }
/// <summary>
/// Use ResponseMessage Transformer.
/// </summary>
public bool? UseTransformer { get; set; }
/// <summary>
/// Use ResponseMessage Transformer.
/// </summary>
public bool? UseTransformer { get; set; }
/// <summary>
/// Gets the type of the transformer.
/// </summary>
public string TransformerType { get; set; }
/// <summary>
/// Gets the type of the transformer.
/// </summary>
public string? TransformerType { get; set; }
/// <summary>
/// The ReplaceNodeOptions to use when transforming a JSON node.
/// </summary>
public string TransformerReplaceNodeOptions { get; set; }
}
/// <summary>
/// The ReplaceNodeOptions to use when transforming a JSON node.
/// </summary>
public string? TransformerReplaceNodeOptions { get; set; }
/// <summary>
/// Gets or sets the delay in milliseconds.
/// </summary>
public int? Delay { get; set; }
/// <summary>
/// Gets or sets the minimum random delay in milliseconds.
/// </summary>
public int? MinimumRandomDelay { get; set; }
/// <summary>
/// Gets or sets the maximum random delay in milliseconds.
/// </summary>
public int? MaximumRandomDelay { get; set; }
}

View File

@@ -30,12 +30,12 @@ public class LogEntryModel
/// <summary>
/// The mapping unique title.
/// </summary>
public string MappingTitle { get; set; }
public string? MappingTitle { get; set; }
/// <summary>
/// The request match result.
/// </summary>
public LogRequestMatchModel RequestMatchResult { get; set; }
public LogRequestMatchModel? RequestMatchResult { get; set; }
/// <summary>
/// The partial mapping unique identifier.
@@ -45,10 +45,10 @@ public class LogEntryModel
/// <summary>
/// The partial mapping unique title.
/// </summary>
public string PartialMappingTitle { get; set; }
public string? PartialMappingTitle { get; set; }
/// <summary>
/// The partial request match result.
/// </summary>
public LogRequestMatchModel PartialRequestMatchResult { get; set; }
public LogRequestMatchModel? PartialRequestMatchResult { get; set; }
}

View File

@@ -54,5 +54,14 @@ namespace WireMock.Admin.Settings
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
/// </summary>
// public bool PreferProxyMapping { get; set; }
/// <summary>
/// When SaveMapping is set to <c>true</c>, this setting can be used to control the behavior of the generated request matchers for the new mapping.
/// - <c>false</c>, the default matchers will be used.
/// - <c>true</c>, the defined mappings in the request wil be used for the new mapping.
///
/// Default value is false.
/// </summary>
public bool UseDefinedRequestMatchers { get; set; }
}
}

View File

@@ -3,60 +3,59 @@ using WireMock.ResponseBuilders;
using WireMock.Types;
using WireMock.Util;
namespace WireMock
namespace WireMock;
/// <summary>
/// IResponseMessage
/// </summary>
public interface IResponseMessage
{
/// <summary>
/// IResponseMessage
/// The Body.
/// </summary>
public interface IResponseMessage
{
/// <summary>
/// The Body.
/// </summary>
IBodyData? BodyData { get; }
IBodyData? BodyData { get; }
/// <summary>
/// Gets the body destination (SameAsSource, String or Bytes).
/// </summary>
string BodyDestination { get; }
/// <summary>
/// Gets the body destination (Null, SameAsSource, String or Bytes).
/// </summary>
string? BodyDestination { get; }
/// <summary>
/// Gets or sets the body.
/// </summary>
string BodyOriginal { get; }
/// <summary>
/// Gets or sets the body.
/// </summary>
string? BodyOriginal { get; }
/// <summary>
/// Gets the Fault percentage.
/// </summary>
double? FaultPercentage { get; }
/// <summary>
/// Gets the Fault percentage.
/// </summary>
double? FaultPercentage { get; }
/// <summary>
/// The FaultType.
/// </summary>
FaultType FaultType { get; }
/// <summary>
/// The FaultType.
/// </summary>
FaultType FaultType { get; }
/// <summary>
/// Gets the headers.
/// </summary>
IDictionary<string, WireMockList<string>>? Headers { get; }
/// <summary>
/// Gets the headers.
/// </summary>
IDictionary<string, WireMockList<string>>? Headers { get; }
/// <summary>
/// Gets or sets the status code.
/// </summary>
object StatusCode { get; }
/// <summary>
/// Gets or sets the status code.
/// </summary>
object? StatusCode { get; }
/// <summary>
/// Adds the header.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="value">The value.</param>
void AddHeader(string name, string value);
/// <summary>
/// Adds the header.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="value">The value.</param>
void AddHeader(string name, string value);
/// <summary>
/// Adds the header.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="values">The values.</param>
void AddHeader(string name, params string[] values);
}
/// <summary>
/// Adds the header.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="values">The values.</param>
void AddHeader(string name, params string[] values);
}

View File

@@ -1,56 +1,55 @@
using System;
using System;
using WireMock.Matchers.Request;
namespace WireMock.Logging
namespace WireMock.Logging;
/// <summary>
/// ILogEntry
/// </summary>
public interface ILogEntry
{
/// <summary>
/// ILogEntry
/// Gets the unique identifier.
/// </summary>
public interface ILogEntry
{
/// <summary>
/// Gets the unique identifier.
/// </summary>
Guid Guid { get; }
Guid Guid { get; }
/// <summary>
/// Gets the mapping unique identifier.
/// </summary>
Guid? MappingGuid { get; }
/// <summary>
/// Gets the mapping unique identifier.
/// </summary>
Guid? MappingGuid { get; }
/// <summary>
/// Gets the mapping unique title.
/// </summary>
string MappingTitle { get; }
/// <summary>
/// Gets the mapping unique title.
/// </summary>
string? MappingTitle { get; }
/// <summary>
/// Gets the partial mapping unique identifier.
/// </summary>
Guid? PartialMappingGuid { get; }
/// <summary>
/// Gets the partial mapping unique identifier.
/// </summary>
Guid? PartialMappingGuid { get; }
/// <summary>
/// Gets the partial mapping unique title.
/// </summary>
string PartialMappingTitle { get; }
/// <summary>
/// Gets the partial mapping unique title.
/// </summary>
string? PartialMappingTitle { get; }
/// <summary>
/// Gets the partial match result.
/// </summary>
IRequestMatchResult PartialMatchResult { get; }
/// <summary>
/// Gets the partial match result.
/// </summary>
IRequestMatchResult PartialMatchResult { get; }
/// <summary>
/// Gets the request match result.
/// </summary>
IRequestMatchResult RequestMatchResult { get; }
/// <summary>
/// Gets the request match result.
/// </summary>
IRequestMatchResult RequestMatchResult { get; }
/// <summary>
/// Gets the request message.
/// </summary>
IRequestMessage RequestMessage { get; }
/// <summary>
/// Gets the request message.
/// </summary>
IRequestMessage RequestMessage { get; }
/// <summary>
/// Gets the response message.
/// </summary>
IResponseMessage ResponseMessage { get; }
}
/// <summary>
/// Gets the response message.
/// </summary>
IResponseMessage ResponseMessage { get; }
}

View File

@@ -1,25 +1,24 @@
using System;
namespace WireMock.Models
namespace WireMock.Models;
/// <summary>
/// TimeSettings: Start, End and TTL
/// </summary>
public interface ITimeSettings
{
/// <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>
public interface ITimeSettings
{
/// <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; }
DateTime? Start { get; set; }
/// <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).
/// </summary>
DateTime? End { get; set; }
/// <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).
/// </summary>
DateTime? End { get; set; }
/// <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).
/// </summary>
int? TTL { get; set; }
}
/// <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).
/// </summary>
int? TTL { get; set; }
}

View File

@@ -1,13 +1,12 @@
namespace WireMock.Models
namespace WireMock.Models;
/// <summary>
/// IWebhook
/// </summary>
public interface IWebhook
{
/// <summary>
/// IWebhook
/// Request
/// </summary>
public interface IWebhook
{
/// <summary>
/// Request
/// </summary>
IWebhookRequest Request { get; set; }
}
IWebhookRequest Request { get; set; }
}

View File

@@ -2,46 +2,60 @@ using System.Collections.Generic;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Models
namespace WireMock.Models;
/// <summary>
/// IWebhookRequest
/// </summary>
public interface IWebhookRequest
{
/// <summary>
/// IWebhookRequest
/// The Webhook Url.
/// </summary>
public interface IWebhookRequest
{
/// <summary>
/// The Webhook Url.
/// </summary>
string Url { get; set; }
string Url { get; set; }
/// <summary>
/// The method to use.
/// </summary>
string Method { get; set; }
/// <summary>
/// The method to use.
/// </summary>
string Method { get; set; }
/// <summary>
/// The Headers to send.
/// </summary>
IDictionary<string, WireMockList<string>>? Headers { get; }
/// <summary>
/// The Headers to send.
/// </summary>
IDictionary<string, WireMockList<string>>? Headers { get; }
/// <summary>
/// The body to send.
/// </summary>
IBodyData? BodyData { get; set; }
/// <summary>
/// The body to send.
/// </summary>
IBodyData? BodyData { get; set; }
/// <summary>
/// Use Transformer.
/// </summary>
bool? UseTransformer { get; set; }
/// <summary>
/// Use Transformer.
/// </summary>
bool? UseTransformer { get; set; }
/// <summary>
/// The transformer type.
/// </summary>
TransformerType TransformerType { get; set; }
/// <summary>
/// The transformer type.
/// </summary>
TransformerType TransformerType { get; set; }
/// <summary>
/// The ReplaceNodeOptions to use when transforming a JSON node.
/// </summary>
ReplaceNodeOptions TransformerReplaceNodeOptions { get; set; }
}
/// <summary>
/// The ReplaceNodeOptions to use when transforming a JSON node.
/// </summary>
ReplaceNodeOptions TransformerReplaceNodeOptions { get; set; }
/// <summary>
/// Gets or sets the delay in milliseconds.
/// </summary>
int? Delay { get; set; }
/// <summary>
/// Gets or sets the minimum random delay in milliseconds.
/// </summary>
int? MinimumRandomDelay { get; set; }
/// <summary>
/// Gets or sets the maximum random delay in milliseconds.
/// </summary>
int? MaximumRandomDelay { get; set; }
}

View File

@@ -0,0 +1,15 @@
using System;
namespace WireMock.Types;
[Flags]
public enum HostingScheme
{
None = 0x0,
Http = 0x1,
Https = 0x2,
HttpAndHttps = Http | Https
}

View File

@@ -1,44 +1,69 @@
using System.Collections.Generic;
using System.Linq;
using System.Collections.Generic;
namespace WireMock.Types
namespace WireMock.Types;
/// <summary>
/// A special List which overrides the ToString() to return first value.
/// </summary>
/// <typeparam name="T">The generic type</typeparam>
/// <seealso cref="List{T}" />
public class WireMockList<T> : List<T>
{
/// <summary>
/// A special List which overrides the ToString() to return first value.
/// Initializes a new instance of the <see cref="WireMockList{T}"/> class.
/// </summary>
/// <typeparam name="T">The generic type</typeparam>
/// <seealso cref="List{T}" />
public class WireMockList<T> : List<T>
public WireMockList()
{
/// <summary>
/// Initializes a new instance of the <see cref="WireMockList{T}"/> class.
/// </summary>
public WireMockList()
{
}
}
/// <summary>
/// Initializes a new instance of the <see cref="WireMockList{T}"/> class.
/// </summary>
/// <param name="collection">The collection whose elements are copied to the new list.</param>
public WireMockList(params T[] collection) : base(collection)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WireMockList{T}"/> class.
/// </summary>
/// <param name="collection">The collection whose elements are copied to the new list.</param>
public WireMockList(params T[] collection) : base(collection)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WireMockList{T}"/> class.
/// </summary>
/// <param name="collection">The collection whose elements are copied to the new list.</param>
public WireMockList(IEnumerable<T> collection) : base(collection)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WireMockList{T}"/> class.
/// </summary>
/// <param name="collection">The collection whose elements are copied to the new list.</param>
public WireMockList(IEnumerable<T> collection) : base(collection)
{
}
/// <summary>
/// Returns a <see cref="string" /> that represents this instance.
/// </summary>
public override string ToString()
/// <summary>
/// Operator for setting T
/// </summary>
/// <param name="value">The value to set.</param>
public static implicit operator WireMockList<T>(T value) => new(value);
/// <summary>
/// Operator for setting T[]
/// </summary>
/// <param name="values">The values to set.</param>
public static implicit operator WireMockList<T>(T[] values) => new(values);
/// <summary>
/// Returns a <see cref="string" /> that represents this instance.
/// </summary>
public override string ToString()
{
switch (Count)
{
return this.Any() ? this.First().ToString() : base.ToString();
case 0:
return string.Empty;
case 1:
if (this[0] is string strValue)
{
return strValue;
}
return this[0]?.ToString();
default:
return base.ToString();
}
}
}

View File

@@ -35,7 +35,7 @@
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<!-- See also https://mstack.nl/blog/20210801-source-generators -->
<PackageReference Include="FluentBuilder" Version="0.4.9">
<PackageReference Include="FluentBuilder" Version="0.5.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -29,7 +29,7 @@ public class WireMockAssertions
"Expected {context:wiremockserver} to have been called at address matching the absolute url {0}{reason}, but no calls were made.",
absoluteUrl)
.Then
.ForCondition(x => _callsCount == null && x.Any(y => y.AbsoluteUrl == absoluteUrl) || _callsCount == x.Count(y => y.AbsoluteUrl == absoluteUrl))
.ForCondition(x => (_callsCount == null && x.Any(y => y.AbsoluteUrl == absoluteUrl)) || (_callsCount == x.Count(y => y.AbsoluteUrl == absoluteUrl)))
.FailWith(
"Expected {context:wiremockserver} to have been called at address matching the absolute url {0}{reason}, but didn't find it among the calls to {1}.",
_ => absoluteUrl, requests => requests.Select(request => request.AbsoluteUrl));
@@ -48,7 +48,7 @@ public class WireMockAssertions
"Expected {context:wiremockserver} to have been called at address matching the url {0}{reason}, but no calls were made.",
url)
.Then
.ForCondition(x => _callsCount == null && x.Any(y => y.Url == url) || _callsCount == x.Count(y => y.Url == url))
.ForCondition(x => (_callsCount == null && x.Any(y => y.Url == url)) || (_callsCount == x.Count(y => y.Url == url)))
.FailWith(
"Expected {context:wiremockserver} to have been called at address matching the url {0}{reason}, but didn't find it among the calls to {1}.",
_ => url, requests => requests.Select(request => request.Url));
@@ -67,7 +67,7 @@ public class WireMockAssertions
"Expected {context:wiremockserver} to have been called with proxy url {0}{reason}, but no calls were made.",
proxyUrl)
.Then
.ForCondition(x => _callsCount == null && x.Any(y => y.ProxyUrl == proxyUrl) || _callsCount == x.Count(y => y.ProxyUrl == proxyUrl))
.ForCondition(x => (_callsCount == null && x.Any(y => y.ProxyUrl == proxyUrl)) || (_callsCount == x.Count(y => y.ProxyUrl == proxyUrl)))
.FailWith(
"Expected {context:wiremockserver} to have been called with proxy url {0}{reason}, but didn't find it among the calls with {1}.",
_ => proxyUrl, requests => requests.Select(request => request.ProxyUrl));
@@ -86,7 +86,7 @@ public class WireMockAssertions
"Expected {context:wiremockserver} to have been called from client IP {0}{reason}, but no calls were made.",
clientIP)
.Then
.ForCondition(x => _callsCount == null && x.Any(y => y.ClientIP == clientIP) || _callsCount == x.Count(y => y.ClientIP == clientIP))
.ForCondition(x => (_callsCount == null && x.Any(y => y.ClientIP == clientIP)) || (_callsCount == x.Count(y => y.ClientIP == clientIP)))
.FailWith(
"Expected {context:wiremockserver} to have been called from client IP {0}{reason}, but didn't find it among the calls from IP(s) {1}.",
_ => clientIP, requests => requests.Select(request => request.ClientIP));
@@ -129,4 +129,59 @@ public class WireMockAssertions
return new AndConstraint<WireMockAssertions>(this);
}
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingConnect(string because = "", params object[] becauseArgs)
=> UsingMethod("CONNECT", because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingDelete(string because = "", params object[] becauseArgs)
=> UsingMethod("DELETE", because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingGet(string because = "", params object[] becauseArgs)
=> UsingMethod("GET", because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingHead(string because = "", params object[] becauseArgs)
=> UsingMethod("HEAD", because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingOptions(string because = "", params object[] becauseArgs)
=> UsingMethod("OPTIONS", because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingPost(string because = "", params object[] becauseArgs)
=> UsingMethod("POST", because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingPatch(string because = "", params object[] becauseArgs)
=> UsingMethod("PATCH", because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingPut(string because = "", params object[] becauseArgs)
=> UsingMethod("PUT", because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingTrace(string because = "", params object[] becauseArgs)
=> UsingMethod("TRACE", because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingMethod(string method, string because = "", params object[] becauseArgs)
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
.Given(() => _subject.LogEntries.Select(x => x.RequestMessage).ToList())
.ForCondition(requests => requests.Any())
.FailWith(
"Expected {context:wiremockserver} to have been called using method {0}{reason}, but no calls were made.",
method)
.Then
.ForCondition(x => (_callsCount == null && x.Any(y => y.Method == method)) || (_callsCount == x.Count(y => y.Method == method)))
.FailWith(
"Expected {context:wiremockserver} to have been called using method {0}{reason}, but didn't find it among the methods {1}.",
_ => method, requests => requests.Select(request => request.Method));
return new AndConstraint<WireMockAssertions>(this);
}
}

View File

@@ -59,7 +59,7 @@ internal class CSharpCodeMatcher : ICSharpCodeMatcher
MatchOperator = matchOperator;
}
public double IsMatch(string input)
public double IsMatch(string? input)
{
return IsMatchInternal(input);
}

View File

@@ -25,7 +25,7 @@
<PackageReference Include="RamlToOpenApiConverter" Version="0.6.1" />
<PackageReference Include="JetBrains.Annotations" Version="2022.1.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.15" />
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.16" />
<PackageReference Include="Stef.Validation" Version="0.1.0" />
</ItemGroup>

View File

@@ -1,4 +1,5 @@
#if !NETSTANDARD1_3
using System;
using System.Globalization;
using System.IdentityModel.Tokens.Jwt;
using System.Text.RegularExpressions;
@@ -6,67 +7,72 @@ using AnyOfTypes;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Stef.Validation;
using WireMock.Matchers;
using WireMock.Models;
namespace WireMock.Authentication
namespace WireMock.Authentication;
/// <summary>
/// https://www.c-sharpcorner.com/article/how-to-validate-azure-ad-token-using-console-application/
/// https://stackoverflow.com/questions/38684865/validation-of-an-azure-ad-bearer-token-in-a-console-application
/// </summary>
internal class AzureADAuthenticationMatcher : IStringMatcher
{
/// <summary>
/// https://www.c-sharpcorner.com/article/how-to-validate-azure-ad-token-using-console-application/
/// https://stackoverflow.com/questions/38684865/validation-of-an-azure-ad-bearer-token-in-a-console-application
/// </summary>
internal class AzureADAuthenticationMatcher : IStringMatcher
private const string BearerPrefix = "Bearer ";
private readonly string _audience;
private readonly string _stsDiscoveryEndpoint;
public AzureADAuthenticationMatcher(string tenant, string audience)
{
private const string BearerPrefix = "Bearer ";
_audience = Guard.NotNullOrEmpty(audience);
_stsDiscoveryEndpoint = string.Format(CultureInfo.InvariantCulture, "https://login.microsoftonline.com/{0}/.well-known/openid-configuration", Guard.NotNullOrEmpty(tenant));
}
private readonly string _audience;
private readonly string _stsDiscoveryEndpoint;
public string Name => nameof(AzureADAuthenticationMatcher);
public AzureADAuthenticationMatcher(string tenant, string audience)
public MatchBehaviour MatchBehaviour => MatchBehaviour.AcceptOnMatch;
public bool ThrowException => false;
public AnyOf<string, StringPattern>[] GetPatterns()
{
return EmptyArray<AnyOf<string, StringPattern>>.Value;
}
public MatchOperator MatchOperator { get; } = MatchOperator.Or;
public double IsMatch(string? input)
{
if (string.IsNullOrEmpty(input))
{
_audience = audience;
_stsDiscoveryEndpoint = string.Format(CultureInfo.InvariantCulture, "https://login.microsoftonline.com/{0}/.well-known/openid-configuration", tenant);
return MatchScores.Mismatch;
}
public string Name => nameof(AzureADAuthenticationMatcher);
var token = Regex.Replace(input, BearerPrefix, string.Empty, RegexOptions.IgnoreCase);
public MatchBehaviour MatchBehaviour => MatchBehaviour.AcceptOnMatch;
public bool ThrowException => false;
public AnyOf<string, StringPattern>[] GetPatterns()
try
{
return new AnyOf<string, StringPattern>[0];
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(_stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
var config = configManager.GetConfigurationAsync().ConfigureAwait(false).GetAwaiter().GetResult();
var validationParameters = new TokenValidationParameters
{
ValidAudience = _audience,
ValidIssuer = config.Issuer,
IssuerSigningKeys = config.SigningKeys,
ValidateLifetime = true
};
// Throws an Exception as the token is invalid (expired, invalid-formatted, etc.)
new JwtSecurityTokenHandler().ValidateToken(token, validationParameters, out var _);
return MatchScores.Perfect;
}
public MatchOperator MatchOperator { get; } = MatchOperator.Or;
public double IsMatch(string input)
catch
{
var token = Regex.Replace(input, BearerPrefix, string.Empty, RegexOptions.IgnoreCase);
try
{
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(_stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
var config = configManager.GetConfigurationAsync().ConfigureAwait(false).GetAwaiter().GetResult();
var validationParameters = new TokenValidationParameters
{
ValidAudience = _audience,
ValidIssuer = config.Issuer,
IssuerSigningKeys = config.SigningKeys,
ValidateLifetime = true
};
// Throws an Exception as the token is invalid (expired, invalid-formatted, etc.)
new JwtSecurityTokenHandler().ValidateToken(token, validationParameters, out var _);
return MatchScores.Perfect;
}
catch
{
return MatchScores.Mismatch;
}
return MatchScores.Mismatch;
}
}
}

View File

@@ -2,19 +2,18 @@ using System;
using System.Text;
using WireMock.Matchers;
namespace WireMock.Authentication
namespace WireMock.Authentication;
internal class BasicAuthenticationMatcher : RegexMatcher
{
internal class BasicAuthenticationMatcher : RegexMatcher
public BasicAuthenticationMatcher(string username, string password) : base(BuildPattern(username, password))
{
public BasicAuthenticationMatcher(string username, string password) : base(BuildPattern(username, password))
{
}
}
public override string Name => nameof(BasicAuthenticationMatcher);
public override string Name => nameof(BasicAuthenticationMatcher);
private static string BuildPattern(string username, string password)
{
return "^(?i)BASIC " + Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password)) + "$";
}
private static string BuildPattern(string username, string password)
{
return "^(?i)BASIC " + Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password)) + "$";
}
}

View File

@@ -0,0 +1,11 @@
// ReSharper disable once CheckNamespace
namespace System;
internal static class EmptyArray<T>
{
#if NET451 || NET452
public static readonly T[] Value = new T[0];
#else
public static readonly T[] Value = Array.Empty<T>();
#endif
}

View File

@@ -1,28 +1,26 @@
#if NETSTANDARD1_3
using System;
using System.Net;
#if NETSTANDARD1_3
namespace System.Net
// ReSharper disable once CheckNamespace
namespace System.Net;
internal class WebProxy : IWebProxy
{
internal class WebProxy : IWebProxy
private readonly string _proxy;
public ICredentials? Credentials { get; set; }
public WebProxy(string proxy)
{
private readonly string _proxy;
public ICredentials Credentials { get; set; }
_proxy = proxy;
}
public WebProxy(string proxy)
{
_proxy = proxy;
}
public Uri GetProxy(Uri destination)
{
return new Uri(_proxy);
}
public Uri GetProxy(Uri destination)
{
return new Uri(_proxy);
}
public bool IsBypassed(Uri host)
{
return true;
}
public bool IsBypassed(Uri host)
{
return true;
}
}
#endif

View File

@@ -1,26 +1,24 @@
using System.Collections.Generic;
using System.Linq;
using AnyOfTypes;
using JetBrains.Annotations;
using WireMock.Models;
namespace WireMock.Extensions
namespace WireMock.Extensions;
internal static class AnyOfExtensions
{
internal static class AnyOfExtensions
public static string GetPattern(this AnyOf<string, StringPattern> value)
{
public static string GetPattern([NotNull] this AnyOf<string, StringPattern> value)
{
return value.IsFirst ? value.First : value.Second.Pattern;
}
return value.IsFirst ? value.First : value.Second.Pattern;
}
public static AnyOf<string, StringPattern>[] ToAnyOfPatterns([NotNull] this IEnumerable<string> patterns)
{
return patterns.Select(p => p.ToAnyOfPattern()).ToArray();
}
public static AnyOf<string, StringPattern>[] ToAnyOfPatterns(this IEnumerable<string> patterns)
{
return patterns.Select(p => p.ToAnyOfPattern()).ToArray();
}
public static AnyOf<string, StringPattern> ToAnyOfPattern([CanBeNull] this string pattern)
{
return new AnyOf<string, StringPattern>(pattern);
}
public static AnyOf<string, StringPattern> ToAnyOfPattern(this string pattern)
{
return new AnyOf<string, StringPattern>(pattern);
}
}

View File

@@ -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;
}
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;
return true;
}
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;
}
}

View File

@@ -1,30 +1,28 @@
using System.Net.Http;
using System.Net.Http;
using System.Net.Http.Headers;
using JetBrains.Annotations;
using Stef.Validation;
namespace WireMock.Http
namespace WireMock.Http;
internal static class ByteArrayContentHelper
{
internal static class ByteArrayContentHelper
/// <summary>
/// Creates a ByteArrayContent object.
/// </summary>
/// <param name="content">The byte[] content (cannot be null)</param>
/// <param name="contentType">The ContentType (can be null)</param>
/// <returns>ByteArrayContent</returns>
internal static ByteArrayContent Create(byte[] content, MediaTypeHeaderValue? contentType)
{
/// <summary>
/// Creates a ByteArrayContent object.
/// </summary>
/// <param name="content">The byte[] content (cannot be null)</param>
/// <param name="contentType">The ContentType (can be null)</param>
/// <returns>ByteArrayContent</returns>
internal static ByteArrayContent Create([NotNull] byte[] content, [CanBeNull] MediaTypeHeaderValue contentType)
Guard.NotNull(content);
var byteContent = new ByteArrayContent(content);
if (contentType != null)
{
Guard.NotNull(content, nameof(content));
var byteContent = new ByteArrayContent(content);
if (contentType != null)
{
byteContent.Headers.Remove(HttpKnownHeaderNames.ContentType);
byteContent.Headers.ContentType = contentType;
}
return byteContent;
byteContent.Headers.Remove(HttpKnownHeaderNames.ContentType);
byteContent.Headers.ContentType = contentType;
}
return byteContent;
}
}
}

View File

@@ -1,122 +1,121 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Linq;
namespace WireMock.Http
namespace WireMock.Http;
/// <summary>
/// Copied from https://raw.githubusercontent.com/dotnet/corefx/master/src/Common/src/System/Net/HttpKnownHeaderNames.cs
/// </summary>
internal static class HttpKnownHeaderNames
{
/// <summary>
/// Copied from https://raw.githubusercontent.com/dotnet/corefx/master/src/Common/src/System/Net/HttpKnownHeaderNames.cs
/// </summary>
internal static class HttpKnownHeaderNames
// https://docs.microsoft.com/en-us/dotnet/api/system.net.webheadercollection.isrestricted
private static readonly string[] RestrictedResponseHeaders =
{
// https://docs.microsoft.com/en-us/dotnet/api/system.net.webheadercollection.isrestricted
private static readonly string[] RestrictedResponseHeaders =
{
Accept,
Connection,
ContentLength,
ContentType,
Date, // RFC1123Pattern
Expect,
Host,
IfModifiedSince,
Range,
Referer,
TransferEncoding,
UserAgent,
ProxyConnection
};
Accept,
Connection,
ContentLength,
ContentType,
Date, // RFC1123Pattern
Expect,
Host,
IfModifiedSince,
Range,
Referer,
TransferEncoding,
UserAgent,
ProxyConnection
};
/// <summary>Tests whether the specified HTTP header can be set for the response.</summary>
/// <param name="headerName">The header to test.</param>
/// <returns>true if the header is restricted; otherwise, false.</returns>
public static bool IsRestrictedResponseHeader(string headerName) => RestrictedResponseHeaders.Contains(headerName, StringComparer.OrdinalIgnoreCase);
/// <summary>Tests whether the specified HTTP header can be set for the response.</summary>
/// <param name="headerName">The header to test.</param>
/// <returns>true if the header is restricted; otherwise, false.</returns>
public static bool IsRestrictedResponseHeader(string headerName) => RestrictedResponseHeaders.Contains(headerName, StringComparer.OrdinalIgnoreCase);
public const string Accept = "Accept";
public const string AcceptCharset = "Accept-Charset";
public const string AcceptEncoding = "Accept-Encoding";
public const string AcceptLanguage = "Accept-Language";
public const string AcceptPatch = "Accept-Patch";
public const string AcceptRanges = "Accept-Ranges";
public const string AccessControlAllowCredentials = "Access-Control-Allow-Credentials";
public const string AccessControlAllowHeaders = "Access-Control-Allow-Headers";
public const string AccessControlAllowMethods = "Access-Control-Allow-Methods";
public const string AccessControlAllowOrigin = "Access-Control-Allow-Origin";
public const string AccessControlExposeHeaders = "Access-Control-Expose-Headers";
public const string AccessControlMaxAge = "Access-Control-Max-Age";
public const string Age = "Age";
public const string Allow = "Allow";
public const string AltSvc = "Alt-Svc";
public const string Authorization = "Authorization";
public const string CacheControl = "Cache-Control";
public const string Connection = "Connection";
public const string ContentDisposition = "Content-Disposition";
public const string ContentEncoding = "Content-Encoding";
public const string ContentLanguage = "Content-Language";
public const string ContentLength = "Content-Length";
public const string ContentLocation = "Content-Location";
public const string ContentMD5 = "Content-MD5";
public const string ContentRange = "Content-Range";
public const string ContentSecurityPolicy = "Content-Security-Policy";
public const string ContentType = "Content-Type";
public const string Cookie = "Cookie";
public const string Cookie2 = "Cookie2";
public const string Date = "Date";
public const string ETag = "ETag";
public const string Expect = "Expect";
public const string Expires = "Expires";
public const string From = "From";
public const string Host = "Host";
public const string IfMatch = "If-Match";
public const string IfModifiedSince = "If-Modified-Since";
public const string IfNoneMatch = "If-None-Match";
public const string IfRange = "If-Range";
public const string IfUnmodifiedSince = "If-Unmodified-Since";
public const string KeepAlive = "Keep-Alive";
public const string LastModified = "Last-Modified";
public const string Link = "Link";
public const string Location = "Location";
public const string MaxForwards = "Max-Forwards";
public const string Origin = "Origin";
public const string P3P = "P3P";
public const string Pragma = "Pragma";
public const string ProxyAuthenticate = "Proxy-Authenticate";
public const string ProxyAuthorization = "Proxy-Authorization";
public const string ProxyConnection = "Proxy-Connection";
public const string PublicKeyPins = "Public-Key-Pins";
public const string Range = "Range";
public const string Referer = "Referer"; // NB: The spelling-mistake "Referer" for "Referrer" must be matched.
public const string RetryAfter = "Retry-After";
public const string SecWebSocketAccept = "Sec-WebSocket-Accept";
public const string SecWebSocketExtensions = "Sec-WebSocket-Extensions";
public const string SecWebSocketKey = "Sec-WebSocket-Key";
public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol";
public const string SecWebSocketVersion = "Sec-WebSocket-Version";
public const string Server = "Server";
public const string SetCookie = "Set-Cookie";
public const string SetCookie2 = "Set-Cookie2";
public const string StrictTransportSecurity = "Strict-Transport-Security";
public const string TE = "TE";
public const string TSV = "TSV";
public const string Trailer = "Trailer";
public const string TransferEncoding = "Transfer-Encoding";
public const string Upgrade = "Upgrade";
public const string UpgradeInsecureRequests = "Upgrade-Insecure-Requests";
public const string UserAgent = "User-Agent";
public const string Vary = "Vary";
public const string Via = "Via";
public const string WWWAuthenticate = "WWW-Authenticate";
public const string Warning = "Warning";
public const string XAspNetVersion = "X-AspNet-Version";
public const string XContentDuration = "X-Content-Duration";
public const string XContentTypeOptions = "X-Content-Type-Options";
public const string XFrameOptions = "X-Frame-Options";
public const string XMSEdgeRef = "X-MSEdge-Ref";
public const string XPoweredBy = "X-Powered-By";
public const string XRequestID = "X-Request-ID";
public const string XUACompatible = "X-UA-Compatible";
}
public const string Accept = "Accept";
public const string AcceptCharset = "Accept-Charset";
public const string AcceptEncoding = "Accept-Encoding";
public const string AcceptLanguage = "Accept-Language";
public const string AcceptPatch = "Accept-Patch";
public const string AcceptRanges = "Accept-Ranges";
public const string AccessControlAllowCredentials = "Access-Control-Allow-Credentials";
public const string AccessControlAllowHeaders = "Access-Control-Allow-Headers";
public const string AccessControlAllowMethods = "Access-Control-Allow-Methods";
public const string AccessControlAllowOrigin = "Access-Control-Allow-Origin";
public const string AccessControlExposeHeaders = "Access-Control-Expose-Headers";
public const string AccessControlMaxAge = "Access-Control-Max-Age";
public const string Age = "Age";
public const string Allow = "Allow";
public const string AltSvc = "Alt-Svc";
public const string Authorization = "Authorization";
public const string CacheControl = "Cache-Control";
public const string Connection = "Connection";
public const string ContentDisposition = "Content-Disposition";
public const string ContentEncoding = "Content-Encoding";
public const string ContentLanguage = "Content-Language";
public const string ContentLength = "Content-Length";
public const string ContentLocation = "Content-Location";
public const string ContentMD5 = "Content-MD5";
public const string ContentRange = "Content-Range";
public const string ContentSecurityPolicy = "Content-Security-Policy";
public const string ContentType = "Content-Type";
public const string Cookie = "Cookie";
public const string Cookie2 = "Cookie2";
public const string Date = "Date";
public const string ETag = "ETag";
public const string Expect = "Expect";
public const string Expires = "Expires";
public const string From = "From";
public const string Host = "Host";
public const string IfMatch = "If-Match";
public const string IfModifiedSince = "If-Modified-Since";
public const string IfNoneMatch = "If-None-Match";
public const string IfRange = "If-Range";
public const string IfUnmodifiedSince = "If-Unmodified-Since";
public const string KeepAlive = "Keep-Alive";
public const string LastModified = "Last-Modified";
public const string Link = "Link";
public const string Location = "Location";
public const string MaxForwards = "Max-Forwards";
public const string Origin = "Origin";
public const string P3P = "P3P";
public const string Pragma = "Pragma";
public const string ProxyAuthenticate = "Proxy-Authenticate";
public const string ProxyAuthorization = "Proxy-Authorization";
public const string ProxyConnection = "Proxy-Connection";
public const string PublicKeyPins = "Public-Key-Pins";
public const string Range = "Range";
public const string Referer = "Referer"; // NB: The spelling-mistake "Referer" for "Referrer" must be matched.
public const string RetryAfter = "Retry-After";
public const string SecWebSocketAccept = "Sec-WebSocket-Accept";
public const string SecWebSocketExtensions = "Sec-WebSocket-Extensions";
public const string SecWebSocketKey = "Sec-WebSocket-Key";
public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol";
public const string SecWebSocketVersion = "Sec-WebSocket-Version";
public const string Server = "Server";
public const string SetCookie = "Set-Cookie";
public const string SetCookie2 = "Set-Cookie2";
public const string StrictTransportSecurity = "Strict-Transport-Security";
public const string TE = "TE";
public const string TSV = "TSV";
public const string Trailer = "Trailer";
public const string TransferEncoding = "Transfer-Encoding";
public const string Upgrade = "Upgrade";
public const string UpgradeInsecureRequests = "Upgrade-Insecure-Requests";
public const string UserAgent = "User-Agent";
public const string Vary = "Vary";
public const string Via = "Via";
public const string WWWAuthenticate = "WWW-Authenticate";
public const string Warning = "Warning";
public const string XAspNetVersion = "X-AspNet-Version";
public const string XContentDuration = "X-Content-Duration";
public const string XContentTypeOptions = "X-Content-Type-Options";
public const string XFrameOptions = "X-Frame-Options";
public const string XMSEdgeRef = "X-MSEdge-Ref";
public const string XPoweredBy = "X-Powered-By";
public const string XRequestID = "X-Request-ID";
public const string XUACompatible = "X-UA-Compatible";
}

View File

@@ -3,89 +3,87 @@ using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using JetBrains.Annotations;
using Newtonsoft.Json;
using WireMock.Types;
using Stef.Validation;
using WireMock.Types;
namespace WireMock.Http
namespace WireMock.Http;
internal static class HttpRequestMessageHelper
{
internal static class HttpRequestMessageHelper
internal static HttpRequestMessage Create(IRequestMessage requestMessage, string url)
{
internal static HttpRequestMessage Create([NotNull] IRequestMessage requestMessage, [NotNull] string url)
Guard.NotNull(requestMessage);
Guard.NotNullOrEmpty(url);
var httpRequestMessage = new HttpRequestMessage(new HttpMethod(requestMessage.Method), url);
MediaTypeHeaderValue? contentType = null;
if (requestMessage.Headers != null && requestMessage.Headers.ContainsKey(HttpKnownHeaderNames.ContentType))
{
Guard.NotNull(requestMessage, nameof(requestMessage));
Guard.NotNullOrEmpty(url, nameof(url));
var value = requestMessage.Headers[HttpKnownHeaderNames.ContentType].FirstOrDefault();
MediaTypeHeaderValue.TryParse(value, out contentType);
}
var httpRequestMessage = new HttpRequestMessage(new HttpMethod(requestMessage.Method), url);
switch (requestMessage.BodyData?.DetectedBodyType)
{
case BodyType.Bytes:
httpRequestMessage.Content = ByteArrayContentHelper.Create(requestMessage.BodyData.BodyAsBytes, contentType);
break;
MediaTypeHeaderValue contentType = null;
if (requestMessage.Headers != null && requestMessage.Headers.ContainsKey(HttpKnownHeaderNames.ContentType))
{
var value = requestMessage.Headers[HttpKnownHeaderNames.ContentType].FirstOrDefault();
MediaTypeHeaderValue.TryParse(value, out contentType);
}
case BodyType.Json:
httpRequestMessage.Content = StringContentHelper.Create(JsonConvert.SerializeObject(requestMessage.BodyData.BodyAsJson), contentType);
break;
switch (requestMessage.BodyData?.DetectedBodyType)
{
case BodyType.Bytes:
httpRequestMessage.Content = ByteArrayContentHelper.Create(requestMessage.BodyData.BodyAsBytes, contentType);
break;
case BodyType.String:
httpRequestMessage.Content = StringContentHelper.Create(requestMessage.BodyData.BodyAsString, contentType);
break;
}
case BodyType.Json:
httpRequestMessage.Content = StringContentHelper.Create(JsonConvert.SerializeObject(requestMessage.BodyData.BodyAsJson), contentType);
break;
case BodyType.String:
httpRequestMessage.Content = StringContentHelper.Create(requestMessage.BodyData.BodyAsString, contentType);
break;
}
// Overwrite the host header
httpRequestMessage.Headers.Host = new Uri(url).Authority;
// Set other headers if present
if (requestMessage.Headers == null || requestMessage.Headers.Count == 0)
{
return httpRequestMessage;
}
var excludeHeaders = new List<string> { HttpKnownHeaderNames.Host, HttpKnownHeaderNames.ContentLength };
if (contentType != null)
{
// Content-Type should be set on the content
excludeHeaders.Add(HttpKnownHeaderNames.ContentType);
}
foreach (var header in requestMessage.Headers.Where(h => !excludeHeaders.Contains(h.Key, StringComparer.OrdinalIgnoreCase)))
{
// Skip if already added. We need to ToList() else calling httpRequestMessage.Headers.Contains() with a header starting with a ":" throws an exception.
if (httpRequestMessage.Headers.ToList().Any(h => string.Equals(h.Key, header.Key, StringComparison.OrdinalIgnoreCase)))
{
continue;
}
// Skip if already added. We need to ToList() else calling httpRequestMessage.Content.Headers.Contains(...) with a header starting with a ":" throws an exception.
if (httpRequestMessage.Content != null && httpRequestMessage.Content.Headers.ToList().Any(h => string.Equals(h.Key, header.Key, StringComparison.OrdinalIgnoreCase)))
{
continue;
}
// Try to add to request headers. If failed - try to add to content headers. If still fails, just ignore this header.
try
{
if (!httpRequestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value))
{
httpRequestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
}
catch
{
// Just continue
}
}
// Overwrite the host header
httpRequestMessage.Headers.Host = new Uri(url).Authority;
// Set other headers if present
if (requestMessage.Headers == null || requestMessage.Headers.Count == 0)
{
return httpRequestMessage;
}
var excludeHeaders = new List<string> { HttpKnownHeaderNames.Host, HttpKnownHeaderNames.ContentLength };
if (contentType != null)
{
// Content-Type should be set on the content
excludeHeaders.Add(HttpKnownHeaderNames.ContentType);
}
foreach (var header in requestMessage.Headers.Where(h => !excludeHeaders.Contains(h.Key, StringComparer.OrdinalIgnoreCase)))
{
// Skip if already added. We need to ToList() else calling httpRequestMessage.Headers.Contains() with a header starting with a ":" throws an exception.
if (httpRequestMessage.Headers.ToList().Any(h => string.Equals(h.Key, header.Key, StringComparison.OrdinalIgnoreCase)))
{
continue;
}
// Skip if already added. We need to ToList() else calling httpRequestMessage.Content.Headers.Contains(...) with a header starting with a ":" throws an exception.
if (httpRequestMessage.Content != null && httpRequestMessage.Content.Headers.ToList().Any(h => string.Equals(h.Key, header.Key, StringComparison.OrdinalIgnoreCase)))
{
continue;
}
// Try to add to request headers. If failed - try to add to content headers. If still fails, just ignore this header.
try
{
if (!httpRequestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value))
{
httpRequestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
}
catch
{
// Just continue
}
}
return httpRequestMessage;
}
}

View File

@@ -1,18 +1,17 @@
namespace WireMock.Http
namespace WireMock.Http;
/// <summary>
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
/// </summary>
internal static class HttpRequestMethods
{
/// <summary>
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
/// </summary>
internal static class HttpRequestMethods
{
public const string CONNECT = "CONNECT";
public const string DELETE = "DELETE";
public const string GET = "GET";
public const string HEAD = "HEAD";
public const string OPTIONS = "OPTIONS";
public const string PATCH = "PATCH";
public const string POST = "POST";
public const string PUT = "PUT";
public const string TRACE = "TRACE";
}
public const string CONNECT = "CONNECT";
public const string DELETE = "DELETE";
public const string GET = "GET";
public const string HEAD = "HEAD";
public const string OPTIONS = "OPTIONS";
public const string PATCH = "PATCH";
public const string POST = "POST";
public const string PUT = "PUT";
public const string TRACE = "TRACE";
}

View File

@@ -1,25 +1,23 @@
using System.Net.Http;
using System.Net.Http;
using System.Net.Http.Headers;
using JetBrains.Annotations;
using Stef.Validation;
namespace WireMock.Http
{
internal static class StringContentHelper
{
/// <summary>
/// Creates a StringContent object.
/// </summary>
/// <param name="content">The string content (cannot be null)</param>
/// <param name="contentType">The ContentType (can be null)</param>
/// <returns>StringContent</returns>
internal static StringContent Create([NotNull] string content, [CanBeNull] MediaTypeHeaderValue contentType)
{
Guard.NotNull(content, nameof(content));
namespace WireMock.Http;
var stringContent = new StringContent(content);
stringContent.Headers.ContentType = contentType;
return stringContent;
}
internal static class StringContentHelper
{
/// <summary>
/// Creates a StringContent object.
/// </summary>
/// <param name="content">The string content (cannot be null)</param>
/// <param name="contentType">The ContentType (can be null)</param>
/// <returns>StringContent</returns>
internal static StringContent Create(string content, MediaTypeHeaderValue? contentType)
{
Guard.NotNull(content);
var stringContent = new StringContent(content);
stringContent.Headers.ContentType = contentType;
return stringContent;
}
}

View File

@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Stef.Validation;
using WireMock.Models;
using WireMock.Settings;
using WireMock.Transformers;
@@ -11,75 +13,102 @@ using WireMock.Transformers.Handlebars;
using WireMock.Transformers.Scriban;
using WireMock.Types;
using WireMock.Util;
using Stef.Validation;
namespace WireMock.Http
namespace WireMock.Http;
internal class WebhookSender
{
internal class WebhookSender
private const string ClientIp = "::1";
private static readonly ThreadLocal<Random> Random = new(() => new Random(DateTime.UtcNow.Millisecond));
private readonly WireMockServerSettings _settings;
public WebhookSender(WireMockServerSettings settings)
{
private const string ClientIp = "::1";
_settings = Guard.NotNull(settings);
}
private readonly WireMockServerSettings _settings;
public async Task<HttpResponseMessage> SendAsync(
HttpClient client,
IMapping mapping,
IWebhookRequest webhookRequest,
IRequestMessage originalRequestMessage,
IResponseMessage originalResponseMessage
)
{
Guard.NotNull(client);
Guard.NotNull(mapping);
Guard.NotNull(webhookRequest);
Guard.NotNull(originalRequestMessage);
Guard.NotNull(originalResponseMessage);
public WebhookSender(WireMockServerSettings settings)
IBodyData? bodyData;
IDictionary<string, WireMockList<string>>? headers;
if (webhookRequest.UseTransformer == true)
{
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
}
public Task<HttpResponseMessage> SendAsync(HttpClient client, IWebhookRequest request, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage)
{
Guard.NotNull(client);
Guard.NotNull(request);
Guard.NotNull(originalRequestMessage);
Guard.NotNull(originalResponseMessage);
IBodyData? bodyData;
IDictionary<string, WireMockList<string>>? headers;
if (request.UseTransformer == true)
ITransformer responseMessageTransformer;
switch (webhookRequest.TransformerType)
{
ITransformer responseMessageTransformer;
switch (request.TransformerType)
{
case TransformerType.Handlebars:
var factoryHandlebars = new HandlebarsContextFactory(_settings.FileSystemHandler, _settings.HandlebarsRegistrationCallback);
responseMessageTransformer = new Transformer(factoryHandlebars);
break;
case TransformerType.Handlebars:
var factoryHandlebars = new HandlebarsContextFactory(_settings.FileSystemHandler, _settings.HandlebarsRegistrationCallback);
responseMessageTransformer = new Transformer(factoryHandlebars);
break;
case TransformerType.Scriban:
case TransformerType.ScribanDotLiquid:
var factoryDotLiquid = new ScribanContextFactory(_settings.FileSystemHandler, request.TransformerType);
responseMessageTransformer = new Transformer(factoryDotLiquid);
break;
case TransformerType.Scriban:
case TransformerType.ScribanDotLiquid:
var factoryDotLiquid = new ScribanContextFactory(_settings.FileSystemHandler, webhookRequest.TransformerType);
responseMessageTransformer = new Transformer(factoryDotLiquid);
break;
default:
throw new NotImplementedException($"TransformerType '{request.TransformerType}' is not supported.");
}
(bodyData, headers) = responseMessageTransformer.Transform(originalRequestMessage, originalResponseMessage, request.BodyData, request.Headers, request.TransformerReplaceNodeOptions);
}
else
{
bodyData = request.BodyData;
headers = request.Headers;
default:
throw new NotImplementedException($"TransformerType '{webhookRequest.TransformerType}' is not supported.");
}
// Create RequestMessage
var requestMessage = new RequestMessage(
new UrlDetails(request.Url),
request.Method,
ClientIp,
bodyData,
headers?.ToDictionary(x => x.Key, x => x.Value.ToArray())
)
{
DateTime = DateTime.UtcNow
};
// Create HttpRequestMessage
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, request.Url);
// Call the URL
return client.SendAsync(httpRequestMessage);
(bodyData, headers) = responseMessageTransformer.Transform(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.BodyData, webhookRequest.Headers, webhookRequest.TransformerReplaceNodeOptions);
}
else
{
bodyData = webhookRequest.BodyData;
headers = webhookRequest.Headers;
}
// Create RequestMessage
var requestMessage = new RequestMessage(
new UrlDetails(webhookRequest.Url),
webhookRequest.Method,
ClientIp,
bodyData,
headers?.ToDictionary(x => x.Key, x => x.Value.ToArray())
)
{
DateTime = DateTime.UtcNow
};
// Create HttpRequestMessage
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, webhookRequest.Url);
// Delay (if required)
if (TryGetDelay(webhookRequest, out var delay))
{
await Task.Delay(delay.Value).ConfigureAwait(false);
}
// Call the URL
return await client.SendAsync(httpRequestMessage).ConfigureAwait(false);
}
private static bool TryGetDelay(IWebhookRequest webhookRequest, [NotNullWhen(true)] out int? delay)
{
delay = webhookRequest.Delay;
var minimumDelay = webhookRequest.MinimumRandomDelay;
var maximumDelay = webhookRequest.MaximumRandomDelay;
if (minimumDelay is not null && maximumDelay is not null && maximumDelay >= minimumDelay)
{
delay = Random.Value!.Next(minimumDelay.Value, maximumDelay.Value);
return true;
}
return delay is not null;
}
}

View File

@@ -2,90 +2,42 @@ using System;
using System.IO;
using System.Security.Cryptography.X509Certificates;
namespace WireMock.HttpsCertificate
namespace WireMock.HttpsCertificate;
internal static class CertificateLoader
{
internal static class CertificateLoader
private const string ExtensionPem = ".PEM";
/// <summary>
/// Used by the WireMock.Net server
/// </summary>
public static X509Certificate2 LoadCertificate(
string? storeName,
string? storeLocation,
string? thumbprintOrSubjectName,
string? filePath,
string? passwordOrKey,
string host)
{
/// <summary>
/// Used by the WireMock.Net server
/// </summary>
public static X509Certificate2 LoadCertificate(
string storeName,
string storeLocation,
string thumbprintOrSubjectName,
string filePath,
string password,
string host)
if (!string.IsNullOrEmpty(storeName) && !string.IsNullOrEmpty(storeLocation))
{
if (!string.IsNullOrEmpty(storeName) && !string.IsNullOrEmpty(storeLocation))
{
var thumbprintOrSubjectNameOrHost = thumbprintOrSubjectName ?? host;
var thumbprintOrSubjectNameOrHost = thumbprintOrSubjectName ?? host;
var certStore = new X509Store((StoreName)Enum.Parse(typeof(StoreName), storeName), (StoreLocation)Enum.Parse(typeof(StoreLocation), storeLocation));
try
{
certStore.Open(OpenFlags.ReadOnly);
// Attempt to find by Thumbprint first
var matchingCertificates = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprintOrSubjectNameOrHost, false);
if (matchingCertificates.Count == 0)
{
// Fallback to SubjectName
matchingCertificates = certStore.Certificates.Find(X509FindType.FindBySubjectName, thumbprintOrSubjectNameOrHost, false);
if (matchingCertificates.Count == 0)
{
// No certificates matched the search criteria.
throw new FileNotFoundException($"No Certificate found with in store '{storeName}', location '{storeLocation}' for Thumbprint or SubjectName '{thumbprintOrSubjectNameOrHost}'.");
}
}
// Use the first matching certificate.
return matchingCertificates[0];
}
finally
{
#if NETSTANDARD || NET46
certStore.Dispose();
#else
certStore.Close();
#endif
}
}
if (!string.IsNullOrEmpty(filePath) && !string.IsNullOrEmpty(password))
{
return new X509Certificate2(filePath, password);
}
if (!string.IsNullOrEmpty(filePath))
{
return new X509Certificate2(filePath);
}
throw new InvalidOperationException("X509StoreName and X509StoreLocation OR X509CertificateFilePath are mandatory. Note that X509CertificatePassword is optional.");
}
/// <summary>
/// Used for Proxy
/// </summary>
public static X509Certificate2 LoadCertificate(string thumbprintOrSubjectName)
{
var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
var certStore = new X509Store((StoreName)Enum.Parse(typeof(StoreName), storeName), (StoreLocation)Enum.Parse(typeof(StoreLocation), storeLocation));
try
{
// Certificate must be in the local machine store
certStore.Open(OpenFlags.ReadOnly);
// Attempt to find by Thumbprint first
var matchingCertificates = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprintOrSubjectName, false);
var matchingCertificates = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprintOrSubjectNameOrHost, false);
if (matchingCertificates.Count == 0)
{
// Fallback to SubjectName
matchingCertificates = certStore.Certificates.Find(X509FindType.FindBySubjectName, thumbprintOrSubjectName, false);
matchingCertificates = certStore.Certificates.Find(X509FindType.FindBySubjectName, thumbprintOrSubjectNameOrHost, false);
if (matchingCertificates.Count == 0)
{
// No certificates matched the search criteria.
throw new FileNotFoundException("No certificate found with specified Thumbprint or SubjectName.", thumbprintOrSubjectName);
throw new FileNotFoundException($"No Certificate found with in store '{storeName}', location '{storeLocation}' for Thumbprint or SubjectName '{thumbprintOrSubjectNameOrHost}'.");
}
}
@@ -101,5 +53,76 @@ namespace WireMock.HttpsCertificate
#endif
}
}
if (!string.IsNullOrEmpty(filePath))
{
if (filePath!.EndsWith(ExtensionPem, StringComparison.OrdinalIgnoreCase))
{
// PEM logic based on: https://www.scottbrady91.com/c-sharp/pem-loading-in-dotnet-core-and-dotnet
#if NET5_0_OR_GREATER
if (!string.IsNullOrEmpty(passwordOrKey))
{
var certPem = File.ReadAllText(filePath);
var cert = X509Certificate2.CreateFromPem(certPem, passwordOrKey);
const string defaultPasswordPem = "WireMock.Net";
return new X509Certificate2(cert.Export(X509ContentType.Pfx, defaultPasswordPem), defaultPasswordPem);
}
return X509Certificate2.CreateFromPemFile(filePath);
#elif NETCOREAPP3_1
var cert = new X509Certificate2(filePath);
if (!string.IsNullOrEmpty(passwordOrKey))
{
var key = System.Security.Cryptography.ECDsa.Create()!;
key.ImportECPrivateKey(System.Text.Encoding.UTF8.GetBytes(passwordOrKey), out _);
return cert.CopyWithPrivateKey(key);
}
return cert;
#else
throw new InvalidOperationException("Loading a PEM Certificate is only supported for .NET Core App 3.1, .NET 5.0 and higher.");
#endif
}
return !string.IsNullOrEmpty(passwordOrKey) ? new X509Certificate2(filePath, passwordOrKey) : new X509Certificate2(filePath);
}
throw new InvalidOperationException("X509StoreName and X509StoreLocation OR X509CertificateFilePath are mandatory. Note that X509CertificatePassword is optional.");
}
/// <summary>
/// Used for Proxy
/// </summary>
public static X509Certificate2 LoadCertificate(string thumbprintOrSubjectName)
{
var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
try
{
// Certificate must be in the local machine store
certStore.Open(OpenFlags.ReadOnly);
// Attempt to find by Thumbprint first
var matchingCertificates = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprintOrSubjectName, false);
if (matchingCertificates.Count == 0)
{
// Fallback to SubjectName
matchingCertificates = certStore.Certificates.Find(X509FindType.FindBySubjectName, thumbprintOrSubjectName, false);
if (matchingCertificates.Count == 0)
{
// No certificates matched the search criteria.
throw new FileNotFoundException("No certificate found with specified Thumbprint or SubjectName.", thumbprintOrSubjectName);
}
}
// Use the first matching certificate.
return matchingCertificates[0];
}
finally
{
#if NETSTANDARD || NET46
certStore.Dispose();
#else
certStore.Close();
#endif
}
}
}

View File

@@ -1,16 +1,16 @@
using System;
using System;
using System.Security.Cryptography.X509Certificates;
namespace WireMock.HttpsCertificate
namespace WireMock.HttpsCertificate;
/// <summary>
/// Only used for NetStandard 1.3
/// </summary>
internal static class PublicCertificateHelper
{
/// <summary>
/// Only used for NetStandard 1.3
/// </summary>
internal static class PublicCertificateHelper
{
// 1] Generate using https://www.pluralsight.com/blog/software-development/selfcert-create-a-self-signed-certificate-interactively-gui-or-programmatically-in-net
// 2] Converted to Base64
private const string Data = @"MIIQMgIBAzCCD+4GCSqGSIb3DQEHAaCCD98Egg/bMIIP1zCCCogGCSqGSIb3DQEHAaCCCnkEggp1
// 1] Generate using https://www.pluralsight.com/blog/software-development/selfcert-create-a-self-signed-certificate-interactively-gui-or-programmatically-in-net
// 2] Converted to Base64
private const string Data = @"MIIQMgIBAzCCD+4GCSqGSIb3DQEHAaCCD98Egg/bMIIP1zCCCogGCSqGSIb3DQEHAaCCCnkEggp1
MIIKcTCCCm0GCyqGSIb3DQEMCgECoIIJfjCCCXowHAYKKoZIhvcNAQwBAzAOBAi1j9x1jTfUewIC
B9AEgglYa48lP16+isiGEVT7zwN3XwaPwPOHZcQ7tRA/DA8LZnZbwU7XhtPObF5bZcHn4engX2An
ISFpe2S5XJ7BfHmsGOO7Bxj6C2IcZIPTefvAd9vWE0WUAGN11SLhJ3fB/ZRt3Nys7JCJzywQCkYK
@@ -85,10 +85,9 @@ TLNGa+UmMnPsnBjlAJ6l9VPsa4uJM2DIQKtZXWq4DkhSAYKF6joIP7nKMDswHzAHBgUrDgMCGgQU
wTM1Z+CJZG9xAcf1zAVGl4ggYyYEFGBFyJ8VBwijS2zy1qwN1WYGtcWoAgIH0A==
";
public static X509Certificate2 GetX509Certificate2()
{
byte[] data = Convert.FromBase64String(Data);
return new X509Certificate2(data);
}
public static X509Certificate2 GetX509Certificate2()
{
byte[] data = Convert.FromBase64String(Data);
return new X509Certificate2(data);
}
}

View File

@@ -20,22 +20,22 @@ public interface IMapping
/// <summary>
/// Gets the TimeSettings (Start, End and TTL).
/// </summary>
ITimeSettings TimeSettings { get; }
ITimeSettings? TimeSettings { get; }
/// <summary>
/// Gets the unique title.
/// </summary>
string Title { get; }
string? Title { get; }
/// <summary>
/// Gets the description.
/// </summary>
string Description { get; }
string? Description { get; }
/// <summary>
/// The full filename path for this mapping (only defined for static mappings).
/// </summary>
string Path { get; set; }
string? Path { get; set; }
/// <summary>
/// Gets the priority. (A low value means higher priority.)
@@ -112,12 +112,17 @@ public interface IMapping
/// </summary>
IWebhook[]? Webhooks { get; }
/// <summary>
/// Use Fire and Forget for the defined webhook(s). [Optional]
/// </summary>
public bool? UseWebhooksFireAndForget { get; set; }
/// <summary>
/// ProvideResponseAsync
/// </summary>
/// <param name="requestMessage">The request message.</param>
/// <returns>The <see cref="ResponseMessage"/> including a new (optional) <see cref="IMapping"/>.</returns>
Task<(IResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(IRequestMessage requestMessage);
Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage);
/// <summary>
/// Gets the RequestMatchResult based on the RequestMessage.

View File

@@ -1,38 +1,37 @@
using System;
using System;
using WireMock.Matchers.Request;
namespace WireMock.Logging
namespace WireMock.Logging;
/// <summary>
/// LogEntry
/// </summary>
public class LogEntry : ILogEntry
{
/// <summary>
/// LogEntry
/// </summary>
public class LogEntry : ILogEntry
{
/// <inheritdoc cref="ILogEntry.Guid" />
public Guid Guid { get; set; }
/// <inheritdoc cref="ILogEntry.Guid" />
public Guid Guid { get; set; }
/// <inheritdoc cref="ILogEntry.RequestMessage" />
public IRequestMessage RequestMessage { get; set; }
/// <inheritdoc cref="ILogEntry.RequestMessage" />
public IRequestMessage RequestMessage { get; set; }
/// <inheritdoc cref="ILogEntry.ResponseMessage" />
public IResponseMessage ResponseMessage { get; set; }
/// <inheritdoc cref="ILogEntry.ResponseMessage" />
public IResponseMessage ResponseMessage { get; set; }
/// <inheritdoc cref="ILogEntry.RequestMatchResult" />
public IRequestMatchResult RequestMatchResult { get; set; }
/// <inheritdoc cref="ILogEntry.RequestMatchResult" />
public IRequestMatchResult RequestMatchResult { get; set; }
/// <inheritdoc cref="ILogEntry.MappingGuid" />
public Guid? MappingGuid { get; set; }
/// <inheritdoc cref="ILogEntry.MappingGuid" />
public Guid? MappingGuid { get; set; }
/// <inheritdoc cref="ILogEntry.MappingTitle" />
public string MappingTitle { get; set; }
/// <inheritdoc cref="ILogEntry.MappingTitle" />
public string? MappingTitle { get; set; }
/// <inheritdoc cref="ILogEntry.PartialMappingGuid" />
public Guid? PartialMappingGuid { get; set; }
/// <inheritdoc cref="ILogEntry.PartialMappingGuid" />
public Guid? PartialMappingGuid { get; set; }
/// <inheritdoc cref="ILogEntry.PartialMappingTitle" />
public string PartialMappingTitle { get; set; }
/// <inheritdoc cref="ILogEntry.PartialMappingTitle" />
public string? PartialMappingTitle { get; set; }
/// <inheritdoc cref="ILogEntry.PartialMatchResult" />
public IRequestMatchResult PartialMatchResult { get; set; }
}
/// <inheritdoc cref="ILogEntry.PartialMatchResult" />
public IRequestMatchResult PartialMatchResult { get; set; }
}

View File

@@ -1,149 +1,153 @@
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;
/// <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 bool? UseWebhooksFireAndForget { get; set; }
/// <inheritdoc />
public ITimeSettings? TimeSettings { get; }
/// <summary>
/// The Mapping.
/// Initializes a new instance of the <see cref="Mapping"/> class.
/// </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="useWebhooksFireAndForget">Use Fire and Forget for the defined webhook(s). [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,
bool? useWebhooksFireAndForget,
ITimeSettings? timeSettings)
{
/// <inheritdoc />
public Guid Guid { get; }
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;
UseWebhooksFireAndForget = useWebhooksFireAndForget;
TimeSettings = timeSettings;
}
/// <inheritdoc />
public string Title { get; }
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />
public Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage)
{
return Provider.ProvideResponseAsync(this, requestMessage, Settings);
}
/// <inheritdoc />
public string Description { get; }
/// <inheritdoc cref="IMapping.GetRequestMatchResult" />
public IRequestMatchResult GetRequestMatchResult(IRequestMessage requestMessage, string? nextState)
{
var result = new RequestMatchResult();
/// <inheritdoc />
public string Path { get; set; }
RequestMatcher.GetMatchingScore(requestMessage, result);
/// <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 || 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)
// Only check state if Scenario is defined
if (Scenario != null)
{
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;
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);
//}
}
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />
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;
}
return result;
}
}

View File

@@ -18,7 +18,7 @@ public class ContentTypeMatcher : WildcardMatcher
/// </summary>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">IgnoreCase (default false)</param>
public ContentTypeMatcher([NotNull] AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase)
public ContentTypeMatcher(AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase)
{
}
@@ -28,7 +28,7 @@ public class ContentTypeMatcher : WildcardMatcher
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">IgnoreCase (default false)</param>
public ContentTypeMatcher(MatchBehaviour matchBehaviour, [NotNull] AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase)
public ContentTypeMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase)
{
}
@@ -57,7 +57,7 @@ public class ContentTypeMatcher : WildcardMatcher
/// <inheritdoc cref="RegexMatcher.IsMatch"/>
public override double IsMatch(string? input)
{
if (string.IsNullOrEmpty(input) || !MediaTypeHeaderValue.TryParse(input, out MediaTypeHeaderValue contentType))
if (string.IsNullOrEmpty(input) || !MediaTypeHeaderValue.TryParse(input, out var contentType))
{
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
}

View File

@@ -1,11 +1,10 @@
namespace WireMock.Matchers
namespace WireMock.Matchers;
/// <summary>
/// CSharpCode / CS-Script Matcher
/// </summary>
/// <inheritdoc cref="IObjectMatcher"/>
/// <inheritdoc cref="IStringMatcher"/>
public interface ICSharpCodeMatcher : IObjectMatcher, IStringMatcher
{
/// <summary>
/// CSharpCode / CS-Script Matcher
/// </summary>
/// <inheritdoc cref="IObjectMatcher"/>
/// <inheritdoc cref="IStringMatcher"/>
public interface ICSharpCodeMatcher : IObjectMatcher, IStringMatcher
{
}
}

View File

@@ -1,14 +1,13 @@
namespace WireMock.Matchers
namespace WireMock.Matchers;
/// <summary>
/// IIgnoreCaseMatcher
/// </summary>
/// <inheritdoc cref="IMatcher"/>
public interface IIgnoreCaseMatcher : IMatcher
{
/// <summary>
/// IIgnoreCaseMatcher
/// Ignore the case from the pattern.
/// </summary>
/// <inheritdoc cref="IMatcher"/>
public interface IIgnoreCaseMatcher : IMatcher
{
/// <summary>
/// Ignore the case from the pattern.
/// </summary>
bool IgnoreCase { get; }
}
bool IgnoreCase { get; }
}

View File

@@ -1,23 +1,22 @@
namespace WireMock.Matchers
namespace WireMock.Matchers;
/// <summary>
/// IMatcher
/// </summary>
public interface IMatcher
{
/// <summary>
/// IMatcher
/// Gets the name.
/// </summary>
public interface IMatcher
{
/// <summary>
/// Gets the name.
/// </summary>
string Name { get; }
string Name { get; }
/// <summary>
/// Gets the match behaviour.
/// </summary>
MatchBehaviour MatchBehaviour { get; }
/// <summary>
/// Gets the match behaviour.
/// </summary>
MatchBehaviour MatchBehaviour { get; }
/// <summary>
/// Should this matcher throw an exception?
/// </summary>
bool ThrowException { get; }
}
/// <summary>
/// Should this matcher throw an exception?
/// </summary>
bool ThrowException { get; }
}

View File

@@ -1,15 +1,14 @@
namespace WireMock.Matchers
namespace WireMock.Matchers;
/// <summary>
/// IObjectMatcher
/// </summary>
public interface IObjectMatcher : IMatcher
{
/// <summary>
/// IObjectMatcher
/// Determines whether the specified input is match.
/// </summary>
public interface IObjectMatcher : IMatcher
{
/// <summary>
/// Determines whether the specified input is match.
/// </summary>
/// <param name="input">The input.</param>
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
double IsMatch(object? input);
}
/// <param name="input">The input.</param>
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
double IsMatch(object? input);
}

View File

@@ -1,15 +1,14 @@
namespace WireMock.Matchers
namespace WireMock.Matchers;
/// <summary>
/// IValueMatcher
/// </summary>
/// <seealso cref="IObjectMatcher" />
public interface IValueMatcher : IObjectMatcher
{
/// <summary>
/// IValueMatcher
/// Gets the value (can be a string or an object).
/// </summary>
/// <seealso cref="IObjectMatcher" />
public interface IValueMatcher : IObjectMatcher
{
/// <summary>
/// Gets the value (can be a string or an object).
/// </summary>
/// <returns>Value</returns>
object Value { get; }
}
/// <returns>Value</returns>
object Value { get; }
}

View File

@@ -6,115 +6,114 @@ using Stef.Validation;
using WireMock.Extensions;
using WireMock.Models;
namespace WireMock.Matchers
namespace WireMock.Matchers;
/// <summary>
/// http://jmespath.org/
/// </summary>
public class JmesPathMatcher : IStringMatcher, IObjectMatcher
{
private readonly AnyOf<string, StringPattern>[] _patterns;
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; }
/// <summary>
/// http://jmespath.org/
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary>
public class JmesPathMatcher : IStringMatcher, IObjectMatcher
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher(params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, patterns.ToAnyOfPatterns())
{
private readonly AnyOf<string, StringPattern>[] _patterns;
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; }
/// <summary>
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher(params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, patterns.ToAnyOfPatterns())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, patterns)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use.</param>
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher(bool throwException = false, MatchOperator matchOperator = MatchOperator.Or, params AnyOf<string, StringPattern>[] patterns) :
this(MatchBehaviour.AcceptOnMatch, throwException, matchOperator, patterns)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use.</param>
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher(
MatchBehaviour matchBehaviour,
bool throwException = false,
MatchOperator matchOperator = MatchOperator.Or,
params AnyOf<string, StringPattern>[] patterns)
{
_patterns = Guard.NotNull(patterns);
MatchBehaviour = matchBehaviour;
ThrowException = throwException;
MatchOperator = matchOperator;
}
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
public double IsMatch(string? input)
{
double match = MatchScores.Mismatch;
if (input != null)
{
try
{
var results = _patterns.Select(pattern => bool.Parse(new JmesPath().Transform(input, pattern.GetPattern()))).ToArray();
match = MatchScores.ToScore(results, MatchOperator);
}
catch (JsonException)
{
if (ThrowException)
{
throw;
}
}
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
}
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
public double IsMatch(object? input)
{
double match = MatchScores.Mismatch;
// When input is null or byte[], return Mismatch.
if (input != null && !(input is byte[]))
{
string inputAsString = JsonConvert.SerializeObject(input);
return IsMatch(inputAsString);
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public AnyOf<string, StringPattern>[] GetPatterns()
{
return _patterns;
}
/// <inheritdoc />
public MatchOperator MatchOperator { get; }
/// <inheritdoc cref="IMatcher.Name"/>
public string Name => "JmesPathMatcher";
}
/// <summary>
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, patterns)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use.</param>
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher(bool throwException = false, MatchOperator matchOperator = MatchOperator.Or, params AnyOf<string, StringPattern>[] patterns) :
this(MatchBehaviour.AcceptOnMatch, throwException, matchOperator, patterns)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use.</param>
/// <param name="patterns">The patterns.</param>
public JmesPathMatcher(
MatchBehaviour matchBehaviour,
bool throwException = false,
MatchOperator matchOperator = MatchOperator.Or,
params AnyOf<string, StringPattern>[] patterns)
{
_patterns = Guard.NotNull(patterns);
MatchBehaviour = matchBehaviour;
ThrowException = throwException;
MatchOperator = matchOperator;
}
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
public double IsMatch(string? input)
{
double match = MatchScores.Mismatch;
if (input != null)
{
try
{
var results = _patterns.Select(pattern => bool.Parse(new JmesPath().Transform(input, pattern.GetPattern()))).ToArray();
match = MatchScores.ToScore(results, MatchOperator);
}
catch (JsonException)
{
if (ThrowException)
{
throw;
}
}
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
}
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
public double IsMatch(object? input)
{
double match = MatchScores.Mismatch;
// When input is null or byte[], return Mismatch.
if (input != null && !(input is byte[]))
{
string inputAsString = JsonConvert.SerializeObject(input);
return IsMatch(inputAsString);
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public AnyOf<string, StringPattern>[] GetPatterns()
{
return _patterns;
}
/// <inheritdoc />
public MatchOperator MatchOperator { get; }
/// <inheritdoc cref="IMatcher.Name"/>
public string Name => "JmesPathMatcher";
}

View File

@@ -119,7 +119,7 @@ public class JsonMatcher : IValueMatcher, IIgnoreCaseMatcher
return tokenValue;
case string stringValue:
return JsonUtils.Parse(stringValue)!;
return JsonUtils.Parse(stringValue);
case IEnumerable enumerableValue:
return JArray.FromObject(enumerableValue);

View File

@@ -1,3 +1,4 @@
using System;
using System.Linq;
using System.Linq.Dynamic.Core;
using AnyOfTypes;
@@ -68,7 +69,7 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
}
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
public double IsMatch(string input)
public double IsMatch(string? input)
{
double match = MatchScores.Mismatch;
@@ -94,7 +95,7 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
}
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
public double IsMatch(object input)
public double IsMatch(object? input)
{
double match = MatchScores.Mismatch;
@@ -105,9 +106,12 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
value = valueAsJObject;
break;
default:
value = JObject.FromObject(input);
case { } valueAsObject:
value = JObject.FromObject(valueAsObject);
break;
default:
return MatchScores.Mismatch;
}
// Convert a single object to a Queryable JObject-list with 1 entry.

View File

@@ -1,18 +1,17 @@
namespace WireMock.Matchers
namespace WireMock.Matchers;
/// <summary>
/// MatchBehaviour
/// </summary>
public enum MatchBehaviour
{
/// <summary>
/// MatchBehaviour
/// Accept on match (default)
/// </summary>
public enum MatchBehaviour
{
/// <summary>
/// Accept on match (default)
/// </summary>
AcceptOnMatch,
AcceptOnMatch,
/// <summary>
/// Reject on match
/// </summary>
RejectOnMatch
}
/// <summary>
/// Reject on match
/// </summary>
RejectOnMatch
}

View File

@@ -1,27 +1,26 @@
namespace WireMock.Matchers
{
internal static class MatchBehaviourHelper
{
/// <summary>
/// Converts the specified match behaviour and match value to a new match value.
///
/// if AcceptOnMatch --> return match (default)
/// if RejectOnMatch and match = 0.0 --> return 1.0
/// if RejectOnMatch and match = 0.? --> return 0.0
/// if RejectOnMatch and match = 1.0 --> return 0.0
/// </summary>
///
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="match">The match.</param>
/// <returns>match value</returns>
internal static double Convert(MatchBehaviour matchBehaviour, double match)
{
if (matchBehaviour == MatchBehaviour.AcceptOnMatch)
{
return match;
}
namespace WireMock.Matchers;
return match <= MatchScores.Tolerance ? MatchScores.Perfect : MatchScores.Mismatch;
internal static class MatchBehaviourHelper
{
/// <summary>
/// Converts the specified match behaviour and match value to a new match value.
///
/// if AcceptOnMatch --> return match (default)
/// if RejectOnMatch and match = 0.0 --> return 1.0
/// if RejectOnMatch and match = 0.? --> return 0.0
/// if RejectOnMatch and match = 1.0 --> return 0.0
/// </summary>
///
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="match">The match.</param>
/// <returns>match value</returns>
internal static double Convert(MatchBehaviour matchBehaviour, double match)
{
if (matchBehaviour == MatchBehaviour.AcceptOnMatch)
{
return match;
}
return match <= MatchScores.Tolerance ? MatchScores.Perfect : MatchScores.Mismatch;
}
}

View File

@@ -1,18 +1,17 @@
namespace WireMock.Matchers.Request
namespace WireMock.Matchers.Request;
/// <summary>
/// CompositeMatcherType
/// </summary>
public enum CompositeMatcherType
{
/// <summary>
/// CompositeMatcherType
/// And
/// </summary>
public enum CompositeMatcherType
{
/// <summary>
/// And
/// </summary>
And = 0,
And = 0,
/// <summary>
/// Or
/// </summary>
Or = 1
}
/// <summary>
/// Or
/// </summary>
Or = 1
}

View File

@@ -1,57 +1,56 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
namespace WireMock.Matchers.Request
namespace WireMock.Matchers.Request;
/// <summary>
/// RequestMatchResult
/// </summary>
public class RequestMatchResult : IRequestMatchResult
{
/// <inheritdoc cref="IRequestMatchResult.TotalScore" />
public double TotalScore => MatchDetails.Sum(md => md.Score);
/// <inheritdoc cref="IRequestMatchResult.TotalNumber" />
public int TotalNumber => MatchDetails.Count;
/// <inheritdoc cref="IRequestMatchResult.IsPerfectMatch" />
public bool IsPerfectMatch => Math.Abs(TotalScore - TotalNumber) < MatchScores.Tolerance;
/// <inheritdoc cref="IRequestMatchResult.AverageTotalScore" />
public double AverageTotalScore => TotalNumber == 0 ? 0.0 : TotalScore / TotalNumber;
/// <inheritdoc cref="IRequestMatchResult.MatchDetails" />
public IList<MatchDetail> MatchDetails { get; } = new List<MatchDetail>();
/// <summary>
/// RequestMatchResult
/// Adds the score.
/// </summary>
public class RequestMatchResult : IRequestMatchResult
/// <param name="matcherType">The matcher Type.</param>
/// <param name="score">The score.</param>
/// <returns>The score.</returns>
public double AddScore(Type matcherType, double score)
{
/// <inheritdoc cref="IRequestMatchResult.TotalScore" />
public double TotalScore => MatchDetails.Sum(md => md.Score);
MatchDetails.Add(new MatchDetail { MatcherType = matcherType, Score = score });
/// <inheritdoc cref="IRequestMatchResult.TotalNumber" />
public int TotalNumber => MatchDetails.Count;
return score;
}
/// <inheritdoc cref="IRequestMatchResult.IsPerfectMatch" />
public bool IsPerfectMatch => Math.Abs(TotalScore - TotalNumber) < MatchScores.Tolerance;
/// <summary>
/// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
/// </summary>
/// <param name="obj">An object to compare with this instance.</param>
/// <returns>
/// A value that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance precedes <paramref name="obj" /> in the sort order. Zero This instance occurs in the same position in the sort order as <paramref name="obj" />. Greater than zero This instance follows <paramref name="obj" /> in the sort order.
/// </returns>
public int CompareTo(object obj)
{
var compareObj = (RequestMatchResult)obj;
/// <inheritdoc cref="IRequestMatchResult.AverageTotalScore" />
public double AverageTotalScore => TotalNumber == 0 ? 0.0 : TotalScore / TotalNumber;
int averageTotalScoreResult = compareObj.AverageTotalScore.CompareTo(AverageTotalScore);
/// <inheritdoc cref="IRequestMatchResult.MatchDetails" />
public IList<MatchDetail> MatchDetails { get; } = new List<MatchDetail>();
/// <summary>
/// Adds the score.
/// </summary>
/// <param name="matcherType">The matcher Type.</param>
/// <param name="score">The score.</param>
/// <returns>The score.</returns>
public double AddScore(Type matcherType, double score)
{
MatchDetails.Add(new MatchDetail { MatcherType = matcherType, Score = score });
return score;
}
/// <summary>
/// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
/// </summary>
/// <param name="obj">An object to compare with this instance.</param>
/// <returns>
/// A value that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance precedes <paramref name="obj" /> in the sort order. Zero This instance occurs in the same position in the sort order as <paramref name="obj" />. Greater than zero This instance follows <paramref name="obj" /> in the sort order.
/// </returns>
public int CompareTo(object obj)
{
var compareObj = (RequestMatchResult)obj;
int averageTotalScoreResult = compareObj.AverageTotalScore.CompareTo(AverageTotalScore);
// In case the score is equal, prefer the one with the most matchers.
return averageTotalScoreResult == 0 ? compareObj.TotalNumber.CompareTo(TotalNumber) : averageTotalScoreResult;
}
// In case the score is equal, prefer the one with the most matchers.
return averageTotalScoreResult == 0 ? compareObj.TotalNumber.CompareTo(TotalNumber) : averageTotalScoreResult;
}
}

View File

@@ -3,50 +3,49 @@ using System.Linq;
using JetBrains.Annotations;
using Stef.Validation;
namespace WireMock.Matchers.Request
namespace WireMock.Matchers.Request;
/// <summary>
/// The composite request matcher.
/// </summary>
public abstract class RequestMessageCompositeMatcher : IRequestMatcher
{
private readonly CompositeMatcherType _type;
/// <summary>
/// The composite request matcher.
/// Gets the request matchers.
/// </summary>
public abstract class RequestMessageCompositeMatcher : IRequestMatcher
/// <value>
/// The request matchers.
/// </value>
private IEnumerable<IRequestMatcher> RequestMatchers { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCompositeMatcher"/> class.
/// </summary>
/// <param name="requestMatchers">The request matchers.</param>
/// <param name="type">The CompositeMatcherType type (Defaults to 'And')</param>
protected RequestMessageCompositeMatcher([NotNull] IEnumerable<IRequestMatcher> requestMatchers, CompositeMatcherType type = CompositeMatcherType.And)
{
private readonly CompositeMatcherType _type;
Guard.NotNull(requestMatchers, nameof(requestMatchers));
/// <summary>
/// Gets the request matchers.
/// </summary>
/// <value>
/// The request matchers.
/// </value>
private IEnumerable<IRequestMatcher> RequestMatchers { get; }
_type = type;
RequestMatchers = requestMatchers;
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCompositeMatcher"/> class.
/// </summary>
/// <param name="requestMatchers">The request matchers.</param>
/// <param name="type">The CompositeMatcherType type (Defaults to 'And')</param>
protected RequestMessageCompositeMatcher([NotNull] IEnumerable<IRequestMatcher> requestMatchers, CompositeMatcherType type = CompositeMatcherType.And)
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
if (!RequestMatchers.Any())
{
Guard.NotNull(requestMatchers, nameof(requestMatchers));
_type = type;
RequestMatchers = requestMatchers;
return MatchScores.Mismatch;
}
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
if (_type == CompositeMatcherType.And)
{
if (!RequestMatchers.Any())
{
return MatchScores.Mismatch;
}
if (_type == CompositeMatcherType.And)
{
return RequestMatchers.Average(requestMatcher => requestMatcher.GetMatchingScore(requestMessage, requestMatchResult));
}
return RequestMatchers.Max(requestMatcher => requestMatcher.GetMatchingScore(requestMessage, requestMatchResult));
return RequestMatchers.Average(requestMatcher => requestMatcher.GetMatchingScore(requestMessage, requestMatchResult));
}
return RequestMatchers.Max(requestMatcher => requestMatcher.GetMatchingScore(requestMessage, requestMatchResult));
}
}

View File

@@ -1,129 +1,123 @@
using Stef.Validation;
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Stef.Validation;
namespace WireMock.Matchers.Request
namespace WireMock.Matchers.Request;
/// <summary>
/// The request cookie matcher.
/// </summary>
/// <inheritdoc cref="IRequestMatcher"/>
public class RequestMessageCookieMatcher : IRequestMatcher
{
private readonly MatchBehaviour _matchBehaviour;
private readonly bool _ignoreCase;
/// <summary>
/// The request cookie matcher.
/// The functions
/// </summary>
/// <inheritdoc cref="IRequestMatcher"/>
public class RequestMessageCookieMatcher : IRequestMatcher
public Func<IDictionary<string, string>, bool>[]? Funcs { get; }
/// <summary>
/// The name
/// </summary>
public string Name { get; }
/// <value>
/// The matchers.
/// </value>
public IStringMatcher[]? Matchers { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
/// <param name="matchBehaviour">The match behaviour.</param>
public RequestMessageCookieMatcher(MatchBehaviour matchBehaviour, string name, string pattern, bool ignoreCase)
{
private readonly MatchBehaviour _matchBehaviour;
private readonly bool _ignoreCase;
_matchBehaviour = matchBehaviour;
_ignoreCase = ignoreCase;
Name = Guard.NotNull(name);
Matchers = new IStringMatcher[] { new WildcardMatcher(matchBehaviour, Guard.NotNull(pattern), ignoreCase) };
}
/// <summary>
/// The functions
/// </summary>
public Func<IDictionary<string, string>, bool>[] Funcs { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="name">The name.</param>
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
public RequestMessageCookieMatcher(MatchBehaviour matchBehaviour, string name, bool ignoreCase, params string[] patterns) :
this(matchBehaviour, name, ignoreCase, patterns.Select(pattern => new WildcardMatcher(matchBehaviour, pattern, ignoreCase)).Cast<IStringMatcher>().ToArray())
{
Guard.NotNull(patterns);
}
/// <summary>
/// The name
/// </summary>
public string Name { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="name">The name.</param>
/// <param name="matchers">The matchers.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
public RequestMessageCookieMatcher(MatchBehaviour matchBehaviour, string name, bool ignoreCase, params IStringMatcher[] matchers)
{
_matchBehaviour = matchBehaviour;
Name = Guard.NotNull(name);
Matchers = Guard.NotNull(matchers);
_ignoreCase = ignoreCase;
}
/// <value>
/// The matchers.
/// </value>
public IStringMatcher[] Matchers { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
/// </summary>
/// <param name="funcs">The funcs.</param>
public RequestMessageCookieMatcher(params Func<IDictionary<string, string>, bool>[] funcs)
{
Guard.NotNull(funcs);
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
/// <param name="matchBehaviour">The match behaviour.</param>
public RequestMessageCookieMatcher(MatchBehaviour matchBehaviour, [NotNull] string name, [NotNull] string pattern, bool ignoreCase)
Funcs = funcs;
Name = string.Empty; // Not used when Func, but set to a non-null valid value.
}
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);
return requestMatchResult.AddScore(GetType(), score);
}
private double IsMatch(IRequestMessage requestMessage)
{
if (requestMessage.Cookies == null)
{
Guard.NotNull(name, nameof(name));
Guard.NotNull(pattern, nameof(pattern));
_matchBehaviour = matchBehaviour;
_ignoreCase = ignoreCase;
Name = name;
Matchers = new IStringMatcher[] { new WildcardMatcher(matchBehaviour, pattern, ignoreCase) };
return MatchBehaviourHelper.Convert(_matchBehaviour, MatchScores.Mismatch);
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="name">The name.</param>
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
public RequestMessageCookieMatcher(MatchBehaviour matchBehaviour, [NotNull] string name, bool ignoreCase, [NotNull] params string[] patterns) :
this(matchBehaviour, name, ignoreCase, patterns.Select(pattern => new WildcardMatcher(matchBehaviour, pattern, ignoreCase)).Cast<IStringMatcher>().ToArray())
// Check if we want to use IgnoreCase to compare the Cookie-Name and Cookie-Value
var cookies = !_ignoreCase ? requestMessage.Cookies : new Dictionary<string, string>(requestMessage.Cookies, StringComparer.OrdinalIgnoreCase);
if (Funcs != null)
{
Guard.NotNull(patterns, nameof(patterns));
return MatchScores.ToScore(Funcs.Any(f => f(cookies)));
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="name">The name.</param>
/// <param name="matchers">The matchers.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
public RequestMessageCookieMatcher(MatchBehaviour matchBehaviour, [NotNull] string name, bool ignoreCase, [NotNull] params IStringMatcher[] matchers)
if (Matchers == null)
{
Guard.NotNull(name, nameof(name));
Guard.NotNull(matchers, nameof(matchers));
_matchBehaviour = matchBehaviour;
Name = name;
Matchers = matchers;
_ignoreCase = ignoreCase;
return MatchScores.Mismatch;
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
/// </summary>
/// <param name="funcs">The funcs.</param>
public RequestMessageCookieMatcher([NotNull] params Func<IDictionary<string, string>, bool>[] funcs)
if (!cookies.ContainsKey(Name))
{
Guard.NotNull(funcs, nameof(funcs));
Funcs = funcs;
return MatchBehaviourHelper.Convert(_matchBehaviour, MatchScores.Mismatch);
}
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);
return requestMatchResult.AddScore(GetType(), score);
}
private double IsMatch(IRequestMessage requestMessage)
{
if (requestMessage.Cookies == null)
{
return MatchBehaviourHelper.Convert(_matchBehaviour, MatchScores.Mismatch);
}
// Check if we want to use IgnoreCase to compare the Cookie-Name and Cookie-Value
var cookies = !_ignoreCase ? requestMessage.Cookies : new Dictionary<string, string>(requestMessage.Cookies, StringComparer.OrdinalIgnoreCase);
if (Funcs != null)
{
return MatchScores.ToScore(Funcs.Any(f => f(cookies)));
}
if (Matchers == null)
{
return MatchScores.Mismatch;
}
if (!cookies.ContainsKey(Name))
{
return MatchBehaviourHelper.Convert(_matchBehaviour, MatchScores.Mismatch);
}
string value = cookies[Name];
return Matchers.Max(m => m.IsMatch(value));
}
string value = cookies[Name];
return Matchers.Max(m => m.IsMatch(value));
}
}

View File

@@ -135,7 +135,7 @@ public class RequestMessageHeaderMatcher : IRequestMatcher
results.Add(MatchScores.ToScore(resultsPerMatcher, MatchOperator.And));
}
return MatchBehaviourHelper.Convert(_matchBehaviour, MatchScores.ToScore(results, MatchOperator));
return MatchScores.ToScore(results, MatchOperator);
}
return MatchBehaviourHelper.Convert(_matchBehaviour, MatchScores.Mismatch);

View File

@@ -11,7 +11,10 @@ namespace WireMock.Matchers.Request;
/// </summary>
public class RequestMessageParamMatcher : IRequestMatcher
{
private readonly MatchBehaviour _matchBehaviour;
/// <summary>
/// MatchBehaviour
/// </summary>
public MatchBehaviour MatchBehaviour { get; }
/// <summary>
/// The funcs
@@ -21,7 +24,7 @@ public class RequestMessageParamMatcher : IRequestMatcher
/// <summary>
/// The key
/// </summary>
public string? Key { get; }
public string Key { get; } = string.Empty;
/// <summary>
/// Defines if the key should be matched using case-ignore.
@@ -63,7 +66,7 @@ public class RequestMessageParamMatcher : IRequestMatcher
/// <param name="matchers">The matchers.</param>
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, string key, bool ignoreCase, IStringMatcher[]? matchers)
{
_matchBehaviour = matchBehaviour;
MatchBehaviour = matchBehaviour;
Key = Guard.NotNull(key);
IgnoreCase = ignoreCase;
Matchers = matchers;
@@ -81,7 +84,7 @@ public class RequestMessageParamMatcher : IRequestMatcher
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
double score = MatchBehaviourHelper.Convert(_matchBehaviour, IsMatch(requestMessage));
double score = MatchBehaviourHelper.Convert(MatchBehaviour, IsMatch(requestMessage));
return requestMatchResult.AddScore(GetType(), score);
}

View File

@@ -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>
/// The scenario and state matcher.
/// Execution state condition for the current mapping.
/// </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>
/// Execution state condition for the current mapping.
/// </summary>
[CanBeNull]
private readonly string _executionConditionState;
_nextState = nextState;
_executionConditionState = 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>
[CanBeNull]
private readonly string _nextState;
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
double score = IsMatch();
return requestMatchResult.AddScore(GetType(), score);
}
/// <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([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;
}
private double IsMatch()
{
return Equals(_executionConditionState, _nextState) ? MatchScores.Perfect : MatchScores.Mismatch;
}
}

View File

@@ -1,11 +1,10 @@
namespace WireMock.Models
namespace WireMock.Models;
/// <summary>
/// Webhook
/// </summary>
public class Webhook : IWebhook
{
/// <summary>
/// Webhook
/// </summary>
public class Webhook : IWebhook
{
/// <inheritdoc cref="IWebhook.Request"/>
public IWebhookRequest Request { get; set; }
}
/// <inheritdoc />
public IWebhookRequest Request { get; set; } = null!;
}

View File

@@ -2,32 +2,40 @@ using System.Collections.Generic;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Models
namespace WireMock.Models;
/// <summary>
/// WebhookRequest
/// </summary>
public class WebhookRequest : IWebhookRequest
{
/// <summary>
/// WebhookRequest
/// </summary>
public class WebhookRequest : IWebhookRequest
{
/// <inheritdoc cref="IWebhookRequest.Url"/>
public string Url { get; set; }
/// <inheritdoc />
public string Url { get; set; } = null!;
/// <inheritdoc cref="IWebhookRequest.Method"/>
public string Method { get; set; }
/// <inheritdoc />
public string Method { get; set; } = null!;
/// <inheritdoc cref="IWebhookRequest.Headers"/>
public IDictionary<string, WireMockList<string>>? Headers { get; set; }
/// <inheritdoc />
public IDictionary<string, WireMockList<string>>? Headers { get; set; }
/// <inheritdoc cref="IWebhookRequest.BodyData"/>
public IBodyData? BodyData { get; set; }
/// <inheritdoc />
public IBodyData? BodyData { get; set; }
/// <inheritdoc cref="IWebhookRequest.UseTransformer"/>
public bool? UseTransformer { get; set; }
/// <inheritdoc />
public bool? UseTransformer { get; set; }
/// <inheritdoc cref="IWebhookRequest.TransformerType"/>
public TransformerType TransformerType { get; set; }
/// <inheritdoc />
public TransformerType TransformerType { get; set; }
/// <inheritdoc cref="IWebhookRequest.TransformerReplaceNodeOptions"/>
public ReplaceNodeOptions TransformerReplaceNodeOptions { get; set; }
}
/// <inheritdoc />
public ReplaceNodeOptions TransformerReplaceNodeOptions { get; set; }
/// <inheritdoc />
public int? Delay { get; set; }
/// <inheritdoc />
public int? MinimumRandomDelay { get; set; }
/// <inheritdoc />
public int? MaximumRandomDelay { get; set; }
}

View File

@@ -3,45 +3,44 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
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 */
services.AddCors(corsOptions => corsOptions
.AddPolicy(CorsPolicyName,
corsPolicyBuilder =>
/* https://stackoverflow.com/questions/31942037/how-to-enable-cors-in-asp-net-core */
/* Enable Cors */
services.AddCors(corsOptions => corsOptions
.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))
{
corsPolicyBuilder.AllowAnyMethod();
}
if (_wireMockMiddlewareOptions.CorsPolicyOptions.Value.HasFlag(CorsPolicyOptions.AllowAnyMethod))
{
corsPolicyBuilder.AllowAnyMethod();
}
if (_wireMockMiddlewareOptions.CorsPolicyOptions.Value.HasFlag(CorsPolicyOptions.AllowAnyOrigin))
{
corsPolicyBuilder.AllowAnyOrigin();
}
}));
}
if (_wireMockMiddlewareOptions.CorsPolicyOptions.Value.HasFlag(CorsPolicyOptions.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);
}
}
}

View File

@@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using WireMock.HttpsCertificate;
using WireMock.Types;
namespace WireMock.Owin
{

View File

@@ -1,4 +1,4 @@
#if USE_ASPNETCORE && NETSTANDARD1_3
#if USE_ASPNETCORE && NETSTANDARD1_3
using System.Collections.Generic;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel;
@@ -6,58 +6,58 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using WireMock.HttpsCertificate;
namespace WireMock.Owin
{
internal partial class AspNetCoreSelfHost
{
private static void SetKestrelOptionsLimits(KestrelServerOptions options)
{
options.Limits.MaxRequestBufferSize = null;
options.Limits.MaxRequestHeaderCount = 100;
options.Limits.MaxResponseBufferSize = null;
}
namespace WireMock.Owin;
private static void SetHttpsAndUrls(KestrelServerOptions options, IWireMockMiddlewareOptions wireMockMiddlewareOptions, IEnumerable<HostUrlDetails> urlDetails)
internal partial class AspNetCoreSelfHost
{
private static void SetKestrelOptionsLimits(KestrelServerOptions options)
{
options.Limits.MaxRequestBufferSize = null;
options.Limits.MaxRequestHeaderCount = 100;
options.Limits.MaxResponseBufferSize = null;
}
private static void SetHttpsAndUrls(KestrelServerOptions options, IWireMockMiddlewareOptions wireMockMiddlewareOptions, IEnumerable<HostUrlDetails> urlDetails)
{
foreach (var urlDetail in urlDetails)
{
foreach (var urlDetail in urlDetails)
if (urlDetail.IsHttps)
{
if (urlDetail.IsHttps)
if (wireMockMiddlewareOptions.CustomCertificateDefined)
{
if (wireMockMiddlewareOptions.CustomCertificateDefined)
{
options.UseHttps(CertificateLoader.LoadCertificate(
wireMockMiddlewareOptions.X509StoreName,
wireMockMiddlewareOptions.X509StoreLocation,
wireMockMiddlewareOptions.X509ThumbprintOrSubjectName,
wireMockMiddlewareOptions.X509CertificateFilePath,
wireMockMiddlewareOptions.X509CertificatePassword,
urlDetail.Host)
);
}
else
{
options.UseHttps(PublicCertificateHelper.GetX509Certificate2());
}
options.UseHttps(CertificateLoader.LoadCertificate(
wireMockMiddlewareOptions.X509StoreName,
wireMockMiddlewareOptions.X509StoreLocation,
wireMockMiddlewareOptions.X509ThumbprintOrSubjectName,
wireMockMiddlewareOptions.X509CertificateFilePath,
wireMockMiddlewareOptions.X509CertificatePassword,
urlDetail.Host)
);
}
else
{
options.UseHttps(PublicCertificateHelper.GetX509Certificate2());
}
}
}
}
}
internal static class IWebHostBuilderExtensions
internal static class IWebHostBuilderExtensions
{
internal static IWebHostBuilder ConfigureAppConfigurationUsingEnvironmentVariables(this IWebHostBuilder builder) => builder;
internal static IWebHostBuilder ConfigureKestrelServerOptions(this IWebHostBuilder builder)
{
internal static IWebHostBuilder ConfigureAppConfigurationUsingEnvironmentVariables(this IWebHostBuilder builder) => builder;
var configuration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.Build();
internal static IWebHostBuilder ConfigureKestrelServerOptions(this IWebHostBuilder builder)
return builder.ConfigureServices(services =>
{
var configuration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.Build();
return builder.ConfigureServices(services =>
{
services.Configure<KestrelServerOptions>(configuration.GetSection("Kestrel"));
});
}
services.Configure<KestrelServerOptions>(configuration.GetSection("Kestrel"));
});
}
}
#endif

View File

@@ -5,7 +5,6 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
@@ -30,16 +29,16 @@ namespace WireMock.Owin
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 AspNetCoreSelfHost([NotNull] IWireMockMiddlewareOptions wireMockMiddlewareOptions, [NotNull] HostUrlOptions urlOptions)
public AspNetCoreSelfHost(IWireMockMiddlewareOptions wireMockMiddlewareOptions, HostUrlOptions urlOptions)
{
Guard.NotNull(wireMockMiddlewareOptions, nameof(wireMockMiddlewareOptions));
Guard.NotNull(urlOptions, nameof(urlOptions));
Guard.NotNull(wireMockMiddlewareOptions);
Guard.NotNull(urlOptions);
_logger = wireMockMiddlewareOptions.Logger ?? new WireMockConsoleLogger();
@@ -119,7 +118,7 @@ namespace WireMock.Owin
{
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);
}
@@ -133,7 +132,7 @@ namespace WireMock.Owin
#elif NETSTANDARD2_1
_logger.Info("Server using netstandard2.1");
#elif NETCOREAPP3_1
_logger.Info("Server using .NET Core 3.1");
_logger.Info("Server using .NET Core App 3.1");
#elif NET5_0
_logger.Info("Server using .NET 5.0");
#elif NET6_0

View File

@@ -1,15 +1,17 @@
namespace WireMock.Owin
namespace WireMock.Owin;
/// <summary>
/// https://en.wikipedia.org/wiki/Uniform_Resource_Identifier
/// </summary>
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 Scheme { get; set; }
public string Host { get; set; }
public string Host { get; set; }
public int Port { get; set; }
}
public int Port { get; set; }
}

View File

@@ -1,46 +1,62 @@
using System.Collections.Generic;
using System.Collections.Generic;
using WireMock.Types;
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 int? HttpsPort { get; set; }
public HostingScheme HostingScheme { get; set; }
public IReadOnlyList<HostUrlDetails> GetDetails()
{
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()
var list = new List<HostUrlDetails>();
if (Urls == null)
{
var list = new List<HostUrlDetails>();
if (Urls == null)
if (HostingScheme is HostingScheme.Http or HostingScheme.Https)
{
int port = Port > 0 ? Port.Value : FindFreeTcpPort();
string protocol = UseSSL ? "https" : "http";
list.Add(new HostUrlDetails { IsHttps = UseSSL, Url = $"{protocol}://{LOCALHOST}:{port}", Protocol = protocol, Host = LOCALHOST, Port = port });
var port = Port > 0 ? Port.Value : FindFreeTcpPort();
var scheme = HostingScheme == HostingScheme.Https ? "https" : "http";
list.Add(new HostUrlDetails { IsHttps = HostingScheme == HostingScheme.Https, Url = $"{scheme}://{Localhost}:{port}", Scheme = scheme, Host = Localhost, Port = port });
}
else
if (HostingScheme == HostingScheme.HttpAndHttps)
{
foreach (string url in Urls)
var httpPort = Port > 0 ? Port.Value : FindFreeTcpPort();
list.Add(new HostUrlDetails { IsHttps = false, Url = $"http://{Localhost}:{httpPort}", Scheme = "http", Host = Localhost, Port = httpPort });
var httpsPort = FindFreeTcpPort(); // In this scenario, always get a free port for https.
list.Add(new HostUrlDetails { IsHttps = true, Url = $"https://{Localhost}:{httpsPort}", Scheme = "https", Host = Localhost, Port = httpsPort });
}
}
else
{
foreach (string url in Urls)
{
if (PortUtils.TryExtract(url, out var isHttps, out var protocol, out var host, out var port))
{
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, Scheme = protocol, Host = host, Port = port });
}
}
return list;
}
private int FindFreeTcpPort()
{
return list;
}
private static int FindFreeTcpPort()
{
#if USE_ASPNETCORE || NETSTANDARD2_0 || NETSTANDARD2_1
return 0;
return 0;
#else
return PortUtils.FindFreeTcpPort();
return PortUtils.FindFreeTcpPort();
#endif
}
}
}

View File

@@ -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);
}

View File

@@ -1,36 +1,35 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading.Tasks;
using System;
namespace WireMock.Owin
namespace WireMock.Owin;
interface IOwinSelfHost
{
interface IOwinSelfHost
{
/// <summary>
/// Gets a value indicating whether this server is started.
/// </summary>
/// <value>
/// <c>true</c> if this server is started; otherwise, <c>false</c>.
/// </value>
bool IsStarted { get; }
/// <summary>
/// Gets a value indicating whether this server is started.
/// </summary>
/// <value>
/// <c>true</c> if this server is started; otherwise, <c>false</c>.
/// </value>
bool IsStarted { get; }
/// <summary>
/// Gets the urls.
/// </summary>
List<string> Urls { get; }
/// <summary>
/// Gets the urls.
/// </summary>
List<string> Urls { get; }
/// <summary>
/// Gets the ports.
/// </summary>
List<int> Ports { get; }
/// <summary>
/// Gets the ports.
/// </summary>
List<int> Ports { get; }
/// <summary>
/// The exception occurred when the host is running
/// </summary>
Exception RunningException { get; }
/// <summary>
/// The exception occurred when the host is running.
/// </summary>
Exception? RunningException { get; }
Task StartAsync();
Task StartAsync();
Task StopAsync();
}
Task StopAsync();
}

View File

@@ -4,70 +4,69 @@ using WireMock.Handlers;
using WireMock.Logging;
using WireMock.Matchers;
using WireMock.Util;
using WireMock.Types;
#if !USE_ASPNETCORE
using Owin;
#else
using IAppBuilder = Microsoft.AspNetCore.Builder.IApplicationBuilder;
using Microsoft.Extensions.DependencyInjection;
using WireMock.Types;
#endif
namespace WireMock.Owin
namespace WireMock.Owin;
internal interface IWireMockMiddlewareOptions
{
internal interface IWireMockMiddlewareOptions
{
IWireMockLogger Logger { get; set; }
IWireMockLogger Logger { get; set; }
TimeSpan? RequestProcessingDelay { get; set; }
TimeSpan? RequestProcessingDelay { get; set; }
IStringMatcher? AuthenticationMatcher { get; set; }
IStringMatcher? AuthenticationMatcher { get; set; }
bool? AllowPartialMapping { get; set; }
bool? AllowPartialMapping { get; set; }
ConcurrentDictionary<Guid, IMapping> Mappings { get; }
ConcurrentDictionary<Guid, IMapping> Mappings { get; }
ConcurrentDictionary<string, ScenarioState> Scenarios { get; }
ConcurrentDictionary<string, ScenarioState> Scenarios { get; }
ConcurrentObservableCollection<LogEntry> LogEntries { get; }
ConcurrentObservableCollection<LogEntry> LogEntries { get; }
int? RequestLogExpirationDuration { get; set; }
int? RequestLogExpirationDuration { get; set; }
int? MaxRequestLogCount { get; set; }
int? MaxRequestLogCount { get; set; }
Action<IAppBuilder>? PreWireMockMiddlewareInit { get; set; }
Action<IAppBuilder>? PreWireMockMiddlewareInit { get; set; }
Action<IAppBuilder>? PostWireMockMiddlewareInit { get; set; }
Action<IAppBuilder>? PostWireMockMiddlewareInit { get; set; }
#if USE_ASPNETCORE
Action<IServiceCollection>? AdditionalServiceRegistration { get; set; }
Action<IServiceCollection>? AdditionalServiceRegistration { get; set; }
CorsPolicyOptions? CorsPolicyOptions { get; set; }
CorsPolicyOptions? CorsPolicyOptions { get; set; }
#endif
IFileSystemHandler? FileSystemHandler { get; set; }
IFileSystemHandler? FileSystemHandler { get; set; }
bool? AllowBodyForAllHttpMethods { get; set; }
bool? AllowBodyForAllHttpMethods { get; set; }
bool? AllowOnlyDefinedHttpStatusCodeInResponse { get; set; }
bool? AllowOnlyDefinedHttpStatusCodeInResponse { get; set; }
bool? DisableJsonBodyParsing { get; set; }
bool? DisableJsonBodyParsing { get; set; }
bool? DisableRequestBodyDecompressing { get; set; }
bool? DisableRequestBodyDecompressing { get; set; }
bool? HandleRequestsSynchronously { get; set; }
bool? HandleRequestsSynchronously { get; set; }
string? X509StoreName { get; set; }
string? X509StoreName { get; set; }
string? X509StoreLocation { get; set; }
string? X509StoreLocation { get; set; }
string? X509ThumbprintOrSubjectName { get; set; }
string? X509ThumbprintOrSubjectName { get; set; }
string? X509CertificateFilePath { get; set; }
string? X509CertificateFilePath { get; set; }
string? X509CertificatePassword { get; set; }
string? X509CertificatePassword { get; set; }
bool CustomCertificateDefined { get; }
bool CustomCertificateDefined { get; }
bool? SaveUnmatchedRequests { get; set; }
}
bool? SaveUnmatchedRequests { get; set; }
}

View File

@@ -27,7 +27,7 @@ namespace WireMock.Owin.Mappers
string method = request.Method;
Dictionary<string, string[]>? headers = null;
var headers = new Dictionary<string, string[]>();
IEnumerable<string>? contentEncodingHeader = null;
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())
{
cookies = new Dictionary<string, string>();
@@ -75,13 +75,24 @@ namespace WireMock.Owin.Mappers
{
#if !USE_ASPNETCORE
var urlDetails = UrlUtils.Parse(request.Uri, request.PathBase);
string clientIP = request.RemoteIpAddress;
var clientIP = request.RemoteIpAddress;
#else
var urlDetails = UrlUtils.Parse(new Uri(request.GetEncodedUrl()), request.PathBase);
var connection = request.HttpContext.Connection;
string clientIP = connection.RemoteIpAddress.IsIPv4MappedToIPv6
? connection.RemoteIpAddress.MapToIPv4().ToString()
: connection.RemoteIpAddress.ToString();
string clientIP;
if (connection.RemoteIpAddress is null)
{
clientIP = string.Empty;
}
else if (connection.RemoteIpAddress.IsIPv4MappedToIPv6)
{
clientIP = connection.RemoteIpAddress.MapToIPv4().ToString();
}
else
{
clientIP = connection.RemoteIpAddress.ToString();
}
#endif
return (urlDetails, clientIP);
}

View File

@@ -71,7 +71,6 @@ namespace WireMock.Owin.Mappers
{
bytes = bytes.Take(bytes.Length / 2).Union(_randomizerBytes.Generate()).ToArray();
}
break;
default:
@@ -80,11 +79,10 @@ namespace WireMock.Owin.Mappers
}
var statusCodeType = responseMessage.StatusCode?.GetType();
switch (statusCodeType)
{
case Type typeAsIntOrEnum when typeAsIntOrEnum == typeof(int) || typeAsIntOrEnum == typeof(int?) || typeAsIntOrEnum.GetTypeInfo().IsEnum:
response.StatusCode = MapStatusCode((int)responseMessage.StatusCode);
response.StatusCode = MapStatusCode((int)responseMessage.StatusCode!);
break;
case Type typeAsString when typeAsString == typeof(string):
@@ -138,7 +136,7 @@ namespace WireMock.Owin.Mappers
return responseMessage.BodyData.BodyAsBytes;
case BodyType.File:
return _options.FileSystemHandler.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile);
return _options.FileSystemHandler?.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile);
}
return null;

View File

@@ -4,72 +4,71 @@ using System.Linq;
using WireMock.Extensions;
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));
_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
{
try
var nextState = GetNextState(mapping);
possibleMappings.Add(new MappingMatcherResult
{
string nextState = GetNextState(mapping);
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}");
}
Mapping = mapping,
RequestMatchResult = mapping.GetRequestMatchResult(request, nextState)
});
}
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)
catch (Exception ex)
{
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,
// 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;
return (partialMatch, partialMatch);
}
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;
}
}

View File

@@ -1,11 +1,10 @@
using WireMock.Matchers.Request;
namespace WireMock.Owin
{
internal class MappingMatcherResult
{
public IMapping Mapping { get; set; }
namespace WireMock.Owin;
public IRequestMatchResult RequestMatchResult { get; set; }
}
internal class MappingMatcherResult
{
public IMapping Mapping { get; set; }
public IRequestMatchResult RequestMatchResult { get; set; }
}

View File

@@ -1,110 +1,109 @@
#if !USE_ASPNETCORE
using JetBrains.Annotations;
using Microsoft.Owin.Hosting;
using Owin;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using WireMock.Logging;
using WireMock.Owin.Mappers;
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;
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
private readonly IWireMockLogger _logger;
Guard.NotNull(options, nameof(options));
Guard.NotNull(urlOptions, nameof(urlOptions));
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));
Guard.NotNull(urlOptions, nameof(urlOptions));
_options = options;
_logger = options.Logger ?? new WireMockConsoleLogger();
foreach (var detail in urlOptions.GetDetails())
{
Urls.Add(detail.Url);
Ports.Add(detail.Port);
}
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]
public Task StartAsync()
{
return Task.Run(StartServers, _cts.Token);
}
[PublicAPI]
public Task StartAsync()
{
return Task.Run(StartServers, _cts.Token);
}
[PublicAPI]
public Task StopAsync()
{
_cts.Cancel();
[PublicAPI]
public Task StopAsync()
{
_cts.Cancel();
return Task.FromResult(true);
}
return Task.FromResult(true);
}
private void StartServers()
{
private void StartServers()
{
#if NET46
_logger.Info("Server using .net 4.6.1 or higher");
_logger.Info("Server using .net 4.6.1 or higher");
#else
_logger.Info("Server using .net 4.5.x");
_logger.Info("Server using .net 4.5.x");
#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();
var responseMapper = new OwinResponseMapper(_options);
var matcher = new MappingMatcher(_options);
app.Use<GlobalExceptionMiddleware>(_options, responseMapper);
_options.PreWireMockMiddlewareInit?.Invoke(app);
app.Use<WireMockMiddleware>(_options, requestMapper, responseMapper, matcher);
_options.PostWireMockMiddlewareInit?.Invoke(app);
};
Action<IAppBuilder> startup = app =>
{
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)
foreach (var url in Urls)
{
// 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());
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
// 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());
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Threading.Tasks;
using System.Linq;
using System.Net;
using Stef.Validation;
using WireMock.Logging;
using WireMock.Matchers;
@@ -10,8 +11,8 @@ using WireMock.Serialization;
using WireMock.Types;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using System.Collections.Generic;
#if !USE_ASPNETCORE
using Microsoft.Owin;
using IContext = Microsoft.Owin.IOwinContext;
using OwinMiddleware = Microsoft.Owin.OwinMiddleware;
using Next = Microsoft.Owin.OwinMiddleware;
@@ -25,7 +26,7 @@ namespace WireMock.Owin
{
internal class WireMockMiddleware : OwinMiddleware
{
private readonly object _lock = new object();
private readonly object _lock = new();
private static readonly Task CompletedTask = Task.FromResult(false);
private readonly IWireMockMiddlewareOptions _options;
private readonly IOwinRequestMapper _requestMapper;
@@ -74,10 +75,16 @@ namespace WireMock.Owin
var logRequest = false;
IResponseMessage? response = null;
(MappingMatcherResult? Match, MappingMatcherResult? Partial) result = (null, null);
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
if (!_options.Scenarios.ContainsKey(mapping.Scenario) && mapping.IsStartState)
{
@@ -101,13 +108,13 @@ namespace WireMock.Owin
logRequest = targetMapping.LogMapping;
if (targetMapping.IsAdminInterface && _options.AuthenticationMatcher != null)
if (targetMapping.IsAdminInterface && _options.AuthenticationMatcher != null && request.Headers != null)
{
bool present = request.Headers.TryGetValue(HttpKnownHeaderNames.Authorization, out WireMockList<string> authorization);
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");
response = ResponseMessageBuilder.Create(null, 401);
response = ResponseMessageBuilder.Create(null, HttpStatusCode.Unauthorized);
return;
}
}
@@ -194,25 +201,51 @@ namespace WireMock.Owin
private async Task SendToWebhooksAsync(IMapping mapping, IRequestMessage request, IResponseMessage response)
{
for (int index = 0; index < mapping.Webhooks.Length; index++)
var tasks = new List<Func<Task>>();
for (int index = 0; index < mapping.Webhooks?.Length; index++)
{
var httpClientForWebhook = HttpClientBuilder.Build(mapping.Settings.WebhookSettings ?? new WebhookSettings());
var webhookSender = new WebhookSender(mapping.Settings);
var webhookRequest = mapping.Webhooks[index].Request;
var webHookIndex = index;
tasks.Add(async () =>
{
try
{
await webhookSender.SendAsync(httpClientForWebhook, mapping, webhookRequest, request, response).ConfigureAwait(false);
}
catch (Exception ex)
{
_options.Logger.Error($"Sending message to Webhook [{webHookIndex}] from Mapping '{mapping.Guid}' failed. Exception: {ex}");
}
});
}
if (mapping.UseWebhooksFireAndForget == true)
{
try
{
await webhookSender.SendAsync(httpClientForWebhook, mapping.Webhooks[index].Request, request, response).ConfigureAwait(false);
// Do not wait
await Task.Run(() =>
{
Task.WhenAll(tasks.Select(async task => await task.Invoke())).ConfigureAwait(false);
});
}
catch (Exception ex)
catch
{
_options.Logger.Error($"Sending message to Webhook [{index}] from Mapping '{mapping.Guid}' failed. Exception: {ex}");
// Ignore
}
}
else
{
await Task.WhenAll(tasks.Select(async task => await task.Invoke())).ConfigureAwait(false);
}
}
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
scenario.Counter++;

View File

@@ -12,77 +12,76 @@ using IAppBuilder = Microsoft.AspNetCore.Builder.IApplicationBuilder;
using Microsoft.Extensions.DependencyInjection;
#endif
namespace WireMock.Owin
namespace WireMock.Owin;
internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
{
internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
{
public IWireMockLogger Logger { get; set; }
public IWireMockLogger Logger { get; set; }
public TimeSpan? RequestProcessingDelay { get; set; }
public TimeSpan? RequestProcessingDelay { get; set; }
public IStringMatcher AuthenticationMatcher { get; set; }
public IStringMatcher AuthenticationMatcher { get; set; }
public bool? AllowPartialMapping { get; set; }
public bool? AllowPartialMapping { get; set; }
public ConcurrentDictionary<Guid, IMapping> Mappings { get; } = new ConcurrentDictionary<Guid, IMapping>();
public ConcurrentDictionary<Guid, IMapping> Mappings { get; } = new ConcurrentDictionary<Guid, IMapping>();
public ConcurrentDictionary<string, ScenarioState> Scenarios { get; } = new ConcurrentDictionary<string, ScenarioState>();
public ConcurrentDictionary<string, ScenarioState> Scenarios { get; } = new();
public ConcurrentObservableCollection<LogEntry> LogEntries { get; } = new ConcurrentObservableCollection<LogEntry>();
public ConcurrentObservableCollection<LogEntry> LogEntries { get; } = new();
public int? RequestLogExpirationDuration { get; set; }
public int? RequestLogExpirationDuration { get; set; }
public int? MaxRequestLogCount { get; set; }
public int? MaxRequestLogCount { get; set; }
public Action<IAppBuilder> PreWireMockMiddlewareInit { get; set; }
public Action<IAppBuilder>? PreWireMockMiddlewareInit { get; set; }
public Action<IAppBuilder> PostWireMockMiddlewareInit { get; set; }
public Action<IAppBuilder>? PostWireMockMiddlewareInit { get; set; }
#if USE_ASPNETCORE
public Action<IServiceCollection> AdditionalServiceRegistration { get; set; }
public Action<IServiceCollection>? AdditionalServiceRegistration { get; set; }
public CorsPolicyOptions? CorsPolicyOptions { get; set; }
public CorsPolicyOptions? CorsPolicyOptions { get; set; }
#endif
/// <inheritdoc cref="IWireMockMiddlewareOptions.FileSystemHandler"/>
public IFileSystemHandler FileSystemHandler { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.FileSystemHandler"/>
public IFileSystemHandler FileSystemHandler { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.AllowBodyForAllHttpMethods"/>
public bool? AllowBodyForAllHttpMethods { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.AllowBodyForAllHttpMethods"/>
public bool? AllowBodyForAllHttpMethods { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.AllowOnlyDefinedHttpStatusCodeInResponse"/>
public bool? AllowOnlyDefinedHttpStatusCodeInResponse { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.AllowOnlyDefinedHttpStatusCodeInResponse"/>
public bool? AllowOnlyDefinedHttpStatusCodeInResponse { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.DisableJsonBodyParsing"/>
public bool? DisableJsonBodyParsing { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.DisableJsonBodyParsing"/>
public bool? DisableJsonBodyParsing { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.DisableRequestBodyDecompressing"/>
public bool? DisableRequestBodyDecompressing { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.DisableRequestBodyDecompressing"/>
public bool? DisableRequestBodyDecompressing { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.HandleRequestsSynchronously"/>
public bool? HandleRequestsSynchronously { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.HandleRequestsSynchronously"/>
public bool? HandleRequestsSynchronously { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509StoreName"/>
public string X509StoreName { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509StoreName"/>
public string? X509StoreName { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509StoreLocation"/>
public string X509StoreLocation { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509StoreLocation"/>
public string? X509StoreLocation { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509ThumbprintOrSubjectName"/>
public string X509ThumbprintOrSubjectName { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509ThumbprintOrSubjectName"/>
public string? X509ThumbprintOrSubjectName { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509CertificateFilePath"/>
public string X509CertificateFilePath { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509CertificateFilePath"/>
public string? X509CertificateFilePath { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509CertificatePassword"/>
public string X509CertificatePassword { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509CertificatePassword"/>
public string? X509CertificatePassword { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.CustomCertificateDefined"/>
public bool CustomCertificateDefined =>
!string.IsNullOrEmpty(X509StoreName) && !string.IsNullOrEmpty(X509StoreLocation) ||
!string.IsNullOrEmpty(X509CertificateFilePath);
/// <inheritdoc cref="IWireMockMiddlewareOptions.CustomCertificateDefined"/>
public bool CustomCertificateDefined =>
!string.IsNullOrEmpty(X509StoreName) && !string.IsNullOrEmpty(X509StoreLocation) ||
!string.IsNullOrEmpty(X509CertificateFilePath);
/// <inheritdoc cref="IWireMockMiddlewareOptions.SaveUnmatchedRequests"/>
public bool? SaveUnmatchedRequests { get; set; }
}
/// <inheritdoc cref="IWireMockMiddlewareOptions.SaveUnmatchedRequests"/>
public bool? SaveUnmatchedRequests { get; set; }
}

View File

@@ -2,11 +2,11 @@ namespace WireMock.Pact.Models.V2;
public class Interaction
{
public string Description { get; set; } = string.Empty;
public string? Description { get; set; }
public string ProviderState { get; set; }
public string? ProviderState { get; set; }
public PactRequest Request { get; set; } = new PactRequest();
public PactRequest Request { get; set; } = new();
public PactResponse Response { get; set; } = new PactResponse();
public PactResponse Response { get; set; } = new();
}

View File

@@ -1,57 +1,56 @@
using System;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Reflection;
namespace WireMock.Plugin
namespace WireMock.Plugin;
internal static class PluginLoader
{
internal static class PluginLoader
private static readonly ConcurrentDictionary<Type, Type> Assemblies = new();
public static T Load<T>(params object[] args) where T : class
{
private static readonly ConcurrentDictionary<Type, Type> Assemblies = new ConcurrentDictionary<Type, Type>();
public static T Load<T>(params object[] args) where T : class
var foundType = Assemblies.GetOrAdd(typeof(T), (type) =>
{
var foundType = Assemblies.GetOrAdd(typeof(T), (type) =>
var files = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.dll");
Type? pluginType = null;
foreach (var file in files)
{
var files = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.dll");
Type pluginType = null;
foreach (var file in files)
try
{
try
var assembly = Assembly.Load(new AssemblyName
{
var assembly = Assembly.Load(new AssemblyName
{
Name = Path.GetFileNameWithoutExtension(file)
});
Name = Path.GetFileNameWithoutExtension(file)
});
pluginType = GetImplementationTypeByInterface<T>(assembly);
if (pluginType != null)
{
break;
}
}
catch
pluginType = GetImplementationTypeByInterface<T>(assembly);
if (pluginType != null)
{
// no-op: just try next .dll
break;
}
}
if (pluginType != null)
catch
{
return pluginType;
// no-op: just try next .dll
}
}
throw new DllNotFoundException($"No dll found which implements type '{type}'");
});
if (pluginType != null)
{
return pluginType;
}
return (T)Activator.CreateInstance(foundType, args);
}
throw new DllNotFoundException($"No dll found which implements type '{type}'");
});
private static Type GetImplementationTypeByInterface<T>(Assembly assembly)
{
return assembly.GetTypes().FirstOrDefault(t => typeof(T).IsAssignableFrom(t) && !t.GetTypeInfo().IsInterface);
}
return (T)Activator.CreateInstance(foundType, args);
}
private static Type? GetImplementationTypeByInterface<T>(Assembly assembly)
{
return assembly.GetTypes().FirstOrDefault(t => typeof(T).IsAssignableFrom(t) && !t.GetTypeInfo().IsInterface);
}
}

View File

@@ -1,15 +1,10 @@
using Stef.Validation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Stef.Validation;
using WireMock.Http;
using WireMock.Matchers;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Serialization;
using WireMock.Settings;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Proxy;
@@ -17,21 +12,24 @@ namespace WireMock.Proxy;
internal class ProxyHelper
{
private readonly WireMockServerSettings _settings;
private readonly ProxyMappingConverter _proxyMappingConverter;
public ProxyHelper(WireMockServerSettings settings)
{
_settings = Guard.NotNull(settings);
_proxyMappingConverter = new ProxyMappingConverter(settings, new GuidUtils());
}
public async Task<(IResponseMessage Message, IMapping? Mapping)> SendAsync(
IMapping? mapping,
ProxyAndRecordSettings proxyAndRecordSettings,
HttpClient client,
IRequestMessage requestMessage,
string url)
{
Guard.NotNull(client, nameof(client));
Guard.NotNull(requestMessage, nameof(requestMessage));
Guard.NotNull(url, nameof(url));
Guard.NotNull(client);
Guard.NotNull(requestMessage);
Guard.NotNull(url);
var originalUri = new Uri(requestMessage.Url);
var requiredUri = new Uri(url);
@@ -48,61 +46,13 @@ internal class ProxyHelper
var responseMessage = await HttpResponseMessageHelper.CreateAsync(httpResponseMessage, requiredUri, originalUri, deserializeJson, decompressGzipAndDeflate).ConfigureAwait(false);
IMapping? mapping = null;
IMapping? newMapping = null;
if (HttpStatusRangeParser.IsMatch(proxyAndRecordSettings.SaveMappingForStatusCodePattern, responseMessage.StatusCode) &&
(proxyAndRecordSettings.SaveMapping || proxyAndRecordSettings.SaveMappingToFile))
{
mapping = ToMapping(proxyAndRecordSettings, requestMessage, responseMessage);
newMapping = _proxyMappingConverter.ToMapping(mapping, proxyAndRecordSettings, requestMessage, responseMessage);
}
return (responseMessage, mapping);
}
private IMapping ToMapping(ProxyAndRecordSettings proxyAndRecordSettings, IRequestMessage requestMessage, ResponseMessage responseMessage)
{
var excludedHeaders = proxyAndRecordSettings.ExcludedHeaders ?? new string[] { };
var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[] { };
var request = Request.Create();
request.WithPath(requestMessage.Path);
request.UsingMethod(requestMessage.Method);
requestMessage.Query?.Loop((key, value) => request.WithParam(key, false, value.ToArray()));
requestMessage.Cookies?.Loop((key, value) =>
{
if (!excludedCookies.Contains(key, StringComparer.OrdinalIgnoreCase))
{
request.WithCookie(key, value);
}
});
var allExcludedHeaders = new List<string>(excludedHeaders) { "Cookie" };
requestMessage.Headers?.Loop((key, value) =>
{
if (!allExcludedHeaders.Contains(key, StringComparer.OrdinalIgnoreCase))
{
request.WithHeader(key, value.ToArray());
}
});
bool throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true;
switch (requestMessage.BodyData?.DetectedBodyType)
{
case BodyType.Json:
request.WithBody(new JsonMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsJson!, true, throwExceptionWhenMatcherFails));
break;
case BodyType.String:
request.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, throwExceptionWhenMatcherFails, MatchOperator.Or, requestMessage.BodyData.BodyAsString));
break;
case BodyType.Bytes:
request.WithBody(new ExactObjectMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsBytes, throwExceptionWhenMatcherFails));
break;
}
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 (responseMessage, newMapping);
}
}

View File

@@ -77,7 +77,7 @@ internal class RegexExtended : Regex
/// <param name="pattern">Pattern to replace token for.</param>
private static string ReplaceGuidPattern(string pattern)
{
Guard.NotNull(pattern, nameof(pattern));
Guard.NotNull(pattern);
foreach (var tokenPattern in GuidTokenPatterns)
{

Some files were not shown because too many files have changed in this diff Show More