mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-02-13 22:07:41 +01:00
Compare commits
74 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 | ||
|
|
7111ab384b | ||
|
|
0670cbc54c | ||
|
|
5b10ef6c48 | ||
|
|
f90d54bace | ||
|
|
8ed82f3124 | ||
|
|
b56fca85a6 | ||
|
|
97c7a15302 | ||
|
|
a5257309c7 | ||
|
|
5d6040df61 | ||
|
|
8e32fd0be2 | ||
|
|
3c22526905 | ||
|
|
08fd94c081 | ||
|
|
f37d1cddf6 | ||
|
|
71b48ae571 | ||
|
|
a901fa6db0 | ||
|
|
360a1227e4 | ||
|
|
961e8b555f | ||
|
|
4ada6d3567 | ||
|
|
23b795ab1e | ||
|
|
31261ec45d | ||
|
|
b25371cf15 | ||
|
|
2c8142b93d | ||
|
|
7bfd9f3343 | ||
|
|
84db9bbf0d |
15
.axoCover/settings.json
Normal file
15
.axoCover/settings.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"TestRunner": "",
|
||||
"TestPlatform": "x86",
|
||||
"TestApartmentState": "STA",
|
||||
"TestSettings": "",
|
||||
"ExcludeAttributes": "System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute",
|
||||
"ExcludeFiles": "",
|
||||
"ExcludeDirectories": "",
|
||||
"Filters": "+[*]*",
|
||||
"IsIncludingSolutionAssemblies": true,
|
||||
"IsExcludingTestAssemblies": false,
|
||||
"IsCoveringByTest": true,
|
||||
"IsMergingByHash": true,
|
||||
"IsSkippingAutoProps": true
|
||||
}
|
||||
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"
|
||||
}
|
||||
]
|
||||
}
|
||||
22
README.md
22
README.md
@@ -2,14 +2,30 @@
|
||||
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://www.nuget.org/packages/WireMock.Net)
|
||||
[](https://github.com/StefH/WireMock.Net/issues)
|
||||
[](https://github.com/StefH/WireMock.Net/stargazers)
|
||||
|
||||
| Name | NuGet |
|
||||
| ---- | ----- |
|
||||
| WireMock.Net | [](https://www.nuget.org/packages/WireMock.Net) |
|
||||
| WireMock.Net.StandAlone | [](https://www.nuget.org/packages/WireMock.Net.StandAlone) |
|
||||
|
||||
### Frameworks
|
||||
The following frameworks are supported:
|
||||
- net45 and up
|
||||
- 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.26403.7
|
||||
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,26 +14,32 @@ 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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.StandAlone", "src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj", "{668F689E-57B4-422E-8846-C0FF643CA999}"
|
||||
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.StandAlone.NETCoreApp", "src\WireMock.Net.StandAlone.NETCoreApp\WireMock.Net.StandAlone.NETCoreApp.csproj", "{14D7298C-2BE5-42C3-A3D5-9433E77218F9}"
|
||||
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("{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.Record.NETCoreApp", "examples\WireMock.Net.Console.Record.NETCoreApp\WireMock.Net.Console.Record.NETCoreApp.csproj", "{1995E414-F197-4AB4-90C2-68D806B5AF59}"
|
||||
EndProject
|
||||
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
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.StandAlone.NETCoreApp", "examples\WireMock.Net.StandAlone.NETCoreApp\WireMock.Net.StandAlone.NETCoreApp.csproj", "{10E16614-61CA-48D8-8BDD-664C13913DED}"
|
||||
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
|
||||
{B6269AAC-170A-43D5-8B9A-579DED3D9A95} = {B6269AAC-170A-43D5-8B9A-579DED3D9A95}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
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
|
||||
@@ -45,36 +51,54 @@ 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
|
||||
{668F689E-57B4-422E-8846-C0FF643CA999}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{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
|
||||
{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
|
||||
{31DC2EF8-C3FE-467D-84BE-FB5D956E612E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{14D7298C-2BE5-42C3-A3D5-9433E77218F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{14D7298C-2BE5-42C3-A3D5-9433E77218F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{14D7298C-2BE5-42C3-A3D5-9433E77218F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{14D7298C-2BE5-42C3-A3D5-9433E77218F9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FE281639-B014-4C8A-96FA-141164A74713}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FE281639-B014-4C8A-96FA-141164A74713}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FE281639-B014-4C8A-96FA-141164A74713}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FE281639-B014-4C8A-96FA-141164A74713}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1995E414-F197-4AB4-90C2-68D806B5AF59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1995E414-F197-4AB4-90C2-68D806B5AF59}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1995E414-F197-4AB4-90C2-68D806B5AF59}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1995E414-F197-4AB4-90C2-68D806B5AF59}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{058D4B6C-C03E-49D0-91DB-A535B058FA0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{058D4B6C-C03E-49D0-91DB-A535B058FA0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{058D4B6C-C03E-49D0-91DB-A535B058FA0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{058D4B6C-C03E-49D0-91DB-A535B058FA0D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B6269AAC-170A-43D5-8B9A-579DED3D9A95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B6269AAC-170A-43D5-8B9A-579DED3D9A95}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B6269AAC-170A-43D5-8B9A-579DED3D9A95}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B6269AAC-170A-43D5-8B9A-579DED3D9A95}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{10E16614-61CA-48D8-8BDD-664C13913DED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{10E16614-61CA-48D8-8BDD-664C13913DED}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{10E16614-61CA-48D8-8BDD-664C13913DED}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{10E16614-61CA-48D8-8BDD-664C13913DED}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{668F689E-57B4-422E-8846-C0FF643CA999}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{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
|
||||
{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}
|
||||
{668F689E-57B4-422E-8846-C0FF643CA999} = {EF242EDF-7133-4277-9A0C-18744DE08707}
|
||||
{31DC2EF8-C3FE-467D-84BE-FB5D956E612E} = {890A1DED-C229-4FA1-969E-AAC3BBFC05E5}
|
||||
{14D7298C-2BE5-42C3-A3D5-9433E77218F9} = {EF242EDF-7133-4277-9A0C-18744DE08707}
|
||||
{FE281639-B014-4C8A-96FA-141164A74713} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
|
||||
{1995E414-F197-4AB4-90C2-68D806B5AF59} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
|
||||
{058D4B6C-C03E-49D0-91DB-A535B058FA0D} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
|
||||
{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}
|
||||
{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>
|
||||
17
appveyor.yml
17
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,28 +20,25 @@ environment:
|
||||
|
||||
before_build:
|
||||
- dotnet restore .\src\WireMock.Net\WireMock.Net.csproj
|
||||
- dotnet restore .\src\WireMock.Net.StandAlone.NETCoreApp\WireMock.Net.StandAlone.NETCoreApp.csproj
|
||||
- nuget restore .\src\WireMock.Net.Standalone\WireMock.Net.Standalone.csproj -PackagesDirectory packages
|
||||
- dotnet restore .\src\WireMock.Net.Standalone\WireMock.Net.Standalone.csproj
|
||||
- nuget restore .\examples\WireMock.Net.ConsoleApplication\WireMock.Net.ConsoleApplication.csproj -PackagesDirectory packages
|
||||
|
||||
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.ConsoleApplication, WireMock.Net.StandAlone.NETCoreApp and WireMock.Net.Standalone
|
||||
- dotnet build .\src\WireMock.Net.StandAlone.NETCoreApp\WireMock.Net.StandAlone.NETCoreApp.csproj -c %CONFIGURATION%
|
||||
- cmd: msbuild .\examples\WireMock.Net.ConsoleApplication\WireMock.Net.ConsoleApplication.csproj /p:Configuration=%CONFIGURATION%
|
||||
- cmd: msbuild .\src\WireMock.Net.Standalone\WireMock.Net.Standalone.csproj /p:Configuration=%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
|
||||
56
examples/WireMock.Net.Client/Program.cs
Normal file
56
examples/WireMock.Net.Client/Program.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using Newtonsoft.Json;
|
||||
using RestEase;
|
||||
using System;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using WireMock.Client;
|
||||
|
||||
namespace WireMock.Net.Client
|
||||
{
|
||||
class Program
|
||||
{
|
||||
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:9091");
|
||||
|
||||
// Set BASIC Auth
|
||||
var value = Convert.ToBase64String(Encoding.ASCII.GetBytes("a:b"));
|
||||
api.Authorization = new AuthenticationHeaderValue("Basic", value);
|
||||
|
||||
var settings1 = api.GetSettingsAsync().Result;
|
||||
Console.WriteLine($"settings1 = {JsonConvert.SerializeObject(settings1)}");
|
||||
|
||||
settings1.GlobalProcessingDelay = 1077;
|
||||
api.PostSettingsAsync(settings1).Wait();
|
||||
|
||||
var settings2 = api.GetSettingsAsync().Result;
|
||||
Console.WriteLine($"settings2 = {JsonConvert.SerializeObject(settings2)}");
|
||||
|
||||
var mappings = api.GetMappingsAsync().Result;
|
||||
Console.WriteLine($"mappings = {JsonConvert.SerializeObject(mappings)}");
|
||||
|
||||
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>
|
||||
@@ -1,10 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Server;
|
||||
using WireMock.Net.ConsoleApplication;
|
||||
|
||||
namespace WireMock.Net.Console.NETCoreApp
|
||||
{
|
||||
@@ -12,99 +6,7 @@ namespace WireMock.Net.Console.NETCoreApp
|
||||
{
|
||||
static void Main(params string[] args)
|
||||
{
|
||||
string url1 = "http://localhost:9090/";
|
||||
string url2 = "http://localhost:9091/";
|
||||
string url3 = "https://localhost:9443/";
|
||||
|
||||
var server = FluentMockServer.Start(new FluentMockServerSettings
|
||||
{
|
||||
Urls = new[] { url1, url2, url3 },
|
||||
StartAdminInterface = true,
|
||||
ReadStaticMappings = true
|
||||
});
|
||||
System.Console.WriteLine("FluentMockServer listening at {0}", string.Join(" and ", server.Urls));
|
||||
|
||||
server.SetBasicAuthentication("a", "b");
|
||||
|
||||
server.AllowPartialMapping();
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath(p => p.Contains("x")).UsingGet())
|
||||
.AtPriority(4)
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""Contains x with FUNC 200""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/data").UsingPost().WithBody(b => b.Contains("e")))
|
||||
.AtPriority(999)
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(201)
|
||||
.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()
|
||||
.WithStatusCode(201)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""json posted with 201""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/json2").UsingPost().WithBody("x"))
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(201)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""json posted with x - 201""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/data").UsingDelete())
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""data deleted with 200""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/nobody").UsingGet())
|
||||
.RespondWith(Response.Create().WithDelay(TimeSpan.FromSeconds(1))
|
||||
.WithStatusCode(200));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/partial").UsingPost().WithBody(new SimMetricsMatcher(new[] { "cat", "dog" })))
|
||||
.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))
|
||||
);
|
||||
|
||||
System.Console.WriteLine("Press any key to stop the server");
|
||||
System.Console.ReadKey();
|
||||
server.Stop();
|
||||
|
||||
System.Console.WriteLine("Displaying all requests");
|
||||
var allRequests = server.LogEntries;
|
||||
System.Console.WriteLine(JsonConvert.SerializeObject(allRequests, Formatting.Indented));
|
||||
|
||||
System.Console.WriteLine("Press any key to quit");
|
||||
System.Console.ReadKey();
|
||||
MainApp.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,14 @@
|
||||
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="__admin\mappings\826aff7c-6208-4a3c-923d-575248907db4.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\WireMock.Net.ConsoleApplication\MainApp.cs" Link="MainApp.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="__admin\mappings\11111110-a633-40e8-a244-5cb80bc0ab66.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
@@ -13,13 +21,17 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineArgumentsParser" Version="3.0.9" />
|
||||
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
|
||||
</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" />
|
||||
<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"
|
||||
}
|
||||
}
|
||||
}
|
||||
37
examples/WireMock.Net.Console.Record.NETCoreApp/Program.cs
Normal file
37
examples/WireMock.Net.Console.Record.NETCoreApp/Program.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Newtonsoft.Json;
|
||||
using WireMock.Server;
|
||||
using WireMock.Settings;
|
||||
|
||||
namespace WireMock.Net.Console.Record.NETCoreApp
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
static void Main(params string[] args)
|
||||
{
|
||||
var server = FluentMockServer.Start(new FluentMockServerSettings
|
||||
{
|
||||
Urls = new[] { "http://localhost:9091/", "https://localhost:9443/" },
|
||||
StartAdminInterface = true,
|
||||
ReadStaticMappings = false,
|
||||
ProxyAndRecordSettings = new ProxyAndRecordSettings
|
||||
{
|
||||
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
|
||||
}
|
||||
});
|
||||
|
||||
System.Console.WriteLine("Press any key to stop the server");
|
||||
System.Console.ReadKey();
|
||||
server.Stop();
|
||||
|
||||
System.Console.WriteLine("Displaying all requests");
|
||||
var allRequests = server.LogEntries;
|
||||
System.Console.WriteLine(JsonConvert.SerializeObject(allRequests, Formatting.Indented));
|
||||
|
||||
System.Console.WriteLine("Press any key to quit");
|
||||
System.Console.ReadKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,16 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineArgumentsParser" Version="3.0.9" />
|
||||
<PackageReference Include="CommandLineArgumentsParser" Version="3.0.10" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WireMock.Net\WireMock.Net.csproj" />
|
||||
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="__admin\mappings\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
194
examples/WireMock.Net.ConsoleApplication/MainApp.cs
Normal file
194
examples/WireMock.Net.ConsoleApplication/MainApp.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Server;
|
||||
using WireMock.Settings;
|
||||
|
||||
namespace WireMock.Net.ConsoleApplication
|
||||
{
|
||||
public static class MainApp
|
||||
{
|
||||
public static void Run()
|
||||
{
|
||||
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,
|
||||
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
|
||||
.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"))
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBodyAsJson(new { access_token = "AT", refresh_token = "RT" }));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/helloworld").UsingGet().WithHeader("Authorization", new RegexMatcher("^(?i)Bearer AT$")))
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithBody("hi"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath(p => p.Contains("x")).UsingGet())
|
||||
.AtPriority(4)
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""Contains x with FUNC 200""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/data").UsingPost().WithBody(b => b.Contains("e")))
|
||||
.AtPriority(999)
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(201)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""data posted with FUNC 201""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/json").UsingPost().WithBody(new JsonPathMatcher("$.things[?(@.name == 'RequiredThing')]")))
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(201)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""json posted with 201""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/json2").UsingPost().WithBody("x"))
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(201)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""json posted with x - 201""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/data").UsingDelete())
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""data deleted with 200""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/nobody").UsingGet())
|
||||
.RespondWith(Response.Create().WithDelay(TimeSpan.FromSeconds(1))
|
||||
.WithStatusCode(200));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/partial").UsingPost().WithBody(new SimMetricsMatcher(new[] { "cat", "dog" })))
|
||||
.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("/state1")
|
||||
.UsingGet())
|
||||
.InScenario("s1")
|
||||
.WillSetStateTo("Test state 1")
|
||||
.RespondWith(Response.Create()
|
||||
.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();
|
||||
server.Stop();
|
||||
|
||||
System.Console.WriteLine("Displaying all requests");
|
||||
var allRequests = server.LogEntries;
|
||||
System.Console.WriteLine(JsonConvert.SerializeObject(allRequests, Formatting.Indented));
|
||||
|
||||
System.Console.WriteLine("Press any key to quit");
|
||||
System.Console.ReadKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,110 +1,10 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Server;
|
||||
|
||||
namespace WireMock.Net.ConsoleApplication
|
||||
namespace WireMock.Net.ConsoleApplication
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
static void Main(params string[] args)
|
||||
{
|
||||
string url1 = "http://localhost:9090/";
|
||||
string url2 = "http://localhost:9091/";
|
||||
string url3 = "https://localhost:9443/";
|
||||
|
||||
var server = FluentMockServer.Start(new FluentMockServerSettings
|
||||
{
|
||||
Urls = new [] { url1, url2, url3 },
|
||||
StartAdminInterface = true,
|
||||
ReadStaticMappings = true
|
||||
});
|
||||
Console.WriteLine("FluentMockServer listening at {0}", string.Join(" and ", server.Urls));
|
||||
|
||||
server.SetBasicAuthentication("a", "b");
|
||||
|
||||
server.AllowPartialMapping();
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath(p => p.Contains("x")).UsingGet())
|
||||
.AtPriority(4)
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""Contains x with FUNC 200""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/data").UsingPost().WithBody(b => b.Contains("e")))
|
||||
.AtPriority(999)
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(201)
|
||||
.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()
|
||||
.WithStatusCode(201)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""json posted with 201""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/json2").UsingPost().WithBody("x"))
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(201)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""json posted with x - 201""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/data").UsingDelete())
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBody(@"{ ""result"": ""data deleted with 200""}"));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/nobody").UsingGet())
|
||||
.RespondWith(Response.Create().WithDelay(TimeSpan.FromSeconds(1))
|
||||
.WithStatusCode(200));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/partial").UsingPost().WithBody(new SimMetricsMatcher(new [] { "cat", "dog" })))
|
||||
.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))
|
||||
);
|
||||
|
||||
Console.WriteLine("Press any key to stop the server");
|
||||
Console.ReadKey();
|
||||
server.Stop();
|
||||
|
||||
Console.WriteLine("Displaying all requests");
|
||||
var allRequests = server.LogEntries;
|
||||
Console.WriteLine(JsonConvert.SerializeObject(allRequests, Formatting.Indented));
|
||||
|
||||
Console.WriteLine("Press any key to quit");
|
||||
Console.ReadKey();
|
||||
MainApp.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,11 +47,9 @@
|
||||
</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" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
@@ -66,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.
|
||||
|
||||
42
examples/WireMock.Net.StandAlone.NETCoreApp/Program.cs
Normal file
42
examples/WireMock.Net.StandAlone.NETCoreApp/Program.cs
Normal file
@@ -0,0 +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)
|
||||
{
|
||||
server = StandAloneApp.Start(args);
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>netcoreapp2.0;netcoreapp1.1</TargetFrameworks>
|
||||
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
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
|
||||
15
examples/WireMock.Net.StandAlone.Net452/Program.cs
Normal file
15
examples/WireMock.Net.StandAlone.Net452/Program.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace WireMock.Net.StandAlone.Net452
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
static void Main(params string[] args)
|
||||
{
|
||||
StandAloneApp.Start(args);
|
||||
|
||||
Console.WriteLine("Press any key to stop the server");
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" 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>{668F689E-57B4-422E-8846-C0FF643CA999}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>WireMock.Net.StandAlone.Net452</RootNamespace>
|
||||
<AssemblyName>WireMock.Net.StandAlone.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>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>..\..\WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject>WireMock.Net.StandAlone.Net452.Program</StartupObject>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<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.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -1,3 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
</configuration>
|
||||
<packages>
|
||||
</packages>
|
||||
@@ -1,46 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<AssemblyTitle>WireMock.Net.Logic</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<TargetFrameworks>net45;netstandard1.3</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net.Logic</AssemblyName>
|
||||
<DebugType>full</DebugType>
|
||||
<ApplicationIcon>../WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
<Version>1.0.2.0</Version>
|
||||
<RootNamespace>WireMock</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
|
||||
<DefineConstants>NETSTANDARD</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="10.4.0">
|
||||
<PrivateAssets>All</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
|
||||
<PackageReference Include="SimMetrics.Net" Version="1.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.OwinSelfHost" Version="5.2.3" />
|
||||
<PackageReference Include="Handlebars.Net" Version="1.8.0" />
|
||||
<PackageReference Include="XPath2" Version="1.0.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Owin" Version="1.1.1" />
|
||||
<!--<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="1.1.1" />-->
|
||||
<PackageReference Include="Handlebars.NetStandard" Version="1.8.1" />
|
||||
<PackageReference Include="System.Threading" Version="4.3.0" />
|
||||
<PackageReference Include="System.Threading.Tasks" Version="4.3.0" />
|
||||
<PackageReference Include="System.Threading.Thread">
|
||||
<Version>4.3.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
|
||||
<PackageReference Include="System.Xml.XPath.XmlDocument" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,49 +0,0 @@
|
||||
namespace WireMock.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// WireMockSettings
|
||||
/// </summary>
|
||||
public class WireMockSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the port.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The port.
|
||||
/// </value>
|
||||
public int? Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the use SSL.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The use SSL.
|
||||
/// </value>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public bool? UseSSL { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start admin interface.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The start admin interface.
|
||||
/// </value>
|
||||
public bool? StartAdminInterface { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the read static mappings.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The read static mappings.
|
||||
/// </value>
|
||||
public bool? ReadStaticMappings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the urls.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The urls.
|
||||
/// </value>
|
||||
public string[] Urls { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>WireMock.Net Owin Middleware.</Description>
|
||||
<Version>1.0.0.0</Version>
|
||||
<AssemblyTitle>WireMock.Net</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<TargetFrameworks>net45;netstandard1.3</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net.Middleware</AssemblyName>
|
||||
<PackageId>WireMock.Net.Middleware</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</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
|
||||
<DefineConstants>NETSTANDARD</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WireMock.Net\WireMock.Net.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="10.4.0">
|
||||
<PrivateAssets>All</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
|
||||
<!--<PackageReference Include="SimMetrics.Net" Version="1.0.3" />-->
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.OwinSelfHost" Version="5.2.3" />
|
||||
<PackageReference Include="Handlebars.Net" Version="1.8.0" />
|
||||
<PackageReference Include="XPath2" Version="1.0.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Owin" Version="1.1.1" />
|
||||
<!--<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="1.1.1" />-->
|
||||
<PackageReference Include="Handlebars.NetStandard" Version="1.8.1" />
|
||||
<PackageReference Include="System.Threading" Version="4.3.0" />
|
||||
<PackageReference Include="System.Threading.Tasks" Version="4.3.0" />
|
||||
<PackageReference Include="System.Threading.Thread">
|
||||
<Version>4.3.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
|
||||
<PackageReference Include="System.Xml.XPath.XmlDocument" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,144 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers.Request;
|
||||
using System.Linq;
|
||||
#if NETSTANDARD
|
||||
using Microsoft.AspNetCore.Http;
|
||||
#else
|
||||
using Microsoft.Owin;
|
||||
#endif
|
||||
|
||||
namespace WireMock.Owin
|
||||
{
|
||||
#if NETSTANDARD
|
||||
internal class WireMockMiddleware
|
||||
#else
|
||||
internal class WireMockMiddleware : OwinMiddleware
|
||||
#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(RequestDelegate next, WireMockMiddlewareOptions options)
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
#else
|
||||
public WireMockMiddleware(OwinMiddleware next, WireMockMiddlewareOptions options) : base(next)
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NETSTANDARD
|
||||
public async Task Invoke(HttpContext ctx)
|
||||
#else
|
||||
public override async Task Invoke(IOwinContext ctx)
|
||||
#endif
|
||||
{
|
||||
if (_options.RequestProcessingDelay > TimeSpan.Zero)
|
||||
{
|
||||
await Task.Delay(_options.RequestProcessingDelay.Value);
|
||||
// Thread.Sleep(_options.RequestProcessingDelay.Value);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
response = await targetMapping.ResponseTo(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CommandLineParser.Arguments;
|
||||
using CommandLineParser.Exceptions;
|
||||
using WireMock.Server;
|
||||
|
||||
namespace WireMock.Net.StandAlone.NETCoreApp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
private class Options
|
||||
{
|
||||
[ValueArgument(typeof(string), 'u', "Urls", Description = "URL(s) to listen on.", Optional = true, AllowMultiple = true)]
|
||||
public List<string> Urls { get; set; }
|
||||
|
||||
[SwitchArgument('p', "AllowPartialMapping", true, Description = "Allow Partial Mapping (default set to true).", Optional = true)]
|
||||
public bool AllowPartialMapping { get; set; }
|
||||
|
||||
[SwitchArgument('s', "StartAdminInterface", true, Description = "Start the AdminInterface (default set to true).", Optional = true)]
|
||||
public bool StartAdminInterface { get; set; }
|
||||
|
||||
[SwitchArgument('r', "ReadStaticMappings", true, Description = "Read StaticMappings from ./__admin/mappings (default set to true).", Optional = true)]
|
||||
public bool ReadStaticMappings { get; set; }
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var options = new Options();
|
||||
var parser = new CommandLineParser.CommandLineParser();
|
||||
parser.ExtractArgumentAttributes(options);
|
||||
|
||||
try
|
||||
{
|
||||
parser.ParseCommandLine(args);
|
||||
|
||||
if (!options.Urls.Any())
|
||||
{
|
||||
options.Urls.Add("http://localhost:9090/");
|
||||
}
|
||||
|
||||
var server = FluentMockServer.Start(new FluentMockServerSettings
|
||||
{
|
||||
Urls = options.Urls.ToArray(),
|
||||
StartAdminInterface = options.StartAdminInterface,
|
||||
ReadStaticMappings = options.ReadStaticMappings
|
||||
});
|
||||
|
||||
if (options.AllowPartialMapping)
|
||||
{
|
||||
server.AllowPartialMapping();
|
||||
}
|
||||
|
||||
Console.WriteLine("WireMock.Net server listening at {0}", string.Join(" and ", server.Urls));
|
||||
}
|
||||
catch (CommandLineException e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
parser.ShowUsage();
|
||||
}
|
||||
|
||||
Console.WriteLine("Press any key to stop the server");
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CommandLineParser.Arguments;
|
||||
using CommandLineParser.Exceptions;
|
||||
using WireMock.Server;
|
||||
|
||||
namespace WireMock.Net.StandAlone
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
private class Options
|
||||
{
|
||||
[ValueArgument(typeof(string), 'u', "Urls", Description = "URL(s) to listen on.", Optional = true, AllowMultiple = true)]
|
||||
public List<string> Urls { get; set; }
|
||||
|
||||
[SwitchArgument('p', "AllowPartialMapping", true, Description = "Allow Partial Mapping (default set to true).", Optional = true)]
|
||||
public bool AllowPartialMapping { get; set; }
|
||||
|
||||
[SwitchArgument('s', "StartAdminInterface", true, Description = "Start the AdminInterface (default set to true).", Optional = true)]
|
||||
public bool StartAdminInterface { get; set; }
|
||||
|
||||
[SwitchArgument('r', "ReadStaticMappings", true, Description = "Read StaticMappings from ./__admin/mappings (default set to true).", Optional = true)]
|
||||
public bool ReadStaticMappings { get; set; }
|
||||
}
|
||||
|
||||
static void Main(params string[] args)
|
||||
{
|
||||
var options = new Options();
|
||||
var parser = new CommandLineParser.CommandLineParser();
|
||||
parser.ExtractArgumentAttributes(options);
|
||||
|
||||
try
|
||||
{
|
||||
parser.ParseCommandLine(args);
|
||||
|
||||
if (!options.Urls.Any())
|
||||
options.Urls.Add("http://localhost:9090/");
|
||||
|
||||
var server = FluentMockServer.Start(new FluentMockServerSettings
|
||||
{
|
||||
Urls = options.Urls.ToArray(),
|
||||
StartAdminInterface = options.StartAdminInterface,
|
||||
ReadStaticMappings = options.ReadStaticMappings
|
||||
});
|
||||
|
||||
if (options.AllowPartialMapping)
|
||||
server.AllowPartialMapping();
|
||||
|
||||
Console.WriteLine("WireMock.Net server listening at {0}", string.Join(" and ", server.Urls));
|
||||
}
|
||||
catch (CommandLineException e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
parser.ShowUsage();
|
||||
}
|
||||
|
||||
Console.WriteLine("Press any key to stop the server");
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
83
src/WireMock.Net.StandAlone/StandAloneApp.cs
Normal file
83
src/WireMock.Net.StandAlone/StandAloneApp.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using WireMock.Server;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Validation;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace WireMock.Net.StandAlone
|
||||
{
|
||||
/// <summary>
|
||||
/// The StandAloneApp
|
||||
/// </summary>
|
||||
public static class StandAloneApp
|
||||
{
|
||||
/// <summary>
|
||||
/// Start WireMock.Net standalone based on the FluentMockServerSettings.
|
||||
/// </summary>
|
||||
/// <param name="settings">The FluentMockServerSettings</param>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer Start([NotNull] FluentMockServerSettings settings)
|
||||
{
|
||||
Check.NotNull(settings, nameof(settings));
|
||||
|
||||
return FluentMockServer.Start(settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start WireMock.Net standalone based on the commandline arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The commandline arguments</param>
|
||||
[PublicAPI]
|
||||
public static FluentMockServer Start([NotNull] string[] args)
|
||||
{
|
||||
Check.NotNull(args, nameof(args));
|
||||
|
||||
Console.WriteLine("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));
|
||||
|
||||
var parser = new SimpleCommandLineParser();
|
||||
parser.Parse(args);
|
||||
|
||||
var settings = new FluentMockServerSettings
|
||||
{
|
||||
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 (parser.Contains("Port"))
|
||||
{
|
||||
settings.Port = parser.GetIntValue("Port");
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.Urls = parser.GetValues("Urls", new[] { "http://*:9091/" });
|
||||
}
|
||||
|
||||
string proxyURL = parser.GetStringValue("ProxyURL");
|
||||
if (!string.IsNullOrEmpty(proxyURL))
|
||||
{
|
||||
settings.ProxyAndRecordSettings = new ProxyAndRecordSettings
|
||||
{
|
||||
Url = proxyURL,
|
||||
SaveMapping = parser.GetBoolValue("SaveMapping"),
|
||||
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
|
||||
X509Certificate2ThumbprintOrSubjectName = parser.GetStringValue("X509Certificate2ThumbprintOrSubjectName")
|
||||
};
|
||||
}
|
||||
|
||||
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,67 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" 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>{668F689E-57B4-422E-8846-C0FF643CA999}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>WireMock.Net.StandAlone</RootNamespace>
|
||||
<AssemblyName>WireMock.Net.StandAlone</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>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>..\..\WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="CommandLineArgumentsParser, Version=3.0.9.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\CommandLineArgumentsParser.3.0.9\lib\net45\CommandLineArgumentsParser.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="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="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</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.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<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>
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="CommandLineArgumentsParser" version="3.0.9" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin.Host.HttpListener" version="3.1.0" targetFramework="net452" />
|
||||
</packages>
|
||||
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,73 +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>
|
||||
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; }
|
||||
}
|
||||
}
|
||||
160
src/WireMock.Net/Client/IFluentMockServerAdmin.cs
Normal file
160
src/WireMock.Net/Client/IFluentMockServerAdmin.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using RestEase;
|
||||
using System;
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// The RestEase interface which defines all admin commands.
|
||||
/// </summary>
|
||||
public interface IFluentMockServerAdmin
|
||||
{
|
||||
/// <summary>
|
||||
/// Authentication header
|
||||
/// </summary>
|
||||
[Header("Authorization")]
|
||||
AuthenticationHeaderValue Authorization { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the settings.
|
||||
/// </summary>
|
||||
/// <returns>SettingsModel</returns>
|
||||
[Get("__admin/settings")]
|
||||
Task<SettingsModel> GetSettingsAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Update the settings.
|
||||
/// </summary>
|
||||
/// <param name="settings">SettingsModel</param>
|
||||
[Put("__admin/settings")]
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<string> PutSettingsAsync([Body] SettingsModel settings);
|
||||
|
||||
/// <summary>
|
||||
/// Update the settings
|
||||
/// </summary>
|
||||
/// <param name="settings">SettingsModel</param>
|
||||
[Post("__admin/settings")]
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<string> PostSettingsAsync([Body] SettingsModel settings);
|
||||
|
||||
/// <summary>
|
||||
/// Get the mappings.
|
||||
/// </summary>
|
||||
/// <returns>MappingModels</returns>
|
||||
[Get("__admin/mappings")]
|
||||
Task<IList<MappingModel>> GetMappingsAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Add a new mapping.
|
||||
/// </summary>
|
||||
/// <param name="mapping">MappingModel</param>
|
||||
[Post("__admin/mappings")]
|
||||
Task<string> PostMappingAsync([Body] MappingModel mapping);
|
||||
|
||||
/// <summary>
|
||||
/// Delete all mappings.
|
||||
/// </summary>
|
||||
[Delete("__admin/mappings")]
|
||||
Task<string> DeleteMappingsAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Delete (reset) all mappings.
|
||||
/// </summary>
|
||||
[Post("__admin/mappings/reset")]
|
||||
Task<string> ResetMappingsAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Get a mapping based on the guid
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid</param>
|
||||
/// <returns>MappingModel</returns>
|
||||
[Get("__admin/mappings/{guid}")]
|
||||
Task<MappingModel> GetMappingAsync([Path] Guid guid);
|
||||
|
||||
/// <summary>
|
||||
/// Update a mapping based on the guid
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid</param>
|
||||
/// <param name="mapping">MappingModel</param>
|
||||
[Put("__admin/mappings/{guid}")]
|
||||
Task<string> PutMappingAsync([Path] Guid guid, [Body] MappingModel mapping);
|
||||
|
||||
/// <summary>
|
||||
/// Delete a mapping based on the guid
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid</param>
|
||||
[Delete("__admin/mappings/{guid}")]
|
||||
Task<string> DeleteMappingAsync([Path] Guid guid);
|
||||
|
||||
/// <summary>
|
||||
/// Save the mappings
|
||||
/// </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();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Validation;
|
||||
using WireMock.Settings;
|
||||
|
||||
namespace WireMock
|
||||
{
|
||||
@@ -16,9 +17,46 @@ namespace WireMock
|
||||
_responseMessageFunc = responseMessageFunc;
|
||||
}
|
||||
|
||||
public Task<ResponseMessage> ProvideResponse(RequestMessage requestMessage)
|
||||
public Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage)
|
||||
{
|
||||
return Task.FromResult(_responseMessageFunc(requestMessage));
|
||||
}
|
||||
}
|
||||
|
||||
internal class DynamicAsyncResponseProvider : IResponseProvider
|
||||
{
|
||||
private readonly Func<RequestMessage, Task<ResponseMessage>> _responseMessageFunc;
|
||||
|
||||
public DynamicAsyncResponseProvider([NotNull] Func<RequestMessage, Task<ResponseMessage>> responseMessageFunc)
|
||||
{
|
||||
Check.NotNull(responseMessageFunc, nameof(responseMessageFunc));
|
||||
|
||||
_responseMessageFunc = responseMessageFunc;
|
||||
}
|
||||
|
||||
public Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage)
|
||||
{
|
||||
return _responseMessageFunc(requestMessage);
|
||||
}
|
||||
}
|
||||
|
||||
internal class ProxyAsyncResponseProvider : IResponseProvider
|
||||
{
|
||||
private readonly Func<RequestMessage, ProxyAndRecordSettings, Task<ResponseMessage>> _responseMessageFunc;
|
||||
private readonly ProxyAndRecordSettings _settings;
|
||||
|
||||
public ProxyAsyncResponseProvider([NotNull] Func<RequestMessage, ProxyAndRecordSettings, Task<ResponseMessage>> responseMessageFunc, [NotNull] ProxyAndRecordSettings settings)
|
||||
{
|
||||
Check.NotNull(responseMessageFunc, nameof(responseMessageFunc));
|
||||
Check.NotNull(settings, nameof(settings));
|
||||
|
||||
_responseMessageFunc = responseMessageFunc;
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage)
|
||||
{
|
||||
return _responseMessageFunc(requestMessage, _settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
121
src/WireMock.Net/Http/HttpClientHelper.cs
Normal file
121
src/WireMock.Net/Http/HttpClientHelper.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Http
|
||||
{
|
||||
internal static class HttpClientHelper
|
||||
{
|
||||
public static HttpClient CreateHttpClient(string clientX509Certificate2ThumbprintOrSubjectName = null)
|
||||
{
|
||||
HttpClientHandler handler;
|
||||
|
||||
if (string.IsNullOrEmpty(clientX509Certificate2ThumbprintOrSubjectName))
|
||||
{
|
||||
handler = new HttpClientHandler();
|
||||
}
|
||||
else
|
||||
{
|
||||
#if NETSTANDARD || NET46
|
||||
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
|
||||
};
|
||||
|
||||
var x509Certificate2 = CertificateUtil.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName);
|
||||
handler.ClientCertificates.Add(x509Certificate2);
|
||||
#else
|
||||
|
||||
var webRequestHandler = new WebRequestHandler
|
||||
{
|
||||
ClientCertificateOptions = ClientCertificateOption.Manual,
|
||||
ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true,
|
||||
AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate
|
||||
};
|
||||
|
||||
var x509Certificate2 = CertificateUtil.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName);
|
||||
webRequestHandler.ClientCertificates.Add(x509Certificate2);
|
||||
handler = webRequestHandler;
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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(HttpClient client, RequestMessage requestMessage, string url)
|
||||
{
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
// Transform response
|
||||
var responseMessage = new ResponseMessage
|
||||
{
|
||||
StatusCode = (int)httpResponseMessage.StatusCode,
|
||||
|
||||
BodyAsBytes = await httpResponseMessage.Content.ReadAsByteArrayAsync(),
|
||||
Body = await httpResponseMessage.Content.ReadAsStringAsync()
|
||||
};
|
||||
|
||||
// Set both content and response headers, replacing URLs in values
|
||||
var headers = httpResponseMessage.Content?.Headers.Union(httpResponseMessage.Headers);
|
||||
|
||||
foreach (var header in headers)
|
||||
{
|
||||
// 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,65 +0,0 @@
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using System.IO;
|
||||
//using System.Linq;
|
||||
//using System.Net;
|
||||
//using System.Text;
|
||||
//#if NET45
|
||||
|
||||
//#else
|
||||
//using System.Net.Http;
|
||||
//#endif
|
||||
|
||||
//namespace WireMock.Http
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// The http listener request mapper.
|
||||
// /// </summary>
|
||||
// public class HttpListenerRequestMapperOld
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// The map.
|
||||
// /// </summary>
|
||||
// /// <param name="listenerRequest">The listener request.</param>
|
||||
// /// <returns>The <see cref="RequestMessage"/>.</returns>
|
||||
// public RequestMessage MapAsync(HttpListenerRequest listenerRequest)
|
||||
// {
|
||||
// Uri url = listenerRequest.Url;
|
||||
// string verb = listenerRequest.HttpMethod;
|
||||
// byte[] body = GetRequestBody(listenerRequest);
|
||||
// Encoding bodyEncoding = body != null ? listenerRequest.ContentEncoding : null;
|
||||
// string bodyAsString = bodyEncoding?.GetString(body);
|
||||
// var listenerHeaders = listenerRequest.Headers;
|
||||
// var headers = listenerHeaders.AllKeys.ToDictionary(k => k, k => listenerHeaders[k]);
|
||||
// var cookies = new Dictionary<string, string>();
|
||||
|
||||
// foreach (Cookie cookie in listenerRequest.Cookies)
|
||||
// cookies.Add(cookie.Name, cookie.Value);
|
||||
|
||||
// return new RequestMessage(url, verb, body, bodyAsString, bodyEncoding, headers, cookies) { DateTime = DateTime.Now };
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// The get request body.
|
||||
// /// </summary>
|
||||
// /// <param name="request">The request.</param>
|
||||
// /// <returns>The <see cref="string"/>.</returns>
|
||||
// private byte[] GetRequestBody(HttpListenerRequest request)
|
||||
// {
|
||||
// if (!request.HasEntityBody)
|
||||
// {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// using (var bodyStream = request.InputStream)
|
||||
// {
|
||||
// using (var memoryStream = new MemoryStream())
|
||||
// {
|
||||
// bodyStream.CopyTo(memoryStream);
|
||||
|
||||
// return memoryStream.ToArray();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -1,44 +0,0 @@
|
||||
//using System.Linq;
|
||||
//using System.Net;
|
||||
//using System.Text;
|
||||
//#if NET45
|
||||
|
||||
//#else
|
||||
//using System.Net.Http;
|
||||
//#endif
|
||||
|
||||
//namespace WireMock.Http
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// The http listener response mapper.
|
||||
// /// </summary>
|
||||
// public class HttpListenerResponseMapperOld
|
||||
// {
|
||||
// private readonly Encoding _utf8NoBom = new UTF8Encoding(false);
|
||||
|
||||
// /// <summary>
|
||||
// /// The map.
|
||||
// /// </summary>
|
||||
// /// <param name="responseMessage">
|
||||
// /// The response.
|
||||
// /// </param>
|
||||
// /// <param name="listenerResponse">The listenerResponse.</param>
|
||||
// public void MapAsync(ResponseMessage responseMessage, HttpListenerResponse listenerResponse)
|
||||
// {
|
||||
// listenerResponse.StatusCode = responseMessage.StatusCode;
|
||||
|
||||
// responseMessage.Headers.ToList().ForEach(pair => listenerResponse.AddHeader(pair.Key, pair.Value));
|
||||
|
||||
// if (responseMessage.Body == null)
|
||||
// return;
|
||||
|
||||
// var encoding = responseMessage.BodyEncoding ?? _utf8NoBom;
|
||||
// byte[] buffer = encoding.GetBytes(responseMessage.Body);
|
||||
|
||||
// listenerResponse.ContentEncoding = encoding;
|
||||
// listenerResponse.ContentLength64 = buffer.Length;
|
||||
// listenerResponse.OutputStream.Write(buffer, 0, buffer.Length);
|
||||
// listenerResponse.OutputStream.Flush();
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -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,117 +0,0 @@
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//#if NET45
|
||||
//using System.Net;
|
||||
//#else
|
||||
//using System.Net.Http;
|
||||
//#endif
|
||||
//using System.Threading;
|
||||
//using System.Threading.Tasks;
|
||||
//using JetBrains.Annotations;
|
||||
//using WireMock.Validation;
|
||||
|
||||
//namespace WireMock.Http
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// The tiny http server.
|
||||
// /// </summary>
|
||||
// public class TinyHttpServerOld
|
||||
// {
|
||||
// private readonly Action<HttpListenerContext, CancellationToken> _httpHandler;
|
||||
|
||||
// private readonly HttpListener _listener;
|
||||
|
||||
// private readonly CancellationTokenSource _cts;
|
||||
|
||||
// /// <summary>
|
||||
// /// Gets a value indicating whether this server is started.
|
||||
// /// </summary>
|
||||
// /// <value>
|
||||
// /// <c>true</c> if this server is started; otherwise, <c>false</c>.
|
||||
// /// </value>
|
||||
// public bool IsStarted { get; private set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// Gets the url.
|
||||
// /// </summary>
|
||||
// /// <value>
|
||||
// /// The urls.
|
||||
// /// </value>
|
||||
// [PublicAPI]
|
||||
// public List<Uri> Urls { get; } = new List<Uri>();
|
||||
|
||||
// /// <summary>
|
||||
// /// Gets the ports.
|
||||
// /// </summary>
|
||||
// /// <value>
|
||||
// /// The ports.
|
||||
// /// </value>
|
||||
// [PublicAPI]
|
||||
// public List<int> Ports { get; } = new List<int>();
|
||||
|
||||
// /// <summary>
|
||||
// /// Initializes a new instance of the <see cref="TinyHttpServer"/> class.
|
||||
// /// </summary>
|
||||
// /// <param name="uriPrefixes">The uriPrefixes.</param>
|
||||
// /// <param name="httpHandler">The http handler.</param>
|
||||
// public TinyHttpServerOld([NotNull] Action<HttpListenerContext, CancellationToken> httpHandler, [NotNull] params string[] uriPrefixes)
|
||||
// {
|
||||
// Check.NotNull(httpHandler, nameof(httpHandler));
|
||||
// Check.NotEmpty(uriPrefixes, nameof(uriPrefixes));
|
||||
|
||||
// _cts = new CancellationTokenSource();
|
||||
|
||||
// _httpHandler = httpHandler;
|
||||
|
||||
// // Create a listener.
|
||||
// _listener = new HttpListener();
|
||||
// foreach (string uriPrefix in uriPrefixes)
|
||||
// {
|
||||
// var uri = new Uri(uriPrefix);
|
||||
// Urls.Add(uri);
|
||||
// Ports.Add(uri.Port);
|
||||
|
||||
// _listener.Prefixes.Add(uriPrefix);
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Start the server.
|
||||
// /// </summary>
|
||||
// [PublicAPI]
|
||||
// public void Start()
|
||||
// {
|
||||
// _listener.Start();
|
||||
|
||||
// IsStarted = true;
|
||||
|
||||
// Task.Run(
|
||||
// async () =>
|
||||
// {
|
||||
// //using (_listener)
|
||||
// {
|
||||
// while (!_cts.Token.IsCancellationRequested)
|
||||
// {
|
||||
// HttpListenerContext context = await _listener.GetContextAsync();
|
||||
// _httpHandler(context, _cts.Token);
|
||||
// }
|
||||
|
||||
// _listener.Stop();
|
||||
// IsStarted = false;
|
||||
// }
|
||||
// },
|
||||
// _cts.Token);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Stop the server.
|
||||
// /// </summary>
|
||||
// [PublicAPI]
|
||||
// public void Stop()
|
||||
// {
|
||||
// _listener?.Stop();
|
||||
|
||||
// _cts.Cancel();
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace WireMock
|
||||
{
|
||||
@@ -10,12 +11,8 @@ namespace WireMock
|
||||
/// <summary>
|
||||
/// The provide response.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">
|
||||
/// The request.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/>.
|
||||
/// </returns>
|
||||
Task<ResponseMessage> ProvideResponse(RequestMessage requestMessage);
|
||||
/// <param name="requestMessage">The request.</param>
|
||||
/// <returns>The <see cref="ResponseMessage"/>.</returns>
|
||||
Task<ResponseMessage> ProvideResponseAsync([NotNull] RequestMessage requestMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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="Task"/>.</returns>
|
||||
public async Task<ResponseMessage> ResponseTo(RequestMessage requestMessage)
|
||||
{
|
||||
return await Provider.ProvideResponse(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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,6 @@
|
||||
/// <summary>
|
||||
/// The registration callback.
|
||||
/// </summary>
|
||||
/// <param name="mapping">
|
||||
/// The route.
|
||||
/// </param>
|
||||
/// <param name="mapping">The mapping.</param>
|
||||
public delegate void RegistrationCallback(Mapping mapping);
|
||||
}
|
||||
@@ -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,25 +43,17 @@ namespace WireMock.Matchers.Request
|
||||
/// </returns>
|
||||
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
|
||||
{
|
||||
var list = new List<double>();
|
||||
if (!RequestMatchers.Any())
|
||||
{
|
||||
return MatchScores.Mismatch;
|
||||
}
|
||||
|
||||
if (_type == CompositeMatcherType.And)
|
||||
{
|
||||
foreach (var requestMatcher in RequestMatchers)
|
||||
{
|
||||
double score = requestMatcher.GetMatchingScore(requestMessage, requestMatchResult);
|
||||
list.Add(score);
|
||||
}
|
||||
|
||||
return list.Sum() / list.Count;
|
||||
}
|
||||
|
||||
foreach (var requestMatcher in RequestMatchers)
|
||||
{
|
||||
double score = requestMatcher.GetMatchingScore(requestMessage, requestMatchResult);
|
||||
list.Add(score);
|
||||
return RequestMatchers.Average(requestMatcher => requestMatcher.GetMatchingScore(requestMessage, requestMatchResult));
|
||||
}
|
||||
|
||||
return list.Max();
|
||||
return RequestMatchers.Max(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 NET45
|
||||
#if !NETSTANDARD1_3
|
||||
using Wmhelp.XPath2;
|
||||
#endif
|
||||
|
||||
@@ -41,10 +41,10 @@ namespace WireMock.Matchers
|
||||
try
|
||||
{
|
||||
var nav = new XmlDocument { InnerXml = input }.CreateNavigator();
|
||||
#if NET45
|
||||
return MatchScores.ToScore(_patterns.Select(p => true.Equals(nav.XPath2Evaluate($"boolean({p})"))));
|
||||
#else
|
||||
#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})"))));
|
||||
#endif
|
||||
}
|
||||
catch (Exception)
|
||||
@@ -65,9 +65,7 @@ namespace WireMock.Matchers
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Name
|
||||
/// </returns>
|
||||
/// <returns>Name</returns>
|
||||
public string GetName()
|
||||
{
|
||||
return "XPathMatcher";
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
#if !NET45
|
||||
using System;
|
||||
#if NETSTANDARD
|
||||
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,70 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
#if NET45
|
||||
using Microsoft.Owin;
|
||||
#else
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
#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 NET45
|
||||
IOwinRequest request
|
||||
#else
|
||||
HttpRequest request
|
||||
#endif
|
||||
)
|
||||
{
|
||||
#if NET45
|
||||
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,7 +2,8 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
#if NET45
|
||||
using WireMock.Http;
|
||||
#if !NETSTANDARD
|
||||
using Microsoft.Owin;
|
||||
#else
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@@ -23,7 +24,7 @@ namespace WireMock.Owin
|
||||
/// <param name="responseMessage"></param>
|
||||
/// <param name="response"></param>
|
||||
public async Task MapAsync(ResponseMessage responseMessage
|
||||
#if NET45
|
||||
#if !NETSTANDARD
|
||||
, IOwinResponse response
|
||||
#else
|
||||
, HttpResponse response
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if NET45
|
||||
#if !NETSTANDARD
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
@@ -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,144 +1,181 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers.Request;
|
||||
using System.Linq;
|
||||
#if NET45
|
||||
using Microsoft.Owin;
|
||||
#else
|
||||
using Microsoft.AspNetCore.Http;
|
||||
#endif
|
||||
|
||||
namespace WireMock.Owin
|
||||
{
|
||||
#if NET45
|
||||
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 NET45
|
||||
public WireMockMiddleware(OwinMiddleware next, WireMockMiddlewareOptions options) : base(next)
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
#else
|
||||
public WireMockMiddleware(RequestDelegate next, WireMockMiddlewareOptions options)
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NET45
|
||||
public override async Task Invoke(IOwinContext ctx)
|
||||
#else
|
||||
public async Task Invoke(HttpContext ctx)
|
||||
#endif
|
||||
{
|
||||
if (_options.RequestProcessingDelay > TimeSpan.Zero)
|
||||
{
|
||||
await Task.Delay(_options.RequestProcessingDelay.Value);
|
||||
// Thread.Sleep(_options.RequestProcessingDelay.Value);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
response = await targetMapping.ResponseTo(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.
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
18
src/WireMock.Net/ResponseBuilders/IProxyResponseBuilder.cs
Normal file
18
src/WireMock.Net/ResponseBuilders/IProxyResponseBuilder.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace WireMock.ResponseBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// The ProxyResponseBuilder interface.
|
||||
/// </summary>
|
||||
public interface IProxyResponseBuilder : IStatusCodeResponseBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// With Proxy URL using X509Certificate2.
|
||||
/// </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>
|
||||
IResponseBuilder WithProxy([NotNull] string proxyUrl, [CanBeNull] string clientX509Certificate2ThumbprintOrSubjectName = null);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// The ResponseBuilder interface.
|
||||
/// </summary>
|
||||
public interface IResponseBuilder : IStatusCodeResponseBuilder
|
||||
public interface IResponseBuilder : IProxyResponseBuilder
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -10,18 +10,14 @@ namespace WireMock.ResponseBuilders
|
||||
/// <summary>
|
||||
/// The with status code.
|
||||
/// </summary>
|
||||
/// <param name="code">
|
||||
/// The code.
|
||||
/// </param>
|
||||
/// <param name="code">The code.</param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithStatusCode(int code);
|
||||
|
||||
/// <summary>
|
||||
/// The with status code.
|
||||
/// </summary>
|
||||
/// <param name="code">
|
||||
/// The code.
|
||||
/// </param>
|
||||
/// <param name="code">The code.</param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithStatusCode(HttpStatusCode code);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace WireMock.ResponseBuilders
|
||||
{
|
||||
/// <summary>
|
||||
/// The BodyResponseBuilder interface.
|
||||
/// The TransformResponseBuilder interface.
|
||||
/// </summary>
|
||||
public interface ITransformResponseBuilder : IDelayResponseBuilder
|
||||
{
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
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 HandlebarsDotNet;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
using WireMock.Http;
|
||||
using WireMock.Transformers;
|
||||
using WireMock.Util;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.ResponseBuilders
|
||||
@@ -15,6 +20,8 @@ namespace WireMock.ResponseBuilders
|
||||
/// </summary>
|
||||
public class Response : IResponseBuilder
|
||||
{
|
||||
private HttpClient httpClientForProxy;
|
||||
|
||||
/// <summary>
|
||||
/// The delay
|
||||
/// </summary>
|
||||
@@ -28,6 +35,16 @@ namespace WireMock.ResponseBuilders
|
||||
/// </value>
|
||||
public bool UseTransformer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Proxy URL to use.
|
||||
/// </summary>
|
||||
public string ProxyUrl { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The client X509Certificate2 Thumbprint or SubjectName to use.
|
||||
/// </summary>
|
||||
public string X509Certificate2ThumbprintOrSubjectName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response message.
|
||||
/// </summary>
|
||||
@@ -114,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));
|
||||
@@ -173,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;
|
||||
|
||||
@@ -230,48 +308,48 @@ namespace WireMock.ResponseBuilders
|
||||
return WithDelay(TimeSpan.FromMilliseconds(milliseconds));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
public IResponseBuilder WithProxy(string proxyUrl, string clientX509Certificate2ThumbprintOrSubjectName = null)
|
||||
{
|
||||
Check.NotEmpty(proxyUrl, nameof(proxyUrl));
|
||||
|
||||
ProxyUrl = proxyUrl;
|
||||
X509Certificate2ThumbprintOrSubjectName = clientX509Certificate2ThumbprintOrSubjectName;
|
||||
httpClientForProxy = HttpClientHelper.CreateHttpClient(clientX509Certificate2ThumbprintOrSubjectName);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The provide response.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">
|
||||
/// The request.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/>.
|
||||
/// </returns>
|
||||
public async Task<ResponseMessage> ProvideResponse(RequestMessage requestMessage)
|
||||
/// <param name="requestMessage">The request.</param>
|
||||
/// <returns>The <see cref="ResponseMessage"/>.</returns>
|
||||
public async Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage)
|
||||
{
|
||||
ResponseMessage responseMessage;
|
||||
if (UseTransformer)
|
||||
{
|
||||
responseMessage = new ResponseMessage { StatusCode = ResponseMessage.StatusCode, BodyOriginal = ResponseMessage.Body };
|
||||
|
||||
var template = new { request = requestMessage };
|
||||
|
||||
// Body
|
||||
var templateBody = Handlebars.Compile(ResponseMessage.Body);
|
||||
responseMessage.Body = templateBody(template);
|
||||
|
||||
// Headers
|
||||
var newHeaders = new Dictionary<string, string>();
|
||||
foreach (var header in ResponseMessage.Headers)
|
||||
{
|
||||
var templateHeaderKey = Handlebars.Compile(header.Key);
|
||||
var templateHeaderValue = Handlebars.Compile(header.Value);
|
||||
|
||||
newHeaders.Add(templateHeaderKey(template), templateHeaderValue(template));
|
||||
}
|
||||
responseMessage.Headers = newHeaders;
|
||||
}
|
||||
else
|
||||
{
|
||||
responseMessage = ResponseMessage;
|
||||
}
|
||||
Check.NotNull(requestMessage, nameof(requestMessage));
|
||||
|
||||
if (Delay != null)
|
||||
await Task.Delay(Delay.Value);
|
||||
|
||||
return responseMessage;
|
||||
if (ProxyUrl != null && httpClientForProxy != null)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return ResponseMessageTransformer.Transform(requestMessage, ResponseMessage);
|
||||
}
|
||||
|
||||
return ResponseMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
226
src/WireMock.Net/Serialization/MappingConverter.cs
Normal file
226
src/WireMock.Net/Serialization/MappingConverter.cs
Normal file
@@ -0,0 +1,226 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using SimMetrics.Net;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Serialization
|
||||
{
|
||||
internal static class MappingConverter
|
||||
{
|
||||
public static MappingModel ToMappingModel(Mapping mapping)
|
||||
{
|
||||
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>();
|
||||
var cookieMatchers = request.GetRequestMessageMatchers<RequestMessageCookieMatcher>();
|
||||
var paramsMatchers = request.GetRequestMessageMatchers<RequestMessageParamMatcher>();
|
||||
var bodyMatcher = request.GetRequestMessageMatcher<RequestMessageBodyMatcher>();
|
||||
var methodMatcher = request.GetRequestMessageMatcher<RequestMessageMethodMatcher>();
|
||||
|
||||
var mappingModel = new MappingModel
|
||||
{
|
||||
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)),
|
||||
Funcs = Map(pathMatchers.Where(m => m.Funcs != null).SelectMany(m => m.Funcs))
|
||||
} : null,
|
||||
|
||||
Url = urlMatchers != null && urlMatchers.Any() ? new UrlModel
|
||||
{
|
||||
Matchers = Map(urlMatchers.Where(m => m.Matchers != null).SelectMany(m => m.Matchers)),
|
||||
Funcs = Map(urlMatchers.Where(m => m.Funcs != null).SelectMany(m => m.Funcs))
|
||||
} : null,
|
||||
|
||||
Methods = methodMatcher?.Methods,
|
||||
|
||||
Headers = headerMatchers != null && headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel
|
||||
{
|
||||
Name = hm.Name,
|
||||
Matchers = Map(hm.Matchers),
|
||||
Funcs = Map(hm.Funcs)
|
||||
}).ToList() : null,
|
||||
|
||||
Cookies = cookieMatchers != null && cookieMatchers.Any() ? cookieMatchers.Select(cm => new CookieModel
|
||||
{
|
||||
Name = cm.Name,
|
||||
Matchers = Map(cm.Matchers),
|
||||
Funcs = Map(cm.Funcs)
|
||||
}).ToList() : null,
|
||||
|
||||
Params = paramsMatchers != null && paramsMatchers.Any() ? paramsMatchers.Select(pm => new ParamModel
|
||||
{
|
||||
Name = pm.Key,
|
||||
Values = pm.Values?.ToList(),
|
||||
Funcs = Map(pm.Funcs)
|
||||
}).ToList() : null,
|
||||
|
||||
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,
|
||||
DataFunc = bodyMatcher != null ? Map(bodyMatcher.DataFunc) : null
|
||||
}
|
||||
},
|
||||
Response = new ResponseModel
|
||||
{
|
||||
Delay = response.Delay?.Milliseconds
|
||||
}
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(response.ProxyUrl))
|
||||
{
|
||||
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 = 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;
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return mappingModel;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return matchers.Select(Map).Where(x => x != null).ToArray();
|
||||
}
|
||||
|
||||
private static MatcherModel Map([CanBeNull] IMatcher matcher)
|
||||
{
|
||||
if (matcher == null)
|
||||
return null;
|
||||
|
||||
var patterns = matcher.GetPatterns();
|
||||
|
||||
return new MatcherModel
|
||||
{
|
||||
Name = matcher.GetName(),
|
||||
Pattern = patterns.Length == 1 ? patterns.First() : null,
|
||||
Patterns = patterns.Length > 1 ? patterns : null
|
||||
};
|
||||
}
|
||||
|
||||
private static string[] Map<T>([CanBeNull] IEnumerable<Func<T, bool>> funcs)
|
||||
{
|
||||
if (funcs == null || !funcs.Any())
|
||||
return null;
|
||||
|
||||
return funcs.Select(Map).Where(x => x != null).ToArray();
|
||||
}
|
||||
|
||||
private static string Map<T>([CanBeNull] Func<T, bool> func)
|
||||
{
|
||||
return func?.ToString();
|
||||
}
|
||||
|
||||
public static IMatcher Map([CanBeNull] MatcherModel matcher)
|
||||
{
|
||||
if (matcher == null)
|
||||
return null;
|
||||
|
||||
var parts = matcher.Name.Split('.');
|
||||
string matcherName = parts[0];
|
||||
string matcherType = parts.Length > 1 ? parts[1] : null;
|
||||
|
||||
string[] patterns = matcher.Patterns ?? new[] { matcher.Pattern };
|
||||
|
||||
switch (matcherName)
|
||||
{
|
||||
case "ExactMatcher":
|
||||
return new ExactMatcher(patterns);
|
||||
|
||||
case "RegexMatcher":
|
||||
return new RegexMatcher(patterns);
|
||||
|
||||
case "JsonPathMatcher":
|
||||
return new JsonPathMatcher(patterns);
|
||||
|
||||
case "XPathMatcher":
|
||||
return new XPathMatcher(matcher.Pattern);
|
||||
|
||||
case "WildcardMatcher":
|
||||
return new WildcardMatcher(patterns, matcher.IgnoreCase == true);
|
||||
|
||||
case "SimMetricsMatcher":
|
||||
SimMetricType type = SimMetricType.Levenstein;
|
||||
if (!string.IsNullOrEmpty(matcherType) && !Enum.TryParse(matcherType, out type))
|
||||
throw new NotSupportedException($"Matcher '{matcherName}' with Type '{matcherType}' is not supported.");
|
||||
|
||||
return new SimMetricsMatcher(matcher.Pattern, type);
|
||||
|
||||
default:
|
||||
throw new NotSupportedException($"Matcher '{matcherName}' is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,18 +2,22 @@ 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;
|
||||
|
||||
@@ -28,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})$");
|
||||
|
||||
@@ -66,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);
|
||||
}
|
||||
@@ -82,12 +86,12 @@ namespace WireMock.Server
|
||||
{
|
||||
// __admin/settings
|
||||
Given(Request.Create().WithPath(AdminSettings).UsingGet()).RespondWith(new DynamicResponseProvider(SettingsGet));
|
||||
Given(Request.Create().WithPath(AdminSettings).UsingVerb("PUT", "POST")).RespondWith(new DynamicResponseProvider(SettingsUpdate));
|
||||
Given(Request.Create().WithPath(AdminSettings).UsingVerb("PUT", "POST").WithHeader("Content-Type", "application/json")).RespondWith(new DynamicResponseProvider(SettingsUpdate));
|
||||
|
||||
|
||||
// __admin/mappings
|
||||
Given(Request.Create().WithPath(AdminMappings).UsingGet()).RespondWith(new DynamicResponseProvider(MappingsGet));
|
||||
Given(Request.Create().WithPath(AdminMappings).UsingPost()).RespondWith(new DynamicResponseProvider(MappingsPost));
|
||||
Given(Request.Create().WithPath(AdminMappings).UsingPost().WithHeader("Content-Type", "application/json")).RespondWith(new DynamicResponseProvider(MappingsPost));
|
||||
Given(Request.Create().WithPath(AdminMappings).UsingDelete()).RespondWith(new DynamicResponseProvider(MappingsDelete));
|
||||
|
||||
// __admin/mappings/reset
|
||||
@@ -115,15 +119,77 @@ 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 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);
|
||||
_options.Mappings.Add(mapping);
|
||||
|
||||
if (settings.SaveMappingToFile)
|
||||
{
|
||||
SaveMappingToFile(mapping);
|
||||
}
|
||||
}
|
||||
|
||||
return responseMessage;
|
||||
}
|
||||
|
||||
private Mapping ToMapping(RequestMessage requestMessage, ResponseMessage responseMessage)
|
||||
{
|
||||
var request = Request.Create();
|
||||
request.WithPath(requestMessage.Path);
|
||||
request.UsingVerb(requestMessage.Method);
|
||||
|
||||
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));
|
||||
|
||||
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
|
||||
|
||||
#region Settings
|
||||
private ResponseMessage SettingsGet(RequestMessage requestMessage)
|
||||
{
|
||||
var model = new SettingsModel
|
||||
{
|
||||
AllowPartialMapping = _options.AllowPartialMapping,
|
||||
GlobalProcessingDelay = _options.RequestProcessingDelay?.Milliseconds
|
||||
MaxRequestLogCount = _options.MaxRequestLogCount,
|
||||
RequestLogExpirationDuration = _options.RequestLogExpirationDuration,
|
||||
GlobalProcessingDelay = (int?)_options.RequestProcessingDelay?.TotalMilliseconds
|
||||
};
|
||||
|
||||
return ToJson(model);
|
||||
@@ -136,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);
|
||||
|
||||
@@ -152,7 +222,7 @@ namespace WireMock.Server
|
||||
if (mapping == null)
|
||||
return new ResponseMessage { StatusCode = 404, Body = "Mapping not found" };
|
||||
|
||||
var model = ToMappingModel(mapping);
|
||||
var model = MappingConverter.ToMappingModel(mapping);
|
||||
|
||||
return ToJson(model);
|
||||
}
|
||||
@@ -194,21 +264,26 @@ namespace WireMock.Server
|
||||
|
||||
#region Mappings
|
||||
private ResponseMessage MappingsSave(RequestMessage requestMessage)
|
||||
{
|
||||
foreach (var mapping in Mappings.Where(m => !m.IsAdminInterface))
|
||||
{
|
||||
SaveMappingToFile(mapping);
|
||||
}
|
||||
|
||||
return new ResponseMessage { Body = "Mappings saved to disk" };
|
||||
}
|
||||
|
||||
private void SaveMappingToFile(Mapping mapping)
|
||||
{
|
||||
string folder = Path.Combine(Directory.GetCurrentDirectory(), AdminMappingsFolder);
|
||||
if (!Directory.Exists(folder))
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
foreach (var mapping in Mappings.Where(m => !m.IsAdminInterface))
|
||||
{
|
||||
var model = ToMappingModel(mapping);
|
||||
string json = JsonConvert.SerializeObject(model, _settings);
|
||||
string filename = !string.IsNullOrEmpty(mapping.Title) ? SanitizeFileName(mapping.Title) : mapping.Guid.ToString();
|
||||
var model = MappingConverter.ToMappingModel(mapping);
|
||||
string json = JsonConvert.SerializeObject(model, _settings);
|
||||
string filename = !string.IsNullOrEmpty(mapping.Title) ? SanitizeFileName(mapping.Title) : mapping.Guid.ToString();
|
||||
|
||||
File.WriteAllText(Path.Combine(folder, filename + ".json"), json);
|
||||
}
|
||||
|
||||
return new ResponseMessage { Body = "Mappings saved to disk" };
|
||||
File.WriteAllText(Path.Combine(folder, filename + ".json"), json);
|
||||
}
|
||||
|
||||
private static string SanitizeFileName(string name, char replaceChar = '_')
|
||||
@@ -221,7 +296,7 @@ namespace WireMock.Server
|
||||
var result = new List<MappingModel>();
|
||||
foreach (var mapping in Mappings.Where(m => !m.IsAdminInterface))
|
||||
{
|
||||
var model = ToMappingModel(mapping);
|
||||
var model = MappingConverter.ToMappingModel(mapping);
|
||||
result.Add(model);
|
||||
}
|
||||
|
||||
@@ -274,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);
|
||||
}
|
||||
|
||||
@@ -281,6 +363,8 @@ namespace WireMock.Server
|
||||
{
|
||||
ResetMappings();
|
||||
|
||||
ResetScenarios();
|
||||
|
||||
return new ResponseMessage { Body = "Mappings deleted" };
|
||||
}
|
||||
#endregion Mappings
|
||||
@@ -328,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,
|
||||
@@ -345,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
|
||||
{
|
||||
@@ -362,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
|
||||
};
|
||||
}
|
||||
@@ -386,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);
|
||||
@@ -396,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(Map).ToArray());
|
||||
{
|
||||
requestBuilder = requestBuilder.WithPath(pathModel.Matchers.Select(MappingConverter.Map).ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,23 +554,29 @@ 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(Map).ToArray());
|
||||
{
|
||||
requestBuilder = requestBuilder.WithUrl(urlModel.Matchers.Select(MappingConverter.Map).ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (requestModel.Methods != null)
|
||||
{
|
||||
requestBuilder = requestBuilder.UsingVerb(requestModel.Methods);
|
||||
}
|
||||
|
||||
if (requestModel.Headers != null)
|
||||
{
|
||||
foreach (var headerModel in requestModel.Headers.Where(h => h.Matchers != null))
|
||||
{
|
||||
requestBuilder = requestBuilder.WithHeader(headerModel.Name, headerModel.Matchers.Select(Map).ToArray());
|
||||
requestBuilder = requestBuilder.WithHeader(headerModel.Name, headerModel.Matchers.Select(MappingConverter.Map).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,7 +584,7 @@ namespace WireMock.Server
|
||||
{
|
||||
foreach (var cookieModel in requestModel.Cookies.Where(c => c.Matchers != null))
|
||||
{
|
||||
requestBuilder = requestBuilder.WithCookie(cookieModel.Name, cookieModel.Matchers.Select(Map).ToArray());
|
||||
requestBuilder = requestBuilder.WithCookie(cookieModel.Name, cookieModel.Matchers.Select(MappingConverter.Map).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,7 +598,7 @@ namespace WireMock.Server
|
||||
|
||||
if (requestModel.Body?.Matcher != null)
|
||||
{
|
||||
var bodyMatcher = Map(requestModel.Body.Matcher);
|
||||
var bodyMatcher = MappingConverter.Map(requestModel.Body.Matcher);
|
||||
requestBuilder = requestBuilder.WithBody(bodyMatcher);
|
||||
}
|
||||
|
||||
@@ -466,11 +609,35 @@ namespace WireMock.Server
|
||||
{
|
||||
IResponseBuilder responseBuilder = Response.Create();
|
||||
|
||||
if (responseModel.Delay > 0)
|
||||
{
|
||||
responseBuilder = responseBuilder.WithDelay(responseModel.Delay.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(responseModel.ProxyUrl))
|
||||
{
|
||||
if (string.IsNullOrEmpty(responseModel.X509Certificate2ThumbprintOrSubjectName))
|
||||
{
|
||||
return responseBuilder.WithProxy(responseModel.ProxyUrl);
|
||||
}
|
||||
|
||||
return responseBuilder.WithProxy(responseModel.ProxyUrl, responseModel.X509Certificate2ThumbprintOrSubjectName);
|
||||
}
|
||||
|
||||
if (responseModel.StatusCode.HasValue)
|
||||
{
|
||||
responseBuilder = responseBuilder.WithStatusCode(responseModel.StatusCode.Value);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
foreach (string headerLine in responseModel.HeadersRaw.Split(new[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries))
|
||||
@@ -482,185 +649,38 @@ namespace WireMock.Server
|
||||
}
|
||||
}
|
||||
|
||||
if (responseModel.Body != null)
|
||||
responseBuilder = responseBuilder.WithBody(responseModel.Body, ToEncoding(responseModel.BodyEncoding));
|
||||
if (responseModel.BodyAsBytes != null)
|
||||
{
|
||||
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)
|
||||
responseBuilder = responseBuilder.WithBodyAsBase64(responseModel.BodyAsBase64, ToEncoding(responseModel.BodyEncoding));
|
||||
}
|
||||
else if (responseModel.BodyFromBase64 != null)
|
||||
{
|
||||
responseBuilder = responseBuilder.WithBodyFromBase64(responseModel.BodyFromBase64, ToEncoding(responseModel.BodyEncoding));
|
||||
}
|
||||
|
||||
if (responseModel.UseTransformer)
|
||||
{
|
||||
responseBuilder = responseBuilder.WithTransformer();
|
||||
|
||||
if (responseModel.Delay > 0)
|
||||
responseBuilder = responseBuilder.WithDelay(responseModel.Delay.Value);
|
||||
}
|
||||
|
||||
return responseBuilder;
|
||||
}
|
||||
|
||||
private MappingModel ToMappingModel(Mapping mapping)
|
||||
{
|
||||
var request = (Request)mapping.RequestMatcher;
|
||||
var response = (Response)mapping.Provider;
|
||||
|
||||
var pathMatchers = request.GetRequestMessageMatchers<RequestMessagePathMatcher>();
|
||||
var urlMatchers = request.GetRequestMessageMatchers<RequestMessageUrlMatcher>();
|
||||
var headerMatchers = request.GetRequestMessageMatchers<RequestMessageHeaderMatcher>();
|
||||
var cookieMatchers = request.GetRequestMessageMatchers<RequestMessageCookieMatcher>();
|
||||
var paramsMatchers = request.GetRequestMessageMatchers<RequestMessageParamMatcher>();
|
||||
var bodyMatcher = request.GetRequestMessageMatcher<RequestMessageBodyMatcher>();
|
||||
var methodMatcher = request.GetRequestMessageMatcher<RequestMessageMethodMatcher>();
|
||||
|
||||
return new MappingModel
|
||||
{
|
||||
Guid = mapping.Guid,
|
||||
Title = mapping.Title,
|
||||
Priority = mapping.Priority,
|
||||
Request = new RequestModel
|
||||
{
|
||||
Path = pathMatchers != null && pathMatchers.Any() ? new PathModel
|
||||
{
|
||||
Matchers = Map(pathMatchers.Where(m => m.Matchers != null).SelectMany(m => m.Matchers)),
|
||||
Funcs = Map(pathMatchers.Where(m => m.Funcs != null).SelectMany(m => m.Funcs))
|
||||
} : null,
|
||||
|
||||
Url = urlMatchers != null && urlMatchers.Any() ? new UrlModel
|
||||
{
|
||||
Matchers = Map(urlMatchers.Where(m => m.Matchers != null).SelectMany(m => m.Matchers)),
|
||||
Funcs = Map(urlMatchers.Where(m => m.Funcs != null).SelectMany(m => m.Funcs))
|
||||
} : null,
|
||||
|
||||
Methods = methodMatcher?.Methods,
|
||||
|
||||
Headers = headerMatchers != null && headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel
|
||||
{
|
||||
Name = hm.Name,
|
||||
Matchers = Map(hm.Matchers),
|
||||
Funcs = Map(hm.Funcs)
|
||||
}).ToList() : null,
|
||||
|
||||
Cookies = cookieMatchers != null && cookieMatchers.Any() ? cookieMatchers.Select(cm => new CookieModel
|
||||
{
|
||||
Name = cm.Name,
|
||||
Matchers = Map(cm.Matchers),
|
||||
Funcs = Map(cm.Funcs)
|
||||
}).ToList() : null,
|
||||
|
||||
Params = paramsMatchers != null && paramsMatchers.Any() ? paramsMatchers.Select(pm => new ParamModel
|
||||
{
|
||||
Name = pm.Key,
|
||||
Values = pm.Values?.ToList(),
|
||||
Funcs = Map(pm.Funcs)
|
||||
}).ToList() : null,
|
||||
|
||||
Body = methodMatcher?.Methods != null && methodMatcher.Methods.Count(m => m == "get") == 1 ? null : new BodyModel
|
||||
{
|
||||
Matcher = bodyMatcher != null ? Map(bodyMatcher.Matcher) : null,
|
||||
Func = bodyMatcher != null ? Map(bodyMatcher.Func) : null,
|
||||
DataFunc = bodyMatcher != null ? Map(bodyMatcher.DataFunc) : null
|
||||
}
|
||||
},
|
||||
Response = new ResponseModel
|
||||
{
|
||||
StatusCode = response.ResponseMessage.StatusCode,
|
||||
Headers = response.ResponseMessage.Headers,
|
||||
Body = response.ResponseMessage.Body,
|
||||
UseTransformer = response.UseTransformer,
|
||||
Delay = response.Delay?.Milliseconds,
|
||||
|
||||
BodyEncoding = response.ResponseMessage.BodyEncoding != null ? new EncodingModel
|
||||
{
|
||||
EncodingName = response.ResponseMessage.BodyEncoding.EncodingName,
|
||||
CodePage = response.ResponseMessage.BodyEncoding.CodePage,
|
||||
WebName = response.ResponseMessage.BodyEncoding.WebName
|
||||
} : null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private MatcherModel[] Map([CanBeNull] IEnumerable<IMatcher> matchers)
|
||||
{
|
||||
if (matchers == null || !matchers.Any())
|
||||
return null;
|
||||
|
||||
return matchers.Select(Map).Where(x => x != null).ToArray();
|
||||
}
|
||||
|
||||
private MatcherModel Map([CanBeNull] IMatcher matcher)
|
||||
{
|
||||
if (matcher == null)
|
||||
return null;
|
||||
|
||||
var patterns = matcher.GetPatterns();
|
||||
|
||||
return new MatcherModel
|
||||
{
|
||||
Name = matcher.GetName(),
|
||||
Pattern = patterns.Length == 1 ? patterns.First() : null,
|
||||
Patterns = patterns.Length > 1 ? patterns : null
|
||||
};
|
||||
}
|
||||
|
||||
private string[] Map<T>([CanBeNull] IEnumerable<Func<T, bool>> funcs)
|
||||
{
|
||||
if (funcs == null || !funcs.Any())
|
||||
return null;
|
||||
|
||||
return funcs.Select(Map).Where(x => x != null).ToArray();
|
||||
}
|
||||
|
||||
private string Map<T>([CanBeNull] Func<T, bool> func)
|
||||
{
|
||||
return func?.ToString();
|
||||
}
|
||||
|
||||
private IMatcher Map([CanBeNull] MatcherModel matcher)
|
||||
{
|
||||
if (matcher == null)
|
||||
return null;
|
||||
|
||||
var parts = matcher.Name.Split('.');
|
||||
string matcherName = parts[0];
|
||||
string matcherType = parts.Length > 1 ? parts[1] : null;
|
||||
|
||||
string[] patterns = matcher.Patterns ?? new[] { matcher.Pattern };
|
||||
|
||||
switch (matcherName)
|
||||
{
|
||||
case "ExactMatcher":
|
||||
return new ExactMatcher(patterns);
|
||||
|
||||
case "RegexMatcher":
|
||||
return new RegexMatcher(patterns);
|
||||
|
||||
case "JsonPathMatcher":
|
||||
return new JsonPathMatcher(patterns);
|
||||
|
||||
case "XPathMatcher":
|
||||
return new XPathMatcher(matcher.Pattern);
|
||||
|
||||
case "WildcardMatcher":
|
||||
return new WildcardMatcher(patterns, matcher.IgnoreCase == true);
|
||||
|
||||
case "SimMetricsMatcher":
|
||||
SimMetricType type = SimMetricType.Levenstein;
|
||||
if (!string.IsNullOrEmpty(matcherType) && !Enum.TryParse(matcherType, out type))
|
||||
throw new NotSupportedException($"Matcher '{matcherName}' with Type '{matcherType}' is not supported.");
|
||||
|
||||
return new SimMetricsMatcher(matcher.Pattern, type);
|
||||
|
||||
default:
|
||||
throw new NotSupportedException($"Matcher '{matcherName}' is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
private ResponseMessage ToJson<T>(T result)
|
||||
{
|
||||
return new ResponseMessage
|
||||
{
|
||||
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") } }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
86
src/WireMock.Net/Server/FluentMockServer.LogEntries.cs
Normal file
86
src/WireMock.Net/Server/FluentMockServer.LogEntries.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
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,496 +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.Logging;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.RequestBuilders;
|
||||
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 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>
|
||||
/// Gets the mappings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public IEnumerable<Mapping> Mappings
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (((ICollection)_options.Mappings).SyncRoot)
|
||||
{
|
||||
return new ReadOnlyCollection<Mapping>(_options.Mappings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 NET45
|
||||
_httpServer = new OwinSelfHost(_options, Urls);
|
||||
#else
|
||||
_httpServer = new AspNetCoreSelfHost(_options, Urls);
|
||||
#endif
|
||||
Ports = _httpServer.Ports;
|
||||
|
||||
_httpServer.StartAsync();
|
||||
|
||||
if (settings.StartAdminInterface == true)
|
||||
{
|
||||
InitAdmin();
|
||||
}
|
||||
|
||||
if (settings.ReadStaticMappings == true)
|
||||
{
|
||||
ReadStaticMappings();
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Stop this server.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void Stop()
|
||||
{
|
||||
_httpServer?.StopAsync();
|
||||
}
|
||||
|
||||
/// <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 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the Mappings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void ResetMappings()
|
||||
{
|
||||
lock (((ICollection)_options.Mappings).SyncRoot)
|
||||
{
|
||||
_options.Mappings = _options.Mappings.Where(m => m.Provider is DynamicResponseProvider).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);
|
||||
}
|
||||
}
|
||||
|
||||
//private async void HandleRequestOld(IOwinContext ctx)
|
||||
//{
|
||||
// if (_requestProcessingDelay > TimeSpan.Zero)
|
||||
// {
|
||||
// lock (_syncRoot)
|
||||
// {
|
||||
// Task.Delay(_requestProcessingDelay.Value).Wait();
|
||||
// }
|
||||
// }
|
||||
|
||||
// var request = _requestMapper.MapAsync(ctx.Request);
|
||||
|
||||
// ResponseMessage response = null;
|
||||
// Mapping targetMapping = null;
|
||||
// RequestMatchResult requestMatchResult = null;
|
||||
// try
|
||||
// {
|
||||
// var mappings = _mappings
|
||||
// .Select(m => new { Mapping = m, MatchResult = m.IsRequestHandled(request) })
|
||||
// .ToList();
|
||||
|
||||
// if (_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 && _authorizationMatcher != null)
|
||||
// {
|
||||
// string authorization;
|
||||
// bool present = request.Headers.TryGetValue("Authorization", out authorization);
|
||||
// if (!present || _authorizationMatcher.IsMatch(authorization) < 1.0)
|
||||
// {
|
||||
// response = new ResponseMessage { StatusCode = 401 };
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// response = await targetMapping.ResponseTo(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);
|
||||
|
||||
// _responseMapper.MapAsync(response, ctx.Response);
|
||||
// ctx.Response.Close();
|
||||
// }
|
||||
//}
|
||||
}
|
||||
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,54 +0,0 @@
|
||||
namespace WireMock.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// FluentMockServerSettings
|
||||
/// </summary>
|
||||
public class FluentMockServerSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the port.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The port.
|
||||
/// </value>
|
||||
public int? Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the use SSL.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The use SSL.
|
||||
/// </value>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public bool? UseSSL { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start admin interface.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The start admin interface.
|
||||
/// </value>
|
||||
public bool? StartAdminInterface { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the read static mappings.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The read static mappings.
|
||||
/// </value>
|
||||
public bool? ReadStaticMappings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the urls.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The urls.
|
||||
/// </value>
|
||||
public string[] Urls { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// StartTimeout
|
||||
/// </summary>
|
||||
public int StartTimeout { get; set; } = 10000;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,65 @@
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user