mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-01-13 22:03:35 +01:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
018d2a904d | ||
|
|
939a5a61ab | ||
|
|
a15e6747b0 | ||
|
|
6c38400827 | ||
|
|
cb1117fdaa | ||
|
|
e25c873765 | ||
|
|
d83f308591 | ||
|
|
c8c9ab99c5 | ||
|
|
d134684bcb | ||
|
|
a96b7bca1e | ||
|
|
cbe6a0a2b4 | ||
|
|
15370a89ca | ||
|
|
656f7f5432 | ||
|
|
bcdc3d646a | ||
|
|
10350fac04 | ||
|
|
37244d5c2f | ||
|
|
07f03997c0 | ||
|
|
7c289d44a7 | ||
|
|
e87e09e10d | ||
|
|
eff21473e3 | ||
|
|
fce60f273d | ||
|
|
8199cdc382 | ||
|
|
782418f107 | ||
|
|
207688e42b | ||
|
|
2d39a18b70 | ||
|
|
9c55ff5ea6 | ||
|
|
8386d93e0d | ||
|
|
5424b1e2e2 | ||
|
|
ab017beaa6 | ||
|
|
8827531391 | ||
|
|
881e4b2af3 | ||
|
|
139ea77888 | ||
|
|
f776911ef8 | ||
|
|
3a50c81895 | ||
|
|
de5277d258 | ||
|
|
23d26ca914 | ||
|
|
f8ad2cd3d6 | ||
|
|
76f0e04874 | ||
|
|
1a77fee273 | ||
|
|
8d072e8b47 | ||
|
|
5c5d408abe | ||
|
|
2aee658dfa | ||
|
|
ef73accc9a | ||
|
|
f38c24e8c4 | ||
|
|
65ac8c6462 | ||
|
|
49ce2f0dfb | ||
|
|
c29b88eb43 | ||
|
|
85e51b6240 | ||
|
|
cece260203 | ||
|
|
538195551d |
22
.vscode/launch.json
vendored
Normal file
22
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": ".NET Core Launch (WireMock.Net.StandAlone.NETCoreApp)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build_WireMock.Net.StandAlone.NETCoreApp",
|
||||
"program": "${workspaceRoot}/examples/WireMock.Net.StandAlone.NETCoreApp/bin/Debug/netcoreapp2.0/WireMock.Net.StandAlone.NETCoreApp.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"stopAtEntry": false,
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach",
|
||||
"processId": "${command:pickProcess}"
|
||||
}
|
||||
]
|
||||
}
|
||||
17
.vscode/tasks.json
vendored
Normal file
17
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"taskName": "build_WireMock.Net.StandAlone.NETCoreApp",
|
||||
"command": "dotnet build ${workspaceRoot}/examples/WireMock.Net.StandAlone.NETCoreApp/WireMock.Net.StandAlone.NETCoreApp.csproj -f netcoreapp2.0 ",
|
||||
"type": "shell",
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"reveal": "silent"
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
13
README.md
13
README.md
@@ -2,7 +2,7 @@
|
||||
A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) which mimics the functionality from the JAVA based http://WireMock.org
|
||||
|
||||
[](https://ci.appveyor.com/project/StefH/wiremock-net)
|
||||
[](https://codecov.io/gh/StefH/WireMock.Net)
|
||||
[](https://codecov.io/gh/WireMock-Net/WireMock.Net)
|
||||
[](https://coveralls.io/github/StefH/WireMock.Net?branch=master)
|
||||
[](https://github.com/StefH/WireMock.Net/issues)
|
||||
[](https://github.com/StefH/WireMock.Net/stargazers)
|
||||
@@ -14,9 +14,18 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w
|
||||
|
||||
### Frameworks
|
||||
The following frameworks are supported:
|
||||
- net 4.5
|
||||
- net 4.5.2 and up
|
||||
- net 4.6 and up
|
||||
- netstandard 1.3
|
||||
- netstandard 2.0
|
||||
|
||||
## Build info
|
||||
To build you need:
|
||||
- Microsoft .NET Framework 4.5.2 Developer Pack (https://www.microsoft.com/en-us/download/details.aspx?id=42637)
|
||||
- Microsoft .NET Framework 4.6 Targeting Pack (https://www.microsoft.com/en-us/download/confirmation.aspx?id=48136)
|
||||
- Microsoft .NET Framework 4.6.2 Developer Pack (https://www.microsoft.com/en-us/download/confirmation.aspx?id=53321)
|
||||
- .NET Core 2.0 (https://www.microsoft.com/net/core)
|
||||
|
||||
|
||||
## Stubbing
|
||||
A core feature of WireMock.Net is the ability to return canned/predefined HTTP responses for requests matching criteria, see [Wiki : Stubbing](https://github.com/StefH/WireMock.Net/wiki/Stubbing).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26430.13
|
||||
VisualStudioVersion = 15.0.27004.2005
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF242EDF-7133-4277-9A0C-18744DE08707}"
|
||||
EndProject
|
||||
@@ -14,23 +14,21 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{F0C22C47-DF71-463C-9B04-B4E0F3B8708A}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
examples\WireMock.Net.Console.Record.NETCoreApp\__admin\mappings\ab38efae-4e4d-4f20-8afe-635533ec2535.json = examples\WireMock.Net.Console.Record.NETCoreApp\__admin\mappings\ab38efae-4e4d-4f20-8afe-635533ec2535.json
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{890A1DED-C229-4FA1-969E-AAC3BBFC05E5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net", "src\WireMock.Net\WireMock.Net.csproj", "{D3804228-91F4-4502-9595-39584E5A01AD}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.ConsoleApplication", "examples\WireMock.Net.ConsoleApplication\WireMock.Net.ConsoleApplication.csproj", "{668F689E-57B4-422E-8846-C0FF643CA268}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{D3804228-91F4-4502-9595-39584E5A01AD} = {D3804228-91F4-4502-9595-39584E5A01AD}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Tests", "test\WireMock.Net.Tests\WireMock.Net.Tests.csproj", "{31DC2EF8-C3FE-467D-84BE-FB5D956E612E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.NETCoreApp", "examples\WireMock.Net.Console.NETCoreApp\WireMock.Net.Console.NETCoreApp.csproj", "{FE281639-B014-4C8A-96FA-141164A74713}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.NETCoreApp", "examples\WireMock.Net.Console.NETCoreApp\WireMock.Net.Console.NETCoreApp.csproj", "{FE281639-B014-4C8A-96FA-141164A74713}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.Record.NETCoreApp", "examples\WireMock.Net.Console.Record.NETCoreApp\WireMock.Net.Console.Record.NETCoreApp.csproj", "{1995E414-F197-4AB4-90C2-68D806B5AF59}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Client", "src\WireMock.Net.Client\WireMock.Net.Client.csproj", "{058D4B6C-C03E-49D0-91DB-A535B058FA0D}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Client", "examples\WireMock.Net.Client\WireMock.Net.Client.csproj", "{058D4B6C-C03E-49D0-91DB-A535B058FA0D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.StandAlone", "src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj", "{B6269AAC-170A-43D5-8B9A-579DED3D9A95}"
|
||||
EndProject
|
||||
@@ -38,11 +36,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.StandAlone.NET
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.StandAlone.Net452", "examples\WireMock.Net.StandAlone.Net452\WireMock.Net.StandAlone.Net452.csproj", "{668F689E-57B4-422E-8846-C0FF643CA999}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{058D4B6C-C03E-49D0-91DB-A535B058FA0D} = {058D4B6C-C03E-49D0-91DB-A535B058FA0D}
|
||||
{B6269AAC-170A-43D5-8B9A-579DED3D9A95} = {B6269AAC-170A-43D5-8B9A-579DED3D9A95}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.ConsoleTest.Net452", "examples\WireMock.Net.ConsoleTest.Net452\WireMock.Net.ConsoleTest.Net452.csproj", "{B5F1A150-37E1-434D-929F-FAD5AC428107}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.ConsoleApplication", "examples\WireMock.Net.ConsoleApplication\WireMock.Net.ConsoleApplication.csproj", "{668F689E-57B4-422E-8846-C0FF643CA268}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -54,10 +51,6 @@ Global
|
||||
{D3804228-91F4-4502-9595-39584E5A01AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D3804228-91F4-4502-9595-39584E5A01AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D3804228-91F4-4502-9595-39584E5A01AD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{668F689E-57B4-422E-8846-C0FF643CA268}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{668F689E-57B4-422E-8846-C0FF643CA268}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{668F689E-57B4-422E-8846-C0FF643CA268}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{668F689E-57B4-422E-8846-C0FF643CA268}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -86,17 +79,16 @@ Global
|
||||
{668F689E-57B4-422E-8846-C0FF643CA999}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{668F689E-57B4-422E-8846-C0FF643CA999}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{668F689E-57B4-422E-8846-C0FF643CA999}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B5F1A150-37E1-434D-929F-FAD5AC428107}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B5F1A150-37E1-434D-929F-FAD5AC428107}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B5F1A150-37E1-434D-929F-FAD5AC428107}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B5F1A150-37E1-434D-929F-FAD5AC428107}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{668F689E-57B4-422E-8846-C0FF643CA268}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{668F689E-57B4-422E-8846-C0FF643CA268}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{668F689E-57B4-422E-8846-C0FF643CA268}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{668F689E-57B4-422E-8846-C0FF643CA268}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{D3804228-91F4-4502-9595-39584E5A01AD} = {EF242EDF-7133-4277-9A0C-18744DE08707}
|
||||
{668F689E-57B4-422E-8846-C0FF643CA268} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
|
||||
{31DC2EF8-C3FE-467D-84BE-FB5D956E612E} = {890A1DED-C229-4FA1-969E-AAC3BBFC05E5}
|
||||
{FE281639-B014-4C8A-96FA-141164A74713} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
|
||||
{1995E414-F197-4AB4-90C2-68D806B5AF59} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
|
||||
@@ -104,6 +96,9 @@ Global
|
||||
{B6269AAC-170A-43D5-8B9A-579DED3D9A95} = {EF242EDF-7133-4277-9A0C-18744DE08707}
|
||||
{10E16614-61CA-48D8-8BDD-664C13913DED} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
|
||||
{668F689E-57B4-422E-8846-C0FF643CA999} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
|
||||
{B5F1A150-37E1-434D-929F-FAD5AC428107} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
|
||||
{668F689E-57B4-422E-8846-C0FF643CA268} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {BF428BCC-C837-433B-87D2-15C7014B73E9}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<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/Environment/InjectedLayers/FileInjectedLayer/=FBE2016FCEACD34DB66E1CC40B87F9F2/AbsolutePath/@EntryValue">C:\Users\azureuser\Documents\Github\WireMock.Net\ReSharper_WireMock.DotSettings</s:String>
|
||||
<s:String x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=FBE2016FCEACD34DB66E1CC40B87F9F2/RelativePath/@EntryValue">..\ReSharper_WireMock.DotSettings</s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=FBE2016FCEACD34DB66E1CC40B87F9F2/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/InjectedLayers/InjectedLayerCustomization/=FileFBE2016FCEACD34DB66E1CC40B87F9F2/@KeyIndexDefined">True</s:Boolean>
|
||||
<s:Double x:Key="/Default/Environment/InjectedLayers/InjectedLayerCustomization/=FileFBE2016FCEACD34DB66E1CC40B87F9F2/RelativePriority/@EntryValue">1</s:Double></wpf:ResourceDictionary>
|
||||
13
appveyor.yml
13
appveyor.yml
@@ -9,8 +9,8 @@ init:
|
||||
- ps: $Env:LABEL = "CI" + $Env:APPVEYOR_BUILD_NUMBER.PadLeft(5, "0")
|
||||
|
||||
install:
|
||||
- ps: Start-FileDownload 'https://download.microsoft.com/download/8/F/9/8F9659B9-E628-4D1A-B6BF-C3004C8C954B/dotnet-1.1.1-sdk-win-x64.exe'
|
||||
- cmd: dotnet-1.1.1-sdk-win-x64.exe /quiet
|
||||
- ps: Start-FileDownload 'https://download.microsoft.com/download/0/F/D/0FD852A4-7EA1-4E2A-983A-0484AC19B92C/dotnet-sdk-2.0.0-win-gs-x64.exe'
|
||||
- cmd: dotnet-sdk-2.0.0-win-gs-x64.exe /quiet
|
||||
- ps: $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = "true"
|
||||
|
||||
environment:
|
||||
@@ -20,7 +20,6 @@ environment:
|
||||
|
||||
before_build:
|
||||
- dotnet restore .\src\WireMock.Net\WireMock.Net.csproj
|
||||
- dotnet restore .\src\WireMock.Net.Client\WireMock.Net.Client.csproj
|
||||
- dotnet restore .\src\WireMock.Net.Standalone\WireMock.Net.Standalone.csproj
|
||||
- nuget restore .\examples\WireMock.Net.ConsoleApplication\WireMock.Net.ConsoleApplication.csproj -PackagesDirectory packages
|
||||
|
||||
@@ -28,18 +27,18 @@ build_script:
|
||||
# build WireMock.Net
|
||||
- dotnet build .\src\WireMock.Net\WireMock.Net.csproj -c %CONFIGURATION%
|
||||
|
||||
# build WireMock.Net.Standalone
|
||||
- dotnet build .\src\WireMock.Net.Standalone\WireMock.Net.Standalone.csproj -c %CONFIGURATION%
|
||||
|
||||
# restore and build WireMock.Net.Tests
|
||||
- dotnet restore .\test\WireMock.Net.Tests\WireMock.Net.Tests.csproj
|
||||
- dotnet build .\test\WireMock.Net.Tests\WireMock.Net.Tests.csproj -c %CONFIGURATION%
|
||||
|
||||
# build WireMock.Net.Standalone
|
||||
- dotnet build .\src\WireMock.Net.Standalone\WireMock.Net.Standalone.csproj -c %CONFIGURATION%
|
||||
|
||||
test_script:
|
||||
- nuget.exe install OpenCover -ExcludeVersion
|
||||
- nuget.exe install coveralls.net -ExcludeVersion
|
||||
- pip install codecov
|
||||
|
||||
- cmd: '"OpenCover\tools\OpenCover.Console.exe" -register:user -target:dotnet.exe -targetargs:"test test\WireMock.Net.Tests\WireMock.Net.Tests.csproj --no-build" -returntargetcode -filter:"+[WireMock.Net]* -[WireMock.Net.Tests*]*" -output:coverage.xml -oldstyle -searchdirs:".\test\WireMock.Net.Tests\bin\%CONFIGURATION%\net452"'
|
||||
- cmd: '"OpenCover\tools\OpenCover.Console.exe" -target:dotnet.exe -targetargs:"test test\WireMock.Net.Tests\WireMock.Net.Tests.csproj --no-build" -output:coverage.xml -returntargetcode -register:user -filter:"+[WireMock.Net]* -[WireMock.Net.Tests*]*" -nodefaultfilters -returntargetcode -oldstyle'
|
||||
- codecov -f "coverage.xml"
|
||||
- coveralls.net\tools\csmacnz.Coveralls.exe --opencover -i .\coverage.xml
|
||||
@@ -12,7 +12,7 @@ namespace WireMock.Net.Client
|
||||
static void Main(string[] args)
|
||||
{
|
||||
// Create an implementation of the IFluentMockServerAdmin and pass in the base URL for the API.
|
||||
var api = RestClient.For<IFluentMockServerAdmin>("http://localhost:9090");
|
||||
var api = RestClient.For<IFluentMockServerAdmin>("http://localhost:9091");
|
||||
|
||||
// Set BASIC Auth
|
||||
var value = Convert.ToBase64String(Encoding.ASCII.GetBytes("a:b"));
|
||||
@@ -30,9 +30,24 @@ namespace WireMock.Net.Client
|
||||
var mappings = api.GetMappingsAsync().Result;
|
||||
Console.WriteLine($"mappings = {JsonConvert.SerializeObject(mappings)}");
|
||||
|
||||
var guid = Guid.Parse("11111110-a633-40e8-a244-5cb80bc0ab66");
|
||||
var mapping = api.GetMappingAsync(guid).Result;
|
||||
Console.WriteLine($"mapping = {JsonConvert.SerializeObject(mapping)}");
|
||||
try
|
||||
{
|
||||
var guid = Guid.Parse("11111110-a633-40e8-a244-5cb80bc0ab66");
|
||||
var mapping = api.GetMappingAsync(guid).Result;
|
||||
Console.WriteLine($"mapping = {JsonConvert.SerializeObject(mapping)}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
|
||||
var request = api.GetRequestsAsync().Result;
|
||||
Console.WriteLine($"request = {JsonConvert.SerializeObject(request)}");
|
||||
|
||||
string deleteRequestsAsync = api.DeleteRequestsAsync().Result;
|
||||
Console.WriteLine($"deleteRequestsAsync = {deleteRequestsAsync}");
|
||||
|
||||
string resetRequestsAsync = api.ResetRequestsAsync().Result;
|
||||
Console.WriteLine($"resetRequestsAsync = {resetRequestsAsync}");
|
||||
|
||||
Console.WriteLine("Press any key to quit");
|
||||
Console.ReadKey();
|
||||
19
examples/WireMock.Net.Client/WireMock.Net.Client.csproj
Normal file
19
examples/WireMock.Net.Client/WireMock.Net.Client.csproj
Normal file
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<!-- <RuntimeFrameworkVersion>1.0.1</RuntimeFrameworkVersion> -->
|
||||
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
<PackageReference Include="RestEase" Version="1.4.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -18,19 +18,20 @@
|
||||
<Content Include="__admin\mappings\11111110-a633-40e8-a244-5cb80bc0ab66.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="__admin\mappings\826aff7c-6208-4a3c-923d-575248907db4.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineArgumentsParser" Version="3.0.10" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
||||
<!--<ProjectReference Include="..\WireMock.Net\WireMock.Net.csproj" />-->
|
||||
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="__admin\mappings\791a3f31-6946-4ce7-8e6f-0237c7443275.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="__admin\mappings\791a3f31-6946-4ce7-8e6f-0237c7443275.json">
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -14,8 +14,9 @@
|
||||
},
|
||||
"Response": {
|
||||
"BodyAsJson": { "body": "static mapping" },
|
||||
"Headers": {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
"Headers": {
|
||||
"Content-Type": "application/json",
|
||||
"Test-X": [ "test 1", "test 2" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"Guid": "791a3f31-6946-4ce7-8e6f-0237c7443275",
|
||||
"Title": "",
|
||||
"Priority": 0,
|
||||
"Request": {
|
||||
"Path": {
|
||||
"Matchers": [
|
||||
{
|
||||
"Name": "WildcardMatcher",
|
||||
"Pattern": "/proxy-google-test-post"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Methods": [
|
||||
"post"
|
||||
],
|
||||
"Body": {}
|
||||
},
|
||||
"Response": {
|
||||
"StatusCode": 404,
|
||||
"Body": "<!DOCTYPE html>\n<html lang=en>\n <meta charset=utf-8>\n <meta name=viewport content=\"initial-scale=1, minimum-scale=1, width=device-width\">\n <title>Error 404 (Not Found)!!1</title>\n <style>\n *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}\n </style>\n <a href=//www.google.com/><span id=logo aria-label=Google></span></a>\n <p><b>404.</b> <ins>That’s an error.</ins>\n <p>The requested URL <code>/proxy-google-test-post</code> was not found on this server. <ins>That’s all we know.</ins>\n",
|
||||
"BodyAsBytes": "PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ZW4+CiAgPG1ldGEgY2hhcnNldD11dGYtOD4KICA8bWV0YSBuYW1lPXZpZXdwb3J0IGNvbnRlbnQ9ImluaXRpYWwtc2NhbGU9MSwgbWluaW11bS1zY2FsZT0xLCB3aWR0aD1kZXZpY2Utd2lkdGgiPgogIDx0aXRsZT5FcnJvciA0MDQgKE5vdCBGb3VuZCkhITE8L3RpdGxlPgogIDxzdHlsZT4KICAgICp7bWFyZ2luOjA7cGFkZGluZzowfWh0bWwsY29kZXtmb250OjE1cHgvMjJweCBhcmlhbCxzYW5zLXNlcmlmfWh0bWx7YmFja2dyb3VuZDojZmZmO2NvbG9yOiMyMjI7cGFkZGluZzoxNXB4fWJvZHl7bWFyZ2luOjclIGF1dG8gMDttYXgtd2lkdGg6MzkwcHg7bWluLWhlaWdodDoxODBweDtwYWRkaW5nOjMwcHggMCAxNXB4fSogPiBib2R5e2JhY2tncm91bmQ6dXJsKC8vd3d3Lmdvb2dsZS5jb20vaW1hZ2VzL2Vycm9ycy9yb2JvdC5wbmcpIDEwMCUgNXB4IG5vLXJlcGVhdDtwYWRkaW5nLXJpZ2h0OjIwNXB4fXB7bWFyZ2luOjExcHggMCAyMnB4O292ZXJmbG93OmhpZGRlbn1pbnN7Y29sb3I6Izc3Nzt0ZXh0LWRlY29yYXRpb246bm9uZX1hIGltZ3tib3JkZXI6MH1AbWVkaWEgc2NyZWVuIGFuZCAobWF4LXdpZHRoOjc3MnB4KXtib2R5e2JhY2tncm91bmQ6bm9uZTttYXJnaW4tdG9wOjA7bWF4LXdpZHRoOm5vbmU7cGFkZGluZy1yaWdodDowfX0jbG9nb3tiYWNrZ3JvdW5kOnVybCgvL3d3dy5nb29nbGUuY29tL2ltYWdlcy9icmFuZGluZy9nb29nbGVsb2dvLzF4L2dvb2dsZWxvZ29fY29sb3JfMTUweDU0ZHAucG5nKSBuby1yZXBlYXQ7bWFyZ2luLWxlZnQ6LTVweH1AbWVkaWEgb25seSBzY3JlZW4gYW5kIChtaW4tcmVzb2x1dGlvbjoxOTJkcGkpeyNsb2dve2JhY2tncm91bmQ6dXJsKC8vd3d3Lmdvb2dsZS5jb20vaW1hZ2VzL2JyYW5kaW5nL2dvb2dsZWxvZ28vMngvZ29vZ2xlbG9nb19jb2xvcl8xNTB4NTRkcC5wbmcpIG5vLXJlcGVhdCAwJSAwJS8xMDAlIDEwMCU7LW1vei1ib3JkZXItaW1hZ2U6dXJsKC8vd3d3Lmdvb2dsZS5jb20vaW1hZ2VzL2JyYW5kaW5nL2dvb2dsZWxvZ28vMngvZ29vZ2xlbG9nb19jb2xvcl8xNTB4NTRkcC5wbmcpIDB9fUBtZWRpYSBvbmx5IHNjcmVlbiBhbmQgKC13ZWJraXQtbWluLWRldmljZS1waXhlbC1yYXRpbzoyKXsjbG9nb3tiYWNrZ3JvdW5kOnVybCgvL3d3dy5nb29nbGUuY29tL2ltYWdlcy9icmFuZGluZy9nb29nbGVsb2dvLzJ4L2dvb2dsZWxvZ29fY29sb3JfMTUweDU0ZHAucG5nKSBuby1yZXBlYXQ7LXdlYmtpdC1iYWNrZ3JvdW5kLXNpemU6MTAwJSAxMDAlfX0jbG9nb3tkaXNwbGF5OmlubGluZS1ibG9jaztoZWlnaHQ6NTRweDt3aWR0aDoxNTBweH0KICA8L3N0eWxlPgogIDxhIGhyZWY9Ly93d3cuZ29vZ2xlLmNvbS8+PHNwYW4gaWQ9bG9nbyBhcmlhLWxhYmVsPUdvb2dsZT48L3NwYW4+PC9hPgogIDxwPjxiPjQwNC48L2I+IDxpbnM+VGhhdOKAmXMgYW4gZXJyb3IuPC9pbnM+CiAgPHA+VGhlIHJlcXVlc3RlZCBVUkwgPGNvZGU+L3Byb3h5LWdvb2dsZS10ZXN0LXBvc3Q8L2NvZGU+IHdhcyBub3QgZm91bmQgb24gdGhpcyBzZXJ2ZXIuICA8aW5zPlRoYXTigJlzIGFsbCB3ZSBrbm93LjwvaW5zPgo=",
|
||||
"BodyEncoding": {
|
||||
"CodePage": 65001,
|
||||
"EncodingName": "Unicode (UTF-8)",
|
||||
"WebName": "utf-8"
|
||||
},
|
||||
"UseTransformer": false,
|
||||
"Headers": {
|
||||
"Date": "Wed, 25 Oct 2017 18:57:40 GMT",
|
||||
"Alt-Svc": "quic=\":443\"; ma=2592000; v=\"39,38,37,35\"",
|
||||
"Referrer-Policy": "no-referrer",
|
||||
"Connection": "close"
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -10,12 +10,15 @@ namespace WireMock.Net.Console.Record.NETCoreApp
|
||||
{
|
||||
var server = FluentMockServer.Start(new FluentMockServerSettings
|
||||
{
|
||||
Urls = new[] { "http://localhost:9095/", "https://localhost:9096/" },
|
||||
Urls = new[] { "http://localhost:9091/", "https://localhost:9443/" },
|
||||
StartAdminInterface = true,
|
||||
ReadStaticMappings = false,
|
||||
ProxyAndRecordSettings = new ProxyAndRecordSettings
|
||||
{
|
||||
Url = "https://www.msn.com",
|
||||
SaveMapping = true
|
||||
Url = "https://www.google.com",
|
||||
//X509Certificate2ThumbprintOrSubjectName = "www.yourclientcertname.com OR yourcertificatethumbprint (only if the service you're proxying to requires it)",
|
||||
SaveMapping = true,
|
||||
SaveMappingToFile = false
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -12,8 +12,11 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
||||
<!--<ProjectReference Include="..\WireMock.Net\WireMock.Net.csproj" />-->
|
||||
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="__admin\mappings\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.RequestBuilders;
|
||||
@@ -13,21 +12,63 @@ namespace WireMock.Net.ConsoleApplication
|
||||
{
|
||||
public static void Run()
|
||||
{
|
||||
string url1 = "http://localhost:9090/";
|
||||
string url2 = "http://localhost:9091/";
|
||||
string url1 = "http://localhost:9091/";
|
||||
string url2 = "http://localhost:9092/";
|
||||
string url3 = "https://localhost:9443/";
|
||||
|
||||
var server = FluentMockServer.Start(new FluentMockServerSettings
|
||||
{
|
||||
Urls = new[] { url1, url2, url3 },
|
||||
StartAdminInterface = true,
|
||||
ReadStaticMappings = true
|
||||
ReadStaticMappings = true,
|
||||
PreWireMockMiddlewareInit = app => { System.Console.WriteLine($"PreWireMockMiddlewareInit : {app.GetType()}"); },
|
||||
PostWireMockMiddlewareInit = app => { System.Console.WriteLine($"PostWireMockMiddlewareInit : {app.GetType()}"); }
|
||||
});
|
||||
System.Console.WriteLine("FluentMockServer listening at {0}", string.Join(" and ", server.Urls));
|
||||
|
||||
server.SetBasicAuthentication("a", "b");
|
||||
|
||||
server.AllowPartialMapping();
|
||||
// server.AllowPartialMapping();
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/headers", "/headers_test").UsingPost().WithHeader("Content-Type", "application/json*"))
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(201)
|
||||
.WithHeader("MyHeader", "application/json", "application/json2")
|
||||
.WithBody(@"{ ""result"": ""data posted with 201""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/file").UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithBodyFromFile(@"c:\temp\x.json", false)
|
||||
);
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/filecache").UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithBodyFromFile(@"c:\temp\x.json")
|
||||
);
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/file_rel").UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithBodyFromFile("Program.cs", false)
|
||||
);
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithHeader("ProxyThis", "true")
|
||||
.UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithProxy("http://www.google.com")
|
||||
);
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/bodyasbytes.png")
|
||||
.UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithHeader("Content-Type", "image/png")
|
||||
.WithBody(Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTczbp9jAAAAJ0lEQVQoU2NgUPuPD6Hz0RCEAtJoiAxpCCBXGgmRIo0TofORkdp/AMiMdRVnV6O0AAAAAElFTkSuQmCC"))
|
||||
);
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/oauth2/access").UsingPost().WithBody("grant_type=password;username=u;password=p"))
|
||||
@@ -58,13 +99,6 @@ namespace WireMock.Net.ConsoleApplication
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""data posted with FUNC 201""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/data", "/ax").UsingPost().WithHeader("Content-Type", "application/json*"))
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(201)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""data posted with 201""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/json").UsingPost().WithBody(new JsonPathMatcher("$.things[?(@.name == 'RequiredThing')]")))
|
||||
.RespondWith(Response.Create()
|
||||
@@ -96,18 +130,54 @@ namespace WireMock.Net.ConsoleApplication
|
||||
.RespondWith(Response.Create().WithStatusCode(200).WithBody("partial = 200"));
|
||||
|
||||
// http://localhost:8080/any/any?start=1000&stop=1&stop=2
|
||||
//server
|
||||
// .Given(Request.Create().WithPath("/*").UsingGet())
|
||||
// .WithGuid("90356dba-b36c-469a-a17e-669cd84f1f05")
|
||||
// .AtPriority(server.Mappings.Count() + 1)
|
||||
// .RespondWith(Response.Create()
|
||||
// .WithStatusCode(200)
|
||||
// .WithHeader("Content-Type", "application/json")
|
||||
// .WithHeader("Transformed-Postman-Token", "token is {{request.headers.Postman-Token}}")
|
||||
// .WithBody(@"{""msg"": ""Hello world CATCH-ALL on /*, {{request.path}}, bykey={{request.query.start}}, bykey={{request.query.stop}}, byidx0={{request.query.stop.[0]}}, byidx1={{request.query.stop.[1]}}"" }")
|
||||
// .WithTransformer()
|
||||
// .WithDelay(TimeSpan.FromMilliseconds(100))
|
||||
// );
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/*").UsingGet())
|
||||
.WithGuid("90356dba-b36c-469a-a17e-669cd84f1f05")
|
||||
.AtPriority(server.Mappings.Count() + 1)
|
||||
.Given(Request.Create()
|
||||
.WithPath("/state1")
|
||||
.UsingGet())
|
||||
.InScenario("s1")
|
||||
.WillSetStateTo("Test state 1")
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithHeader("Transformed-Postman-Token", "token is {{request.headers.Postman-Token}}")
|
||||
.WithBody(@"{""msg"": ""Hello world CATCH-ALL on /*, {{request.path}}, bykey={{request.query.start}}, bykey={{request.query.stop}}, byidx0={{request.query.stop.[0]}}, byidx1={{request.query.stop.[1]}}"" }")
|
||||
.WithTransformer()
|
||||
.WithDelay(TimeSpan.FromMilliseconds(100))
|
||||
);
|
||||
.WithBody("No state msg 1"));
|
||||
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/foostate1")
|
||||
.UsingGet())
|
||||
.InScenario("s1")
|
||||
.WhenStateIs("Test state 1")
|
||||
.RespondWith(Response.Create()
|
||||
.WithBody("Test state msg 1"));
|
||||
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/state2")
|
||||
.UsingGet())
|
||||
.InScenario("s2")
|
||||
.WillSetStateTo("Test state 2")
|
||||
.RespondWith(Response.Create()
|
||||
.WithBody("No state msg 2"));
|
||||
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/foostate2")
|
||||
.UsingGet())
|
||||
.InScenario("s2")
|
||||
.WhenStateIs("Test state 2")
|
||||
.RespondWith(Response.Create()
|
||||
.WithBody("Test state msg 2"));
|
||||
|
||||
System.Console.WriteLine("Press any key to stop the server");
|
||||
System.Console.ReadKey();
|
||||
@@ -121,4 +191,4 @@ namespace WireMock.Net.ConsoleApplication
|
||||
System.Console.ReadKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,9 +47,6 @@
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="WireMock.Net">
|
||||
<HintPath>..\..\src\WireMock.Net\bin\$(Configuration)\net45\WireMock.Net.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="MainApp.cs" />
|
||||
@@ -67,6 +64,16 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj">
|
||||
<Project>{b6269aac-170a-43d5-8b9a-579ded3d9a95}</Project>
|
||||
<Name>WireMock.Net.StandAlone</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" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
|
||||
</startup>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
@@ -1,16 +0,0 @@
|
||||
using System;
|
||||
using WireMock.Net.StandAlone;
|
||||
|
||||
namespace WireMock.Net.ConsoleTest.Net452
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
StandAloneApp.Start(args);
|
||||
|
||||
Console.WriteLine("Press any key to stop the server");
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("WireMock.Net.ConsoleTest.Net452")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("WireMock.Net.ConsoleTest.Net452")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("b5f1a150-37e1-434d-929f-fad5ac428107")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -1,96 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{B5F1A150-37E1-434D-929F-FAD5AC428107}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>WireMock.Net.ConsoleTest.Net452</RootNamespace>
|
||||
<AssemblyName>WireMock.Net.ConsoleTest.Net452</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="CommandLineArgumentsParser, Version=3.0.11.0, Culture=neutral, PublicKeyToken=16ad7bf6f4a1666c, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\CommandLineArgumentsParser.3.0.11\lib\net45\CommandLineArgumentsParser.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Handlebars, Version=1.0.0.0, Culture=neutral, PublicKeyToken=22225d0bf33cd661, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.1.8.0\lib\portable-net45+sl5+wp8+win8\Handlebars.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Owin.2.0.2\lib\net45\Microsoft.Owin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Host.HttpListener, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Owin.Host.HttpListener.2.0.2\lib\net45\Microsoft.Owin.Host.HttpListener.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Hosting, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Owin.Hosting.2.0.2\lib\net45\Microsoft.Owin.Hosting.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SimMetrics.Net, Version=1.0.3.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\SimMetrics.Net.1.0.3\lib\net45\SimMetrics.Net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Net.Http.Formatting, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Web.Http, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.Http.Owin, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="WireMock.Net, Version=1.0.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\WireMock.Net.1.0.2\lib\net45\WireMock.Net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="WireMock.Net.StandAlone, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\WireMock.Net.StandAlone.1.0.0\lib\net452\WireMock.Net.StandAlone.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="XPath2, Version=1.0.3.1, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\XPath2.1.0.3.1\lib\net452\XPath2.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="CommandLineArgumentsParser" version="3.0.11" targetFramework="net452" />
|
||||
<package id="Handlebars.Net" version="1.8.0" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNet.WebApi.OwinSelfHost" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin" version="2.0.2" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin.Host.HttpListener" version="2.0.2" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin.Hosting" version="2.0.2" targetFramework="net452" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
|
||||
<package id="Owin" version="1.0" targetFramework="net452" />
|
||||
<package id="SimMetrics.Net" version="1.0.3" targetFramework="net452" />
|
||||
<package id="WireMock.Net" version="1.0.2" targetFramework="net452" />
|
||||
<package id="WireMock.Net.StandAlone" version="1.0.0" targetFramework="net452" />
|
||||
<package id="XPath2" version="1.0.3.1" targetFramework="net452" />
|
||||
</packages>
|
||||
@@ -1,15 +1,42 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using WireMock.Server;
|
||||
|
||||
namespace WireMock.Net.StandAlone.NETCoreApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
private static int sleepTime = 30000;
|
||||
private static FluentMockServer server;
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
StandAloneApp.Start(args);
|
||||
server = StandAloneApp.Start(args);
|
||||
|
||||
Console.WriteLine("Press any key to stop the server");
|
||||
Console.ReadKey();
|
||||
Console.WriteLine($"{DateTime.UtcNow} Press Ctrl+C to shut down");
|
||||
|
||||
System.Console.CancelKeyPress += (s,e) =>
|
||||
{
|
||||
Stop("CancelKeyPress");
|
||||
};
|
||||
|
||||
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += ctx =>
|
||||
{
|
||||
Stop("AssemblyLoadContext.Default.Unloading");
|
||||
};
|
||||
|
||||
while(true)
|
||||
{
|
||||
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server running");
|
||||
Thread.Sleep(sleepTime);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Stop(string why)
|
||||
{
|
||||
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopping because '{why}'");
|
||||
server.Stop();
|
||||
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopped");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"profiles": {
|
||||
"WireMock.Net.StandAlone.NETCoreApp": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "--Urls http://*:9091"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp1.1</TargetFramework>
|
||||
<TargetFrameworks>netcoreapp2.0;netcoreapp1.1</TargetFrameworks>
|
||||
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
1
examples/WireMock.Net.StandAlone.NETCoreApp/run1.cmd
Normal file
1
examples/WireMock.Net.StandAlone.NETCoreApp/run1.cmd
Normal file
@@ -0,0 +1 @@
|
||||
dotnet run --framework netcoreapp1.1
|
||||
1
examples/WireMock.Net.StandAlone.NETCoreApp/run2.cmd
Normal file
1
examples/WireMock.Net.StandAlone.NETCoreApp/run2.cmd
Normal file
@@ -0,0 +1 @@
|
||||
dotnet run --framework netcoreapp2.0
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace WireMock.Net.StandAlone
|
||||
namespace WireMock.Net.StandAlone.Net452
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
<ProjectGuid>{668F689E-57B4-422E-8846-C0FF643CA999}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>WireMock.Net.StandAlone</RootNamespace>
|
||||
<AssemblyName>WireMock.Net.StandAlone</AssemblyName>
|
||||
<RootNamespace>WireMock.Net.StandAlone.Net452</RootNamespace>
|
||||
<AssemblyName>WireMock.Net.StandAlone.Net452</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
@@ -35,38 +35,15 @@
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>..\..\WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject>WireMock.Net.StandAlone.Net452.Program</StartupObject>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Handlebars, Version=1.8.1.0, Culture=neutral, PublicKeyToken=22225d0bf33cd661, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.1.9.0\lib\net40\Handlebars.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Owin.Host.HttpListener, Version=3.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Microsoft.Owin.Host.HttpListener.3.1.0\lib\net45\Microsoft.Owin.Host.HttpListener.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RestEase, Version=1.4.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\RestEase.1.4.1\lib\net45\RestEase.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SimMetrics.Net, Version=1.0.3.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\SimMetrics.Net.1.0.3\lib\net45\SimMetrics.Net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="WireMock.Net, Version=1.0.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\src\WireMock.Net.StandAlone\bin\$(Configuration)\net452\WireMock.Net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="WireMock.Net.StandAlone, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\src\WireMock.Net.StandAlone\bin\$(Configuration)\net452\WireMock.Net.StandAlone.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="XPath2, Version=1.0.3.1, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\XPath2.1.0.3.1\lib\net452\XPath2.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
@@ -75,6 +52,16 @@
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj">
|
||||
<Project>{b6269aac-170a-43d5-8b9a-579ded3d9a95}</Project>
|
||||
<Name>WireMock.Net.StandAlone</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" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Handlebars.Net" version="1.9.0" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin.Host.HttpListener" version="3.1.0" targetFramework="net452" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
|
||||
<package id="RestEase" version="1.4.1" targetFramework="net452" />
|
||||
<package id="SimMetrics.Net" version="1.0.3" targetFramework="net452" />
|
||||
<package id="XPath2" version="1.0.3.1" targetFramework="net452" />
|
||||
</packages>
|
||||
@@ -1,18 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp1.1</TargetFramework>
|
||||
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
|
||||
<PackageReference Include="RestEase" Version="1.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WireMock.Net\WireMock.Net.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
82
src/WireMock.Net.StandAlone/SimpleCommandLineParser.cs
Normal file
82
src/WireMock.Net.StandAlone/SimpleCommandLineParser.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace WireMock.Net.StandAlone
|
||||
{
|
||||
// Based on http://blog.gauffin.org/2014/12/simple-command-line-parser/
|
||||
internal class SimpleCommandLineParser
|
||||
{
|
||||
private IDictionary<string, string[]> Arguments { get; } = new Dictionary<string, string[]>();
|
||||
|
||||
public void Parse(string[] args)
|
||||
{
|
||||
string currentName = null;
|
||||
|
||||
var values = new List<string>();
|
||||
foreach (string arg in args)
|
||||
{
|
||||
if (arg.StartsWith("--"))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(currentName))
|
||||
{
|
||||
Arguments[currentName] = values.ToArray();
|
||||
}
|
||||
|
||||
values.Clear();
|
||||
currentName = arg.Substring(2);
|
||||
}
|
||||
else if (string.IsNullOrEmpty(currentName))
|
||||
{
|
||||
Arguments[arg] = new string[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
values.Add(arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(currentName))
|
||||
{
|
||||
Arguments[currentName] = values.ToArray();
|
||||
}
|
||||
}
|
||||
public bool Contains(string name)
|
||||
{
|
||||
return Arguments.ContainsKey(name);
|
||||
}
|
||||
|
||||
public string[] GetValues(string name, string[] defaultValue = null)
|
||||
{
|
||||
return Contains(name) ? Arguments[name] : defaultValue;
|
||||
}
|
||||
|
||||
public T GetValue<T>(string name, Func<string[], T> func, T defaultValue = default(T))
|
||||
{
|
||||
return Contains(name) ? func(Arguments[name]) : defaultValue;
|
||||
}
|
||||
|
||||
public bool GetBoolValue(string name, bool defaultValue = false)
|
||||
{
|
||||
return GetValue(name, values =>
|
||||
{
|
||||
string value = values.FirstOrDefault();
|
||||
return !string.IsNullOrEmpty(value) ? bool.Parse(value) : defaultValue;
|
||||
}, defaultValue);
|
||||
}
|
||||
|
||||
public int? GetIntValue(string name, int? defaultValue = null)
|
||||
{
|
||||
return GetValue(name, values =>
|
||||
{
|
||||
string value = values.FirstOrDefault();
|
||||
return !string.IsNullOrEmpty(value) ? int.Parse(value) : defaultValue;
|
||||
}, defaultValue);
|
||||
}
|
||||
|
||||
public string GetStringValue(string name, string defaultValue = null)
|
||||
{
|
||||
return GetValue(name, values => values.FirstOrDefault() ?? defaultValue, defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CommandLineParser.Arguments;
|
||||
using CommandLineParser.Exceptions;
|
||||
using WireMock.Server;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Validation;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace WireMock.Net.StandAlone
|
||||
{
|
||||
@@ -13,83 +13,71 @@ namespace WireMock.Net.StandAlone
|
||||
/// </summary>
|
||||
public static class StandAloneApp
|
||||
{
|
||||
private class Options
|
||||
/// <summary>
|
||||
/// Start WireMock.Net standalone based on the FluentMockServerSettings.
|
||||
/// </summary>
|
||||
/// <param name="settings">The FluentMockServerSettings</param>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer Start([NotNull] FluentMockServerSettings settings)
|
||||
{
|
||||
[ValueArgument(typeof(string), "Urls", Description = "URL(s) to listen on.", Optional = true, AllowMultiple = true)]
|
||||
public List<string> Urls { get; set; }
|
||||
Check.NotNull(settings, nameof(settings));
|
||||
|
||||
[SwitchArgument("AllowPartialMapping", false, Description = "Allow Partial Mapping (default set to false).", Optional = true)]
|
||||
public bool AllowPartialMapping { get; set; }
|
||||
|
||||
[SwitchArgument("StartAdminInterface", true, Description = "Start the AdminInterface (default set to true).", Optional = true)]
|
||||
public bool StartAdminInterface { get; set; }
|
||||
|
||||
[SwitchArgument("ReadStaticMappings", true, Description = "Read StaticMappings from ./__admin/mappings (default set to true).", Optional = true)]
|
||||
public bool ReadStaticMappings { get; set; }
|
||||
|
||||
[ValueArgument(typeof(string), "ProxyURL", Description = "The ProxyURL to use.", Optional = true)]
|
||||
public string ProxyURL { get; set; }
|
||||
|
||||
[SwitchArgument("SaveProxyMapping", true, Description = "Save the proxied request and response mapping files in ./__admin/mappings. (default set to true).", Optional = true)]
|
||||
public bool SaveMapping { get; set; }
|
||||
|
||||
[ValueArgument(typeof(string), "X509Certificate2", Description = "The X509Certificate2 Filename to use.", Optional = true)]
|
||||
public string X509Certificate2Filename { get; set; }
|
||||
return FluentMockServer.Start(settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start WireMock.Net standalone bases on the commandline arguments.
|
||||
/// Start WireMock.Net standalone based on the commandline arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The commandline arguments</param>
|
||||
public static FluentMockServer Start(string[] args)
|
||||
[PublicAPI]
|
||||
public static FluentMockServer Start([NotNull] string[] args)
|
||||
{
|
||||
var options = new Options();
|
||||
var parser = new CommandLineParser.CommandLineParser();
|
||||
parser.ExtractArgumentAttributes(options);
|
||||
Check.NotNull(args, nameof(args));
|
||||
|
||||
try
|
||||
Console.WriteLine("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));
|
||||
|
||||
var parser = new SimpleCommandLineParser();
|
||||
parser.Parse(args);
|
||||
|
||||
var settings = new FluentMockServerSettings
|
||||
{
|
||||
parser.ParseCommandLine(args);
|
||||
StartAdminInterface = parser.GetBoolValue("StartAdminInterface", true),
|
||||
ReadStaticMappings = parser.GetBoolValue("ReadStaticMappings"),
|
||||
AllowPartialMapping = parser.GetBoolValue("AllowPartialMapping", true),
|
||||
AdminUsername = parser.GetStringValue("AdminUsername"),
|
||||
AdminPassword = parser.GetStringValue("AdminPassword"),
|
||||
MaxRequestLogCount = parser.GetIntValue("MaxRequestLogCount"),
|
||||
RequestLogExpirationDuration = parser.GetIntValue("RequestLogExpirationDuration"),
|
||||
};
|
||||
|
||||
if (!options.Urls.Any())
|
||||
{
|
||||
options.Urls.Add("http://localhost:9090/");
|
||||
}
|
||||
if (parser.Contains("Port"))
|
||||
{
|
||||
settings.Port = parser.GetIntValue("Port");
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.Urls = parser.GetValues("Urls", new[] { "http://*:9091/" });
|
||||
}
|
||||
|
||||
var settings = new FluentMockServerSettings
|
||||
string proxyURL = parser.GetStringValue("ProxyURL");
|
||||
if (!string.IsNullOrEmpty(proxyURL))
|
||||
{
|
||||
settings.ProxyAndRecordSettings = new ProxyAndRecordSettings
|
||||
{
|
||||
Urls = options.Urls.ToArray(),
|
||||
StartAdminInterface = options.StartAdminInterface,
|
||||
ReadStaticMappings = options.ReadStaticMappings,
|
||||
Url = proxyURL,
|
||||
SaveMapping = parser.GetBoolValue("SaveMapping"),
|
||||
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
|
||||
X509Certificate2ThumbprintOrSubjectName = parser.GetStringValue("X509Certificate2ThumbprintOrSubjectName")
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(options.ProxyURL))
|
||||
{
|
||||
settings.ProxyAndRecordSettings = new ProxyAndRecordSettings
|
||||
{
|
||||
Url = options.ProxyURL,
|
||||
SaveMapping = options.SaveMapping,
|
||||
X509Certificate2Filename = options.X509Certificate2Filename
|
||||
};
|
||||
}
|
||||
|
||||
var server = FluentMockServer.Start(settings);
|
||||
if (options.AllowPartialMapping)
|
||||
{
|
||||
server.AllowPartialMapping();
|
||||
}
|
||||
|
||||
Console.WriteLine("WireMock.Net server listening at {0}", string.Join(" and ", server.Urls));
|
||||
|
||||
return server;
|
||||
}
|
||||
catch (CommandLineException e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
parser.ShowUsage();
|
||||
|
||||
throw;
|
||||
}
|
||||
Console.WriteLine("WireMock.Net server settings {0}", JsonConvert.SerializeObject(settings, Formatting.Indented));
|
||||
|
||||
FluentMockServer server = Start(settings);
|
||||
|
||||
Console.WriteLine("WireMock.Net server listening at {0}", string.Join(" and ", server.Urls));
|
||||
|
||||
return server;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,43 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>Lightweight StandAlone Http Mocking Server for .Net.</Description>
|
||||
<AssemblyTitle>WireMock.Net.StandAlone</AssemblyTitle>
|
||||
<Version>1.0.0.0</Version>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<TargetFrameworks>net45;net452;net46;netstandard1.3</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net.StandAlone</AssemblyName>
|
||||
<PackageId>WireMock.Net.StandAlone</PackageId>
|
||||
<PackageTags>tdd;mock;http;wiremock;test;server;unittest</PackageTags>
|
||||
<PackageReleaseNotes>Initial version</PackageReleaseNotes>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/StefH/WireMock.Net/master/WireMock.Net-Logo.png</PackageIconUrl>
|
||||
<PackageProjectUrl>https://github.com/StefH/WireMock.Net</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://raw.githubusercontent.com/StefH/WireMock.Net/master/LICENSE</PackageLicenseUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/StefH/WireMock.Net</RepositoryUrl>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<DebugType>full</DebugType>
|
||||
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
<RootNamespace>WireMock.Net.StandAlone</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
|
||||
<DefineConstants>NETSTANDARD</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineArgumentsParser" Version="3.0.11" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WireMock.Net\WireMock.Net.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>Lightweight StandAlone Http Mocking Server for .Net.</Description>
|
||||
<AssemblyTitle>WireMock.Net.StandAlone</AssemblyTitle>
|
||||
<Version>1.0.2.7</Version>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net.StandAlone</AssemblyName>
|
||||
<PackageId>WireMock.Net.StandAlone</PackageId>
|
||||
<PackageTags>tdd;mock;http;wiremock;test;server;unittest</PackageTags>
|
||||
<PackageReleaseNotes></PackageReleaseNotes>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/WireMock.Net-Logo.png</PackageIconUrl>
|
||||
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/LICENSE</PackageLicenseUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<DebugType>portable</DebugType>
|
||||
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
<RootNamespace>WireMock.Net.StandAlone</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' or '$(TargetFramework)' == 'netstandard2.0' ">
|
||||
<DefineConstants>NETSTANDARD</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="10.4.0">
|
||||
<PrivateAssets>All</PrivateAssets>
|
||||
</PackageReference>
|
||||
<!-- <PackageReference Include="CommandLineArgumentsParser" Version="3.0.16" /> -->
|
||||
<!-- <PackageReference Include="Marsonsoft.CommandLineParser" Version="1.0.34" /> -->
|
||||
<!-- <PackageReference Include="BurnSystems.CommandLine" Version="1.1.0" /> -->
|
||||
<!-- <ProjectReference Include="..\..\..\CommandLineParser\src\CommandLineArgumentsParser\CommandLineArgumentsParser.csproj" /> -->
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WireMock.Net\WireMock.Net.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
24
src/WireMock.Net/Admin/Mappings/ClientIPModel.cs
Normal file
24
src/WireMock.Net/Admin/Mappings/ClientIPModel.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace WireMock.Admin.Mappings
|
||||
{
|
||||
/// <summary>
|
||||
/// ClientIPModel
|
||||
/// </summary>
|
||||
public class ClientIPModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the matchers.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The matchers.
|
||||
/// </value>
|
||||
public MatcherModel[] Matchers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the functions.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The functions.
|
||||
/// </value>
|
||||
public string[] Funcs { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,22 @@ namespace WireMock.Admin.Mappings
|
||||
/// </value>
|
||||
public int? Priority { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Scenario.
|
||||
/// </summary>
|
||||
public string Scenario { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Execution state condition for the current mapping.
|
||||
/// </summary>
|
||||
public object WhenStateIs { get; set; }
|
||||
|
||||
/// <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>
|
||||
public object SetStateTo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the request.
|
||||
/// </summary>
|
||||
|
||||
@@ -7,11 +7,19 @@ namespace WireMock.Admin.Mappings
|
||||
/// </summary>
|
||||
public class RequestModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the ClientIP. (Can be a string or a ClientIPModel)
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The ClientIP.
|
||||
/// </value>
|
||||
public object ClientIP { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Path. (Can be a string or a PathModel)
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The URL.
|
||||
/// The Path.
|
||||
/// </value>
|
||||
public object Path { get; set; }
|
||||
|
||||
|
||||
@@ -10,79 +10,76 @@ namespace WireMock.Admin.Mappings
|
||||
/// <summary>
|
||||
/// Gets or sets the HTTP status.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The HTTP status.
|
||||
/// </value>
|
||||
public int? StatusCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body destination (SameAsSource, String or Bytes).
|
||||
/// </summary>
|
||||
public string BodyDestination { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The body.
|
||||
/// </value>
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The body.
|
||||
/// </value>
|
||||
public string BodyAsBase64 { get; set; }
|
||||
public string BodyFromBase64 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body (as JSON object).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The body.
|
||||
/// </value>
|
||||
public object BodyAsJson { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body (as bytearray).
|
||||
/// </summary>
|
||||
public byte[] BodyAsBytes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body as a file.
|
||||
/// </summary>
|
||||
public string BodyAsFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the body as file cached?
|
||||
/// </summary>
|
||||
public bool? BodyAsFileIsCached { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body encoding.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The body encoding.
|
||||
/// </value>
|
||||
public EncodingModel BodyEncoding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether [use transformer].
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if [use transformer]; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool UseTransformer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the headers.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The headers.
|
||||
/// </value>
|
||||
public IDictionary<string, string> Headers { get; set; }
|
||||
public IDictionary<string, object> Headers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Headers (Raw).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The Headers (Raw).
|
||||
/// </value>
|
||||
public string HeadersRaw { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delay in milliseconds.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The delay in milliseconds.
|
||||
/// </value>
|
||||
public int? Delay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Proxy URL.
|
||||
/// </summary>
|
||||
/// <value>ProxyUrl</value>
|
||||
public string ProxyUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The client X509Certificate2 Thumbprint or SubjectName to use.
|
||||
/// </summary>
|
||||
public string X509Certificate2ThumbprintOrSubjectName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace WireMock.Admin.Requests
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WireMock.Admin.Requests
|
||||
{
|
||||
/// <summary>
|
||||
/// LogRequestMatchModel
|
||||
@@ -36,5 +38,13 @@
|
||||
/// The match percentage.
|
||||
/// </value>
|
||||
public double AverageTotalScore { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the match details.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The match details.
|
||||
/// </value>
|
||||
public IList<object> MatchDetails { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,11 @@ namespace WireMock.Admin.Requests
|
||||
/// </summary>
|
||||
public class LogRequestModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Client IP Address.
|
||||
/// </summary>
|
||||
public string ClientIP { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the DateTime.
|
||||
/// </summary>
|
||||
@@ -18,17 +23,11 @@ namespace WireMock.Admin.Requests
|
||||
/// <summary>
|
||||
/// Gets or sets the Path.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The Path.
|
||||
/// </value>
|
||||
public string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the absolete URL.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The absolute URL.
|
||||
/// </value>
|
||||
public string AbsoluteUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -39,41 +38,26 @@ namespace WireMock.Admin.Requests
|
||||
/// <summary>
|
||||
/// Gets or sets the method.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The method.
|
||||
/// </value>
|
||||
public string Method { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Headers.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The Headers.
|
||||
/// </value>
|
||||
public IDictionary<string, string> Headers { get; set; }
|
||||
public IDictionary<string, WireMockList<string>> Headers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Cookies.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The Cookies.
|
||||
/// </value>
|
||||
public IDictionary<string, string> Cookies { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The body.
|
||||
/// </value>
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body encoding.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The body encoding.
|
||||
/// </value>
|
||||
public EncodingModel BodyEncoding { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Admin.Requests
|
||||
{
|
||||
@@ -16,13 +17,33 @@ namespace WireMock.Admin.Requests
|
||||
/// <summary>
|
||||
/// Gets the headers.
|
||||
/// </summary>
|
||||
public IDictionary<string, string> Headers { get; set; }
|
||||
public IDictionary<string, WireMockList<string>> Headers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body destination (SameAsSource, String or Bytes).
|
||||
/// </summary>
|
||||
public string BodyDestination { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body as bytes.
|
||||
/// </summary>
|
||||
public byte[] BodyAsBytes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body as file.
|
||||
/// </summary>
|
||||
public string BodyAsFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the body as file cached?
|
||||
/// </summary>
|
||||
public bool? BodyAsFileIsCached { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the original body.
|
||||
/// </summary>
|
||||
|
||||
@@ -14,5 +14,15 @@
|
||||
/// Gets or sets if partial mapping is allowed.
|
||||
/// </summary>
|
||||
public bool? AllowPartialMapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the RequestLog expiration in hours
|
||||
/// </summary>
|
||||
public int? RequestLogExpirationDuration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the MaxRequestLog count.
|
||||
/// </summary>
|
||||
public int? MaxRequestLogCount { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Admin.Requests;
|
||||
using WireMock.Admin.Settings;
|
||||
|
||||
namespace WireMock.Client
|
||||
@@ -96,5 +97,64 @@ namespace WireMock.Client
|
||||
/// </summary>
|
||||
[Post("__admin/mappings/save")]
|
||||
Task<string> SaveMappingAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Get the requests.
|
||||
/// </summary>
|
||||
/// <returns>LogRequestModels</returns>
|
||||
[Get("__admin/requests")]
|
||||
Task<IList<LogRequestModel>> GetRequestsAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Delete all requests.
|
||||
/// </summary>
|
||||
[Delete("__admin/requests")]
|
||||
Task<string> DeleteRequestsAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Delete (reset) all requests.
|
||||
/// </summary>
|
||||
[Post("__admin/requests/reset")]
|
||||
Task<string> ResetRequestsAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Get a request based on the guid
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid</param>
|
||||
/// <returns>MappingModel</returns>
|
||||
[Get("__admin/requests/{guid}")]
|
||||
Task<LogRequestModel> GetRequestAsync([Path] Guid guid);
|
||||
|
||||
/// <summary>
|
||||
/// Delete a request based on the guid
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid</param>
|
||||
[Delete("__admin/requests/{guid}")]
|
||||
Task<string> DeleteRequestAsync([Path] Guid guid);
|
||||
|
||||
/// <summary>
|
||||
/// Find a request based on the criteria
|
||||
/// </summary>
|
||||
/// <param name="model">The RequestModel</param>
|
||||
[Post("__admin/requests/find")]
|
||||
Task<IList<LogRequestModel>> FindRequestsAsync([Body] RequestModel model);
|
||||
|
||||
/// <summary>
|
||||
/// Get all scenarios
|
||||
/// </summary>
|
||||
[Get("__admin/scenarios")]
|
||||
Task<string> GetScenariosAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Delete (reset) all scenarios
|
||||
/// </summary>
|
||||
[Delete("__admin/scenarios")]
|
||||
Task<string> DeleteScenariosAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Delete (reset) all scenarios
|
||||
/// </summary>
|
||||
[Post("__admin/scenarios")]
|
||||
Task<string> ResetScenariosAsync();
|
||||
}
|
||||
}
|
||||
41
src/WireMock.Net/Http/CertificateUtil.cs
Normal file
41
src/WireMock.Net/Http/CertificateUtil.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace WireMock.Http
|
||||
{
|
||||
internal static class CertificateUtil
|
||||
{
|
||||
public static X509Certificate2 GetCertificate(string thumbprintOrSubjectName)
|
||||
{
|
||||
X509Store 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 subject name
|
||||
matchingCertificates = certStore.Certificates.Find(X509FindType.FindBySubjectName, thumbprintOrSubjectName, false);
|
||||
if (matchingCertificates.Count == 0)
|
||||
{
|
||||
// No certificates matched the search criteria.
|
||||
throw new Exception($"No certificate found with Thumbprint or SubjectName '{thumbprintOrSubjectName}'");
|
||||
}
|
||||
}
|
||||
// Use the first matching certificate.
|
||||
return matchingCertificates[0];
|
||||
}
|
||||
finally
|
||||
{
|
||||
#if NETSTANDARD || NET46
|
||||
certStore.Dispose();
|
||||
#else
|
||||
certStore.Close();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,64 +2,87 @@
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Http
|
||||
{
|
||||
internal static class HttpClientHelper
|
||||
{
|
||||
private static HttpClient CreateHttpClient(string clientX509Certificate2Filename = null)
|
||||
public static HttpClient CreateHttpClient(string clientX509Certificate2ThumbprintOrSubjectName = null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(clientX509Certificate2Filename))
|
||||
HttpClientHandler handler;
|
||||
|
||||
if (string.IsNullOrEmpty(clientX509Certificate2ThumbprintOrSubjectName))
|
||||
{
|
||||
handler = new HttpClientHandler();
|
||||
}
|
||||
else
|
||||
{
|
||||
#if NETSTANDARD || NET46
|
||||
var handler = new HttpClientHandler
|
||||
handler = new HttpClientHandler
|
||||
{
|
||||
ClientCertificateOptions = ClientCertificateOption.Manual,
|
||||
SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls,
|
||||
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
|
||||
};
|
||||
|
||||
handler.ClientCertificates.Add(new System.Security.Cryptography.X509Certificates.X509Certificate2(clientX509Certificate2Filename));
|
||||
return new HttpClient(handler);
|
||||
var x509Certificate2 = CertificateUtil.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName);
|
||||
handler.ClientCertificates.Add(x509Certificate2);
|
||||
#else
|
||||
var handler = new WebRequestHandler
|
||||
|
||||
var webRequestHandler = new WebRequestHandler
|
||||
{
|
||||
ClientCertificateOptions = ClientCertificateOption.Manual,
|
||||
ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true
|
||||
ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true,
|
||||
AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate
|
||||
};
|
||||
|
||||
handler.ClientCertificates.Add(new System.Security.Cryptography.X509Certificates.X509Certificate2(clientX509Certificate2Filename));
|
||||
return new HttpClient(handler);
|
||||
var x509Certificate2 = CertificateUtil.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName);
|
||||
webRequestHandler.ClientCertificates.Add(x509Certificate2);
|
||||
handler = webRequestHandler;
|
||||
#endif
|
||||
}
|
||||
|
||||
return new HttpClient();
|
||||
// For proxy we shouldn't follow auto redirects
|
||||
handler.AllowAutoRedirect = false;
|
||||
|
||||
// If UseCookies enabled, httpClient ignores Cookie header
|
||||
handler.UseCookies = false;
|
||||
|
||||
return new HttpClient(handler);
|
||||
}
|
||||
|
||||
public static async Task<ResponseMessage> SendAsync(RequestMessage requestMessage, string url, string clientX509Certificate2Filename = null)
|
||||
public static async Task<ResponseMessage> SendAsync(HttpClient client, RequestMessage requestMessage, string url)
|
||||
{
|
||||
var client = CreateHttpClient(clientX509Certificate2Filename);
|
||||
Check.NotNull(client, nameof(client));
|
||||
|
||||
var originalUri = new Uri(requestMessage.Url);
|
||||
var requiredUri = new Uri(url);
|
||||
|
||||
var httpRequestMessage = new HttpRequestMessage(new HttpMethod(requestMessage.Method), url);
|
||||
|
||||
// Overwrite the host header
|
||||
httpRequestMessage.Headers.Host = new Uri(url).Authority;
|
||||
|
||||
// Set headers if present
|
||||
if (requestMessage.Headers != null)
|
||||
{
|
||||
foreach (var headerName in requestMessage.Headers.Keys.Where(k => k.ToUpper() != "HOST"))
|
||||
{
|
||||
httpRequestMessage.Headers.Add(headerName, new[] { requestMessage.Headers[headerName] });
|
||||
}
|
||||
}
|
||||
|
||||
// Set Body if present
|
||||
if (requestMessage.BodyAsBytes != null && requestMessage.BodyAsBytes.Length > 0)
|
||||
{
|
||||
httpRequestMessage.Content = new ByteArrayContent(requestMessage.BodyAsBytes);
|
||||
}
|
||||
|
||||
// Overwrite the host header
|
||||
httpRequestMessage.Headers.Host = requiredUri.Authority;
|
||||
|
||||
// Set headers if present
|
||||
if (requestMessage.Headers != null)
|
||||
{
|
||||
foreach (var header in requestMessage.Headers.Where(header => !string.Equals(header.Key, "HOST", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
// Try to add to request headers. If failed - try to add to content headers
|
||||
if (!httpRequestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value))
|
||||
{
|
||||
httpRequestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call the URL
|
||||
var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead);
|
||||
|
||||
@@ -67,12 +90,29 @@ namespace WireMock.Http
|
||||
var responseMessage = new ResponseMessage
|
||||
{
|
||||
StatusCode = (int)httpResponseMessage.StatusCode,
|
||||
|
||||
BodyAsBytes = await httpResponseMessage.Content.ReadAsByteArrayAsync(),
|
||||
Body = await httpResponseMessage.Content.ReadAsStringAsync()
|
||||
};
|
||||
|
||||
foreach (var header in httpResponseMessage.Headers)
|
||||
// Set both content and response headers, replacing URLs in values
|
||||
var headers = httpResponseMessage.Content?.Headers.Union(httpResponseMessage.Headers);
|
||||
|
||||
foreach (var header in headers)
|
||||
{
|
||||
responseMessage.AddHeader(header.Key, header.Value.FirstOrDefault());
|
||||
// if Location header contains absolute redirect URL, and base URL is one that we proxy to,
|
||||
// we need to replace it to original one.
|
||||
if (string.Equals(header.Key, "Location", StringComparison.OrdinalIgnoreCase)
|
||||
&& Uri.TryCreate(header.Value.First(), UriKind.Absolute, out Uri absoluteLocationUri)
|
||||
&& string.Equals(absoluteLocationUri.Host, requiredUri.Host, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var replacedLocationUri = new Uri(originalUri, absoluteLocationUri.PathAndQuery);
|
||||
responseMessage.AddHeader(header.Key, replacedLocationUri.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
responseMessage.AddHeader(header.Key, header.Value.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
return responseMessage;
|
||||
|
||||
96
src/WireMock.Net/Http/HttpKnownHeaderNames.cs
Normal file
96
src/WireMock.Net/Http/HttpKnownHeaderNames.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
// 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.
|
||||
|
||||
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
|
||||
{
|
||||
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";
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace WireMock.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// The ports.
|
||||
/// Utility class
|
||||
/// </summary>
|
||||
public static class PortUtil
|
||||
{
|
||||
private static readonly Regex UrlDetailsRegex = new Regex(@"^(?<proto>\w+)://[^/]+?(?<port>\d+)?/", RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// The find free TCP port.
|
||||
/// </summary>
|
||||
@@ -30,5 +33,24 @@ namespace WireMock.Http
|
||||
tcpListener?.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract a proto and port from a URL.
|
||||
/// </summary>
|
||||
public static bool TryExtractProtocolAndPort(string url, out string proto, out int port)
|
||||
{
|
||||
proto = null;
|
||||
port = -1;
|
||||
|
||||
Match m = UrlDetailsRegex.Match(url);
|
||||
if (m.Success)
|
||||
{
|
||||
proto = m.Groups["proto"].Value;
|
||||
|
||||
return int.TryParse(m.Groups["port"].Value, out port);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,96 +1,132 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Matchers.Request;
|
||||
|
||||
namespace WireMock
|
||||
{
|
||||
/// <summary>
|
||||
/// The Mapping.
|
||||
/// </summary>
|
||||
public class Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique identifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The unique identifier.
|
||||
/// </value>
|
||||
public Guid Guid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique title.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The unique title.
|
||||
/// </value>
|
||||
public string Title { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the priority.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The priority.
|
||||
/// </value>
|
||||
public int Priority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Request matcher.
|
||||
/// </summary>
|
||||
public IRequestMatcher RequestMatcher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Provider.
|
||||
/// </summary>
|
||||
public IResponseProvider Provider { 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="requestMatcher">The request matcher.</param>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="priority">The priority for this mapping.</param>
|
||||
public Mapping(Guid guid, [CanBeNull] string title, IRequestMatcher requestMatcher, IResponseProvider provider, int priority)
|
||||
{
|
||||
Priority = priority;
|
||||
Guid = guid;
|
||||
Title = title;
|
||||
RequestMatcher = requestMatcher;
|
||||
Provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The response to.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The request message.</param>
|
||||
/// <returns>The <see cref="ResponseMessage"/>.</returns>
|
||||
public async Task<ResponseMessage> ResponseToAsync(RequestMessage requestMessage)
|
||||
{
|
||||
return await Provider.ProvideResponseAsync(requestMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the RequestMessage is handled.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The request message.</param>
|
||||
/// <returns>The <see cref="RequestMatchResult"/>.</returns>
|
||||
public RequestMatchResult IsRequestHandled(RequestMessage requestMessage)
|
||||
{
|
||||
var result = new RequestMatchResult();
|
||||
|
||||
RequestMatcher.GetMatchingScore(requestMessage, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this mapping is an Admin Interface.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this mapping is an Admin Interface; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsAdminInterface => Provider is DynamicResponseProvider || Provider is DynamicAsyncResponseProvider || Provider is ProxyAsyncResponseProvider;
|
||||
}
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Matchers.Request;
|
||||
|
||||
namespace WireMock
|
||||
{
|
||||
/// <summary>
|
||||
/// The Mapping.
|
||||
/// </summary>
|
||||
public class Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique identifier.
|
||||
/// </summary>
|
||||
public Guid Guid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique title.
|
||||
/// </summary>
|
||||
public string Title { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the priority.
|
||||
/// </summary>
|
||||
public int Priority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Scenario.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
public string Scenario { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Execution state condition for the current mapping.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
public object ExecutionConditionState { get; }
|
||||
|
||||
/// <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]
|
||||
public object NextState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Request matcher.
|
||||
/// </summary>
|
||||
public IRequestMatcher RequestMatcher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Provider.
|
||||
/// </summary>
|
||||
public IResponseProvider Provider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is State started ?
|
||||
/// </summary>
|
||||
public bool IsStartState => Scenario == null || Scenario != null && NextState != null && ExecutionConditionState == null;
|
||||
|
||||
/// <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="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>
|
||||
public Mapping(Guid guid, [CanBeNull] string title, IRequestMatcher requestMatcher, IResponseProvider provider, int priority, [CanBeNull] string scenario, [CanBeNull] object executionConditionState, [CanBeNull] object nextState)
|
||||
{
|
||||
Guid = guid;
|
||||
Title = title;
|
||||
RequestMatcher = requestMatcher;
|
||||
Provider = provider;
|
||||
Priority = priority;
|
||||
Scenario = scenario;
|
||||
ExecutionConditionState = executionConditionState;
|
||||
NextState = nextState;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The response to.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The request message.</param>
|
||||
/// <returns>The <see cref="ResponseMessage"/>.</returns>
|
||||
public async Task<ResponseMessage> ResponseToAsync(RequestMessage requestMessage)
|
||||
{
|
||||
return await Provider.ProvideResponseAsync(requestMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RequestMatchResult based on the RequestMessage.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The request message.</param>
|
||||
/// <param name="nextState">The Next State.</param>
|
||||
/// <returns>The <see cref="RequestMatchResult"/>.</returns>
|
||||
public RequestMatchResult GetRequestMatchResult(RequestMessage requestMessage, [CanBeNull] object 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this mapping is an Admin Interface.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this mapping is an Admin Interface; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsAdminInterface => Provider is DynamicResponseProvider || Provider is DynamicAsyncResponseProvider || Provider is ProxyAsyncResponseProvider;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,11 @@ namespace WireMock.Matchers
|
||||
/// </summary>
|
||||
public const double Perfect = 1.0;
|
||||
|
||||
/// <summary>
|
||||
/// The almost perfect match score
|
||||
/// </summary>
|
||||
public const double AlmostPerfect = 0.99;
|
||||
|
||||
/// <summary>
|
||||
/// Convert a bool to the score.
|
||||
/// </summary>
|
||||
@@ -37,22 +42,20 @@ namespace WireMock.Matchers
|
||||
/// Calculates the score from multiple funcs.
|
||||
/// </summary>
|
||||
/// <param name="values">The values.</param>
|
||||
/// <returns>score</returns>
|
||||
/// <returns>average score</returns>
|
||||
public static double ToScore(IEnumerable<bool> values)
|
||||
{
|
||||
var list = values.Select(ToScore).ToList();
|
||||
return list.Sum() / list.Count;
|
||||
return values.Any() ? values.Select(ToScore).Average() : Mismatch;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the score from multiple funcs.
|
||||
/// </summary>
|
||||
/// <param name="values">The values.</param>
|
||||
/// <returns>score</returns>
|
||||
/// <returns>average score</returns>
|
||||
public static double ToScore(IEnumerable<double> values)
|
||||
{
|
||||
var list = values.ToList();
|
||||
return list.Sum() / list.Count;
|
||||
return values.Any() ? values.Average() : Mismatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WireMock.Matchers.Request
|
||||
{
|
||||
@@ -13,7 +14,7 @@ namespace WireMock.Matchers.Request
|
||||
/// <value>
|
||||
/// The match-score.
|
||||
/// </value>
|
||||
public double TotalScore { get; set; }
|
||||
public double TotalScore { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the total number of matches.
|
||||
@@ -21,7 +22,7 @@ namespace WireMock.Matchers.Request
|
||||
/// <value>
|
||||
/// The total number of matches.
|
||||
/// </value>
|
||||
public int TotalNumber { get; set; }
|
||||
public int TotalNumber { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is perfect match.
|
||||
@@ -39,6 +40,31 @@ namespace WireMock.Matchers.Request
|
||||
/// </value>
|
||||
public double AverageTotalScore => TotalNumber == 0 ? 0.0 : TotalScore / TotalNumber;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the match details.
|
||||
/// </summary>
|
||||
public IList<KeyValuePair<Type, double>> MatchDetails { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMatchResult"/> class.
|
||||
/// </summary>
|
||||
public RequestMatchResult() => MatchDetails = new List<KeyValuePair<Type, double>>();
|
||||
|
||||
/// <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)
|
||||
{
|
||||
TotalScore += score;
|
||||
TotalNumber++;
|
||||
MatchDetails.Add(new KeyValuePair<Type, double>(matcherType, 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>
|
||||
@@ -46,7 +72,6 @@ namespace WireMock.Matchers.Request
|
||||
/// <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>
|
||||
/// <exception cref="System.NotImplementedException"></exception>
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
var compareObj = (RequestMatchResult)obj;
|
||||
|
||||
@@ -98,11 +98,7 @@ namespace WireMock.Matchers.Request
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
double score = IsMatch(requestMessage);
|
||||
requestMatchResult.TotalScore += score;
|
||||
|
||||
requestMatchResult.TotalNumber++;
|
||||
|
||||
return score;
|
||||
return requestMatchResult.AddScore(GetType(), score);
|
||||
}
|
||||
|
||||
private double IsMatch(RequestMessage requestMessage)
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Matchers.Request
|
||||
{
|
||||
/// <summary>
|
||||
/// The request ClientIP matcher.
|
||||
/// </summary>
|
||||
public class RequestMessageClientIPMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The matchers.
|
||||
/// </summary>
|
||||
public IReadOnlyList<IMatcher> Matchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The ClientIP functions.
|
||||
/// </summary>
|
||||
public Func<string, bool>[] Funcs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageClientIPMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="clientIPs">The clientIPs.</param>
|
||||
public RequestMessageClientIPMatcher([NotNull] params string[] clientIPs) : this(clientIPs.Select(ip => new WildcardMatcher(ip)).Cast<IMatcher>().ToArray())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageClientIPMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
public RequestMessageClientIPMatcher([NotNull] params IMatcher[] matchers)
|
||||
{
|
||||
Check.NotNull(matchers, nameof(matchers));
|
||||
Matchers = matchers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageClientIPMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The clientIP functions.</param>
|
||||
public RequestMessageClientIPMatcher([NotNull] params Func<string, bool>[] funcs)
|
||||
{
|
||||
Check.NotNull(funcs, nameof(funcs));
|
||||
Funcs = funcs;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IRequestMatcher.GetMatchingScore"/>
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
double score = IsMatch(requestMessage);
|
||||
return requestMatchResult.AddScore(GetType(), score);
|
||||
}
|
||||
|
||||
private double IsMatch(RequestMessage requestMessage)
|
||||
{
|
||||
if (Matchers != null)
|
||||
return Matchers.Max(matcher => matcher.IsMatch(requestMessage.ClientIP));
|
||||
|
||||
if (Funcs != null)
|
||||
return MatchScores.ToScore(requestMessage.ClientIP != null && Funcs.Any(func => func(requestMessage.ClientIP)));
|
||||
|
||||
return MatchScores.Mismatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,11 @@ namespace WireMock.Matchers.Request
|
||||
/// </returns>
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
if (!RequestMatchers.Any())
|
||||
{
|
||||
return MatchScores.Mismatch;
|
||||
}
|
||||
|
||||
if (_type == CompositeMatcherType.And)
|
||||
{
|
||||
return RequestMatchers.Average(requestMatcher => requestMatcher.GetMatchingScore(requestMessage, requestMatchResult));
|
||||
|
||||
@@ -77,11 +77,7 @@ namespace WireMock.Matchers.Request
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
double score = IsMatch(requestMessage);
|
||||
requestMatchResult.TotalScore += score;
|
||||
|
||||
requestMatchResult.TotalNumber++;
|
||||
|
||||
return score;
|
||||
return requestMatchResult.AddScore(GetType(), score);
|
||||
}
|
||||
|
||||
private double IsMatch(RequestMessage requestMessage)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Util;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Matchers.Request
|
||||
@@ -9,12 +10,13 @@ namespace WireMock.Matchers.Request
|
||||
/// <summary>
|
||||
/// The request header matcher.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IRequestMatcher"/>
|
||||
public class RequestMessageHeaderMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The functions
|
||||
/// </summary>
|
||||
public Func<IDictionary<string, string>, bool>[] Funcs { get; }
|
||||
public Func<IDictionary<string, string[]>, bool>[] Funcs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name
|
||||
@@ -31,7 +33,7 @@ namespace WireMock.Matchers.Request
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">The ignoreCase.</param>
|
||||
/// <param name="ignoreCase">if set to <c>true</c> [ignore case].</param>
|
||||
public RequestMessageHeaderMatcher([NotNull] string name, [NotNull] string pattern, bool ignoreCase = true)
|
||||
{
|
||||
Check.NotNull(name, nameof(name));
|
||||
@@ -41,6 +43,21 @@ namespace WireMock.Matchers.Request
|
||||
Matchers = new IMatcher[] { new WildcardMatcher(pattern, ignoreCase) };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">if set to <c>true</c> [ignore case].</param>
|
||||
public RequestMessageHeaderMatcher([NotNull] string name, [NotNull] string[] patterns, bool ignoreCase = true)
|
||||
{
|
||||
Check.NotNull(name, nameof(name));
|
||||
Check.NotNull(patterns, nameof(patterns));
|
||||
|
||||
Name = name;
|
||||
Matchers = patterns.Select(pattern => new WildcardMatcher(pattern, ignoreCase)).Cast<IMatcher>().ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> class.
|
||||
/// </summary>
|
||||
@@ -59,29 +76,18 @@ namespace WireMock.Matchers.Request
|
||||
/// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The funcs.</param>
|
||||
public RequestMessageHeaderMatcher([NotNull] params Func<IDictionary<string, string>, bool>[] funcs)
|
||||
public RequestMessageHeaderMatcher([NotNull] params Func<IDictionary<string, string[]>, bool>[] funcs)
|
||||
{
|
||||
Check.NotNull(funcs, nameof(funcs));
|
||||
|
||||
Funcs = funcs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified RequestMessage is match.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The RequestMessage.</param>
|
||||
/// <param name="requestMatchResult">The RequestMatchResult.</param>
|
||||
/// <returns>
|
||||
/// A value between 0.0 - 1.0 of the similarity.
|
||||
/// </returns>
|
||||
/// <inheritdoc cref="IRequestMatcher.GetMatchingScore"/>
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
double score = IsMatch(requestMessage);
|
||||
requestMatchResult.TotalScore += score;
|
||||
|
||||
requestMatchResult.TotalNumber++;
|
||||
|
||||
return score;
|
||||
return requestMatchResult.AddScore(GetType(), score);
|
||||
}
|
||||
|
||||
private double IsMatch(RequestMessage requestMessage)
|
||||
@@ -90,7 +96,7 @@ namespace WireMock.Matchers.Request
|
||||
return MatchScores.Mismatch;
|
||||
|
||||
if (Funcs != null)
|
||||
return MatchScores.ToScore(Funcs.Any(f => f(requestMessage.Headers)));
|
||||
return MatchScores.ToScore(Funcs.Any(f => f(requestMessage.Headers.ToDictionary(entry => entry.Key, entry => entry.Value.ToArray()))));
|
||||
|
||||
if (Matchers == null)
|
||||
return MatchScores.Mismatch;
|
||||
@@ -98,8 +104,8 @@ namespace WireMock.Matchers.Request
|
||||
if (!requestMessage.Headers.ContainsKey(Name))
|
||||
return MatchScores.Mismatch;
|
||||
|
||||
string value = requestMessage.Headers[Name];
|
||||
return Matchers.Max(m => m.IsMatch(value));
|
||||
WireMockList<string> list = requestMessage.Headers[Name];
|
||||
return Matchers.Max(m => list.Max(value => m.IsMatch(value))); // TODO : is this correct ?
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,11 +37,7 @@ namespace WireMock.Matchers.Request
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
double score = IsMatch(requestMessage);
|
||||
requestMatchResult.TotalScore += score;
|
||||
|
||||
requestMatchResult.TotalNumber++;
|
||||
|
||||
return score;
|
||||
return requestMatchResult.AddScore(GetType(), score);
|
||||
}
|
||||
|
||||
private double IsMatch(RequestMessage requestMessage)
|
||||
|
||||
@@ -65,11 +65,7 @@ namespace WireMock.Matchers.Request
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
double score = IsMatch(requestMessage);
|
||||
requestMatchResult.TotalScore += score;
|
||||
|
||||
requestMatchResult.TotalNumber++;
|
||||
|
||||
return score;
|
||||
return requestMatchResult.AddScore(GetType(), score);
|
||||
}
|
||||
|
||||
private double IsMatch(RequestMessage requestMessage)
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace WireMock.Matchers.Request
|
||||
/// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="paths">The paths.</param>
|
||||
public RequestMessagePathMatcher([NotNull] params string[] paths) : this(paths.Select(path => new WildcardMatcher(path)).ToArray())
|
||||
public RequestMessagePathMatcher([NotNull] params string[] paths) : this(paths.Select(path => new WildcardMatcher(path)).Cast<IMatcher>().ToArray())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -49,22 +49,11 @@ namespace WireMock.Matchers.Request
|
||||
Funcs = funcs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified RequestMessage is match.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The RequestMessage.</param>
|
||||
/// <param name="requestMatchResult">The RequestMatchResult.</param>
|
||||
/// <returns>
|
||||
/// A value between 0.0 - 1.0 of the similarity.
|
||||
/// </returns>
|
||||
/// <inheritdoc cref="IRequestMatcher.GetMatchingScore"/>
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
double score = IsMatch(requestMessage);
|
||||
requestMatchResult.TotalScore += score;
|
||||
|
||||
requestMatchResult.TotalNumber++;
|
||||
|
||||
return score;
|
||||
return requestMatchResult.AddScore(GetType(), score);
|
||||
}
|
||||
|
||||
private double IsMatch(RequestMessage requestMessage)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace WireMock.Matchers.Request
|
||||
{
|
||||
/// <summary>
|
||||
/// The scenario and state matcher.
|
||||
/// </summary>
|
||||
internal class RequestMessageScenarioAndStateMatcher : IRequestMatcher
|
||||
{
|
||||
///// <summary>
|
||||
///// Scenario.
|
||||
///// </summary>
|
||||
//[CanBeNull] private string _scenario;
|
||||
|
||||
/// <summary>
|
||||
/// Execution state condition for the current mapping.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
private readonly object _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 object _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([CanBeNull] object nextState, [CanBeNull] object executionConditionState)
|
||||
{
|
||||
_nextState = nextState;
|
||||
_executionConditionState = executionConditionState;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
double score = IsMatch();
|
||||
return requestMatchResult.AddScore(GetType(), score);
|
||||
}
|
||||
|
||||
private double IsMatch()
|
||||
{
|
||||
return Equals(_executionConditionState, _nextState) ? MatchScores.Perfect : MatchScores.Mismatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,12 @@ namespace WireMock.Matchers.Request
|
||||
public class RequestMessageUrlMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The matcher.
|
||||
/// The matchers.
|
||||
/// </summary>
|
||||
public IReadOnlyList<IMatcher> Matchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The url functions
|
||||
/// The url functions.
|
||||
/// </summary>
|
||||
public Func<string, bool>[] Funcs { get; }
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace WireMock.Matchers.Request
|
||||
/// Initializes a new instance of the <see cref="RequestMessageUrlMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="urls">The urls.</param>
|
||||
public RequestMessageUrlMatcher([NotNull] params string[] urls) : this(urls.Select(url => new WildcardMatcher(url)).ToArray())
|
||||
public RequestMessageUrlMatcher([NotNull] params string[] urls) : this(urls.Select(url => new WildcardMatcher(url)).Cast<IMatcher>().ToArray())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -49,22 +49,11 @@ namespace WireMock.Matchers.Request
|
||||
Funcs = funcs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified RequestMessage is match.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The RequestMessage.</param>
|
||||
/// <param name="requestMatchResult">The RequestMatchResult.</param>
|
||||
/// <returns>
|
||||
/// A value between 0.0 - 1.0 of the similarity.
|
||||
/// </returns>
|
||||
/// <inheritdoc cref="IRequestMatcher.GetMatchingScore"/>
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
double score = IsMatch(requestMessage);
|
||||
requestMatchResult.TotalScore += score;
|
||||
|
||||
requestMatchResult.TotalNumber++;
|
||||
|
||||
return score;
|
||||
return requestMatchResult.AddScore(GetType(), score);
|
||||
}
|
||||
|
||||
private double IsMatch(RequestMessage requestMessage)
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Linq;
|
||||
using System.Xml;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Validation;
|
||||
#if !NETSTANDARD
|
||||
#if !NETSTANDARD1_3
|
||||
using Wmhelp.XPath2;
|
||||
#endif
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace WireMock.Matchers
|
||||
try
|
||||
{
|
||||
var nav = new XmlDocument { InnerXml = input }.CreateNavigator();
|
||||
#if NETSTANDARD
|
||||
#if NETSTANDARD1_3
|
||||
return MatchScores.ToScore(_patterns.Select(p => true.Equals(nav.Evaluate($"boolean({p})"))));
|
||||
#else
|
||||
return MatchScores.ToScore(_patterns.Select(p => true.Equals(nav.XPath2Evaluate($"boolean({p})"))));
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
#if NETSTANDARD
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using WireMock.Http;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Owin
|
||||
@@ -18,9 +16,11 @@ namespace WireMock.Owin
|
||||
private readonly WireMockMiddlewareOptions _options;
|
||||
private readonly string[] _uriPrefixes;
|
||||
|
||||
private IWebHost _host;
|
||||
|
||||
public bool IsStarted { get; private set; }
|
||||
|
||||
public List<Uri> Urls { get; } = new List<Uri>();
|
||||
public List<string> Urls { get; } = new List<string>();
|
||||
|
||||
public List<int> Ports { get; } = new List<int>();
|
||||
|
||||
@@ -31,9 +31,10 @@ namespace WireMock.Owin
|
||||
|
||||
foreach (string uriPrefix in uriPrefixes)
|
||||
{
|
||||
var uri = new Uri(uriPrefix);
|
||||
Urls.Add(uri);
|
||||
Ports.Add(uri.Port);
|
||||
Urls.Add(uriPrefix);
|
||||
|
||||
PortUtil.TryExtractProtocolAndPort(uriPrefix, out string host, out int port);
|
||||
Ports.Add(port);
|
||||
}
|
||||
|
||||
_options = options;
|
||||
@@ -42,23 +43,30 @@ namespace WireMock.Owin
|
||||
|
||||
public Task StartAsync()
|
||||
{
|
||||
_host = new WebHostBuilder()
|
||||
.Configure(appBuilder =>
|
||||
{
|
||||
appBuilder.UseMiddleware<GlobalExceptionMiddleware>();
|
||||
_options.PreWireMockMiddlewareInit?.Invoke(appBuilder);
|
||||
appBuilder.UseMiddleware<WireMockMiddleware>(_options);
|
||||
_options.PostWireMockMiddlewareInit?.Invoke(appBuilder);
|
||||
})
|
||||
.UseKestrel()
|
||||
.UseUrls(_uriPrefixes)
|
||||
.Build();
|
||||
|
||||
#if NETSTANDARD1_3
|
||||
System.Console.WriteLine("WireMock.Net server using netstandard1.3");
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var host = new WebHostBuilder()
|
||||
.ConfigureLogging(factory => factory.AddConsole(LogLevel.None))
|
||||
.Configure(appBuilder =>
|
||||
{
|
||||
// appBuilder.UseExceptionHandler(builder => )
|
||||
appBuilder.UseMiddleware<WireMockMiddleware>(_options);
|
||||
})
|
||||
.UseKestrel()
|
||||
.UseUrls(_uriPrefixes)
|
||||
.Build();
|
||||
|
||||
host.Run(_cts.Token);
|
||||
|
||||
_host.Run(_cts.Token);
|
||||
IsStarted = true;
|
||||
}, _cts.Token);
|
||||
#else
|
||||
System.Console.WriteLine("WireMock.Net server using netstandard2.0");
|
||||
IsStarted = true;
|
||||
return _host.RunAsync(_cts.Token);
|
||||
#endif
|
||||
}
|
||||
|
||||
public Task StopAsync()
|
||||
@@ -66,34 +74,11 @@ namespace WireMock.Owin
|
||||
_cts.Cancel();
|
||||
|
||||
IsStarted = false;
|
||||
|
||||
#if NETSTANDARD1_3
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
internal class Startup
|
||||
{
|
||||
public Startup(IHostingEnvironment env)
|
||||
{
|
||||
//var builder = new ConfigurationBuilder()
|
||||
// .SetBasePath(env.ContentRootPath)
|
||||
// .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
|
||||
// .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
|
||||
// .AddEnvironmentVariables();
|
||||
//Configuration = builder.Build();
|
||||
}
|
||||
|
||||
// public IConfigurationRoot Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
||||
{
|
||||
app.UseMiddleware<WireMockMiddleware>();
|
||||
#else
|
||||
return _host.WaitForShutdownAsync();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
49
src/WireMock.Net/Owin/GlobalExceptionMiddleware.cs
Normal file
49
src/WireMock.Net/Owin/GlobalExceptionMiddleware.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
#if !NETSTANDARD
|
||||
using Microsoft.Owin;
|
||||
#else
|
||||
using Microsoft.AspNetCore.Http;
|
||||
#endif
|
||||
|
||||
namespace WireMock.Owin
|
||||
{
|
||||
#if !NETSTANDARD
|
||||
internal class GlobalExceptionMiddleware : OwinMiddleware
|
||||
#else
|
||||
internal class GlobalExceptionMiddleware
|
||||
#endif
|
||||
{
|
||||
#if !NETSTANDARD
|
||||
public GlobalExceptionMiddleware(OwinMiddleware next) : base(next) { }
|
||||
#else
|
||||
public GlobalExceptionMiddleware(RequestDelegate next)
|
||||
{
|
||||
Next = next;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NETSTANDARD
|
||||
public RequestDelegate Next { get; private set; }
|
||||
#endif
|
||||
|
||||
private readonly OwinResponseMapper _responseMapper = new OwinResponseMapper();
|
||||
|
||||
#if !NETSTANDARD
|
||||
public override async Task Invoke(IOwinContext ctx)
|
||||
#else
|
||||
public async Task Invoke(HttpContext ctx)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
await Next?.Invoke(ctx);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await _responseMapper.MapAsync(new ResponseMessage { StatusCode = 500, Body = JsonConvert.SerializeObject(ex) }, ctx.Response);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WireMock.Owin
|
||||
@@ -15,12 +14,12 @@ namespace WireMock.Owin
|
||||
bool IsStarted { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the url.
|
||||
/// Gets the urls.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The urls.
|
||||
/// </value>
|
||||
List<Uri> Urls { get; }
|
||||
List<string> Urls { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ports.
|
||||
|
||||
@@ -1,69 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
#if !NETSTANDARD
|
||||
using Microsoft.Owin;
|
||||
#else
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
#endif
|
||||
|
||||
namespace WireMock.Owin
|
||||
{
|
||||
/// <summary>
|
||||
/// OwinRequestMapper
|
||||
/// </summary>
|
||||
public class OwinRequestMapper
|
||||
{
|
||||
/// <summary>
|
||||
/// MapAsync IOwinRequest to RequestMessage
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<RequestMessage> MapAsync(
|
||||
#if !NETSTANDARD
|
||||
IOwinRequest request
|
||||
#else
|
||||
HttpRequest request
|
||||
#endif
|
||||
)
|
||||
{
|
||||
#if !NETSTANDARD
|
||||
Uri url = request.Uri;
|
||||
#else
|
||||
Uri url = new Uri(request.GetEncodedUrl());
|
||||
#endif
|
||||
string verb = request.Method;
|
||||
|
||||
string bodyAsString = null;
|
||||
byte[] body = null;
|
||||
Encoding bodyEncoding = null;
|
||||
if (request.Body != null)
|
||||
{
|
||||
using (var streamReader = new StreamReader(request.Body))
|
||||
{
|
||||
bodyAsString = await streamReader.ReadToEndAsync();
|
||||
bodyEncoding = streamReader.CurrentEncoding;
|
||||
}
|
||||
|
||||
body = bodyEncoding.GetBytes(bodyAsString);
|
||||
}
|
||||
|
||||
var listenerHeaders = request.Headers;
|
||||
|
||||
var headers = new Dictionary<string, string>();
|
||||
foreach (var header in listenerHeaders)
|
||||
headers.Add(header.Key, header.Value.FirstOrDefault());
|
||||
|
||||
var cookies = new Dictionary<string, string>();
|
||||
|
||||
foreach (var cookie in request.Cookies)
|
||||
cookies.Add(cookie.Key, cookie.Value);
|
||||
|
||||
return new RequestMessage(url, verb, body, bodyAsString, bodyEncoding, headers, cookies) { DateTime = DateTime.Now };
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
#if !NETSTANDARD
|
||||
using Microsoft.Owin;
|
||||
#else
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
#endif
|
||||
|
||||
namespace WireMock.Owin
|
||||
{
|
||||
/// <summary>
|
||||
/// OwinRequestMapper
|
||||
/// </summary>
|
||||
public class OwinRequestMapper
|
||||
{
|
||||
/// <summary>
|
||||
/// MapAsync IOwinRequest to RequestMessage
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<RequestMessage> MapAsync(
|
||||
#if !NETSTANDARD
|
||||
IOwinRequest request
|
||||
#else
|
||||
HttpRequest request
|
||||
#endif
|
||||
)
|
||||
{
|
||||
#if !NETSTANDARD
|
||||
Uri url = request.Uri;
|
||||
string clientIP = request.RemoteIpAddress;
|
||||
#else
|
||||
Uri url = new Uri(request.GetEncodedUrl());
|
||||
var connection = request.HttpContext.Connection;
|
||||
string clientIP = connection.RemoteIpAddress.IsIPv4MappedToIPv6
|
||||
? connection.RemoteIpAddress.MapToIPv4().ToString()
|
||||
: connection.RemoteIpAddress.ToString();
|
||||
#endif
|
||||
string method = request.Method;
|
||||
|
||||
string bodyAsString = null;
|
||||
byte[] body = null;
|
||||
Encoding bodyEncoding = null;
|
||||
if (ParseBody(method) && request.Body != null)
|
||||
{
|
||||
using (var streamReader = new StreamReader(request.Body))
|
||||
{
|
||||
bodyAsString = await streamReader.ReadToEndAsync();
|
||||
bodyEncoding = streamReader.CurrentEncoding;
|
||||
}
|
||||
|
||||
body = bodyEncoding.GetBytes(bodyAsString);
|
||||
}
|
||||
|
||||
Dictionary<string, string[]> headers = null;
|
||||
if (request.Headers.Any())
|
||||
{
|
||||
headers = new Dictionary<string, string[]>();
|
||||
foreach (var header in request.Headers)
|
||||
{
|
||||
headers.Add(header.Key, header.Value);
|
||||
}
|
||||
}
|
||||
|
||||
IDictionary<string, string> cookies = null;
|
||||
if (request.Cookies.Any())
|
||||
{
|
||||
cookies = new Dictionary<string, string>();
|
||||
foreach (var cookie in request.Cookies)
|
||||
{
|
||||
cookies.Add(cookie.Key, cookie.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return new RequestMessage(url, method, clientIP, body, bodyAsString, bodyEncoding, headers, cookies) { DateTime = DateTime.Now };
|
||||
}
|
||||
|
||||
private bool ParseBody(string method)
|
||||
{
|
||||
/*
|
||||
HEAD - No defined body semantics.
|
||||
GET - No defined body semantics.
|
||||
PUT - Body supported.
|
||||
POST - Body supported.
|
||||
DELETE - No defined body semantics.
|
||||
TRACE - Body not supported.
|
||||
OPTIONS - Body supported but no semantics on usage (maybe in the future).
|
||||
CONNECT - No defined body semantics
|
||||
*/
|
||||
return new[] { "PUT", "POST", "OPTIONS" }.Contains(method.ToUpper());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Http;
|
||||
#if !NETSTANDARD
|
||||
using Microsoft.Owin;
|
||||
#else
|
||||
@@ -32,15 +33,43 @@ namespace WireMock.Owin
|
||||
{
|
||||
response.StatusCode = responseMessage.StatusCode;
|
||||
|
||||
responseMessage.Headers.ToList().ForEach(pair => response.Headers.Append(pair.Key, pair.Value));
|
||||
if (responseMessage.Headers.ContainsKey(HttpKnownHeaderNames.ContentType))
|
||||
{
|
||||
response.ContentType = responseMessage.Headers[HttpKnownHeaderNames.ContentType].FirstOrDefault();
|
||||
}
|
||||
|
||||
if (responseMessage.Body == null)
|
||||
var headers = responseMessage.Headers.Where(h => h.Key != HttpKnownHeaderNames.ContentType).ToList();
|
||||
|
||||
#if !NETSTANDARD
|
||||
headers.ForEach(pair => response.Headers.AppendValues(pair.Key, pair.Value.ToArray()));
|
||||
#else
|
||||
headers.ForEach(pair => response.Headers.Append(pair.Key, pair.Value.ToArray()));
|
||||
#endif
|
||||
|
||||
if (responseMessage.Body == null && responseMessage.BodyAsBytes == null && responseMessage.BodyAsFile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (responseMessage.BodyAsBytes != null)
|
||||
{
|
||||
await response.Body.WriteAsync(responseMessage.BodyAsBytes, 0, responseMessage.BodyAsBytes.Length);
|
||||
return;
|
||||
}
|
||||
|
||||
if (responseMessage.BodyAsFile != null)
|
||||
{
|
||||
byte[] bytes = File.ReadAllBytes(responseMessage.BodyAsFile);
|
||||
|
||||
await response.Body.WriteAsync(bytes, 0, bytes.Length);
|
||||
return;
|
||||
}
|
||||
|
||||
Encoding encoding = responseMessage.BodyEncoding ?? _utf8NoBom;
|
||||
using (var writer = new StreamWriter(response.Body, encoding))
|
||||
{
|
||||
await writer.WriteAsync(responseMessage.Body);
|
||||
// TODO : response.ContentLength = responseMessage.Body.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using JetBrains.Annotations;
|
||||
using WireMock.Validation;
|
||||
using Owin;
|
||||
using Microsoft.Owin.Hosting;
|
||||
using WireMock.Http;
|
||||
|
||||
namespace WireMock.Owin
|
||||
{
|
||||
@@ -14,7 +15,6 @@ namespace WireMock.Owin
|
||||
{
|
||||
private readonly WireMockMiddlewareOptions _options;
|
||||
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
|
||||
private System.Threading.Thread _internalThread;
|
||||
|
||||
public OwinSelfHost([NotNull] WireMockMiddlewareOptions options, [NotNull] params string[] uriPrefixes)
|
||||
{
|
||||
@@ -23,9 +23,10 @@ namespace WireMock.Owin
|
||||
|
||||
foreach (string uriPrefix in uriPrefixes)
|
||||
{
|
||||
var uri = new Uri(uriPrefix);
|
||||
Urls.Add(uri);
|
||||
Ports.Add(uri.Port);
|
||||
Urls.Add(uriPrefix);
|
||||
|
||||
PortUtil.TryExtractProtocolAndPort(uriPrefix, out string host, out int port);
|
||||
Ports.Add(port);
|
||||
}
|
||||
|
||||
_options = options;
|
||||
@@ -33,7 +34,7 @@ namespace WireMock.Owin
|
||||
|
||||
public bool IsStarted { get; private set; }
|
||||
|
||||
public List<Uri> Urls { get; } = new List<Uri>();
|
||||
public List<string> Urls { get; } = new List<string>();
|
||||
|
||||
public List<int> Ports { get; } = new List<int>();
|
||||
|
||||
@@ -44,12 +45,6 @@ namespace WireMock.Owin
|
||||
{
|
||||
StartServers();
|
||||
}, _cts.Token);
|
||||
|
||||
//if (_internalThread != null)
|
||||
// throw new InvalidOperationException("Cannot start a multiple threads.");
|
||||
|
||||
//_internalThread = new Thread(ThreadWorkInternal);
|
||||
//_internalThread.Start();
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
@@ -57,59 +52,40 @@ namespace WireMock.Owin
|
||||
{
|
||||
_cts.Cancel();
|
||||
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
var timer = new System.Timers.Timer(999);
|
||||
timer.Elapsed += (sender, e) =>
|
||||
{
|
||||
if (_internalThread == null)
|
||||
{
|
||||
timer.Stop();
|
||||
tcs.SetResult(true);
|
||||
}
|
||||
};
|
||||
timer.Start();
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
private void ThreadWorkInternal()
|
||||
{
|
||||
try
|
||||
{
|
||||
StartServers();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_internalThread = null;
|
||||
}
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
private void StartServers()
|
||||
{
|
||||
Console.WriteLine("WireMock.Net server using .net 4.5.x or .net 4.6.x");
|
||||
|
||||
Action<IAppBuilder> startup = app =>
|
||||
{
|
||||
app.Use<GlobalExceptionMiddleware>();
|
||||
_options.PreWireMockMiddlewareInit?.Invoke(app);
|
||||
app.Use<WireMockMiddleware>(_options);
|
||||
_options.PostWireMockMiddlewareInit?.Invoke(app);
|
||||
};
|
||||
|
||||
var servers = new List<IDisposable>();
|
||||
foreach (var url in Urls)
|
||||
{
|
||||
servers.Add(WebApp.Start(url.ToString(), startup));
|
||||
servers.Add(WebApp.Start(url, startup));
|
||||
}
|
||||
|
||||
IsStarted = true;
|
||||
|
||||
while (!_cts.IsCancellationRequested)
|
||||
Thread.Sleep(1000);
|
||||
{
|
||||
Thread.Sleep(30000);
|
||||
}
|
||||
|
||||
IsStarted = false;
|
||||
|
||||
foreach (var server in servers)
|
||||
{
|
||||
server.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,143 +1,181 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers.Request;
|
||||
using System.Linq;
|
||||
#if !NETSTANDARD
|
||||
using Microsoft.Owin;
|
||||
#else
|
||||
using Microsoft.AspNetCore.Http;
|
||||
#endif
|
||||
|
||||
namespace WireMock.Owin
|
||||
{
|
||||
#if !NETSTANDARD
|
||||
internal class WireMockMiddleware : OwinMiddleware
|
||||
#else
|
||||
internal class WireMockMiddleware
|
||||
#endif
|
||||
{
|
||||
private static readonly Task CompletedTask = Task.FromResult(false);
|
||||
private readonly WireMockMiddlewareOptions _options;
|
||||
|
||||
private readonly OwinRequestMapper _requestMapper = new OwinRequestMapper();
|
||||
private readonly OwinResponseMapper _responseMapper = new OwinResponseMapper();
|
||||
|
||||
#if !NETSTANDARD
|
||||
public WireMockMiddleware(OwinMiddleware next, WireMockMiddlewareOptions options) : base(next)
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
#else
|
||||
public WireMockMiddleware(RequestDelegate next, WireMockMiddlewareOptions options)
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !NETSTANDARD
|
||||
public override async Task Invoke(IOwinContext ctx)
|
||||
#else
|
||||
public async Task Invoke(HttpContext ctx)
|
||||
#endif
|
||||
{
|
||||
var request = await _requestMapper.MapAsync(ctx.Request);
|
||||
|
||||
ResponseMessage response = null;
|
||||
Mapping targetMapping = null;
|
||||
RequestMatchResult requestMatchResult = null;
|
||||
try
|
||||
{
|
||||
var mappings = _options.Mappings
|
||||
.Select(m => new
|
||||
{
|
||||
Mapping = m,
|
||||
MatchResult = m.IsRequestHandled(request)
|
||||
})
|
||||
.ToList();
|
||||
|
||||
if (_options.AllowPartialMapping)
|
||||
{
|
||||
var partialMappings = mappings
|
||||
.Where(pm => pm.Mapping.IsAdminInterface && pm.MatchResult.IsPerfectMatch || !pm.Mapping.IsAdminInterface)
|
||||
.OrderBy(m => m.MatchResult)
|
||||
.ThenBy(m => m.Mapping.Priority)
|
||||
.ToList();
|
||||
|
||||
var bestPartialMatch = partialMappings.FirstOrDefault(pm => pm.MatchResult.AverageTotalScore > 0.0);
|
||||
|
||||
targetMapping = bestPartialMatch?.Mapping;
|
||||
requestMatchResult = bestPartialMatch?.MatchResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
var perfectMatch = mappings
|
||||
.OrderBy(m => m.Mapping.Priority)
|
||||
.FirstOrDefault(m => m.MatchResult.IsPerfectMatch);
|
||||
|
||||
targetMapping = perfectMatch?.Mapping;
|
||||
requestMatchResult = perfectMatch?.MatchResult;
|
||||
}
|
||||
|
||||
if (targetMapping == null)
|
||||
{
|
||||
response = new ResponseMessage { StatusCode = 404, Body = "No matching mapping found" };
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetMapping.IsAdminInterface && _options.AuthorizationMatcher != null)
|
||||
{
|
||||
string authorization;
|
||||
bool present = request.Headers.TryGetValue("Authorization", out authorization);
|
||||
if (!present || _options.AuthorizationMatcher.IsMatch(authorization) < 1.0)
|
||||
{
|
||||
response = new ResponseMessage { StatusCode = 401 };
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetMapping.IsAdminInterface && _options.RequestProcessingDelay > TimeSpan.Zero)
|
||||
{
|
||||
await Task.Delay(_options.RequestProcessingDelay.Value);
|
||||
}
|
||||
|
||||
response = await targetMapping.ResponseToAsync(request);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response = new ResponseMessage { StatusCode = 500, Body = ex.ToString() };
|
||||
}
|
||||
finally
|
||||
{
|
||||
var log = new LogEntry
|
||||
{
|
||||
Guid = Guid.NewGuid(),
|
||||
RequestMessage = request,
|
||||
ResponseMessage = response,
|
||||
MappingGuid = targetMapping?.Guid,
|
||||
MappingTitle = targetMapping?.Title,
|
||||
RequestMatchResult = requestMatchResult
|
||||
};
|
||||
|
||||
LogRequest(log);
|
||||
|
||||
await _responseMapper.MapAsync(response, ctx.Response);
|
||||
}
|
||||
|
||||
await CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The log request.
|
||||
/// </summary>
|
||||
/// <param name="entry">The request.</param>
|
||||
private void LogRequest(LogEntry entry)
|
||||
{
|
||||
lock (((ICollection)_options.LogEntries).SyncRoot)
|
||||
{
|
||||
_options.LogEntries.Add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers.Request;
|
||||
using System.Linq;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Util;
|
||||
using Newtonsoft.Json;
|
||||
#if !NETSTANDARD
|
||||
using Microsoft.Owin;
|
||||
#else
|
||||
using Microsoft.AspNetCore.Http;
|
||||
#endif
|
||||
|
||||
namespace WireMock.Owin
|
||||
{
|
||||
#if !NETSTANDARD
|
||||
internal class WireMockMiddleware : OwinMiddleware
|
||||
#else
|
||||
internal class WireMockMiddleware
|
||||
#endif
|
||||
{
|
||||
private static readonly Task CompletedTask = Task.FromResult(false);
|
||||
private readonly WireMockMiddlewareOptions _options;
|
||||
|
||||
private readonly OwinRequestMapper _requestMapper = new OwinRequestMapper();
|
||||
private readonly OwinResponseMapper _responseMapper = new OwinResponseMapper();
|
||||
|
||||
#if !NETSTANDARD
|
||||
public WireMockMiddleware(OwinMiddleware next, WireMockMiddlewareOptions options) : base(next)
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
#else
|
||||
public WireMockMiddleware(RequestDelegate next, WireMockMiddlewareOptions options)
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !NETSTANDARD
|
||||
public override async Task Invoke(IOwinContext ctx)
|
||||
#else
|
||||
public async Task Invoke(HttpContext ctx)
|
||||
#endif
|
||||
{
|
||||
var request = await _requestMapper.MapAsync(ctx.Request);
|
||||
|
||||
bool logRequest = false;
|
||||
ResponseMessage response = null;
|
||||
Mapping targetMapping = null;
|
||||
RequestMatchResult requestMatchResult = null;
|
||||
try
|
||||
{
|
||||
foreach (var mapping in _options.Mappings.Where(m => m?.Scenario != null))
|
||||
{
|
||||
// Set start
|
||||
if (!_options.Scenarios.ContainsKey(mapping.Scenario) && mapping.IsStartState)
|
||||
{
|
||||
_options.Scenarios.Add(mapping.Scenario, null);
|
||||
}
|
||||
}
|
||||
|
||||
var mappings = _options.Mappings
|
||||
.Select(m => new
|
||||
{
|
||||
Mapping = m,
|
||||
MatchResult = m.GetRequestMatchResult(request, m.Scenario != null && _options.Scenarios.ContainsKey(m.Scenario) ? _options.Scenarios[m.Scenario] : null)
|
||||
})
|
||||
.ToList();
|
||||
|
||||
if (_options.AllowPartialMapping)
|
||||
{
|
||||
var partialMappings = mappings
|
||||
.Where(pm => pm.Mapping.IsAdminInterface && pm.MatchResult.IsPerfectMatch || !pm.Mapping.IsAdminInterface)
|
||||
.OrderBy(m => m.MatchResult)
|
||||
.ThenBy(m => m.Mapping.Priority)
|
||||
.ToList();
|
||||
|
||||
var bestPartialMatch = partialMappings.FirstOrDefault(pm => pm.MatchResult.AverageTotalScore > 0.0);
|
||||
|
||||
targetMapping = bestPartialMatch?.Mapping;
|
||||
requestMatchResult = bestPartialMatch?.MatchResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
var perfectMatch = mappings
|
||||
.OrderBy(m => m.Mapping.Priority)
|
||||
.FirstOrDefault(m => m.MatchResult.IsPerfectMatch);
|
||||
|
||||
targetMapping = perfectMatch?.Mapping;
|
||||
requestMatchResult = perfectMatch?.MatchResult;
|
||||
}
|
||||
|
||||
if (targetMapping == null)
|
||||
{
|
||||
logRequest = true;
|
||||
response = new ResponseMessage { StatusCode = 404, Body = "No matching mapping found" };
|
||||
return;
|
||||
}
|
||||
|
||||
logRequest = !targetMapping.IsAdminInterface;
|
||||
|
||||
if (targetMapping.IsAdminInterface && _options.AuthorizationMatcher != null)
|
||||
{
|
||||
bool present = request.Headers.TryGetValue("Authorization", out WireMockList<string> authorization);
|
||||
if (!present || _options.AuthorizationMatcher.IsMatch(authorization.ToString()) < MatchScores.Perfect)
|
||||
{
|
||||
response = new ResponseMessage { StatusCode = 401 };
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetMapping.IsAdminInterface && _options.RequestProcessingDelay > TimeSpan.Zero)
|
||||
{
|
||||
await Task.Delay(_options.RequestProcessingDelay.Value);
|
||||
}
|
||||
|
||||
response = await targetMapping.ResponseToAsync(request);
|
||||
|
||||
if (targetMapping.Scenario != null)
|
||||
{
|
||||
_options.Scenarios[targetMapping.Scenario] = targetMapping.NextState;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response = new ResponseMessage { StatusCode = 500, Body = JsonConvert.SerializeObject(ex) };
|
||||
}
|
||||
finally
|
||||
{
|
||||
var log = new LogEntry
|
||||
{
|
||||
Guid = Guid.NewGuid(),
|
||||
RequestMessage = request,
|
||||
ResponseMessage = response,
|
||||
MappingGuid = targetMapping?.Guid,
|
||||
MappingTitle = targetMapping?.Title,
|
||||
RequestMatchResult = requestMatchResult
|
||||
};
|
||||
|
||||
LogRequest(log, logRequest);
|
||||
|
||||
await _responseMapper.MapAsync(response, ctx.Response);
|
||||
}
|
||||
|
||||
await CompletedTask;
|
||||
}
|
||||
|
||||
private void LogRequest(LogEntry entry, bool addRequest)
|
||||
{
|
||||
if (addRequest)
|
||||
{
|
||||
_options.LogEntries.Add(entry);
|
||||
}
|
||||
|
||||
if (_options.MaxRequestLogCount != null)
|
||||
{
|
||||
var amount = _options.LogEntries.Count - _options.MaxRequestLogCount.Value;
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
_options.LogEntries.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (_options.RequestLogExpirationDuration != null)
|
||||
{
|
||||
var checkTime = DateTime.Now.AddHours(-_options.RequestLogExpirationDuration.Value);
|
||||
|
||||
for (var i = _options.LogEntries.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var le = _options.LogEntries[i];
|
||||
if (le.RequestMessage.DateTime <= checkTime)
|
||||
{
|
||||
_options.LogEntries.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Util;
|
||||
#if !NETSTANDARD
|
||||
using Owin;
|
||||
#else
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
#endif
|
||||
|
||||
namespace WireMock.Owin
|
||||
{
|
||||
@@ -13,14 +21,24 @@ namespace WireMock.Owin
|
||||
|
||||
public bool AllowPartialMapping { get; set; }
|
||||
|
||||
public IList<Mapping> Mappings { get; set; }
|
||||
public IList<Mapping> Mappings { get; set; } = new List<Mapping>();
|
||||
|
||||
public IList<LogEntry> LogEntries { get; set; }
|
||||
public ObservableCollection<LogEntry> LogEntries { get; } = new ConcurentObservableCollection<LogEntry>();
|
||||
|
||||
public WireMockMiddlewareOptions()
|
||||
{
|
||||
Mappings = new List<Mapping>();
|
||||
LogEntries = new List<LogEntry>();
|
||||
}
|
||||
public int? RequestLogExpirationDuration { get; set; }
|
||||
|
||||
public int? MaxRequestLogCount { get; set; }
|
||||
|
||||
public IDictionary<string, object> Scenarios { get; } = new ConcurrentDictionary<string, object>();
|
||||
|
||||
#if !NETSTANDARD
|
||||
public Action<IAppBuilder> PreWireMockMiddlewareInit { get; set; }
|
||||
|
||||
public Action<IAppBuilder> PostWireMockMiddlewareInit { get; set; }
|
||||
#else
|
||||
public Action<IApplicationBuilder> PreWireMockMiddlewareInit { get; set; }
|
||||
|
||||
public Action<IApplicationBuilder> PostWireMockMiddlewareInit { get; set; }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
4
src/WireMock.Net/Properties/AssemblyInfo.cs
Normal file
4
src/WireMock.Net/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.StandAlone")]
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.Tests")]
|
||||
33
src/WireMock.Net/RequestBuilders/IClientIPRequestBuilder.cs
Normal file
33
src/WireMock.Net/RequestBuilders/IClientIPRequestBuilder.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Matchers;
|
||||
|
||||
namespace WireMock.RequestBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// The IClientIPRequestBuilder interface.
|
||||
/// </summary>
|
||||
public interface IClientIPRequestBuilder : IUrlAndPathRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The with ClientIP.
|
||||
/// </summary>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithClientIP([NotNull] params IMatcher[] matchers);
|
||||
|
||||
/// <summary>
|
||||
/// The with ClientIP.
|
||||
/// </summary>
|
||||
/// <param name="clientIPs">The clientIPs.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithClientIP([NotNull] params string[] clientIPs);
|
||||
|
||||
/// <summary>
|
||||
/// The with ClientIP.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The path funcs.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithClientIP([NotNull] params Func<string, bool>[] funcs);
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ namespace WireMock.RequestBuilders
|
||||
public interface IHeadersAndCookiesRequestBuilder : IBodyRequestBuilder, IRequestMatcher, IParamsRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The with header.
|
||||
/// Add Header matching based on name, pattern and ignoreCase.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
@@ -20,6 +20,15 @@ namespace WireMock.RequestBuilders
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithHeader([NotNull] string name, string pattern, bool ignoreCase = true);
|
||||
|
||||
/// <summary>
|
||||
/// Add Header matching based on name, patterns and ignoreCase.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">ignore Case</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithHeader([NotNull] string name, string[] patterns, bool ignoreCase = true);
|
||||
|
||||
/// <summary>
|
||||
/// The with header.
|
||||
/// </summary>
|
||||
@@ -33,7 +42,7 @@ namespace WireMock.RequestBuilders
|
||||
/// </summary>
|
||||
/// <param name="funcs">The headers funcs.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithHeader([NotNull] params Func<IDictionary<string, string>, bool>[] funcs);
|
||||
IRequestBuilder WithHeader([NotNull] params Func<IDictionary<string, string[]>, bool>[] funcs);
|
||||
|
||||
/// <summary>
|
||||
/// The with cookie.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// IRequestBuilder
|
||||
/// </summary>
|
||||
public interface IRequestBuilder : IUrlAndPathRequestBuilder
|
||||
public interface IRequestBuilder : IClientIPRequestBuilder
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,45 @@ namespace WireMock.RequestBuilders
|
||||
return _requestMatchers.Where(rm => rm is T).Cast<T>().FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with clientIP.
|
||||
/// </summary>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithClientIP(params IMatcher[] matchers)
|
||||
{
|
||||
Check.NotEmpty(matchers, nameof(matchers));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageClientIPMatcher(matchers));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with clientIP.
|
||||
/// </summary>
|
||||
/// <param name="clientIPs">The ClientIPs.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithClientIP(params string[] clientIPs)
|
||||
{
|
||||
Check.NotEmpty(clientIPs, nameof(clientIPs));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageClientIPMatcher(clientIPs));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with clientIP.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The clientIP funcs.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithClientIP(params Func<string, bool>[] funcs)
|
||||
{
|
||||
Check.NotEmpty(funcs, nameof(funcs));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageClientIPMatcher(funcs));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with path.
|
||||
/// </summary>
|
||||
@@ -320,13 +359,7 @@ namespace WireMock.RequestBuilders
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// With header.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">if set to <c>true</c> [ignore case].</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
/// <inheritdoc cref="IHeadersAndCookiesRequestBuilder.WithHeader(string,string,bool)"/>
|
||||
public IRequestBuilder WithHeader(string name, string pattern, bool ignoreCase = true)
|
||||
{
|
||||
Check.NotNull(name, nameof(name));
|
||||
@@ -336,6 +369,16 @@ namespace WireMock.RequestBuilders
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IHeadersAndCookiesRequestBuilder.WithHeader(string,string[],bool)"/>
|
||||
public IRequestBuilder WithHeader(string name, string[] patterns, bool ignoreCase = true)
|
||||
{
|
||||
Check.NotNull(name, nameof(name));
|
||||
Check.NotNull(patterns, nameof(patterns));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageHeaderMatcher(name, patterns, ignoreCase));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// With header.
|
||||
/// </summary>
|
||||
@@ -356,7 +399,7 @@ namespace WireMock.RequestBuilders
|
||||
/// </summary>
|
||||
/// <param name="funcs">The funcs.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public IRequestBuilder WithHeader(params Func<IDictionary<string, string>, bool>[] funcs)
|
||||
public IRequestBuilder WithHeader(params Func<IDictionary<string, string[]>, bool>[] funcs)
|
||||
{
|
||||
Check.NotEmpty(funcs, nameof(funcs));
|
||||
|
||||
|
||||
@@ -1,127 +1,170 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Util;
|
||||
using WireMock.Validation;
|
||||
using System.Text;
|
||||
|
||||
namespace WireMock
|
||||
{
|
||||
/// <summary>
|
||||
/// The request.
|
||||
/// </summary>
|
||||
public class RequestMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the url.
|
||||
/// </summary>
|
||||
public string Url { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the DateTime.
|
||||
/// </summary>
|
||||
public DateTime DateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path.
|
||||
/// </summary>
|
||||
public string Path { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the method.
|
||||
/// </summary>
|
||||
public string Method { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the headers.
|
||||
/// </summary>
|
||||
public IDictionary<string, string> Headers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cookies.
|
||||
/// </summary>
|
||||
public IDictionary<string, string> Cookies { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the query.
|
||||
/// </summary>
|
||||
public IDictionary<string, WireMockList<string>> Query { get; } = new Dictionary<string, WireMockList<string>>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bodyAsBytes.
|
||||
/// </summary>
|
||||
public byte[] BodyAsBytes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the body.
|
||||
/// </summary>
|
||||
public string Body { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the body encoding.
|
||||
/// </summary>
|
||||
public Encoding BodyEncoding { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessage"/> class.
|
||||
/// </summary>
|
||||
/// <param name="url">The original url.</param>
|
||||
/// <param name="verb">The verb.</param>
|
||||
/// <param name="bodyAsBytes">The bodyAsBytes byte[].</param>
|
||||
/// <param name="body">The body string.</param>
|
||||
/// <param name="bodyEncoding">The body encoding</param>
|
||||
/// <param name="headers">The headers.</param>
|
||||
/// <param name="cookies">The cookies.</param>
|
||||
public RequestMessage([NotNull] Uri url, [NotNull] string verb, [CanBeNull] byte[] bodyAsBytes = null, [CanBeNull] string body = null, [CanBeNull] Encoding bodyEncoding = null, [CanBeNull] IDictionary<string, string> headers = null, [CanBeNull] IDictionary<string, string> cookies = null)
|
||||
{
|
||||
Check.NotNull(url, nameof(url));
|
||||
Check.NotNull(verb, nameof(verb));
|
||||
|
||||
Url = url.ToString();
|
||||
Path = url.AbsolutePath;
|
||||
Method = verb.ToLower();
|
||||
BodyAsBytes = bodyAsBytes;
|
||||
Body = body;
|
||||
BodyEncoding = bodyEncoding;
|
||||
Headers = headers;
|
||||
Cookies = cookies;
|
||||
|
||||
string query = url.Query;
|
||||
if (!string.IsNullOrEmpty(query))
|
||||
{
|
||||
if (query.StartsWith("?"))
|
||||
{
|
||||
query = query.Substring(1);
|
||||
}
|
||||
|
||||
Query = query.Split('&').Aggregate(
|
||||
new Dictionary<string, WireMockList<string>>(),
|
||||
(dict, term) =>
|
||||
{
|
||||
var parts = term.Split('=');
|
||||
var key = parts[0];
|
||||
if (!dict.ContainsKey(key))
|
||||
{
|
||||
dict.Add(key, new WireMockList<string>());
|
||||
}
|
||||
|
||||
if (parts.Length == 2)
|
||||
dict[key].Add(parts[1]);
|
||||
|
||||
return dict;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The get a query parameter.
|
||||
/// </summary>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <returns>The query parameter.</returns>
|
||||
public List<string> GetParameter(string key)
|
||||
{
|
||||
return Query.ContainsKey(key) ? Query[key] : null;
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Util;
|
||||
using WireMock.Validation;
|
||||
using System.Text;
|
||||
|
||||
namespace WireMock
|
||||
{
|
||||
/// <summary>
|
||||
/// The request.
|
||||
/// </summary>
|
||||
public class RequestMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the Client IP Address.
|
||||
/// </summary>
|
||||
public string ClientIP { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the url.
|
||||
/// </summary>
|
||||
public string Url { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the DateTime.
|
||||
/// </summary>
|
||||
public DateTime DateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path.
|
||||
/// </summary>
|
||||
public string Path { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the method.
|
||||
/// </summary>
|
||||
public string Method { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the headers.
|
||||
/// </summary>
|
||||
public IDictionary<string, WireMockList<string>> Headers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cookies.
|
||||
/// </summary>
|
||||
public IDictionary<string, string> Cookies { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the query.
|
||||
/// </summary>
|
||||
public IDictionary<string, WireMockList<string>> Query { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bodyAsBytes.
|
||||
/// </summary>
|
||||
public byte[] BodyAsBytes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the body.
|
||||
/// </summary>
|
||||
public string Body { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Host
|
||||
/// </summary>
|
||||
public string Host { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the protocol
|
||||
/// </summary>
|
||||
public string Protocol { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the port
|
||||
/// </summary>
|
||||
public int Port { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the origin
|
||||
/// </summary>
|
||||
public string Origin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the body encoding.
|
||||
/// </summary>
|
||||
public Encoding BodyEncoding { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessage"/> class.
|
||||
/// </summary>
|
||||
/// <param name="url">The original url.</param>
|
||||
/// <param name="method">The HTTP method.</param>
|
||||
/// <param name="clientIP">The client IP Address.</param>
|
||||
/// <param name="bodyAsBytes">The bodyAsBytes byte[].</param>
|
||||
/// <param name="body">The body string.</param>
|
||||
/// <param name="bodyEncoding">The body encoding</param>
|
||||
/// <param name="headers">The headers.</param>
|
||||
/// <param name="cookies">The cookies.</param>
|
||||
public RequestMessage([NotNull] Uri url, [NotNull] string method, [NotNull] string clientIP, [CanBeNull] byte[] bodyAsBytes = null, [CanBeNull] string body = null, [CanBeNull] Encoding bodyEncoding = null, [CanBeNull] IDictionary<string, string[]> headers = null, [CanBeNull] IDictionary<string, string> cookies = null)
|
||||
{
|
||||
Check.NotNull(url, nameof(url));
|
||||
Check.NotNull(method, nameof(method));
|
||||
Check.NotNull(clientIP, nameof(clientIP));
|
||||
|
||||
Url = url.ToString();
|
||||
Protocol = url.Scheme;
|
||||
Host = url.Host;
|
||||
Port = url.Port;
|
||||
Origin = $"{url.Scheme}://{url.Host}:{url.Port}";
|
||||
Path = url.AbsolutePath;
|
||||
Method = method.ToLower();
|
||||
ClientIP = clientIP;
|
||||
BodyAsBytes = bodyAsBytes;
|
||||
Body = body;
|
||||
BodyEncoding = bodyEncoding;
|
||||
Headers = headers?.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
|
||||
Cookies = cookies;
|
||||
Query = ParseQuery(url.Query);
|
||||
}
|
||||
|
||||
private IDictionary<string, WireMockList<string>> ParseQuery(string queryString)
|
||||
{
|
||||
if (string.IsNullOrEmpty(queryString))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (queryString.StartsWith("?"))
|
||||
{
|
||||
queryString = queryString.Substring(1);
|
||||
}
|
||||
|
||||
return queryString.Split('&').Aggregate(new Dictionary<string, WireMockList<string>>(),
|
||||
(dict, term) =>
|
||||
{
|
||||
var parts = term.Split('=');
|
||||
string key = parts[0];
|
||||
if (!dict.ContainsKey(key))
|
||||
{
|
||||
dict.Add(key, new WireMockList<string>());
|
||||
}
|
||||
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
dict[key].Add(parts[1]);
|
||||
}
|
||||
|
||||
return dict;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The get a query parameter.
|
||||
/// </summary>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <returns>The query parameter.</returns>
|
||||
public List<string> GetParameter(string key)
|
||||
{
|
||||
if (Query == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Query.ContainsKey(key) ? Query[key] : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/WireMock.Net/ResponseBuilders/BodyDestinationFormat.cs
Normal file
23
src/WireMock.Net/ResponseBuilders/BodyDestinationFormat.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace WireMock.ResponseBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the BodyDestinationFormat
|
||||
/// </summary>
|
||||
public static class BodyDestinationFormat
|
||||
{
|
||||
/// <summary>
|
||||
/// Same as source (no conversion)
|
||||
/// </summary>
|
||||
public const string SameAsSource = "SameAsSource";
|
||||
|
||||
/// <summary>
|
||||
/// Convert to string
|
||||
/// </summary>
|
||||
public const string String = "String";
|
||||
|
||||
/// <summary>
|
||||
/// Convert to bytes
|
||||
/// </summary>
|
||||
public const string Bytes = "Bytes";
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace WireMock.ResponseBuilders
|
||||
@@ -9,15 +10,25 @@ namespace WireMock.ResponseBuilders
|
||||
public interface IBodyResponseBuilder : ITransformResponseBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The with body.
|
||||
/// WithBody : Create a ... response based on a string.
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
|
||||
/// <param name="encoding">The body encoding.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBody([NotNull] string body, [CanBeNull] Encoding encoding = null);
|
||||
IResponseBuilder WithBody([NotNull] string body, [CanBeNull] string destination = BodyDestinationFormat.SameAsSource, [CanBeNull] Encoding encoding = null);
|
||||
|
||||
/// <summary>
|
||||
/// The with body as Json.
|
||||
/// WithBody : Create a ... response based on a bytearray.
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
|
||||
/// <param name="encoding">The body encoding.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBody([NotNull] byte[] body, [CanBeNull] string destination = BodyDestinationFormat.SameAsSource, [CanBeNull] Encoding encoding = null);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody : Create a string response based on a object (which will be converted to a JSON string).
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="encoding">The body encoding.</param>
|
||||
@@ -25,11 +36,20 @@ namespace WireMock.ResponseBuilders
|
||||
IResponseBuilder WithBodyAsJson([NotNull] object body, [CanBeNull] Encoding encoding = null);
|
||||
|
||||
/// <summary>
|
||||
/// The with body as base64.
|
||||
/// WithBody : Create a string response based on a Base64 string (which will be decoded to a normal string).
|
||||
/// </summary>
|
||||
/// <param name="bodyAsbase64">The body asbase64.</param>
|
||||
/// <param name="bodyAsbase64">The body.</param>
|
||||
/// <param name="encoding">The Encoding.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBodyAsBase64([NotNull] string bodyAsbase64, [CanBeNull] Encoding encoding = null);
|
||||
[Obsolete]
|
||||
IResponseBuilder WithBodyFromBase64([NotNull] string bodyAsbase64, [CanBeNull] Encoding encoding = null);
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyFromFile : Create a ... response based on a File.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="cache">Defines if this file is cached in memory or retrieved from disk everytime the response is created.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBodyFromFile([NotNull] string filename, bool cache = true);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.ResponseBuilders
|
||||
{
|
||||
@@ -12,15 +13,29 @@ namespace WireMock.ResponseBuilders
|
||||
/// The with header.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="values">The values.</param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithHeader([NotNull] string name, string value);
|
||||
IResponseBuilder WithHeader([NotNull] string name, params string[] values);
|
||||
|
||||
/// <summary>
|
||||
/// The with headers.
|
||||
/// </summary>
|
||||
/// <param name="headers">The headers.</param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithHeaders([NotNull] IDictionary<string,string> headers);
|
||||
IResponseBuilder WithHeaders([NotNull] IDictionary<string, string> headers);
|
||||
|
||||
/// <summary>
|
||||
/// The with headers.
|
||||
/// </summary>
|
||||
/// <param name="headers">The headers.</param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithHeaders([NotNull] IDictionary<string, string[]> headers);
|
||||
|
||||
/// <summary>
|
||||
/// The with headers.
|
||||
/// </summary>
|
||||
/// <param name="headers">The headers.</param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithHeaders([NotNull] IDictionary<string, WireMockList<string>> headers);
|
||||
}
|
||||
}
|
||||
@@ -7,19 +7,12 @@ namespace WireMock.ResponseBuilders
|
||||
/// </summary>
|
||||
public interface IProxyResponseBuilder : IStatusCodeResponseBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// With Proxy URL.
|
||||
/// </summary>
|
||||
/// <param name="proxyUrl">The proxy url.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithProxy([NotNull] string proxyUrl);
|
||||
|
||||
/// <summary>
|
||||
/// With Proxy URL using X509Certificate2.
|
||||
/// </summary>
|
||||
/// <param name="proxyUrl">The proxy url.</param>
|
||||
/// <param name="clientX509Certificate2Filename">The X509Certificate2 file to use for client authentication.</param>
|
||||
/// <param name="clientX509Certificate2ThumbprintOrSubjectName">The X509Certificate2 file to use for client authentication.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithProxy([NotNull] string proxyUrl, [CanBeNull] string clientX509Certificate2Filename);
|
||||
IResponseBuilder WithProxy([NotNull] string proxyUrl, [CanBeNull] string clientX509Certificate2ThumbprintOrSubjectName = null);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
using WireMock.Validation;
|
||||
using WireMock.Http;
|
||||
using WireMock.Transformers;
|
||||
using WireMock.Util;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.ResponseBuilders
|
||||
{
|
||||
@@ -16,6 +20,8 @@ namespace WireMock.ResponseBuilders
|
||||
/// </summary>
|
||||
public class Response : IResponseBuilder
|
||||
{
|
||||
private HttpClient httpClientForProxy;
|
||||
|
||||
/// <summary>
|
||||
/// The delay
|
||||
/// </summary>
|
||||
@@ -35,9 +41,9 @@ namespace WireMock.ResponseBuilders
|
||||
public string ProxyUrl { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The client X509Certificate2Filename to use.
|
||||
/// The client X509Certificate2 Thumbprint or SubjectName to use.
|
||||
/// </summary>
|
||||
public string X509Certificate2Filename { get; private set; } = null;
|
||||
public string X509Certificate2ThumbprintOrSubjectName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response message.
|
||||
@@ -125,53 +131,117 @@ namespace WireMock.ResponseBuilders
|
||||
return WithStatusCode((int)HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with header.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
public IResponseBuilder WithHeader(string name, string value)
|
||||
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeader(string, string[])"/>
|
||||
public IResponseBuilder WithHeader(string name, params string[] values)
|
||||
{
|
||||
Check.NotNull(name, nameof(name));
|
||||
|
||||
ResponseMessage.AddHeader(name, value);
|
||||
ResponseMessage.AddHeader(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with headers.
|
||||
/// </summary>
|
||||
/// <param name="headers">The headers.</param>
|
||||
/// <returns></returns>
|
||||
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeaders(IDictionary{string, string})"/>
|
||||
public IResponseBuilder WithHeaders(IDictionary<string, string> headers)
|
||||
{
|
||||
Check.NotNull(headers, nameof(headers));
|
||||
|
||||
ResponseMessage.Headers = headers.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeaders(IDictionary{string, string[]})"/>
|
||||
public IResponseBuilder WithHeaders(IDictionary<string, string[]> headers)
|
||||
{
|
||||
Check.NotNull(headers, nameof(headers));
|
||||
|
||||
ResponseMessage.Headers = headers.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeaders(IDictionary{string, WireMockList{string}})"/>
|
||||
public IResponseBuilder WithHeaders(IDictionary<string, WireMockList<string>> headers)
|
||||
{
|
||||
ResponseMessage.Headers = headers;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with body.
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="encoding">The body encoding.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
public IResponseBuilder WithBody(string body, Encoding encoding = null)
|
||||
/// <inheritdoc cref="IBodyResponseBuilder.WithBody(byte[], string, Encoding)"/>
|
||||
public IResponseBuilder WithBody(byte[] body, string destination, Encoding encoding = null)
|
||||
{
|
||||
Check.NotNull(body, nameof(body));
|
||||
|
||||
ResponseMessage.Body = body;
|
||||
ResponseMessage.BodyEncoding = encoding ?? Encoding.UTF8;
|
||||
ResponseMessage.BodyDestination = destination;
|
||||
|
||||
switch (destination)
|
||||
{
|
||||
case BodyDestinationFormat.String:
|
||||
var enc = encoding ?? Encoding.UTF8;
|
||||
ResponseMessage.BodyAsBytes = null;
|
||||
ResponseMessage.Body = enc.GetString(body);
|
||||
ResponseMessage.BodyEncoding = enc;
|
||||
break;
|
||||
|
||||
default:
|
||||
ResponseMessage.BodyAsBytes = body;
|
||||
ResponseMessage.BodyEncoding = null;
|
||||
break;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with body (AsJson object).
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="encoding">The body encoding.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
/// <inheritdoc cref="IBodyResponseBuilder.WithBodyFromFile"/>
|
||||
public IResponseBuilder WithBodyFromFile(string filename, bool cache = true)
|
||||
{
|
||||
Check.NotNull(filename, nameof(filename));
|
||||
|
||||
ResponseMessage.BodyEncoding = null;
|
||||
ResponseMessage.BodyAsFileIsCached = cache;
|
||||
|
||||
if (cache)
|
||||
{
|
||||
ResponseMessage.Body = null;
|
||||
ResponseMessage.BodyAsBytes = File.ReadAllBytes(filename);
|
||||
ResponseMessage.BodyAsFile = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
ResponseMessage.Body = null;
|
||||
ResponseMessage.BodyAsBytes = null;
|
||||
ResponseMessage.BodyAsFile = filename;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyResponseBuilder.WithBody(string, string, Encoding)"/>
|
||||
public IResponseBuilder WithBody(string body, string destination = BodyDestinationFormat.SameAsSource, Encoding encoding = null)
|
||||
{
|
||||
Check.NotNull(body, nameof(body));
|
||||
|
||||
encoding = encoding ?? Encoding.UTF8;
|
||||
|
||||
ResponseMessage.BodyDestination = destination;
|
||||
|
||||
switch (destination)
|
||||
{
|
||||
case BodyDestinationFormat.Bytes:
|
||||
ResponseMessage.Body = null;
|
||||
ResponseMessage.BodyAsBytes = encoding.GetBytes(body);
|
||||
ResponseMessage.BodyEncoding = encoding;
|
||||
break;
|
||||
|
||||
default:
|
||||
ResponseMessage.Body = body;
|
||||
ResponseMessage.BodyAsBytes = null;
|
||||
ResponseMessage.BodyEncoding = encoding;
|
||||
break;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyResponseBuilder.WithBodyAsJson"/>
|
||||
public IResponseBuilder WithBodyAsJson(object body, Encoding encoding = null)
|
||||
{
|
||||
Check.NotNull(body, nameof(body));
|
||||
@@ -184,23 +254,20 @@ namespace WireMock.ResponseBuilders
|
||||
ResponseMessage.BodyEncoding = encoding;
|
||||
}
|
||||
|
||||
ResponseMessage.BodyDestination = null;
|
||||
ResponseMessage.Body = jsonBody;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The with body as base64.
|
||||
/// </summary>
|
||||
/// <param name="bodyAsbase64">The body asbase64.</param>
|
||||
/// <param name="encoding">The Encoding.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
public IResponseBuilder WithBodyAsBase64(string bodyAsbase64, Encoding encoding = null)
|
||||
/// <inheritdoc cref="IBodyResponseBuilder.WithBodyFromBase64"/>
|
||||
public IResponseBuilder WithBodyFromBase64(string bodyAsbase64, Encoding encoding = null)
|
||||
{
|
||||
Check.NotNull(bodyAsbase64, nameof(bodyAsbase64));
|
||||
|
||||
encoding = encoding ?? Encoding.UTF8;
|
||||
|
||||
ResponseMessage.BodyDestination = null;
|
||||
ResponseMessage.Body = encoding.GetString(Convert.FromBase64String(bodyAsbase64));
|
||||
ResponseMessage.BodyEncoding = encoding;
|
||||
|
||||
@@ -245,29 +312,15 @@ namespace WireMock.ResponseBuilders
|
||||
/// With Proxy URL.
|
||||
/// </summary>
|
||||
/// <param name="proxyUrl">The proxy url.</param>
|
||||
/// <param name="clientX509Certificate2ThumbprintOrSubjectName">The X509Certificate2 file to use for client authentication.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
[PublicAPI]
|
||||
public IResponseBuilder WithProxy(string proxyUrl)
|
||||
public IResponseBuilder WithProxy(string proxyUrl, string clientX509Certificate2ThumbprintOrSubjectName = null)
|
||||
{
|
||||
Check.NotEmpty(proxyUrl, nameof(proxyUrl));
|
||||
|
||||
ProxyUrl = proxyUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// With Proxy URL.
|
||||
/// </summary>
|
||||
/// <param name="proxyUrl">The proxy url.</param>
|
||||
/// <param name="clientX509Certificate2Filename">The X509Certificate2 file to use for client authentication.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
public IResponseBuilder WithProxy(string proxyUrl, string clientX509Certificate2Filename)
|
||||
{
|
||||
Check.NotEmpty(proxyUrl, nameof(proxyUrl));
|
||||
Check.NotEmpty(clientX509Certificate2Filename, nameof(clientX509Certificate2Filename));
|
||||
|
||||
ProxyUrl = proxyUrl;
|
||||
X509Certificate2Filename = clientX509Certificate2Filename;
|
||||
X509Certificate2ThumbprintOrSubjectName = clientX509Certificate2ThumbprintOrSubjectName;
|
||||
httpClientForProxy = HttpClientHelper.CreateHttpClient(clientX509Certificate2ThumbprintOrSubjectName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -283,9 +336,12 @@ namespace WireMock.ResponseBuilders
|
||||
if (Delay != null)
|
||||
await Task.Delay(Delay.Value);
|
||||
|
||||
if (ProxyUrl != null)
|
||||
if (ProxyUrl != null && httpClientForProxy != null)
|
||||
{
|
||||
return await HttpClientHelper.SendAsync(requestMessage, ProxyUrl, X509Certificate2Filename);
|
||||
var requestUri = new Uri(requestMessage.Url);
|
||||
var proxyUri = new Uri(ProxyUrl);
|
||||
var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery);
|
||||
return await HttpClientHelper.SendAsync(httpClientForProxy, requestMessage, proxyUriWithRequestPathAndQuery.AbsoluteUri);
|
||||
}
|
||||
|
||||
if (UseTransformer)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using WireMock.Util;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock
|
||||
{
|
||||
@@ -12,7 +15,7 @@ namespace WireMock
|
||||
/// <summary>
|
||||
/// Gets the headers.
|
||||
/// </summary>
|
||||
public IDictionary<string, string> Headers { get; set; } = new ConcurrentDictionary<string, string>();
|
||||
public IDictionary<string, WireMockList<string>> Headers { get; set; } = new ConcurrentDictionary<string, WireMockList<string>>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the status code.
|
||||
@@ -25,27 +28,59 @@ namespace WireMock
|
||||
public string BodyOriginal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// Gets or sets the body destination (SameAsSource, String or Bytes).
|
||||
/// </summary>
|
||||
public string BodyDestination { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body as a string.
|
||||
/// </summary>
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body as bytes.
|
||||
/// </summary>
|
||||
public byte[] BodyAsBytes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body as a file.
|
||||
/// </summary>
|
||||
public string BodyAsFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the body as file cached?
|
||||
/// </summary>
|
||||
public bool? BodyAsFileIsCached { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body encoding.
|
||||
/// </summary>
|
||||
public Encoding BodyEncoding { get; set; } = new UTF8Encoding(false);
|
||||
|
||||
/// <summary>
|
||||
/// The add header.
|
||||
/// Adds the header.
|
||||
/// </summary>
|
||||
/// <param name="name">
|
||||
/// The name.
|
||||
/// </param>
|
||||
/// <param name="value">
|
||||
/// The value.
|
||||
/// </param>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
public void AddHeader(string name, string value)
|
||||
{
|
||||
Headers.Add(name, value);
|
||||
Headers.Add(name, new WireMockList<string>(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the header.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="values">The values.</param>
|
||||
public void AddHeader(string name, params string[] values)
|
||||
{
|
||||
Check.NotEmpty(values, nameof(values));
|
||||
|
||||
var newHeaderValues = Headers.TryGetValue(name, out WireMockList<string> existingValues)
|
||||
? values.Union(existingValues).ToArray()
|
||||
: values;
|
||||
|
||||
Headers[name] = new WireMockList<string>(newHeaderValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Serialization
|
||||
{
|
||||
@@ -18,6 +19,7 @@ namespace WireMock.Serialization
|
||||
var request = (Request)mapping.RequestMatcher;
|
||||
var response = (Response)mapping.Provider;
|
||||
|
||||
var clientIPMatchers = request.GetRequestMessageMatchers<RequestMessageClientIPMatcher>();
|
||||
var pathMatchers = request.GetRequestMessageMatchers<RequestMessagePathMatcher>();
|
||||
var urlMatchers = request.GetRequestMessageMatchers<RequestMessageUrlMatcher>();
|
||||
var headerMatchers = request.GetRequestMessageMatchers<RequestMessageHeaderMatcher>();
|
||||
@@ -31,8 +33,17 @@ namespace WireMock.Serialization
|
||||
Guid = mapping.Guid,
|
||||
Title = mapping.Title,
|
||||
Priority = mapping.Priority,
|
||||
Scenario = mapping.Scenario,
|
||||
WhenStateIs = mapping.ExecutionConditionState,
|
||||
SetStateTo = mapping.NextState,
|
||||
Request = new RequestModel
|
||||
{
|
||||
ClientIP = clientIPMatchers != null && clientIPMatchers.Any() ? new ClientIPModel
|
||||
{
|
||||
Matchers = Map(clientIPMatchers.Where(m => m.Matchers != null).SelectMany(m => m.Matchers)),
|
||||
Funcs = Map(clientIPMatchers.Where(m => m.Funcs != null).SelectMany(m => m.Funcs))
|
||||
} : null,
|
||||
|
||||
Path = pathMatchers != null && pathMatchers.Any() ? new PathModel
|
||||
{
|
||||
Matchers = Map(pathMatchers.Where(m => m.Matchers != null).SelectMany(m => m.Matchers)),
|
||||
@@ -68,7 +79,7 @@ namespace WireMock.Serialization
|
||||
Funcs = Map(pm.Funcs)
|
||||
}).ToList() : null,
|
||||
|
||||
Body = methodMatcher?.Methods != null && methodMatcher.Methods.Count(m => m == "get") == 1 ? null : new BodyModel
|
||||
Body = methodMatcher?.Methods != null && methodMatcher.Methods.Any(m => m == "get") ? null : new BodyModel
|
||||
{
|
||||
Matcher = bodyMatcher != null ? Map(bodyMatcher.Matcher) : null,
|
||||
Func = bodyMatcher != null ? Map(bodyMatcher.Func) : null,
|
||||
@@ -85,31 +96,58 @@ namespace WireMock.Serialization
|
||||
{
|
||||
mappingModel.Response.StatusCode = null;
|
||||
mappingModel.Response.Headers = null;
|
||||
mappingModel.Response.BodyDestination = null;
|
||||
mappingModel.Response.Body = null;
|
||||
mappingModel.Response.BodyAsBytes = null;
|
||||
mappingModel.Response.BodyAsFile = null;
|
||||
mappingModel.Response.BodyAsFileIsCached = null;
|
||||
mappingModel.Response.UseTransformer = false;
|
||||
mappingModel.Response.BodyEncoding = null;
|
||||
mappingModel.Response.ProxyUrl = response.ProxyUrl;
|
||||
}
|
||||
else
|
||||
{
|
||||
mappingModel.Response.BodyDestination = response.ResponseMessage.BodyDestination;
|
||||
mappingModel.Response.StatusCode = response.ResponseMessage.StatusCode;
|
||||
mappingModel.Response.Headers = response.ResponseMessage.Headers;
|
||||
mappingModel.Response.Headers = Map(response.ResponseMessage.Headers);
|
||||
mappingModel.Response.Body = response.ResponseMessage.Body;
|
||||
mappingModel.Response.BodyAsBytes = response.ResponseMessage.BodyAsBytes;
|
||||
mappingModel.Response.BodyAsFile = response.ResponseMessage.BodyAsFile;
|
||||
mappingModel.Response.BodyAsFileIsCached = response.ResponseMessage.BodyAsFileIsCached;
|
||||
mappingModel.Response.UseTransformer = response.UseTransformer;
|
||||
mappingModel.Response.BodyEncoding = response.ResponseMessage.BodyEncoding != null
|
||||
? new EncodingModel
|
||||
|
||||
if (response.ResponseMessage.BodyEncoding != null)
|
||||
{
|
||||
mappingModel.Response.BodyEncoding = new EncodingModel
|
||||
{
|
||||
EncodingName = response.ResponseMessage.BodyEncoding.EncodingName,
|
||||
CodePage = response.ResponseMessage.BodyEncoding.CodePage,
|
||||
WebName = response.ResponseMessage.BodyEncoding.WebName
|
||||
}
|
||||
: null;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return mappingModel;
|
||||
}
|
||||
|
||||
public static MatcherModel[] Map([CanBeNull] IEnumerable<IMatcher> matchers)
|
||||
private static IDictionary<string, object> Map(IDictionary<string, WireMockList<string>> dictionary)
|
||||
{
|
||||
if (dictionary == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var newDictionary = new Dictionary<string, object>();
|
||||
foreach (var entry in dictionary)
|
||||
{
|
||||
object value = entry.Value.Count == 1 ? (object)entry.Value.ToString() : entry.Value;
|
||||
newDictionary.Add(entry.Key, value);
|
||||
}
|
||||
|
||||
return newDictionary;
|
||||
}
|
||||
|
||||
private static MatcherModel[] Map([CanBeNull] IEnumerable<IMatcher> matchers)
|
||||
{
|
||||
if (matchers == null || !matchers.Any())
|
||||
return null;
|
||||
@@ -117,7 +155,7 @@ namespace WireMock.Serialization
|
||||
return matchers.Select(Map).Where(x => x != null).ToArray();
|
||||
}
|
||||
|
||||
public static MatcherModel Map([CanBeNull] IMatcher matcher)
|
||||
private static MatcherModel Map([CanBeNull] IMatcher matcher)
|
||||
{
|
||||
if (matcher == null)
|
||||
return null;
|
||||
@@ -132,7 +170,7 @@ namespace WireMock.Serialization
|
||||
};
|
||||
}
|
||||
|
||||
public static string[] Map<T>([CanBeNull] IEnumerable<Func<T, bool>> funcs)
|
||||
private static string[] Map<T>([CanBeNull] IEnumerable<Func<T, bool>> funcs)
|
||||
{
|
||||
if (funcs == null || !funcs.Any())
|
||||
return null;
|
||||
@@ -140,7 +178,7 @@ namespace WireMock.Serialization
|
||||
return funcs.Select(Map).Where(x => x != null).ToArray();
|
||||
}
|
||||
|
||||
public static string Map<T>([CanBeNull] Func<T, bool> func)
|
||||
private static string Map<T>([CanBeNull] Func<T, bool> func)
|
||||
{
|
||||
return func?.ToString();
|
||||
}
|
||||
@@ -184,6 +222,5 @@ namespace WireMock.Serialization
|
||||
throw new NotSupportedException($"Matcher '{matcherName}' is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,24 +2,24 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
using SimMetrics.Net;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Admin.Requests;
|
||||
using WireMock.Admin.Settings;
|
||||
using WireMock.Http;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Serialization;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Util;
|
||||
using WireMock.Validation;
|
||||
using WireMock.Http;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Serialization;
|
||||
|
||||
namespace WireMock.Server
|
||||
{
|
||||
@@ -32,6 +32,7 @@ namespace WireMock.Server
|
||||
private const string AdminMappings = "/__admin/mappings";
|
||||
private const string AdminRequests = "/__admin/requests";
|
||||
private const string AdminSettings = "/__admin/settings";
|
||||
private const string AdminScenarios = "/__admin/scenarios";
|
||||
private readonly RegexMatcher _adminMappingsGuidPathMatcher = new RegexMatcher(@"^\/__admin\/mappings\/(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$");
|
||||
private readonly RegexMatcher _adminRequestsGuidPathMatcher = new RegexMatcher(@"^\/__admin\/requests\/(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$");
|
||||
|
||||
@@ -70,9 +71,8 @@ namespace WireMock.Server
|
||||
Check.NotNull(filename, nameof(filename));
|
||||
|
||||
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename);
|
||||
Guid guidFromFilename;
|
||||
|
||||
if (Guid.TryParse(filenameWithoutExtension, out guidFromFilename))
|
||||
if (Guid.TryParse(filenameWithoutExtension, out var guidFromFilename))
|
||||
{
|
||||
DeserializeAndAddMapping(File.ReadAllText(filename), guidFromFilename);
|
||||
}
|
||||
@@ -119,22 +119,42 @@ namespace WireMock.Server
|
||||
|
||||
// __admin/requests/find
|
||||
Given(Request.Create().WithPath(AdminRequests + "/find").UsingPost()).RespondWith(new DynamicResponseProvider(RequestsFind));
|
||||
|
||||
|
||||
// __admin/scenarios
|
||||
Given(Request.Create().WithPath(AdminScenarios).UsingGet()).RespondWith(new DynamicResponseProvider(ScenariosGet));
|
||||
Given(Request.Create().WithPath(AdminScenarios).UsingDelete()).RespondWith(new DynamicResponseProvider(ScenariosReset));
|
||||
|
||||
// __admin/scenarios/reset
|
||||
Given(Request.Create().WithPath(AdminScenarios + "/reset").UsingPost()).RespondWith(new DynamicResponseProvider(ScenariosReset));
|
||||
}
|
||||
|
||||
#region Proxy and Record
|
||||
private HttpClient _httpClientForProxy;
|
||||
|
||||
private void InitProxyAndRecord(ProxyAndRecordSettings settings)
|
||||
{
|
||||
_httpClientForProxy = HttpClientHelper.CreateHttpClient(settings.X509Certificate2ThumbprintOrSubjectName);
|
||||
Given(Request.Create().WithPath("/*").UsingAnyVerb()).RespondWith(new ProxyAsyncResponseProvider(ProxyAndRecordAsync, settings));
|
||||
}
|
||||
|
||||
private async Task<ResponseMessage> ProxyAndRecordAsync(RequestMessage requestMessage, ProxyAndRecordSettings settings)
|
||||
{
|
||||
var responseMessage = await HttpClientHelper.SendAsync(requestMessage, settings.Url);
|
||||
var requestUri = new Uri(requestMessage.Url);
|
||||
var proxyUri = new Uri(settings.Url);
|
||||
var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery);
|
||||
|
||||
var responseMessage = await HttpClientHelper.SendAsync(_httpClientForProxy, requestMessage, proxyUriWithRequestPathAndQuery.AbsoluteUri);
|
||||
|
||||
if (settings.SaveMapping)
|
||||
{
|
||||
var mapping = ToMapping(requestMessage, responseMessage);
|
||||
SaveMappingToFile(mapping);
|
||||
_options.Mappings.Add(mapping);
|
||||
|
||||
if (settings.SaveMappingToFile)
|
||||
{
|
||||
SaveMappingToFile(mapping);
|
||||
}
|
||||
}
|
||||
|
||||
return responseMessage;
|
||||
@@ -142,13 +162,22 @@ namespace WireMock.Server
|
||||
|
||||
private Mapping ToMapping(RequestMessage requestMessage, ResponseMessage responseMessage)
|
||||
{
|
||||
var request = (Request)Request.Create();
|
||||
var request = Request.Create();
|
||||
request.WithPath(requestMessage.Path);
|
||||
request.UsingVerb(requestMessage.Method);
|
||||
|
||||
var response = (Response)Response.Create(responseMessage);
|
||||
requestMessage.Query.Loop((key, value) => request.WithParam(key, value.ToArray()));
|
||||
requestMessage.Headers.Loop((key, value) => request.WithHeader(key, value.ToArray()));
|
||||
requestMessage.Cookies.Loop((key, value) => request.WithCookie(key, value));
|
||||
|
||||
return new Mapping(Guid.NewGuid(), string.Empty, request, response, 0);
|
||||
if (requestMessage.Body != null)
|
||||
{
|
||||
request.WithBody(new ExactMatcher(requestMessage.Body));
|
||||
}
|
||||
|
||||
var response = Response.Create(responseMessage);
|
||||
|
||||
return new Mapping(Guid.NewGuid(), string.Empty, request, response, 0, null, null, null);
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -158,7 +187,9 @@ namespace WireMock.Server
|
||||
var model = new SettingsModel
|
||||
{
|
||||
AllowPartialMapping = _options.AllowPartialMapping,
|
||||
GlobalProcessingDelay = (int?) _options.RequestProcessingDelay?.TotalMilliseconds
|
||||
MaxRequestLogCount = _options.MaxRequestLogCount,
|
||||
RequestLogExpirationDuration = _options.RequestLogExpirationDuration,
|
||||
GlobalProcessingDelay = (int?)_options.RequestProcessingDelay?.TotalMilliseconds
|
||||
};
|
||||
|
||||
return ToJson(model);
|
||||
@@ -171,6 +202,10 @@ namespace WireMock.Server
|
||||
if (settings.AllowPartialMapping != null)
|
||||
_options.AllowPartialMapping = settings.AllowPartialMapping.Value;
|
||||
|
||||
_options.MaxRequestLogCount = settings.MaxRequestLogCount;
|
||||
|
||||
_options.RequestLogExpirationDuration = settings.RequestLogExpirationDuration;
|
||||
|
||||
if (settings.GlobalProcessingDelay != null)
|
||||
_options.RequestProcessingDelay = TimeSpan.FromMilliseconds(settings.GlobalProcessingDelay.Value);
|
||||
|
||||
@@ -314,6 +349,13 @@ namespace WireMock.Server
|
||||
if (mappingModel.Priority != null)
|
||||
respondProvider = respondProvider.AtPriority(mappingModel.Priority.Value);
|
||||
|
||||
if (mappingModel.Scenario != null)
|
||||
{
|
||||
respondProvider = respondProvider.InScenario(mappingModel.Scenario);
|
||||
respondProvider = respondProvider.WhenStateIs(mappingModel.WhenStateIs);
|
||||
respondProvider = respondProvider.WillSetStateTo(mappingModel.SetStateTo);
|
||||
}
|
||||
|
||||
respondProvider.RespondWith(responseBuilder);
|
||||
}
|
||||
|
||||
@@ -321,6 +363,8 @@ namespace WireMock.Server
|
||||
{
|
||||
ResetMappings();
|
||||
|
||||
ResetScenarios();
|
||||
|
||||
return new ResponseMessage { Body = "Mappings deleted" };
|
||||
}
|
||||
#endregion Mappings
|
||||
@@ -368,6 +412,7 @@ namespace WireMock.Server
|
||||
Request = new LogRequestModel
|
||||
{
|
||||
DateTime = logEntry.RequestMessage.DateTime,
|
||||
ClientIP = logEntry.RequestMessage.ClientIP,
|
||||
Path = logEntry.RequestMessage.Path,
|
||||
AbsoluteUrl = logEntry.RequestMessage.Url,
|
||||
Query = logEntry.RequestMessage.Query,
|
||||
@@ -385,8 +430,12 @@ namespace WireMock.Server
|
||||
Response = new LogResponseModel
|
||||
{
|
||||
StatusCode = logEntry.ResponseMessage.StatusCode,
|
||||
BodyDestination = logEntry.ResponseMessage.BodyDestination,
|
||||
Body = logEntry.ResponseMessage.Body,
|
||||
BodyAsBytes = logEntry.ResponseMessage.BodyAsBytes,
|
||||
BodyOriginal = logEntry.ResponseMessage.BodyOriginal,
|
||||
BodyAsFile = logEntry.ResponseMessage.BodyAsFile,
|
||||
BodyAsFileIsCached = logEntry.ResponseMessage.BodyAsFileIsCached,
|
||||
Headers = logEntry.ResponseMessage.Headers,
|
||||
BodyEncoding = logEntry.ResponseMessage.BodyEncoding != null ? new EncodingModel
|
||||
{
|
||||
@@ -402,7 +451,12 @@ namespace WireMock.Server
|
||||
TotalScore = logEntry.RequestMatchResult.TotalScore,
|
||||
TotalNumber = logEntry.RequestMatchResult.TotalNumber,
|
||||
IsPerfectMatch = logEntry.RequestMatchResult.IsPerfectMatch,
|
||||
AverageTotalScore = logEntry.RequestMatchResult.AverageTotalScore
|
||||
AverageTotalScore = logEntry.RequestMatchResult.AverageTotalScore,
|
||||
MatchDetails = logEntry.RequestMatchResult.MatchDetails.Select(x => new
|
||||
{
|
||||
Name = x.Key.Name.Replace("RequestMessage", string.Empty),
|
||||
Score = x.Value
|
||||
} as object).ToList()
|
||||
} : null
|
||||
};
|
||||
}
|
||||
@@ -426,8 +480,10 @@ namespace WireMock.Server
|
||||
foreach (var logEntry in LogEntries.Where(le => !le.RequestMessage.Path.StartsWith("/__admin/")))
|
||||
{
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
if (request.GetMatchingScore(logEntry.RequestMessage, requestMatchResult) > 0.99)
|
||||
if (request.GetMatchingScore(logEntry.RequestMessage, requestMatchResult) > MatchScores.AlmostPerfect)
|
||||
{
|
||||
dict.Add(logEntry, requestMatchResult);
|
||||
}
|
||||
}
|
||||
|
||||
var result = dict.OrderBy(x => x.Value.AverageTotalScore).Select(x => x.Key);
|
||||
@@ -436,20 +492,61 @@ namespace WireMock.Server
|
||||
}
|
||||
#endregion Requests/find
|
||||
|
||||
#region Scenarios
|
||||
private ResponseMessage ScenariosGet(RequestMessage requestMessage)
|
||||
{
|
||||
var scenarios = Scenarios.Select(s => new
|
||||
{
|
||||
Name = s.Key,
|
||||
Started = s.Value != null,
|
||||
NextState = s.Value
|
||||
});
|
||||
return ToJson(scenarios);
|
||||
}
|
||||
|
||||
private ResponseMessage ScenariosReset(RequestMessage requestMessage)
|
||||
{
|
||||
ResetScenarios();
|
||||
|
||||
return new ResponseMessage { Body = "Scenarios reset" };
|
||||
}
|
||||
#endregion
|
||||
|
||||
private IRequestBuilder InitRequestBuilder(RequestModel requestModel)
|
||||
{
|
||||
IRequestBuilder requestBuilder = Request.Create();
|
||||
|
||||
if (requestModel.ClientIP != null)
|
||||
{
|
||||
string clientIP = requestModel.ClientIP as string;
|
||||
if (clientIP != null)
|
||||
{
|
||||
requestBuilder = requestBuilder.WithClientIP(clientIP);
|
||||
}
|
||||
else
|
||||
{
|
||||
var clientIPModel = JsonUtils.ParseJTokenToObject<ClientIPModel>(requestModel.ClientIP);
|
||||
if (clientIPModel?.Matchers != null)
|
||||
{
|
||||
requestBuilder = requestBuilder.WithPath(clientIPModel.Matchers.Select(MappingConverter.Map).ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (requestModel.Path != null)
|
||||
{
|
||||
string path = requestModel.Path as string;
|
||||
if (path != null)
|
||||
{
|
||||
requestBuilder = requestBuilder.WithPath(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
var pathModel = JsonUtils.ParseJTokenToObject<PathModel>(requestModel.Path);
|
||||
if (pathModel?.Matchers != null)
|
||||
{
|
||||
requestBuilder = requestBuilder.WithPath(pathModel.Matchers.Select(MappingConverter.Map).ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,17 +554,23 @@ namespace WireMock.Server
|
||||
{
|
||||
string url = requestModel.Url as string;
|
||||
if (url != null)
|
||||
{
|
||||
requestBuilder = requestBuilder.WithUrl(url);
|
||||
}
|
||||
else
|
||||
{
|
||||
var urlModel = JsonUtils.ParseJTokenToObject<UrlModel>(requestModel.Url);
|
||||
if (urlModel?.Matchers != null)
|
||||
{
|
||||
requestBuilder = requestBuilder.WithUrl(urlModel.Matchers.Select(MappingConverter.Map).ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (requestModel.Methods != null)
|
||||
{
|
||||
requestBuilder = requestBuilder.UsingVerb(requestModel.Methods);
|
||||
}
|
||||
|
||||
if (requestModel.Headers != null)
|
||||
{
|
||||
@@ -513,7 +616,12 @@ namespace WireMock.Server
|
||||
|
||||
if (!string.IsNullOrEmpty(responseModel.ProxyUrl))
|
||||
{
|
||||
return responseBuilder.WithProxy(responseModel.ProxyUrl);
|
||||
if (string.IsNullOrEmpty(responseModel.X509Certificate2ThumbprintOrSubjectName))
|
||||
{
|
||||
return responseBuilder.WithProxy(responseModel.ProxyUrl);
|
||||
}
|
||||
|
||||
return responseBuilder.WithProxy(responseModel.ProxyUrl, responseModel.X509Certificate2ThumbprintOrSubjectName);
|
||||
}
|
||||
|
||||
if (responseModel.StatusCode.HasValue)
|
||||
@@ -523,7 +631,12 @@ namespace WireMock.Server
|
||||
|
||||
if (responseModel.Headers != null)
|
||||
{
|
||||
responseBuilder = responseBuilder.WithHeaders(responseModel.Headers);
|
||||
foreach (var entry in responseModel.Headers)
|
||||
{
|
||||
responseBuilder = entry.Value is string value ?
|
||||
responseBuilder.WithHeader(entry.Key, value) :
|
||||
responseBuilder.WithHeader(entry.Key, JsonUtils.ParseJTokenToObject<string[]>(entry.Value));
|
||||
}
|
||||
}
|
||||
else if (responseModel.HeadersRaw != null)
|
||||
{
|
||||
@@ -536,17 +649,21 @@ namespace WireMock.Server
|
||||
}
|
||||
}
|
||||
|
||||
if (responseModel.Body != null)
|
||||
if (responseModel.BodyAsBytes != null)
|
||||
{
|
||||
responseBuilder = responseBuilder.WithBody(responseModel.Body, ToEncoding(responseModel.BodyEncoding));
|
||||
responseBuilder = responseBuilder.WithBody(responseModel.BodyAsBytes, responseModel.BodyDestination, ToEncoding(responseModel.BodyEncoding));
|
||||
}
|
||||
else if (responseModel.Body != null)
|
||||
{
|
||||
responseBuilder = responseBuilder.WithBody(responseModel.Body, responseModel.BodyDestination, ToEncoding(responseModel.BodyEncoding));
|
||||
}
|
||||
else if (responseModel.BodyAsJson != null)
|
||||
{
|
||||
responseBuilder = responseBuilder.WithBodyAsJson(responseModel.BodyAsJson, ToEncoding(responseModel.BodyEncoding));
|
||||
}
|
||||
else if (responseModel.BodyAsBase64 != null)
|
||||
else if (responseModel.BodyFromBase64 != null)
|
||||
{
|
||||
responseBuilder = responseBuilder.WithBodyAsBase64(responseModel.BodyAsBase64, ToEncoding(responseModel.BodyEncoding));
|
||||
responseBuilder = responseBuilder.WithBodyFromBase64(responseModel.BodyFromBase64, ToEncoding(responseModel.BodyEncoding));
|
||||
}
|
||||
|
||||
if (responseModel.UseTransformer)
|
||||
@@ -563,7 +680,7 @@ namespace WireMock.Server
|
||||
{
|
||||
Body = JsonConvert.SerializeObject(result, _settings),
|
||||
StatusCode = 200,
|
||||
Headers = new Dictionary<string, string> { { "Content-Type", "application/json" } }
|
||||
Headers = new Dictionary<string, WireMockList<string>> { { "Content-Type", new WireMockList<string>("application/json") } }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,90 +1,86 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers.Request;
|
||||
using System.Linq;
|
||||
|
||||
namespace WireMock.Server
|
||||
{
|
||||
public partial class FluentMockServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the request logs.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public IEnumerable<LogEntry> LogEntries
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (((ICollection)_options.LogEntries).SyncRoot)
|
||||
{
|
||||
return new ReadOnlyCollection<LogEntry>(_options.LogEntries);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The search log-entries based on matchers.
|
||||
/// </summary>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IEnumerable"/>.</returns>
|
||||
[PublicAPI]
|
||||
public IEnumerable<LogEntry> FindLogEntries([NotNull] params IRequestMatcher[] matchers)
|
||||
{
|
||||
lock (((ICollection)_options.LogEntries).SyncRoot)
|
||||
{
|
||||
var results = new Dictionary<LogEntry, RequestMatchResult>();
|
||||
|
||||
foreach (var log in _options.LogEntries)
|
||||
{
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
foreach (var matcher in matchers)
|
||||
{
|
||||
matcher.GetMatchingScore(log.RequestMessage, requestMatchResult);
|
||||
}
|
||||
|
||||
if (requestMatchResult.AverageTotalScore > 0.99)
|
||||
results.Add(log, requestMatchResult);
|
||||
}
|
||||
|
||||
return new ReadOnlyCollection<LogEntry>(results.OrderBy(x => x.Value).Select(x => x.Key).ToList());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the LogEntries.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void ResetLogEntries()
|
||||
{
|
||||
lock (((ICollection)_options.LogEntries).SyncRoot)
|
||||
{
|
||||
_options.LogEntries.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
[PublicAPI]
|
||||
public bool DeleteLogEntry(Guid guid)
|
||||
{
|
||||
lock (((ICollection)_options.LogEntries).SyncRoot)
|
||||
{
|
||||
// Check a logentry exists with the same GUID, if so, remove it.
|
||||
var existing = _options.LogEntries.FirstOrDefault(m => m.Guid == guid);
|
||||
if (existing != null)
|
||||
{
|
||||
_options.LogEntries.Remove(existing);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers.Request;
|
||||
using System.Linq;
|
||||
using WireMock.Matchers;
|
||||
|
||||
namespace WireMock.Server
|
||||
{
|
||||
public partial class FluentMockServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Log entries notification handler
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public event NotifyCollectionChangedEventHandler LogEntriesChanged
|
||||
{
|
||||
add => _options.LogEntries.CollectionChanged += value;
|
||||
remove => _options.LogEntries.CollectionChanged -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request logs.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public IEnumerable<LogEntry> LogEntries => new ReadOnlyCollection<LogEntry>(_options.LogEntries);
|
||||
|
||||
/// <summary>
|
||||
/// The search log-entries based on matchers.
|
||||
/// </summary>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IEnumerable"/>.</returns>
|
||||
[PublicAPI]
|
||||
public IEnumerable<LogEntry> FindLogEntries([NotNull] params IRequestMatcher[] matchers)
|
||||
{
|
||||
var results = new Dictionary<LogEntry, RequestMatchResult>();
|
||||
|
||||
foreach (var log in _options.LogEntries)
|
||||
{
|
||||
var requestMatchResult = new RequestMatchResult();
|
||||
foreach (var matcher in matchers)
|
||||
{
|
||||
matcher.GetMatchingScore(log.RequestMessage, requestMatchResult);
|
||||
}
|
||||
|
||||
if (requestMatchResult.AverageTotalScore > MatchScores.AlmostPerfect)
|
||||
{
|
||||
results.Add(log, requestMatchResult);
|
||||
}
|
||||
}
|
||||
|
||||
return new ReadOnlyCollection<LogEntry>(results.OrderBy(x => x.Value).Select(x => x.Key).ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the LogEntries.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void ResetLogEntries()
|
||||
{
|
||||
_options.LogEntries.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
[PublicAPI]
|
||||
public bool DeleteLogEntry(Guid guid)
|
||||
{
|
||||
// Check a logentry exists with the same GUID, if so, remove it.
|
||||
var existing = _options.LogEntries.FirstOrDefault(m => m.Guid == guid);
|
||||
if (existing != null)
|
||||
{
|
||||
_options.LogEntries.Remove(existing);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,337 +1,380 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Http;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Validation;
|
||||
using WireMock.Owin;
|
||||
|
||||
namespace WireMock.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// The fluent mock server.
|
||||
/// </summary>
|
||||
public partial class FluentMockServer : IDisposable
|
||||
{
|
||||
private readonly IOwinSelfHost _httpServer;
|
||||
private readonly object _syncRoot = new object();
|
||||
private readonly WireMockMiddlewareOptions _options = new WireMockMiddlewareOptions();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ports.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The ports.
|
||||
/// </value>
|
||||
[PublicAPI]
|
||||
public List<int> Ports { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the urls.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string[] Urls { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mappings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public IEnumerable<Mapping> Mappings
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (((ICollection)_options.Mappings).SyncRoot)
|
||||
{
|
||||
return new ReadOnlyCollection<Mapping>(_options.Mappings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Start/Stop
|
||||
/// <summary>
|
||||
/// Starts the specified settings.
|
||||
/// </summary>
|
||||
/// <param name="settings">The FluentMockServerSettings.</param>
|
||||
/// <returns>The <see cref="FluentMockServer"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer Start(FluentMockServerSettings settings)
|
||||
{
|
||||
Check.NotNull(settings, nameof(settings));
|
||||
|
||||
return new FluentMockServer(settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start this FluentMockServer.
|
||||
/// </summary>
|
||||
/// <param name="port">The port.</param>
|
||||
/// <param name="ssl">The SSL support.</param>
|
||||
/// <returns>The <see cref="FluentMockServer"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer Start([CanBeNull] int? port = 0, bool ssl = false)
|
||||
{
|
||||
return new FluentMockServer(new FluentMockServerSettings
|
||||
{
|
||||
Port = port,
|
||||
UseSSL = ssl
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start this FluentMockServer.
|
||||
/// </summary>
|
||||
/// <param name="urls">The urls to listen on.</param>
|
||||
/// <returns>The <see cref="FluentMockServer"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer Start(params string[] urls)
|
||||
{
|
||||
Check.NotEmpty(urls, nameof(urls));
|
||||
|
||||
return new FluentMockServer(new FluentMockServerSettings
|
||||
{
|
||||
Urls = urls
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start this FluentMockServer with the admin interface.
|
||||
/// </summary>
|
||||
/// <param name="port">The port.</param>
|
||||
/// <param name="ssl">The SSL support.</param>
|
||||
/// <returns>The <see cref="FluentMockServer"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer StartWithAdminInterface(int? port = 0, bool ssl = false)
|
||||
{
|
||||
return new FluentMockServer(new FluentMockServerSettings
|
||||
{
|
||||
Port = port,
|
||||
UseSSL = ssl,
|
||||
StartAdminInterface = true
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start this FluentMockServer with the admin interface.
|
||||
/// </summary>
|
||||
/// <param name="urls">The urls.</param>
|
||||
/// <returns>The <see cref="FluentMockServer"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer StartWithAdminInterface(params string[] urls)
|
||||
{
|
||||
Check.NotEmpty(urls, nameof(urls));
|
||||
|
||||
return new FluentMockServer(new FluentMockServerSettings
|
||||
{
|
||||
Urls = urls,
|
||||
StartAdminInterface = true
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start this FluentMockServer with the admin interface and read static mappings.
|
||||
/// </summary>
|
||||
/// <param name="urls">The urls.</param>
|
||||
/// <returns>The <see cref="FluentMockServer"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer StartWithAdminInterfaceAndReadStaticMappings(params string[] urls)
|
||||
{
|
||||
Check.NotEmpty(urls, nameof(urls));
|
||||
|
||||
return new FluentMockServer(new FluentMockServerSettings
|
||||
{
|
||||
Urls = urls,
|
||||
StartAdminInterface = true,
|
||||
ReadStaticMappings = true
|
||||
});
|
||||
}
|
||||
|
||||
private FluentMockServer(FluentMockServerSettings settings)
|
||||
{
|
||||
if (settings.Urls != null)
|
||||
{
|
||||
Urls = settings.Urls;
|
||||
}
|
||||
else
|
||||
{
|
||||
int port = settings.Port > 0 ? settings.Port.Value : PortUtil.FindFreeTcpPort();
|
||||
Urls = new[] { (settings.UseSSL == true ? "https" : "http") + "://localhost:" + port + "/" };
|
||||
}
|
||||
|
||||
#if NETSTANDARD
|
||||
_httpServer = new AspNetCoreSelfHost(_options, Urls);
|
||||
#else
|
||||
_httpServer = new OwinSelfHost(_options, Urls);
|
||||
#endif
|
||||
Ports = _httpServer.Ports;
|
||||
|
||||
_httpServer.StartAsync();
|
||||
|
||||
if (settings.StartAdminInterface == true)
|
||||
{
|
||||
InitAdmin();
|
||||
}
|
||||
|
||||
if (settings.ReadStaticMappings == true)
|
||||
{
|
||||
ReadStaticMappings();
|
||||
}
|
||||
|
||||
if (settings.ProxyAndRecordSettings != null)
|
||||
{
|
||||
InitProxyAndRecord(settings.ProxyAndRecordSettings);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop this server.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void Stop()
|
||||
{
|
||||
_httpServer?.StopAsync();
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Adds the catch all mapping.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void AddCatchAllMapping()
|
||||
{
|
||||
Given(Request.Create().WithPath("/*").UsingAnyVerb())
|
||||
.WithGuid(Guid.Parse("90008000-0000-4444-a17e-669cd84f1f05"))
|
||||
.AtPriority(1000)
|
||||
.RespondWith(new DynamicResponseProvider(request => new ResponseMessage { StatusCode = 404, Body = "No matching mapping found" }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_httpServer != null && _httpServer.IsStarted)
|
||||
{
|
||||
_httpServer.StopAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets LogEntries and Mappings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void Reset()
|
||||
{
|
||||
ResetLogEntries();
|
||||
|
||||
ResetMappings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the Mappings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void ResetMappings()
|
||||
{
|
||||
lock (((ICollection)_options.Mappings).SyncRoot)
|
||||
{
|
||||
_options.Mappings = _options.Mappings.Where(m => m.IsAdminInterface).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
[PublicAPI]
|
||||
public bool DeleteMapping(Guid guid)
|
||||
{
|
||||
lock (((ICollection)_options.Mappings).SyncRoot)
|
||||
{
|
||||
// Check a mapping exists with the same GUID, if so, remove it.
|
||||
var existingMapping = _options.Mappings.FirstOrDefault(m => m.Guid == guid);
|
||||
if (existingMapping != null)
|
||||
{
|
||||
_options.Mappings.Remove(existingMapping);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The add request processing delay.
|
||||
/// </summary>
|
||||
/// <param name="delay">The delay.</param>
|
||||
[PublicAPI]
|
||||
public void AddGlobalProcessingDelay(TimeSpan delay)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
_options.RequestProcessingDelay = delay;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows the partial mapping.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void AllowPartialMapping()
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
_options.AllowPartialMapping = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the basic authentication.
|
||||
/// </summary>
|
||||
/// <param name="username">The username.</param>
|
||||
/// <param name="password">The password.</param>
|
||||
[PublicAPI]
|
||||
public void SetBasicAuthentication([NotNull] string username, [NotNull] string password)
|
||||
{
|
||||
Check.NotNull(username, nameof(username));
|
||||
Check.NotNull(password, nameof(password));
|
||||
|
||||
string authorization = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
|
||||
_options.AuthorizationMatcher = new RegexMatcher("^(?i)BASIC " + authorization + "$");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The given.
|
||||
/// </summary>
|
||||
/// <param name="requestMatcher">The request matcher.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
[PublicAPI]
|
||||
public IRespondWithAProvider Given(IRequestMatcher requestMatcher)
|
||||
{
|
||||
return new RespondWithAProvider(RegisterMapping, requestMatcher);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The register mapping.
|
||||
/// </summary>
|
||||
/// <param name="mapping">
|
||||
/// The mapping.
|
||||
/// </param>
|
||||
private void RegisterMapping(Mapping mapping)
|
||||
{
|
||||
lock (((ICollection)_options.Mappings).SyncRoot)
|
||||
{
|
||||
// Check a mapping exists with the same GUID, if so, remove it first.
|
||||
DeleteMapping(mapping.Guid);
|
||||
|
||||
_options.Mappings.Add(mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Http;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Validation;
|
||||
using WireMock.Owin;
|
||||
|
||||
namespace WireMock.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// The fluent mock server.
|
||||
/// </summary>
|
||||
public partial class FluentMockServer : IDisposable
|
||||
{
|
||||
private const int ServerStartDelay = 100;
|
||||
private readonly IOwinSelfHost _httpServer;
|
||||
private readonly WireMockMiddlewareOptions _options = new WireMockMiddlewareOptions();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ports.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The ports.
|
||||
/// </value>
|
||||
[PublicAPI]
|
||||
public List<int> Ports { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the urls.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string[] Urls { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mappings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public IEnumerable<Mapping> Mappings => new ReadOnlyCollection<Mapping>(_options.Mappings);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scenarios.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public IDictionary<string, object> Scenarios => new ConcurrentDictionary<string, object>(_options.Scenarios);
|
||||
|
||||
#region Start/Stop
|
||||
/// <summary>
|
||||
/// Starts the specified settings.
|
||||
/// </summary>
|
||||
/// <param name="settings">The FluentMockServerSettings.</param>
|
||||
/// <returns>The <see cref="FluentMockServer"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer Start(FluentMockServerSettings settings)
|
||||
{
|
||||
Check.NotNull(settings, nameof(settings));
|
||||
|
||||
return new FluentMockServer(settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start this FluentMockServer.
|
||||
/// </summary>
|
||||
/// <param name="port">The port.</param>
|
||||
/// <param name="ssl">The SSL support.</param>
|
||||
/// <returns>The <see cref="FluentMockServer"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer Start([CanBeNull] int? port = 0, bool ssl = false)
|
||||
{
|
||||
return new FluentMockServer(new FluentMockServerSettings
|
||||
{
|
||||
Port = port,
|
||||
UseSSL = ssl
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start this FluentMockServer.
|
||||
/// </summary>
|
||||
/// <param name="urls">The urls to listen on.</param>
|
||||
/// <returns>The <see cref="FluentMockServer"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer Start(params string[] urls)
|
||||
{
|
||||
Check.NotEmpty(urls, nameof(urls));
|
||||
|
||||
return new FluentMockServer(new FluentMockServerSettings
|
||||
{
|
||||
Urls = urls
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start this FluentMockServer with the admin interface.
|
||||
/// </summary>
|
||||
/// <param name="port">The port.</param>
|
||||
/// <param name="ssl">The SSL support.</param>
|
||||
/// <returns>The <see cref="FluentMockServer"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer StartWithAdminInterface(int? port = 0, bool ssl = false)
|
||||
{
|
||||
return new FluentMockServer(new FluentMockServerSettings
|
||||
{
|
||||
Port = port,
|
||||
UseSSL = ssl,
|
||||
StartAdminInterface = true
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start this FluentMockServer with the admin interface.
|
||||
/// </summary>
|
||||
/// <param name="urls">The urls.</param>
|
||||
/// <returns>The <see cref="FluentMockServer"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer StartWithAdminInterface(params string[] urls)
|
||||
{
|
||||
Check.NotEmpty(urls, nameof(urls));
|
||||
|
||||
return new FluentMockServer(new FluentMockServerSettings
|
||||
{
|
||||
Urls = urls,
|
||||
StartAdminInterface = true
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start this FluentMockServer with the admin interface and read static mappings.
|
||||
/// </summary>
|
||||
/// <param name="urls">The urls.</param>
|
||||
/// <returns>The <see cref="FluentMockServer"/>.</returns>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer StartWithAdminInterfaceAndReadStaticMappings(params string[] urls)
|
||||
{
|
||||
Check.NotEmpty(urls, nameof(urls));
|
||||
|
||||
return new FluentMockServer(new FluentMockServerSettings
|
||||
{
|
||||
Urls = urls,
|
||||
StartAdminInterface = true,
|
||||
ReadStaticMappings = true
|
||||
});
|
||||
}
|
||||
|
||||
private FluentMockServer(FluentMockServerSettings settings)
|
||||
{
|
||||
if (settings.Urls != null)
|
||||
{
|
||||
Urls = settings.Urls;
|
||||
}
|
||||
else
|
||||
{
|
||||
int port = settings.Port > 0 ? settings.Port.Value : PortUtil.FindFreeTcpPort();
|
||||
Urls = new[] { (settings.UseSSL == true ? "https" : "http") + "://localhost:" + port + "/" };
|
||||
}
|
||||
|
||||
_options.PreWireMockMiddlewareInit = settings.PreWireMockMiddlewareInit;
|
||||
_options.PostWireMockMiddlewareInit = settings.PostWireMockMiddlewareInit;
|
||||
|
||||
#if NETSTANDARD
|
||||
_httpServer = new AspNetCoreSelfHost(_options, Urls);
|
||||
#else
|
||||
_httpServer = new OwinSelfHost(_options, Urls);
|
||||
#endif
|
||||
Ports = _httpServer.Ports;
|
||||
|
||||
_httpServer.StartAsync();
|
||||
|
||||
// Fix for 'Bug: Server not listening after Start() returns (on macOS)'
|
||||
Task.Delay(ServerStartDelay).Wait();
|
||||
|
||||
if (settings.AllowPartialMapping == true)
|
||||
{
|
||||
AllowPartialMapping();
|
||||
}
|
||||
|
||||
if (settings.StartAdminInterface == true)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(settings.AdminUsername) && !string.IsNullOrEmpty(settings.AdminPassword))
|
||||
{
|
||||
SetBasicAuthentication(settings.AdminUsername, settings.AdminPassword);
|
||||
}
|
||||
|
||||
InitAdmin();
|
||||
}
|
||||
|
||||
if (settings.ReadStaticMappings == true)
|
||||
{
|
||||
ReadStaticMappings();
|
||||
}
|
||||
|
||||
if (settings.ProxyAndRecordSettings != null)
|
||||
{
|
||||
InitProxyAndRecord(settings.ProxyAndRecordSettings);
|
||||
}
|
||||
|
||||
if (settings.MaxRequestLogCount != null)
|
||||
{
|
||||
SetMaxRequestLogCount(settings.MaxRequestLogCount);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop this server.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void Stop()
|
||||
{
|
||||
_httpServer?.StopAsync();
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Adds the catch all mapping.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void AddCatchAllMapping()
|
||||
{
|
||||
Given(Request.Create().WithPath("/*").UsingAnyVerb())
|
||||
.WithGuid(Guid.Parse("90008000-0000-4444-a17e-669cd84f1f05"))
|
||||
.AtPriority(1000)
|
||||
.RespondWith(new DynamicResponseProvider(request => new ResponseMessage { StatusCode = 404, Body = "No matching mapping found" }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_httpServer != null && _httpServer.IsStarted)
|
||||
{
|
||||
_httpServer.StopAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets LogEntries and Mappings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void Reset()
|
||||
{
|
||||
ResetLogEntries();
|
||||
|
||||
ResetMappings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the Mappings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void ResetMappings()
|
||||
{
|
||||
_options.Mappings = _options.Mappings.Where(m => m.IsAdminInterface).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
[PublicAPI]
|
||||
public bool DeleteMapping(Guid guid)
|
||||
{
|
||||
// Check a mapping exists with the same GUID, if so, remove it.
|
||||
var existingMapping = _options.Mappings.FirstOrDefault(m => m.Guid == guid);
|
||||
if (existingMapping != null)
|
||||
{
|
||||
_options.Mappings.Remove(existingMapping);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The add request processing delay.
|
||||
/// </summary>
|
||||
/// <param name="delay">The delay.</param>
|
||||
[PublicAPI]
|
||||
public void AddGlobalProcessingDelay(TimeSpan delay)
|
||||
{
|
||||
_options.RequestProcessingDelay = delay;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows the partial mapping.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void AllowPartialMapping()
|
||||
{
|
||||
_options.AllowPartialMapping = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the basic authentication.
|
||||
/// </summary>
|
||||
/// <param name="username">The username.</param>
|
||||
/// <param name="password">The password.</param>
|
||||
[PublicAPI]
|
||||
public void SetBasicAuthentication([NotNull] string username, [NotNull] string password)
|
||||
{
|
||||
Check.NotNull(username, nameof(username));
|
||||
Check.NotNull(password, nameof(password));
|
||||
|
||||
string authorization = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
|
||||
_options.AuthorizationMatcher = new RegexMatcher("^(?i)BASIC " + authorization + "$");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the basic authentication.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void RemoveBasicAuthentication()
|
||||
{
|
||||
_options.AuthorizationMatcher = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the maximum RequestLog count.
|
||||
/// </summary>
|
||||
/// <param name="maxRequestLogCount">The maximum RequestLog count.</param>
|
||||
[PublicAPI]
|
||||
public void SetMaxRequestLogCount([CanBeNull] int? maxRequestLogCount)
|
||||
{
|
||||
_options.MaxRequestLogCount = maxRequestLogCount;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets RequestLog expiration in hours.
|
||||
/// </summary>
|
||||
/// <param name="requestLogExpirationDuration">The RequestLog expiration in hours.</param>
|
||||
[PublicAPI]
|
||||
public void SetRequestLogExpirationDuration([CanBeNull] int? requestLogExpirationDuration)
|
||||
{
|
||||
_options.RequestLogExpirationDuration = requestLogExpirationDuration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the Scenarios.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void ResetScenarios()
|
||||
{
|
||||
_options.Scenarios.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The given.
|
||||
/// </summary>
|
||||
/// <param name="requestMatcher">The request matcher.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
[PublicAPI]
|
||||
public IRespondWithAProvider Given(IRequestMatcher requestMatcher)
|
||||
{
|
||||
return new RespondWithAProvider(RegisterMapping, requestMatcher);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The register mapping.
|
||||
/// </summary>
|
||||
/// <param name="mapping">
|
||||
/// The mapping.
|
||||
/// </param>
|
||||
private void RegisterMapping(Mapping mapping)
|
||||
{
|
||||
// Check a mapping exists with the same GUID, if so, remove it first.
|
||||
DeleteMapping(mapping.Guid);
|
||||
|
||||
_options.Mappings.Add(mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +1,65 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WireMock.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// IRespondWithAProvider
|
||||
/// </summary>
|
||||
public interface IRespondWithAProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Define a unique identifier for this mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider WithGuid(Guid guid);
|
||||
|
||||
/// <summary>
|
||||
/// Define a unique title for this mapping.
|
||||
/// </summary>
|
||||
/// <param name="title">The unique title.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider WithTitle(string title);
|
||||
|
||||
/// <summary>
|
||||
/// Define a unique identifier for this mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider WithGuid(string guid);
|
||||
|
||||
/// <summary>
|
||||
/// Define the priority for this mapping.
|
||||
/// </summary>
|
||||
/// <param name="priority">The priority.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider AtPriority(int priority);
|
||||
|
||||
/// <summary>
|
||||
/// The respond with.
|
||||
/// </summary>
|
||||
/// <param name="provider">The provider.</param>
|
||||
void RespondWith(IResponseProvider provider);
|
||||
|
||||
///// <summary>
|
||||
///// The respond with.
|
||||
///// </summary>
|
||||
///// <param name="provider">The provider.</param>
|
||||
//Task RespondWithAsync(IResponseProvider provider);
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace WireMock.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// IRespondWithAProvider
|
||||
/// </summary>
|
||||
public interface IRespondWithAProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Define a unique identifier for this mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider WithGuid(Guid guid);
|
||||
|
||||
/// <summary>
|
||||
/// Define a unique title for this mapping.
|
||||
/// </summary>
|
||||
/// <param name="title">The unique title.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider WithTitle(string title);
|
||||
|
||||
/// <summary>
|
||||
/// Define a unique identifier for this mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider WithGuid(string guid);
|
||||
|
||||
/// <summary>
|
||||
/// Define the priority for this mapping.
|
||||
/// </summary>
|
||||
/// <param name="priority">The priority.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider AtPriority(int priority);
|
||||
|
||||
/// <summary>
|
||||
/// The respond with.
|
||||
/// </summary>
|
||||
/// <param name="provider">The provider.</param>
|
||||
void RespondWith(IResponseProvider provider);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the the scenario.
|
||||
/// </summary>
|
||||
/// <param name="scenario">The scenario.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider InScenario(string scenario);
|
||||
|
||||
/// <summary>
|
||||
/// Execute this respond only in case the current state is equal to specified one.
|
||||
/// </summary>
|
||||
/// <param name="state">Any object which identifies the current state</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider WhenStateIs(object state);
|
||||
|
||||
/// <summary>
|
||||
/// Once this mapping is executed the state will be changed to specified one.
|
||||
/// </summary>
|
||||
/// <param name="state">Any object which identifies the new state</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider WillSetStateTo(object state);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,9 @@ namespace WireMock.Server
|
||||
private int _priority;
|
||||
private Guid? _guid;
|
||||
private string _title;
|
||||
private object _executionConditionState;
|
||||
private object _nextState;
|
||||
private string _scenario;
|
||||
|
||||
/// <summary>
|
||||
/// The _registration callback.
|
||||
@@ -42,7 +45,7 @@ namespace WireMock.Server
|
||||
public void RespondWith(IResponseProvider provider)
|
||||
{
|
||||
var mappingGuid = _guid ?? Guid.NewGuid();
|
||||
_registrationCallback(new Mapping(mappingGuid, _title, _requestMatcher, provider, _priority));
|
||||
_registrationCallback(new Mapping(mappingGuid, _title, _requestMatcher, provider, _priority, _scenario, _executionConditionState, _nextState));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -90,5 +93,41 @@ namespace WireMock.Server
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public IRespondWithAProvider InScenario(string scenario)
|
||||
{
|
||||
_scenario = scenario;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public IRespondWithAProvider WhenStateIs(object state)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_scenario))
|
||||
{
|
||||
throw new NotSupportedException("Unable to set state condition when no scenario is defined.");
|
||||
}
|
||||
|
||||
//if (_nextState != null)
|
||||
//{
|
||||
// throw new NotSupportedException("Unable to set state condition when next state is defined.");
|
||||
//}
|
||||
|
||||
_executionConditionState = state;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public IRespondWithAProvider WillSetStateTo(object state)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_scenario))
|
||||
{
|
||||
throw new NotSupportedException("Unable to set next state when no scenario is defined.");
|
||||
}
|
||||
|
||||
_nextState = state;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
namespace WireMock.Settings
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace WireMock.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// FluentMockServerSettings
|
||||
@@ -8,51 +11,87 @@
|
||||
/// <summary>
|
||||
/// Gets or sets the port.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The port.
|
||||
/// </value>
|
||||
[PublicAPI]
|
||||
public int? Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the use SSL.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The use SSL.
|
||||
/// </value>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
[PublicAPI]
|
||||
public bool? UseSSL { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start admin interface.
|
||||
/// Gets or sets wether to start admin interface.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The start admin interface.
|
||||
/// </value>
|
||||
[PublicAPI]
|
||||
public bool? StartAdminInterface { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the static mappings should be read at startup.
|
||||
/// </summary>
|
||||
/// <value>true/false</value>
|
||||
[PublicAPI]
|
||||
public bool? ReadStaticMappings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the server should record and save requests and responses.
|
||||
/// </summary>
|
||||
/// <value>true/false</value>
|
||||
[PublicAPI]
|
||||
public ProxyAndRecordSettings ProxyAndRecordSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the urls.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The urls.
|
||||
/// </value>
|
||||
[PublicAPI]
|
||||
public string[] Urls { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// StartTimeout
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public int StartTimeout { get; set; } = 10000;
|
||||
|
||||
/// <summary>
|
||||
/// Allow Partial Mapping (default set to false).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? AllowPartialMapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The username needed for __admin access.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string AdminUsername { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The password needed for __admin access.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string AdminPassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The RequestLog expiration in hours (optional).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public int? RequestLogExpirationDuration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The MaxRequestLog count (optional).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public int? MaxRequestLogCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Action which is called (with the IAppBuilder or IApplicationBuilder) before the internal WireMockMiddleware is initialized. [Optional]
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public Action<object> PreWireMockMiddlewareInit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Action which is called (with the IAppBuilder or IApplicationBuilder) after the internal WireMockMiddleware is initialized. [Optional]
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public Action<object> PostWireMockMiddlewareInit { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -11,13 +11,18 @@
|
||||
public string Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Save the mapping for each request/response.
|
||||
/// Save the mapping for each request/response to the internal Mappings.
|
||||
/// </summary>
|
||||
public bool SaveMapping { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// The clientCertificateFilename to use. Example : "C:\certificates\cert.pfx"
|
||||
/// Save the mapping for each request/response to also file. (Note that SaveMapping must also be set to true.)
|
||||
/// </summary>
|
||||
public string X509Certificate2Filename { get; set; }
|
||||
public bool SaveMappingToFile { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// The clientCertificate thumbprint or subject name fragment to use. Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
|
||||
/// </summary>
|
||||
public string X509Certificate2ThumbprintOrSubjectName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using HandlebarsDotNet;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using HandlebarsDotNet;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Transformers
|
||||
{
|
||||
@@ -16,13 +18,16 @@ namespace WireMock.Transformers
|
||||
responseMessage.Body = templateBody(template);
|
||||
|
||||
// Headers
|
||||
var newHeaders = new Dictionary<string, string>();
|
||||
var newHeaders = new Dictionary<string, WireMockList<string>>();
|
||||
foreach (var header in original.Headers)
|
||||
{
|
||||
var templateHeaderKey = Handlebars.Compile(header.Key);
|
||||
var templateHeaderValue = Handlebars.Compile(header.Value);
|
||||
var templateHeaderValues = header.Value
|
||||
.Select(Handlebars.Compile)
|
||||
.Select(func => func(template))
|
||||
.ToArray();
|
||||
|
||||
newHeaders.Add(templateHeaderKey(template), templateHeaderValue(template));
|
||||
newHeaders.Add(templateHeaderKey(template), new WireMockList<string>(templateHeaderValues));
|
||||
}
|
||||
|
||||
responseMessage.Headers = newHeaders;
|
||||
|
||||
77
src/WireMock.Net/Util/ConcurentObservableCollection.cs
Normal file
77
src/WireMock.Net/Util/ConcurentObservableCollection.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace WireMock.Util
|
||||
{
|
||||
/// <summary>
|
||||
/// A special Collection that overrides methods of <see cref="ObservableCollection{T}"/> to make them thread safe
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of elements in the collection.</typeparam>
|
||||
/// <inheritdoc cref="ObservableCollection{T}" />
|
||||
public class ConcurentObservableCollection<T> : ObservableCollection<T>
|
||||
{
|
||||
private readonly object _lockObject = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:WireMock.Util.ConcurentObservableCollection`1" /> class.
|
||||
/// </summary>
|
||||
public ConcurentObservableCollection() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConcurentObservableCollection{T}"/> class that contains elements copied from the specified list.
|
||||
/// </summary>
|
||||
/// <param name="list">The list from which the elements are copied.</param>
|
||||
public ConcurentObservableCollection(List<T> list) : base(list) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConcurentObservableCollection{T}"/> class that contains elements copied from the specified collection.
|
||||
/// </summary>
|
||||
/// <param name="collection">The collection from which the elements are copied.</param>
|
||||
public ConcurentObservableCollection(IEnumerable<T> collection) : base(collection) { }
|
||||
|
||||
/// <inheritdoc cref="ObservableCollection{T}.ClearItems"/>
|
||||
protected override void ClearItems()
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
base.ClearItems();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ObservableCollection{T}.RemoveItem"/>
|
||||
protected override void RemoveItem(int index)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
base.RemoveItem(index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ObservableCollection{T}.InsertItem"/>
|
||||
protected override void InsertItem(int index, T item)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
base.InsertItem(index, item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ObservableCollection{T}.SetItem"/>
|
||||
protected override void SetItem(int index, T item)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
base.SetItem(index, item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ObservableCollection{T}.MoveItem"/>
|
||||
protected override void MoveItem(int oldIndex, int newIndex)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
base.MoveItem(oldIndex, newIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/WireMock.Net/Util/DictionaryExtensions.cs
Normal file
33
src/WireMock.Net/Util/DictionaryExtensions.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Util
|
||||
{
|
||||
/// <summary>
|
||||
/// Some IDictionary Extensions
|
||||
/// </summary>
|
||||
public static class DictionaryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Loops the dictionary and executes the specified action.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the key.</typeparam>
|
||||
/// <typeparam name="TValue">The type of the value.</typeparam>
|
||||
/// <param name="dictionary">The dictionary to loop (can be null).</param>
|
||||
/// <param name="action">The action.</param>
|
||||
public static void Loop<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, [NotNull] Action<TKey, TValue> action)
|
||||
{
|
||||
Check.NotNull(action, nameof(action));
|
||||
|
||||
if (dictionary != null)
|
||||
{
|
||||
foreach (var entry in dictionary)
|
||||
{
|
||||
action(entry.Key, entry.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,12 @@ namespace WireMock.Util
|
||||
public static T ParseJTokenToObject<T>(object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
JToken token = value as JToken;
|
||||
if (token == null)
|
||||
return default(T);
|
||||
|
||||
return token.ToObject<T>();
|
||||
var token = value as JToken;
|
||||
return token == null ? default(T) : token.ToObject<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
80
src/WireMock.Net/Util/NamedReaderWriterLocker.cs
Normal file
80
src/WireMock.Net/Util/NamedReaderWriterLocker.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace WireMock.Util
|
||||
{
|
||||
/// <summary>
|
||||
/// http://johnculviner.com/achieving-named-lock-locker-functionality-in-c-4-0/
|
||||
/// </summary>
|
||||
internal class NamedReaderWriterLocker
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, ReaderWriterLockSlim> _lockDict = new ConcurrentDictionary<string, ReaderWriterLockSlim>();
|
||||
|
||||
public ReaderWriterLockSlim GetLock(string name)
|
||||
{
|
||||
return _lockDict.GetOrAdd(name, s => new ReaderWriterLockSlim());
|
||||
}
|
||||
|
||||
public TResult RunWithReadLock<TResult>(string name, Func<TResult> body)
|
||||
{
|
||||
var rwLock = GetLock(name);
|
||||
try
|
||||
{
|
||||
rwLock.EnterReadLock();
|
||||
return body();
|
||||
}
|
||||
finally
|
||||
{
|
||||
rwLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void RunWithReadLock(string name, Action body)
|
||||
{
|
||||
var rwLock = GetLock(name);
|
||||
try
|
||||
{
|
||||
rwLock.EnterReadLock();
|
||||
body();
|
||||
}
|
||||
finally
|
||||
{
|
||||
rwLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public TResult RunWithWriteLock<TResult>(string name, Func<TResult> body)
|
||||
{
|
||||
var rwLock = GetLock(name);
|
||||
try
|
||||
{
|
||||
rwLock.EnterWriteLock();
|
||||
return body();
|
||||
}
|
||||
finally
|
||||
{
|
||||
rwLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void RunWithWriteLock(string name, Action body)
|
||||
{
|
||||
var rwLock = GetLock(name);
|
||||
try
|
||||
{
|
||||
rwLock.EnterWriteLock();
|
||||
body();
|
||||
}
|
||||
finally
|
||||
{
|
||||
rwLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveLock(string name)
|
||||
{
|
||||
_lockDict.TryRemove(name, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,9 +36,6 @@ namespace WireMock.Util
|
||||
/// <summary>
|
||||
/// Returns a <see cref="string" /> that represents this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="string" /> that represents this instance.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (this != null && this.Any())
|
||||
|
||||
@@ -3,26 +3,26 @@
|
||||
<PropertyGroup>
|
||||
<Description>Lightweight Http Mocking Server for .Net, inspired by WireMock from the Java landscape.</Description>
|
||||
<AssemblyTitle>WireMock.Net</AssemblyTitle>
|
||||
<Version>1.0.2.1</Version>
|
||||
<Version>1.0.2.7</Version>
|
||||
<Authors>Alexandre Victoor;Stef Heyenrath</Authors>
|
||||
<TargetFrameworks>net45;net452;net46;netstandard1.3</TargetFrameworks>
|
||||
<TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net</AssemblyName>
|
||||
<PackageId>WireMock.Net</PackageId>
|
||||
<PackageTags>tdd;mock;http;wiremock;test;server;unittest</PackageTags>
|
||||
<PackageReleaseNotes></PackageReleaseNotes>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/StefH/WireMock.Net/master/WireMock.Net-Logo.png</PackageIconUrl>
|
||||
<PackageProjectUrl>https://github.com/StefH/WireMock.Net</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://raw.githubusercontent.com/StefH/WireMock.Net/master/LICENSE</PackageLicenseUrl>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/WireMock.Net-Logo.png</PackageIconUrl>
|
||||
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/LICENSE</PackageLicenseUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/StefH/WireMock.Net</RepositoryUrl>
|
||||
<RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<DebugType>full</DebugType>
|
||||
<DebugType>portable</DebugType>
|
||||
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
<RootNamespace>WireMock</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' or '$(TargetFramework)' == 'netstandard2.0'">
|
||||
<DefineConstants>NETSTANDARD</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -31,33 +31,32 @@
|
||||
<PrivateAssets>All</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Handlebars.Net" Version="1.9.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.1" />
|
||||
<PackageReference Include="SimMetrics.Net" Version="1.0.3" />
|
||||
<PackageReference Include="System.Net.Http" Version="4.3.2" />
|
||||
<PackageReference Include="RestEase" Version="1.4.1" />
|
||||
<PackageReference Include="RestEase" Version="1.4.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.OwinSelfHost" Version="5.2.3" />
|
||||
<PackageReference Include="XPath2" Version="1.0.3.1" />
|
||||
<Reference Include="System.Net.Http.WebRequest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net452' ">
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.OwinSelfHost" Version="5.2.3" />
|
||||
<PackageReference Include="XPath2" Version="1.0.3.1" />
|
||||
<PackageReference Include="XPath2" Version="1.0.5.1" />
|
||||
<Reference Include="System.Net.Http.WebRequest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net46' ">
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.OwinSelfHost" Version="5.2.3" />
|
||||
<PackageReference Include="XPath2" Version="1.0.3.1" />
|
||||
<PackageReference Include="XPath2" Version="1.0.5.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.2" />
|
||||
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
|
||||
<PackageReference Include="System.Xml.XPath.XmlDocument" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0" />
|
||||
<PackageReference Include="XPath2" Version="1.0.5.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
157
test/WireMock.Net.Tests/FluentMockServerTests.Proxy.cs
Normal file
157
test/WireMock.Net.Tests/FluentMockServerTests.Proxy.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading.Tasks;
|
||||
using NFluent;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Server;
|
||||
using Xunit;
|
||||
|
||||
namespace WireMock.Net.Tests
|
||||
{
|
||||
public partial class FluentMockServerTests
|
||||
{
|
||||
private FluentMockServer _serverForProxyForwarding;
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Should_proxy_responses()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
_server
|
||||
.Given(Request.Create().WithPath("/*"))
|
||||
.RespondWith(Response.Create().WithProxy("http://www.google.com"));
|
||||
|
||||
// when
|
||||
var result = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/search?q=test");
|
||||
|
||||
// then
|
||||
Check.That(result).Contains("google");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Should_preserve_content_header_in_proxied_request()
|
||||
{
|
||||
// given
|
||||
_serverForProxyForwarding = FluentMockServer.Start();
|
||||
_serverForProxyForwarding
|
||||
.Given(Request.Create().WithPath("/*"))
|
||||
.RespondWith(Response.Create());
|
||||
|
||||
_server = FluentMockServer.Start();
|
||||
_server
|
||||
.Given(Request.Create().WithPath("/*"))
|
||||
.RespondWith(Response.Create().WithProxy(_serverForProxyForwarding.Urls[0]));
|
||||
|
||||
// when
|
||||
var requestMessage = new HttpRequestMessage
|
||||
{
|
||||
Method = HttpMethod.Post,
|
||||
RequestUri = new Uri(_server.Urls[0]),
|
||||
Content = new StringContent("stringContent")
|
||||
};
|
||||
requestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
|
||||
await new HttpClient().SendAsync(requestMessage);
|
||||
|
||||
// then
|
||||
var receivedRequest = _serverForProxyForwarding.LogEntries.First().RequestMessage;
|
||||
Check.That(receivedRequest.Body).IsEqualTo("stringContent");
|
||||
Check.That(receivedRequest.Headers).ContainsKey("Content-Type");
|
||||
Check.That(receivedRequest.Headers["Content-Type"]).ContainsExactly("text/plain");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Should_preserve_content_header_in_proxied_response()
|
||||
{
|
||||
// given
|
||||
_serverForProxyForwarding = FluentMockServer.Start();
|
||||
_serverForProxyForwarding
|
||||
.Given(Request.Create().WithPath("/*"))
|
||||
.RespondWith(Response.Create()
|
||||
.WithBody("body")
|
||||
.WithHeader("Content-Type", "text/plain"));
|
||||
|
||||
_server = FluentMockServer.Start();
|
||||
_server
|
||||
.Given(Request.Create().WithPath("/*"))
|
||||
.RespondWith(Response.Create().WithProxy(_serverForProxyForwarding.Urls[0]));
|
||||
|
||||
// when
|
||||
var requestMessage = new HttpRequestMessage
|
||||
{
|
||||
Method = HttpMethod.Get,
|
||||
RequestUri = new Uri(_server.Urls[0])
|
||||
};
|
||||
var response = await new HttpClient().SendAsync(requestMessage);
|
||||
|
||||
// then
|
||||
Check.That(await response.Content.ReadAsStringAsync()).IsEqualTo("body");
|
||||
Check.That(response.Content.Headers.Contains("Content-Type")).IsTrue();
|
||||
Check.That(response.Content.Headers.GetValues("Content-Type")).ContainsExactly("text/plain");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Should_change_absolute_location_header_in_proxied_response()
|
||||
{
|
||||
// given
|
||||
_serverForProxyForwarding = FluentMockServer.Start();
|
||||
_serverForProxyForwarding
|
||||
.Given(Request.Create().WithPath("/*"))
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(HttpStatusCode.Redirect)
|
||||
.WithHeader("Location", _serverForProxyForwarding.Urls[0] + "testpath"));
|
||||
|
||||
_server = FluentMockServer.Start();
|
||||
_server
|
||||
.Given(Request.Create().WithPath("/*"))
|
||||
.RespondWith(Response.Create().WithProxy(_serverForProxyForwarding.Urls[0]));
|
||||
|
||||
// when
|
||||
var requestMessage = new HttpRequestMessage
|
||||
{
|
||||
Method = HttpMethod.Get,
|
||||
RequestUri = new Uri(_server.Urls[0])
|
||||
};
|
||||
var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false };
|
||||
var response = await new HttpClient(httpClientHandler).SendAsync(requestMessage);
|
||||
|
||||
// then
|
||||
Check.That(response.Headers.Contains("Location")).IsTrue();
|
||||
Check.That(response.Headers.GetValues("Location")).ContainsExactly(_server.Urls[0] + "testpath");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Should_preserve_cookie_header_in_proxied_request()
|
||||
{
|
||||
// given
|
||||
_serverForProxyForwarding = FluentMockServer.Start();
|
||||
_serverForProxyForwarding
|
||||
.Given(Request.Create().WithPath("/*"))
|
||||
.RespondWith(Response.Create());
|
||||
|
||||
_server = FluentMockServer.Start();
|
||||
_server
|
||||
.Given(Request.Create().WithPath("/*"))
|
||||
.RespondWith(Response.Create().WithProxy(_serverForProxyForwarding.Urls[0]));
|
||||
|
||||
// when
|
||||
var requestUri = new Uri(_server.Urls[0]);
|
||||
var requestMessage = new HttpRequestMessage
|
||||
{
|
||||
Method = HttpMethod.Get,
|
||||
RequestUri = requestUri
|
||||
};
|
||||
var clientHandler = new HttpClientHandler();
|
||||
clientHandler.CookieContainer.Add(requestUri, new Cookie("name", "value"));
|
||||
await new HttpClient(clientHandler).SendAsync(requestMessage);
|
||||
|
||||
// then
|
||||
var receivedRequest = _serverForProxyForwarding.LogEntries.First().RequestMessage;
|
||||
Check.That(receivedRequest.Cookies).IsNotNull();
|
||||
Check.That(receivedRequest.Cookies).ContainsPair("name", "value");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,378 +1,417 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using NFluent;
|
||||
using Xunit;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Server;
|
||||
|
||||
namespace WireMock.Net.Tests
|
||||
{
|
||||
//[TestFixture]
|
||||
//[Timeout(5000)]
|
||||
public class FluentMockServerTests : IDisposable
|
||||
{
|
||||
private FluentMockServer _server;
|
||||
|
||||
// For for AppVeyor + OpenCover
|
||||
private string GetCurrentFolder()
|
||||
{
|
||||
string current = Directory.GetCurrentDirectory();
|
||||
//if (!current.EndsWith("WireMock.Net.Tests"))
|
||||
// return Path.Combine(current, "test", "WireMock.Net.Tests");
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FluentMockServer_StartStop()
|
||||
{
|
||||
var server1 = FluentMockServer.Start("http://localhost:9090/");
|
||||
server1.Stop();
|
||||
|
||||
var server2 = FluentMockServer.Start("http://localhost:9090/");
|
||||
server2.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FluentMockServer_ReadStaticMapping_WithNonGuidFilename()
|
||||
{
|
||||
var guid = Guid.Parse("04ee4872-9efd-4770-90d3-88d445265d0d");
|
||||
string title = "documentdb_root_title";
|
||||
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings", "documentdb_root.json");
|
||||
_server.ReadStaticMapping(folder);
|
||||
|
||||
var mappings = _server.Mappings.ToArray();
|
||||
Check.That(mappings).HasSize(1);
|
||||
|
||||
Check.That(mappings.First().RequestMatcher).IsNotNull();
|
||||
Check.That(mappings.First().Provider).IsNotNull();
|
||||
Check.That(mappings.First().Guid).Equals(guid);
|
||||
Check.That(mappings.First().Title).Equals(title);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FluentMockServer_ReadStaticMapping_WithGuidFilename()
|
||||
{
|
||||
string guid = "00000002-ee28-4f29-ae63-1ac9b0802d86";
|
||||
|
||||
_server = FluentMockServer.Start();
|
||||
string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings", guid + ".json");
|
||||
_server.ReadStaticMapping(folder);
|
||||
|
||||
var mappings = _server.Mappings.ToArray();
|
||||
Check.That(mappings).HasSize(1);
|
||||
|
||||
Check.That(mappings.First().RequestMatcher).IsNotNull();
|
||||
Check.That(mappings.First().Provider).IsNotNull();
|
||||
Check.That(mappings.First().Guid).Equals(Guid.Parse(guid));
|
||||
Check.That(mappings.First().Title).IsNullOrEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FluentMockServer_ReadStaticMappings()
|
||||
{
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings");
|
||||
_server.ReadStaticMappings(folder);
|
||||
|
||||
var mappings = _server.Mappings.ToArray();
|
||||
Check.That(mappings).HasSize(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FluentMockServer_Admin_Mappings_Get()
|
||||
{
|
||||
var guid = Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f05");
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server.Given(Request.Create().WithPath("/foo1").UsingGet())
|
||||
.WithGuid(guid)
|
||||
.RespondWith(Response.Create().WithStatusCode(201).WithBody("1"));
|
||||
|
||||
_server.Given(Request.Create().WithPath("/foo2").UsingGet())
|
||||
.RespondWith(Response.Create().WithStatusCode(202).WithBody("2"));
|
||||
|
||||
var mappings = _server.Mappings.ToArray();
|
||||
Check.That(mappings).HasSize(2);
|
||||
|
||||
Check.That(mappings.First().RequestMatcher).IsNotNull();
|
||||
Check.That(mappings.First().Provider).IsNotNull();
|
||||
Check.That(mappings.First().Guid).Equals(guid);
|
||||
|
||||
Check.That(mappings[1].Guid).Not.Equals(guid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FluentMockServer_Admin_Mappings_Add_SameGuid()
|
||||
{
|
||||
var guid = Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f05");
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server.Given(Request.Create().WithPath("/1").UsingGet())
|
||||
.WithGuid(guid)
|
||||
.RespondWith(Response.Create().WithStatusCode(500));
|
||||
|
||||
var mappings = _server.Mappings.ToArray();
|
||||
Check.That(mappings).HasSize(1);
|
||||
Check.That(mappings.First().Guid).Equals(guid);
|
||||
|
||||
_server.Given(Request.Create().WithPath("/2").UsingGet())
|
||||
.WithGuid(guid)
|
||||
.RespondWith(Response.Create().WithStatusCode(500));
|
||||
|
||||
Check.That(mappings).HasSize(1);
|
||||
Check.That(mappings.First().Guid).Equals(guid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Admin_Mappings_AtPriority()
|
||||
{
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
// given
|
||||
_server.Given(Request.Create().WithPath("/1").UsingGet())
|
||||
.AtPriority(2)
|
||||
.RespondWith(Response.Create().WithStatusCode(200));
|
||||
|
||||
_server.Given(Request.Create().WithPath("/1").UsingGet())
|
||||
.AtPriority(1)
|
||||
.RespondWith(Response.Create().WithStatusCode(400));
|
||||
|
||||
var mappings = _server.Mappings.ToArray();
|
||||
Check.That(mappings).HasSize(2);
|
||||
Check.That(mappings[0].Priority).Equals(2);
|
||||
Check.That(mappings[1].Priority).Equals(1);
|
||||
|
||||
// when
|
||||
var response = await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/1");
|
||||
|
||||
// then
|
||||
Check.That((int)response.StatusCode).IsEqualTo(400);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Admin_Requests_Get()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
// when
|
||||
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
|
||||
// then
|
||||
Check.That(_server.LogEntries).HasSize(1);
|
||||
var requestLogged = _server.LogEntries.First();
|
||||
Check.That(requestLogged.RequestMessage.Method).IsEqualTo("get");
|
||||
Check.That(requestLogged.RequestMessage.BodyAsBytes).IsEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_respond_to_request()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/foo")
|
||||
.UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithBody(@"{ msg: ""Hello world!""}"));
|
||||
|
||||
// when
|
||||
var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
|
||||
// then
|
||||
Check.That(response).IsEqualTo(@"{ msg: ""Hello world!""}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_respond_to_request_bodyAsBase64()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server.Given(Request.Create().WithPath("/foo").UsingGet()).RespondWith(Response.Create().WithBodyAsBase64("SGVsbG8gV29ybGQ/"));
|
||||
|
||||
// when
|
||||
var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
|
||||
// then
|
||||
Check.That(response).IsEqualTo("Hello World?");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_respond_404_for_unexpected_request()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
// when
|
||||
var response = await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
|
||||
// then
|
||||
Check.That(response.StatusCode).IsEqualTo(HttpStatusCode.NotFound);
|
||||
Check.That((int)response.StatusCode).IsEqualTo(404);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_find_a_request_satisfying_a_request_spec()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
// when
|
||||
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/bar");
|
||||
|
||||
// then
|
||||
var result = _server.FindLogEntries(Request.Create().WithPath(new RegexMatcher("^/b.*"))).ToList();
|
||||
Check.That(result).HasSize(1);
|
||||
|
||||
var requestLogged = result.First();
|
||||
Check.That(requestLogged.RequestMessage.Path).IsEqualTo("/bar");
|
||||
Check.That(requestLogged.RequestMessage.Url).IsEqualTo("http://localhost:" + _server.Ports[0] + "/bar");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_reset_requestlogs()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
// when
|
||||
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
_server.ResetLogEntries();
|
||||
|
||||
// then
|
||||
Check.That(_server.LogEntries).IsEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_reset_mappings()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/foo")
|
||||
.UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithBody(@"{ msg: ""Hello world!""}"));
|
||||
|
||||
// when
|
||||
_server.ResetMappings();
|
||||
|
||||
// then
|
||||
Check.That(_server.Mappings).IsEmpty();
|
||||
Check.ThatAsyncCode(() => new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo"))
|
||||
.ThrowsAny();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_respond_a_redirect_without_body()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/foo")
|
||||
.UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(307)
|
||||
.WithHeader("Location", "/bar"));
|
||||
_server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/bar")
|
||||
.UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithBody("REDIRECT SUCCESSFUL"));
|
||||
|
||||
// when
|
||||
var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
|
||||
// then
|
||||
Check.That(response).IsEqualTo("REDIRECT SUCCESSFUL");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_delay_responses_for_a_given_route()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/*"))
|
||||
.RespondWith(Response.Create()
|
||||
.WithBody(@"{ msg: ""Hello world!""}")
|
||||
.WithDelay(TimeSpan.FromMilliseconds(200)));
|
||||
|
||||
// when
|
||||
var watch = new Stopwatch();
|
||||
watch.Start();
|
||||
await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
watch.Stop();
|
||||
|
||||
// then
|
||||
Check.That(watch.ElapsedMilliseconds).IsStrictlyGreaterThan(200);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_delay_responses()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
_server.AddGlobalProcessingDelay(TimeSpan.FromMilliseconds(200));
|
||||
_server
|
||||
.Given(Request.Create().WithPath("/*"))
|
||||
.RespondWith(Response.Create().WithBody(@"{ msg: ""Hello world!""}"));
|
||||
|
||||
// when
|
||||
var watch = new Stopwatch();
|
||||
watch.Start();
|
||||
await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
watch.Stop();
|
||||
|
||||
// then
|
||||
Check.That(watch.ElapsedMilliseconds).IsStrictlyGreaterThan(200);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_proxy_responses()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
_server
|
||||
.Given(Request.Create().WithPath("/*"))
|
||||
.RespondWith(Response.Create().WithProxy("http://www.google.com"));
|
||||
|
||||
// when
|
||||
var result = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
|
||||
// then
|
||||
Check.That(result).Contains("google");
|
||||
}
|
||||
|
||||
//[TearDown]
|
||||
public void Dispose()
|
||||
{
|
||||
_server?.Stop();
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using NFluent;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Server;
|
||||
using Xunit;
|
||||
|
||||
namespace WireMock.Net.Tests
|
||||
{
|
||||
public partial class FluentMockServerTests : IDisposable
|
||||
{
|
||||
private FluentMockServer _server;
|
||||
|
||||
// For for AppVeyor + OpenCover
|
||||
private string GetCurrentFolder()
|
||||
{
|
||||
string current = Directory.GetCurrentDirectory();
|
||||
//if (!current.EndsWith("WireMock.Net.Tests"))
|
||||
// return Path.Combine(current, "test", "WireMock.Net.Tests");
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FluentMockServer_StartStop()
|
||||
{
|
||||
var server1 = FluentMockServer.Start("http://localhost:9091/");
|
||||
server1.Stop();
|
||||
|
||||
var server2 = FluentMockServer.Start("http://localhost:9091/");
|
||||
server2.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FluentMockServer_ReadStaticMapping_WithNonGuidFilename()
|
||||
{
|
||||
var guid = Guid.Parse("04ee4872-9efd-4770-90d3-88d445265d0d");
|
||||
string title = "documentdb_root_title";
|
||||
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings", "documentdb_root.json");
|
||||
_server.ReadStaticMapping(folder);
|
||||
|
||||
var mappings = _server.Mappings.ToArray();
|
||||
Check.That(mappings).HasSize(1);
|
||||
|
||||
Check.That(mappings.First().RequestMatcher).IsNotNull();
|
||||
Check.That(mappings.First().Provider).IsNotNull();
|
||||
Check.That(mappings.First().Guid).Equals(guid);
|
||||
Check.That(mappings.First().Title).Equals(title);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FluentMockServer_ReadStaticMapping_WithGuidFilename()
|
||||
{
|
||||
string guid = "00000002-ee28-4f29-ae63-1ac9b0802d86";
|
||||
|
||||
_server = FluentMockServer.Start();
|
||||
string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings", guid + ".json");
|
||||
_server.ReadStaticMapping(folder);
|
||||
|
||||
var mappings = _server.Mappings.ToArray();
|
||||
Check.That(mappings).HasSize(1);
|
||||
|
||||
Check.That(mappings.First().RequestMatcher).IsNotNull();
|
||||
Check.That(mappings.First().Provider).IsNotNull();
|
||||
Check.That(mappings.First().Guid).Equals(Guid.Parse(guid));
|
||||
Check.That(mappings.First().Title).IsNullOrEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FluentMockServer_ReadStaticMappings()
|
||||
{
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings");
|
||||
_server.ReadStaticMappings(folder);
|
||||
|
||||
var mappings = _server.Mappings.ToArray();
|
||||
Check.That(mappings).HasSize(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FluentMockServer_Admin_Mappings_Get()
|
||||
{
|
||||
var guid = Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f05");
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server.Given(Request.Create().WithPath("/foo1").UsingGet())
|
||||
.WithGuid(guid)
|
||||
.RespondWith(Response.Create().WithStatusCode(201).WithBody("1"));
|
||||
|
||||
_server.Given(Request.Create().WithPath("/foo2").UsingGet())
|
||||
.RespondWith(Response.Create().WithStatusCode(202).WithBody("2"));
|
||||
|
||||
var mappings = _server.Mappings.ToArray();
|
||||
Check.That(mappings).HasSize(2);
|
||||
|
||||
Check.That(mappings.First().RequestMatcher).IsNotNull();
|
||||
Check.That(mappings.First().Provider).IsNotNull();
|
||||
Check.That(mappings.First().Guid).Equals(guid);
|
||||
|
||||
Check.That(mappings[1].Guid).Not.Equals(guid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FluentMockServer_Admin_Mappings_Add_SameGuid()
|
||||
{
|
||||
var guid = Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f05");
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server.Given(Request.Create().WithPath("/1").UsingGet())
|
||||
.WithGuid(guid)
|
||||
.RespondWith(Response.Create().WithStatusCode(500));
|
||||
|
||||
var mappings = _server.Mappings.ToArray();
|
||||
Check.That(mappings).HasSize(1);
|
||||
Check.That(mappings.First().Guid).Equals(guid);
|
||||
|
||||
_server.Given(Request.Create().WithPath("/2").UsingGet())
|
||||
.WithGuid(guid)
|
||||
.RespondWith(Response.Create().WithStatusCode(500));
|
||||
|
||||
Check.That(mappings).HasSize(1);
|
||||
Check.That(mappings.First().Guid).Equals(guid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Admin_Mappings_AtPriority()
|
||||
{
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
// given
|
||||
_server.Given(Request.Create().WithPath("/1").UsingGet())
|
||||
.AtPriority(2)
|
||||
.RespondWith(Response.Create().WithStatusCode(200));
|
||||
|
||||
_server.Given(Request.Create().WithPath("/1").UsingGet())
|
||||
.AtPriority(1)
|
||||
.RespondWith(Response.Create().WithStatusCode(400));
|
||||
|
||||
var mappings = _server.Mappings.ToArray();
|
||||
Check.That(mappings).HasSize(2);
|
||||
Check.That(mappings[0].Priority).Equals(2);
|
||||
Check.That(mappings[1].Priority).Equals(1);
|
||||
|
||||
// when
|
||||
var response = await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/1");
|
||||
|
||||
// then
|
||||
Check.That((int)response.StatusCode).IsEqualTo(400);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Admin_Requests_Get()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
// when
|
||||
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
|
||||
// then
|
||||
Check.That(_server.LogEntries).HasSize(1);
|
||||
var requestLogged = _server.LogEntries.First();
|
||||
Check.That(requestLogged.RequestMessage.Method).IsEqualTo("get");
|
||||
Check.That(requestLogged.RequestMessage.BodyAsBytes).IsNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Should_respond_to_request_bodyAsString()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/foo")
|
||||
.UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithBody("Hello world!"));
|
||||
|
||||
// when
|
||||
var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
|
||||
// then
|
||||
Check.That(response).IsEqualTo("Hello world!");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Should_respond_to_request_bodyAsBase64()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server.Given(Request.Create().WithPath("/foo").UsingGet()).RespondWith(Response.Create().WithBodyFromBase64("SGVsbG8gV29ybGQ/"));
|
||||
|
||||
// when
|
||||
var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
|
||||
// then
|
||||
Check.That(response).IsEqualTo("Hello World?");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Should_respond_to_request_bodyAsBytes()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server.Given(Request.Create().WithPath("/foo").UsingGet()).RespondWith(Response.Create().WithBody(new byte[] { 48, 49 }));
|
||||
|
||||
// when
|
||||
var responseAsString = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
var responseAsBytes = await new HttpClient().GetByteArrayAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
|
||||
// then
|
||||
Check.That(responseAsString).IsEqualTo("01");
|
||||
Check.That(responseAsBytes).ContainsExactly(new byte[] { 48, 49 });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Should_respond_404_for_unexpected_request()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
// when
|
||||
var response = await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
|
||||
// then
|
||||
Check.That(response.StatusCode).IsEqualTo(HttpStatusCode.NotFound);
|
||||
Check.That((int)response.StatusCode).IsEqualTo(404);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Should_find_a_request_satisfying_a_request_spec()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
// when
|
||||
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/bar");
|
||||
|
||||
// then
|
||||
var result = _server.FindLogEntries(Request.Create().WithPath(new RegexMatcher("^/b.*"))).ToList();
|
||||
Check.That(result).HasSize(1);
|
||||
|
||||
var requestLogged = result.First();
|
||||
Check.That(requestLogged.RequestMessage.Path).IsEqualTo("/bar");
|
||||
Check.That(requestLogged.RequestMessage.Url).IsEqualTo("http://localhost:" + _server.Ports[0] + "/bar");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Should_reset_requestlogs()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
// when
|
||||
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
_server.ResetLogEntries();
|
||||
|
||||
// then
|
||||
Check.That(_server.LogEntries).IsEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FluentMockServer_Should_reset_mappings()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/foo")
|
||||
.UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithBody(@"{ msg: ""Hello world!""}"));
|
||||
|
||||
// when
|
||||
_server.ResetMappings();
|
||||
|
||||
// then
|
||||
Check.That(_server.Mappings).IsEmpty();
|
||||
Check.ThatAsyncCode(() => new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo"))
|
||||
.ThrowsAny();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Should_respond_a_redirect_without_body()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/foo")
|
||||
.UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(307)
|
||||
.WithHeader("Location", "/bar"));
|
||||
_server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/bar")
|
||||
.UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithBody("REDIRECT SUCCESSFUL"));
|
||||
|
||||
// when
|
||||
var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
|
||||
// then
|
||||
Check.That(response).IsEqualTo("REDIRECT SUCCESSFUL");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Should_delay_responses_for_a_given_route()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/*"))
|
||||
.RespondWith(Response.Create()
|
||||
.WithBody(@"{ msg: ""Hello world!""}")
|
||||
.WithDelay(TimeSpan.FromMilliseconds(200)));
|
||||
|
||||
// when
|
||||
var watch = new Stopwatch();
|
||||
watch.Start();
|
||||
await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
watch.Stop();
|
||||
|
||||
// then
|
||||
Check.That(watch.ElapsedMilliseconds).IsStrictlyGreaterThan(200);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Should_delay_responses()
|
||||
{
|
||||
// given
|
||||
_server = FluentMockServer.Start();
|
||||
_server.AddGlobalProcessingDelay(TimeSpan.FromMilliseconds(200));
|
||||
_server
|
||||
.Given(Request.Create().WithPath("/*"))
|
||||
.RespondWith(Response.Create().WithBody(@"{ msg: ""Hello world!""}"));
|
||||
|
||||
// when
|
||||
var watch = new Stopwatch();
|
||||
watch.Start();
|
||||
await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
watch.Stop();
|
||||
|
||||
// then
|
||||
Check.That(watch.ElapsedMilliseconds).IsStrictlyGreaterThan(200);
|
||||
}
|
||||
|
||||
//Leaving commented as this requires an actual certificate with password, along with a service that expects a client certificate
|
||||
//[Fact]
|
||||
//public async Task Should_proxy_responses_with_client_certificate()
|
||||
//{
|
||||
// // given
|
||||
// _server = FluentMockServer.Start();
|
||||
// _server
|
||||
// .Given(Request.Create().WithPath("/*"))
|
||||
// .RespondWith(Response.Create().WithProxy("https://server-that-expects-a-client-certificate", @"\\yourclientcertificatecontainingprivatekey.pfx", "yourclientcertificatepassword"));
|
||||
|
||||
// // when
|
||||
// var result = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/someurl?someQuery=someValue");
|
||||
|
||||
// // then
|
||||
// Check.That(result).Contains("google");
|
||||
//}
|
||||
|
||||
[Fact]
|
||||
public async Task FluentMockServer_Logging_SetMaxRequestLogCount()
|
||||
{
|
||||
// Assign
|
||||
var client = new HttpClient();
|
||||
// Act
|
||||
_server = FluentMockServer.Start();
|
||||
_server.SetMaxRequestLogCount(2);
|
||||
|
||||
await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo1");
|
||||
await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo2");
|
||||
await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo3");
|
||||
|
||||
// Assert
|
||||
Check.That(_server.LogEntries).HasSize(2);
|
||||
|
||||
var requestLoggedA = _server.LogEntries.First();
|
||||
Check.That(requestLoggedA.RequestMessage.Path).EndsWith("/foo2");
|
||||
|
||||
var requestLoggedB = _server.LogEntries.Last();
|
||||
Check.That(requestLoggedB.RequestMessage.Path).EndsWith("/foo3");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_server?.Stop();
|
||||
_serverForProxyForwarding?.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
84
test/WireMock.Net.Tests/ObservableLogEntriesTest.cs
Normal file
84
test/WireMock.Net.Tests/ObservableLogEntriesTest.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NFluent;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Server;
|
||||
using Xunit;
|
||||
|
||||
namespace WireMock.Net.Tests
|
||||
{
|
||||
public class ObservableLogEntriesTest : IDisposable
|
||||
{
|
||||
private FluentMockServer _server;
|
||||
|
||||
[Fact]
|
||||
public async void Test()
|
||||
{
|
||||
// Assign
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/foo")
|
||||
.UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithBody(@"{ msg: ""Hello world!""}"));
|
||||
|
||||
int count = 0;
|
||||
_server.LogEntriesChanged += (sender, args) => count++;
|
||||
|
||||
// Act
|
||||
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
|
||||
|
||||
// Assert
|
||||
Check.That(count).Equals(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParallelTest()
|
||||
{
|
||||
var expectedCount = 100;
|
||||
|
||||
// Assign
|
||||
_server = FluentMockServer.Start();
|
||||
|
||||
_server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/foo")
|
||||
.UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithDelay(6)
|
||||
.WithSuccess());
|
||||
|
||||
int count = 0;
|
||||
_server.LogEntriesChanged += (sender, args) => count++;
|
||||
|
||||
var http = new HttpClient();
|
||||
|
||||
// Act
|
||||
var listOfTasks = new List<Task<HttpResponseMessage>>();
|
||||
for (var i = 0; i < expectedCount; i++)
|
||||
{
|
||||
Thread.Sleep(3);
|
||||
listOfTasks.Add(http.GetAsync(_server.Urls[0] + $"/foo"));
|
||||
}
|
||||
var responses = await Task.WhenAll(listOfTasks);
|
||||
var countResponsesWithStatusNotOk = responses.Where(r => r.StatusCode != HttpStatusCode.OK).Count();
|
||||
|
||||
// Assert
|
||||
Check.That(countResponsesWithStatusNotOk).Equals(0);
|
||||
Check.That(count).Equals(expectedCount);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_server?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,13 @@ namespace WireMock.Net.Tests
|
||||
//[TestFixture]
|
||||
public class RequestMessageTests
|
||||
{
|
||||
private const string ClientIp = "::1";
|
||||
|
||||
[Fact]
|
||||
public void Should_handle_empty_query()
|
||||
{
|
||||
// given
|
||||
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST");
|
||||
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp);
|
||||
|
||||
// then
|
||||
Check.That(request.GetParameter("not_there")).IsNull();
|
||||
@@ -24,7 +26,7 @@ namespace WireMock.Net.Tests
|
||||
// given
|
||||
string bodyAsString = "whatever";
|
||||
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
|
||||
var request = new RequestMessage(new Uri("http://localhost?foo=bar&multi=1&multi=2"), "POST", body, bodyAsString, Encoding.UTF8);
|
||||
var request = new RequestMessage(new Uri("http://localhost?foo=bar&multi=1&multi=2"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
|
||||
|
||||
// then
|
||||
Check.That(request.GetParameter("foo")).Contains("bar");
|
||||
@@ -32,4 +34,4 @@ namespace WireMock.Net.Tests
|
||||
Check.That(request.GetParameter("multi")).Contains("2");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user