Compare commits

..

56 Commits

Author SHA1 Message Date
Stef Heyenrath
e87e09e10d 1.0.2.4 2017-10-10 18:12:45 +02:00
Stef Heyenrath
eff21473e3 Also reset scenarios when the mappings are reset. 2017-10-09 09:27:49 +02:00
Stef Heyenrath
fce60f273d WireMock.Net.StandAlone 2017-10-07 18:24:51 +02:00
Stef Heyenrath
8199cdc382 scenario and state (added admin interface for GET and DELETE) 2017-10-07 18:10:17 +02:00
Stef Heyenrath
782418f107 Scenario_and_State_TodoList_Example 2017-10-07 17:34:09 +02:00
Stef Heyenrath
207688e42b Observable logs (refactor some code) 2017-10-07 15:28:36 +02:00
deeptowncitizen
2d39a18b70 Observable logs (#51)
* observable log entries

* event test
2017-10-07 15:05:02 +02:00
Stef Heyenrath
9c55ff5ea6 Merge branch 'stateful-behavior' 2017-10-07 15:01:56 +02:00
Stef Heyenrath
8386d93e0d scenario and state (mapping json model) 2017-10-07 14:44:01 +02:00
Stef Heyenrath
5424b1e2e2 scenario and state 2017-10-07 13:19:48 +02:00
deeptowncitizen
ab017beaa6 stateful behavior + tests 2017-10-04 15:57:49 -04:00
Stef Heyenrath
8827531391 Add start delay from 100ms to fix startup issue on MacOS (#44) 2017-09-30 10:03:00 +02:00
Stef Heyenrath
881e4b2af3 MatchDetails (#47) 2017-09-18 20:45:30 +02:00
Stef Heyenrath
139ea77888 Add ClientIP to RequestMessage / RequestLog (#46) 2017-09-18 20:01:38 +02:00
Stef Heyenrath
f776911ef8 fix projects + sln and updated readme 2017-08-27 11:16:41 +02:00
Stef Heyenrath
3a50c81895 Use SimpleCommandLineParser 2017-08-26 21:08:18 +02:00
Stef Heyenrath
de5277d258 settings 2017-08-20 19:20:42 +02:00
Stef Heyenrath
23d26ca914 Upgrade dependencies 2017-08-20 11:36:51 +02:00
Stef Heyenrath
f8ad2cd3d6 Add RequestLogExpirationDuration and MaxRequestLogCount (#43) (#45) 2017-08-20 11:20:35 +02:00
Stef Heyenrath
76f0e04874 upgrade dependencies for test 2017-08-16 18:30:47 +02:00
Stef Heyenrath
1a77fee273 fix tests 2017-08-11 13:32:43 +02:00
Stef Heyenrath
8d072e8b47 Dotnet 20 preview final (#41)
* 1.0.2.3-preview2-final-01

* fixes for AspNetCoreSelfHost (Task...)

* enable all target frameworks again
2017-08-11 13:27:18 +02:00
Stef Heyenrath
5c5d408abe Expose more settings to stand-alone app (#40) 2017-08-11 13:26:36 +02:00
Stef Heyenrath
2aee658dfa Listen on http://*:9090 (#39)
* Support for http://*:9090 urls

* Update version to 1.0.2.3
2017-08-08 22:58:12 +02:00
Stef Heyenrath
ef73accc9a update csproj 2017-06-17 09:32:56 +02:00
Stef Heyenrath
f38c24e8c4 FindLogEntries and refactored some code (#34) 2017-06-17 09:18:38 +02:00
phillee007
65ac8c6462 Revert changes that were made by mistake in prior PR (#35) 2017-06-17 00:03:07 +02:00
phillee007
49ce2f0dfb [Feature] Add support for client certificate password and test with real services that require client certificate auth (#32)
* Add support for client certificate password and test with some real services that require client certificates. Also update so when proxying, the path and query will be passed to the proxied api.

* Add correct param

* Update to read certificate from store instead of file

* Add support for client certificate password and test with some real services that require client certificates. Also update so when proxying, the path and query will be passed to the proxied api.

* Add correct param

* Fixup PR issues

* Add support for client certificate password and test with some real services that require client certificates. Also update so when proxying, the path and query will be passed to the proxied api.

* Add correct param

* Fixup PR issues
2017-06-16 11:36:10 +02:00
Stef Heyenrath
c29b88eb43 StandAloneApp: FluentMockServerSettings 2017-06-15 13:40:42 +02:00
Stef Heyenrath
85e51b6240 IFluentMockServerAdmin : requests 2017-06-15 11:05:03 +02:00
Stef Heyenrath
cece260203 WireMock.Net.StandAlone.1.0.1 2017-06-15 08:23:52 +02:00
Stef Heyenrath
538195551d fix WireMock.Net.ConsoleTest.Net452 2017-06-14 14:12:42 +02:00
Stef Heyenrath
7111ab384b 1.0.2.1 2017-06-14 12:59:01 +02:00
Stef Heyenrath
0670cbc54c move WireMock.Net.Client 2017-06-10 09:53:13 +02:00
Stef Heyenrath
5b10ef6c48 Readme 2017-06-10 09:50:44 +02:00
Stef Heyenrath
f90d54bace WireMock.Net.Standalone 2017-06-10 09:48:04 +02:00
Stef Heyenrath
8ed82f3124 WireMock.Net.StandAlone (#31) 2017-06-10 09:44:58 +02:00
Stef Heyenrath
b56fca85a6 Merge remote-tracking branch 'origin/master' 2017-06-10 00:49:48 +02:00
Stef Heyenrath
97c7a15302 Standalone : set AllowPartialMapping to false 2017-06-10 00:47:51 +02:00
Stef Heyenrath
a5257309c7 Added more RestEase interface calls (__admin/mapping) 2017-05-27 10:32:19 +02:00
Stef Heyenrath
5d6040df61 example: oauth2 & /helloworld 2017-05-24 23:45:36 +02:00
Stef Heyenrath
8e32fd0be2 Example: oauth2/access 2017-05-24 23:18:40 +02:00
Stef Heyenrath
3c22526905 Added some RestEase interfaces 2017-05-23 21:56:53 +02:00
Stef Heyenrath
08fd94c081 Do not delay Admin Interface commands 2017-05-23 21:56:20 +02:00
Stef Heyenrath
f37d1cddf6 Fix RequestProcessingDelay and refactor code 2017-05-23 21:39:06 +02:00
Stef Heyenrath
71b48ae571 removed folder and added badges 2017-05-20 11:00:20 +02:00
Stef Heyenrath
a901fa6db0 Handlebars.Net, 1.9.0 2017-05-17 21:01:26 +02:00
Stef Heyenrath
360a1227e4 Added X509Certificate2Filename (#27) 2017-05-16 20:20:55 +02:00
Stef Heyenrath
961e8b555f Update standalone commandline parsing (#27) 2017-05-13 10:10:17 +02:00
Stef Heyenrath
4ada6d3567 SaveMappingToFile #27 2017-05-12 22:21:55 +02:00
Stef Heyenrath
23b795ab1e remove some files 2017-05-12 19:42:37 +02:00
Stef Heyenrath
31261ec45d Initial code for proxy and record #27 2017-05-09 21:43:10 +02:00
Stef Heyenrath
b25371cf15 Refactor code 2017-05-09 20:14:52 +02:00
Stef Heyenrath
2c8142b93d WithProxy 2017-05-07 10:01:31 +02:00
Stef Heyenrath
7bfd9f3343 Proxy (#15) 2017-05-05 22:25:57 +02:00
Stef Heyenrath
84db9bbf0d Cleanup some files 2017-05-05 20:32:20 +02:00
109 changed files with 3630 additions and 2577 deletions

15
.axoCover/settings.json Normal file
View 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
View 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
View 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"
}
]
}

View File

@@ -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
[![Build status](https://ci.appveyor.com/api/projects/status/b3n6q3ygbww4lyls?svg=true)](https://ci.appveyor.com/project/StefH/wiremock-net)
[![codecov](https://codecov.io/gh/StefH/WireMock.Net/branch/master/graph/badge.svg)](https://codecov.io/gh/StefH/WireMock.Net)
[![codecov](https://codecov.io/gh/WireMock-Net/WireMock.Net/branch/master/graph/badge.svg)](https://codecov.io/gh/WireMock-Net/WireMock.Net)
[![Coverage Status](https://coveralls.io/repos/github/StefH/WireMock.Net/badge.svg?branch=master)](https://coveralls.io/github/StefH/WireMock.Net?branch=master)
[![NuGet Badge](https://buildstats.info/nuget/WireMock.Net)](https://www.nuget.org/packages/WireMock.Net)
[![GitHub issues](https://img.shields.io/github/issues/StefH/WireMock.Net.svg)](https://github.com/StefH/WireMock.Net/issues)
[![GitHub stars](https://img.shields.io/github/stars/StefH/WireMock.Net.svg)](https://github.com/StefH/WireMock.Net/stargazers)
| Name | NuGet |
| ---- | ----- |
| WireMock.Net | [![NuGet Badge](https://buildstats.info/nuget/WireMock.Net)](https://www.nuget.org/packages/WireMock.Net) |
| WireMock.Net.StandAlone | [![NuGet Badge](https://buildstats.info/nuget/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).

View File

@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26403.7
VisualStudioVersion = 15.0.26430.13
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF242EDF-7133-4277-9A0C-18744DE08707}"
EndProject
@@ -19,22 +19,23 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{890A1DED-C
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}"
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}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.Record.NETCoreApp", "examples\WireMock.Net.Console.Record.NETCoreApp\WireMock.Net.Console.Record.NETCoreApp.csproj", "{1995E414-F197-4AB4-90C2-68D806B5AF59}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Client", "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
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -45,36 +46,46 @@ 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
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}
EndGlobalSection
EndGlobal

View File

@@ -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>

View File

@@ -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,23 +20,20 @@ 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

View 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();
}
}
}

View 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>

View File

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

View File

@@ -6,20 +6,26 @@
<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>
</Content>
<Content Include="__admin\mappings\826aff7c-6208-4a3c-923d-575248907db4.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommandLineArgumentsParser" Version="3.0.9" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<!--<ProjectReference Include="..\WireMock.Net\WireMock.Net.csproj" />-->
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
</ItemGroup>
</Project>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,35 @@
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:9095/", "https://localhost:9096/" },
StartAdminInterface = true,
ProxyAndRecordSettings = new ProxyAndRecordSettings
{
Url = "https://www.msn.com",
//X509Certificate2ThumbprintOrSubjectName = "www.yourclientcertname.com OR yourcertificatethumbprint (only if the service you're proxying to requires it)",
SaveMapping = true
}
});
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();
}
}
}

View File

@@ -7,11 +7,12 @@
</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>
</Project>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,160 @@
using System;
using System.Linq;
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
});
System.Console.WriteLine("FluentMockServer listening at {0}", string.Join(" and ", server.Urls));
server.SetBasicAuthentication("a", "b");
// server.AllowPartialMapping();
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("/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))
// );
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();
}
}
}

View File

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

View File

@@ -52,6 +52,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="MainApp.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>

View 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");
}
}
}

View File

@@ -0,0 +1,8 @@
{
"profiles": {
"WireMock.Net.StandAlone.NETCoreApp": {
"commandName": "Project",
"commandLineArgs": "--Urls http://*:9091"
}
}
}

View File

@@ -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>

View File

@@ -0,0 +1 @@
dotnet run --framework netcoreapp1.1

View File

@@ -0,0 +1 @@
dotnet run --framework netcoreapp2.0

View 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();
}
}
}

View File

@@ -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>

View File

@@ -1,3 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
</configuration>
<packages>
</packages>

View File

@@ -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>

View File

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

View File

@@ -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>

View File

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

View File

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

View File

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

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

View File

@@ -0,0 +1,82 @@
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"),
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;
}
}
}

View File

@@ -1,67 +1,44 @@
<?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.4</Version>
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<!-- <TargetFrameworks>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>

View File

@@ -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>

View 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; }
}
}

View File

@@ -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>

View File

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

View File

@@ -78,5 +78,16 @@ namespace WireMock.Admin.Mappings
/// The delay in milliseconds.
/// </value>
public int? Delay { get; set; }
/// <summary>
/// Gets or sets the Proxy URL.
/// </summary>
/// <value>ProxyUrl</value>
public string ProxyUrl { get; set; }
/// <summary>
/// The client X509Certificate2 Thumbprint or SubjectName to use.
/// </summary>
public string X509Certificate2ThumbprintOrSubjectName { get; set; }
}
}

View File

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

View File

@@ -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>

View File

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

View File

@@ -0,0 +1,142 @@
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);
}
}

View File

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

View 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
}
}
}
}

View File

@@ -0,0 +1,90 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
namespace WireMock.Http
{
internal static class HttpClientHelper
{
private static HttpClient CreateHttpClient(string clientX509Certificate2ThumbprintOrSubjectName = null)
{
if (!string.IsNullOrEmpty(clientX509Certificate2ThumbprintOrSubjectName))
{
#if NETSTANDARD || NET46
var 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 handler = new WebRequestHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual,
ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
var x509Certificate2 = CertificateUtil.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName);
handler.ClientCertificates.Add(x509Certificate2);
return new HttpClient(handler);
#endif
}
return new HttpClient();
}
public static async Task<ResponseMessage> SendAsync(RequestMessage requestMessage, string url, string clientX509Certificate2ThumbprintOrSubjectName = null)
{
var client = CreateHttpClient(clientX509Certificate2ThumbprintOrSubjectName);
var httpRequestMessage = new HttpRequestMessage(new HttpMethod(requestMessage.Method), url);
// Overwrite the host header
httpRequestMessage.Headers.Host = new Uri(url).Authority;
// Set headers if present
if (requestMessage.Headers != null)
{
foreach (var headerName in requestMessage.Headers.Keys.Where(k => k.ToUpper() != "HOST"))
{
httpRequestMessage.Headers.Add(headerName, new[] { requestMessage.Headers[headerName] });
}
}
// Set Body if present
if (requestMessage.BodyAsBytes != null && requestMessage.BodyAsBytes.Length > 0)
{
httpRequestMessage.Content = new ByteArrayContent(requestMessage.BodyAsBytes);
}
// Call the URL
var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead);
// Transform response
var responseMessage = new ResponseMessage
{
StatusCode = (int)httpResponseMessage.StatusCode,
Body = await httpResponseMessage.Content.ReadAsStringAsync()
};
foreach (var header in httpResponseMessage.Headers)
{
responseMessage.AddHeader(header.Key, header.Value.FirstOrDefault());
}
return responseMessage;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,96 +1,141 @@
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>
/// <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>
/// 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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,77 @@
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)).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;
}
/// <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>
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;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -60,11 +60,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)

View File

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

View File

@@ -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; }
@@ -60,11 +60,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)

View File

@@ -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";

View File

@@ -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,27 @@ namespace WireMock.Owin
public Task StartAsync()
{
_host = new WebHostBuilder()
.Configure(appBuilder =>
{
appBuilder.UseMiddleware<WireMockMiddleware>(_options);
})
.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 +71,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
}
}
}

View File

@@ -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.

View File

@@ -1,70 +1,74 @@
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 (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, method, clientIP, body, bodyAsString, bodyEncoding, headers, cookies) { DateTime = DateTime.Now };
}
}
}

View File

@@ -2,7 +2,7 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
#if NET45
#if !NETSTANDARD
using Microsoft.Owin;
#else
using Microsoft.AspNetCore.Http;
@@ -23,7 +23,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

View File

@@ -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,39 +52,13 @@ 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()
{
System.Console.WriteLine("WireMock.Net server using .net 4.5.x or .net 4.6.x");
Action<IAppBuilder> startup = app =>
{
app.Use<WireMockMiddleware>(_options);
@@ -98,18 +67,22 @@ namespace WireMock.Owin
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();
}
}
}
}

View File

@@ -1,144 +1,179 @@
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;
#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 string authorization);
if (!present || _options.AuthorizationMatcher.IsMatch(authorization) < 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 = ex.ToString() };
}
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);
}
}
}
}
}
}

View File

@@ -1,5 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using WireMock.Logging;
using WireMock.Matchers;
@@ -13,14 +15,14 @@ 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 ObservableCollection<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>();
}
}

View File

@@ -0,0 +1,4 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("WireMock.Net.StandAlone")]
[assembly: InternalsVisibleTo("WireMock.Net.Tests")]

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

View File

@@ -3,7 +3,7 @@
/// <summary>
/// IRequestBuilder
/// </summary>
public interface IRequestBuilder : IUrlAndPathRequestBuilder
public interface IRequestBuilder : IClientIPRequestBuilder
{
}
}

View File

@@ -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>

View File

@@ -13,6 +13,11 @@ namespace WireMock
/// </summary>
public class RequestMessage
{
/// <summary>
/// Gets the Client IP Address.
/// </summary>
public string ClientIP { get; }
/// <summary>
/// Gets the url.
/// </summary>
@@ -67,20 +72,23 @@ namespace WireMock
/// 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="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 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)
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(verb, nameof(verb));
Check.NotNull(method, nameof(method));
Check.NotNull(clientIP, nameof(clientIP));
Url = url.ToString();
Path = url.AbsolutePath;
Method = verb.ToLower();
Method = method.ToLower();
ClientIP = clientIP;
BodyAsBytes = bodyAsBytes;
Body = body;
BodyEncoding = bodyEncoding;
@@ -100,14 +108,16 @@ namespace WireMock
(dict, term) =>
{
var parts = term.Split('=');
var key = parts[0];
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;
});

View File

@@ -17,7 +17,7 @@ namespace WireMock.ResponseBuilders
IResponseBuilder WithBody([NotNull] string body, [CanBeNull] Encoding encoding = null);
/// <summary>
/// The with body.
/// The with body as Json.
/// </summary>
/// <param name="body">The body.</param>
/// <param name="encoding">The body encoding.</param>

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

View File

@@ -3,7 +3,7 @@
/// <summary>
/// The ResponseBuilder interface.
/// </summary>
public interface IResponseBuilder : IStatusCodeResponseBuilder
public interface IResponseBuilder : IProxyResponseBuilder
{
}
}

View File

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

View File

@@ -1,7 +1,7 @@
namespace WireMock.ResponseBuilders
{
/// <summary>
/// The BodyResponseBuilder interface.
/// The TransformResponseBuilder interface.
/// </summary>
public interface ITransformResponseBuilder : IDelayResponseBuilder
{

View File

@@ -3,10 +3,11 @@ using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using HandlebarsDotNet;
using JetBrains.Annotations;
using Newtonsoft.Json;
using WireMock.Validation;
using WireMock.Http;
using WireMock.Transformers;
namespace WireMock.ResponseBuilders
{
@@ -28,6 +29,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>
@@ -230,48 +241,47 @@ 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;
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)
{
var requestUri = new Uri(requestMessage.Url);
var proxyUri = new Uri(ProxyUrl);
var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery);
return await HttpClientHelper.SendAsync(requestMessage, proxyUriWithRequestPathAndQuery.AbsoluteUri, X509Certificate2ThumbprintOrSubjectName);
}
if (UseTransformer)
{
return ResponseMessageTransformer.Transform(requestMessage, ResponseMessage);
}
return ResponseMessage;
}
}
}

View File

@@ -0,0 +1,198 @@
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;
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.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
{
Delay = response.Delay?.Milliseconds
}
};
if (!string.IsNullOrEmpty(response.ProxyUrl))
{
mappingModel.Response.StatusCode = null;
mappingModel.Response.Headers = null;
mappingModel.Response.Body = null;
mappingModel.Response.UseTransformer = false;
mappingModel.Response.BodyEncoding = null;
mappingModel.Response.ProxyUrl = response.ProxyUrl;
}
else
{
mappingModel.Response.StatusCode = response.ResponseMessage.StatusCode;
mappingModel.Response.Headers = response.ResponseMessage.Headers;
mappingModel.Response.Body = response.ResponseMessage.Body;
mappingModel.Response.UseTransformer = response.UseTransformer;
mappingModel.Response.BodyEncoding = response.ResponseMessage.BodyEncoding != null
? new EncodingModel
{
EncodingName = response.ResponseMessage.BodyEncoding.EncodingName,
CodePage = response.ResponseMessage.BodyEncoding.CodePage,
WebName = response.ResponseMessage.BodyEncoding.WebName
}
: null;
}
return mappingModel;
}
public static MatcherModel[] Map([CanBeNull] IEnumerable<IMatcher> matchers)
{
if (matchers == null || !matchers.Any())
return null;
return matchers.Select(Map).Where(x => x != null).ToArray();
}
public 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
};
}
public 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();
}
public 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.");
}
}
}
}

View File

@@ -5,7 +5,6 @@ using System.Linq;
using System.Text;
using JetBrains.Annotations;
using Newtonsoft.Json;
using SimMetrics.Net;
using WireMock.Admin.Mappings;
using WireMock.Admin.Requests;
using WireMock.Admin.Settings;
@@ -16,6 +15,10 @@ using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Util;
using WireMock.Validation;
using WireMock.Http;
using System.Threading.Tasks;
using WireMock.Settings;
using WireMock.Serialization;
namespace WireMock.Server
{
@@ -28,6 +31,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 +70,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 +85,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 +118,60 @@ 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 void InitProxyAndRecord(ProxyAndRecordSettings settings)
{
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(requestMessage, proxyUriWithRequestPathAndQuery.AbsoluteUri, settings.X509Certificate2ThumbprintOrSubjectName);
if (settings.SaveMapping)
{
var mapping = ToMapping(requestMessage, responseMessage);
SaveMappingToFile(mapping);
}
return responseMessage;
}
private Mapping ToMapping(RequestMessage requestMessage, ResponseMessage responseMessage)
{
var request = (Request)Request.Create();
request.WithPath(requestMessage.Path);
request.UsingVerb(requestMessage.Method);
var response = (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 +184,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 +204,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 +246,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 +278,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 +331,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 +345,8 @@ namespace WireMock.Server
{
ResetMappings();
ResetScenarios();
return new ResponseMessage { Body = "Mappings deleted" };
}
#endregion Mappings
@@ -328,6 +394,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,
@@ -362,7 +429,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 +458,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,10 +470,47 @@ 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;
@@ -409,7 +520,7 @@ namespace WireMock.Server
{
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());
}
}
@@ -422,7 +533,7 @@ namespace WireMock.Server
{
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());
}
}
@@ -433,7 +544,7 @@ namespace WireMock.Server
{
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 +552,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 +566,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 +577,30 @@ 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);
}
else if (responseModel.HeadersRaw != null)
{
foreach (string headerLine in responseModel.HeadersRaw.Split(new[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries))
@@ -483,177 +613,26 @@ namespace WireMock.Server
}
if (responseModel.Body != null)
{
responseBuilder = responseBuilder.WithBody(responseModel.Body, 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));
}
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

View 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;
}
}
}

View File

@@ -1,496 +1,377 @@
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 + "/" };
}
#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);
}
}
}

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
namespace WireMock.Server
namespace WireMock.Settings
{
/// <summary>
/// FluentMockServerSettings
@@ -31,13 +31,17 @@
public bool? StartAdminInterface { get; set; }
/// <summary>
/// Gets or sets the read static mappings.
/// Gets or sets if the static mappings should be read at startup.
/// </summary>
/// <value>
/// The read static mappings.
/// </value>
/// <value>true/false</value>
public bool? ReadStaticMappings { get; set; }
/// <summary>
/// Gets or sets if the server should record and save requests and responses.
/// </summary>
/// <value>true/false</value>
public ProxyAndRecordSettings ProxyAndRecordSettings { get; set; }
/// <summary>
/// Gets or sets the urls.
/// </summary>
@@ -50,5 +54,30 @@
/// StartTimeout
/// </summary>
public int StartTimeout { get; set; } = 10000;
/// <summary>
/// Allow Partial Mapping (default set to false).
/// </summary>
public bool? AllowPartialMapping { get; set; }
/// <summary>
/// The username needed for __admin access.
/// </summary>
public string AdminUsername { get; set; }
/// <summary>
/// The password needed for __admin access.
/// </summary>
public string AdminPassword { get; set; }
/// <summary>
/// The RequestLog expiration in hours (optional).
/// </summary>
public int? RequestLogExpirationDuration { get; set; }
/// <summary>
/// The MaxRequestLog count (optional).
/// </summary>
public int? MaxRequestLogCount { get; set; }
}
}

View File

@@ -0,0 +1,23 @@
namespace WireMock.Settings
{
/// <summary>
/// RecordAndSaveSettings
/// </summary>
public class ProxyAndRecordSettings
{
/// <summary>
/// The URL to proxy.
/// </summary>
public string Url { get; set; }
/// <summary>
/// Save the mapping for each request/response.
/// </summary>
public bool SaveMapping { get; set; } = true;
/// <summary>
/// The clientCertificate thumbprint or subject name fragment to use. Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
/// </summary>
public string X509Certificate2ThumbprintOrSubjectName { get; set; }
}
}

View File

@@ -0,0 +1,33 @@
using HandlebarsDotNet;
using System.Collections.Generic;
namespace WireMock.Transformers
{
internal static class ResponseMessageTransformer
{
public static ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original)
{
var responseMessage = new ResponseMessage { StatusCode = original.StatusCode, BodyOriginal = original.Body };
var template = new { request = requestMessage };
// Body
var templateBody = Handlebars.Compile(original.Body);
responseMessage.Body = templateBody(template);
// Headers
var newHeaders = new Dictionary<string, string>();
foreach (var header in original.Headers)
{
var templateHeaderKey = Handlebars.Compile(header.Key);
var templateHeaderValue = Handlebars.Compile(header.Value);
newHeaders.Add(templateHeaderKey(template), templateHeaderValue(template));
}
responseMessage.Headers = newHeaders;
return responseMessage;
}
}
}

View File

@@ -0,0 +1,81 @@
using System;
using System.Collections.Concurrent;
using System.Threading;
namespace WireMock.Util
{
/// <summary>
/// http://johnculviner.com/achieving-named-lock-locker-functionality-in-c-4-0/
/// </summary>
internal class NamedReaderWriterLocker
{
private readonly ConcurrentDictionary<string, ReaderWriterLockSlim> _lockDict = new ConcurrentDictionary<string, ReaderWriterLockSlim>();
public ReaderWriterLockSlim GetLock(string name)
{
return _lockDict.GetOrAdd(name, s => new ReaderWriterLockSlim());
}
public TResult RunWithReadLock<TResult>(string name, Func<TResult> body)
{
var rwLock = GetLock(name);
try
{
rwLock.EnterReadLock();
return body();
}
finally
{
rwLock.ExitReadLock();
}
}
public void RunWithReadLock(string name, Action body)
{
var rwLock = GetLock(name);
try
{
rwLock.EnterReadLock();
body();
}
finally
{
rwLock.ExitReadLock();
}
}
public TResult RunWithWriteLock<TResult>(string name, Func<TResult> body)
{
var rwLock = GetLock(name);
try
{
rwLock.EnterWriteLock();
return body();
}
finally
{
rwLock.ExitWriteLock();
}
}
public void RunWithWriteLock(string name, Action body)
{
var rwLock = GetLock(name);
try
{
rwLock.EnterWriteLock();
body();
}
finally
{
rwLock.ExitWriteLock();
}
}
public void RemoveLock(string name)
{
ReaderWriterLockSlim o;
_lockDict.TryRemove(name, out o);
}
}
}

View File

@@ -3,26 +3,26 @@
<PropertyGroup>
<Description>Lightweight Http Mocking Server for .Net, inspired by WireMock from the Java landscape.</Description>
<AssemblyTitle>WireMock.Net</AssemblyTitle>
<Version>1.0.2.0</Version>
<Version>1.0.2.4</Version>
<Authors>Alexandre Victoor;Stef Heyenrath</Authors>
<TargetFrameworks>net45;netstandard1.3</TargetFrameworks>
<TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>WireMock.Net</AssemblyName>
<PackageId>WireMock.Net</PackageId>
<PackageTags>tdd;mock;http;wiremock;test;server;unittest</PackageTags>
<PackageReleaseNotes>Support for NETStandard by replacing HttpListener by Owin/Selfhosted</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>
<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/StefH/WireMock.Net</RepositoryUrl>
<RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<DebugType>full</DebugType>
<DebugType>portable</DebugType>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
<RootNamespace>WireMock</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' or '$(TargetFramework)' == 'netstandard2.0'">
<DefineConstants>NETSTANDARD</DefineConstants>
</PropertyGroup>
@@ -30,28 +30,33 @@
<PackageReference Include="JetBrains.Annotations" Version="10.4.0">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="Handlebars.Net" Version="1.9.0" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.1" />
<PackageReference Include="SimMetrics.Net" Version="1.0.3" />
<PackageReference Include="System.Net.Http" Version="4.3.2" />
<PackageReference Include="RestEase" Version="1.4.2" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<ItemGroup Condition=" '$(TargetFramework)' == 'net452' ">
<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" />
<PackageReference Include="XPath2" Version="1.0.5.1" />
<Reference Include="System.Net.Http.WebRequest" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
<PackageReference Include="System.Threading.Tasks" Version="4.3.0" />
<PackageReference Include="Handlebars.Net" Version="1.8.0" />
<PackageReference Include="XPath2" Version="1.0.3.1" />
<ItemGroup Condition=" '$(TargetFramework)' == 'net46' ">
<PackageReference Include="Microsoft.AspNet.WebApi.OwinSelfHost" Version="5.2.3" />
<PackageReference Include="XPath2" Version="1.0.5.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
<PackageReference Include="Handlebars.NetStandard" Version="1.8.1" />
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.2" />
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
<PackageReference Include="System.Xml.XPath.XmlDocument" Version="4.3.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0" />
<PackageReference Include="XPath2" Version="1.0.5.1" />
</ItemGroup>
</Project>

View File

@@ -33,10 +33,10 @@ namespace WireMock.Net.Tests
[Fact]
public void FluentMockServer_StartStop()
{
var server1 = FluentMockServer.Start("http://localhost:9090/");
var server1 = FluentMockServer.Start("http://localhost:9091/");
server1.Stop();
var server2 = FluentMockServer.Start("http://localhost:9090/");
var server2 = FluentMockServer.Start("http://localhost:9091/");
server2.Stop();
}
@@ -353,7 +353,62 @@ namespace WireMock.Net.Tests
Check.That(watch.ElapsedMilliseconds).IsStrictlyGreaterThan(200);
}
//[TearDown]
[Fact]
public async Task Should_proxy_responses()
{
// given
_server = FluentMockServer.Start();
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create().WithProxy("http://www.google.com"));
// when
var result = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/search?q=test");
// then
Check.That(result).Contains("google");
}
//Leaving commented as this requires an actual certificate with password, along with a service that expects a client certificate
//[Fact]
//public async Task Should_proxy_responses_with_client_certificate()
//{
// // given
// _server = FluentMockServer.Start();
// _server
// .Given(Request.Create().WithPath("/*"))
// .RespondWith(Response.Create().WithProxy("https://server-that-expects-a-client-certificate", @"\\yourclientcertificatecontainingprivatekey.pfx", "yourclientcertificatepassword"));
// // when
// var result = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/someurl?someQuery=someValue");
// // then
// Check.That(result).Contains("google");
//}
[Fact]
public async Task FluentMockServer_Logging_SetMaxRequestLogCount()
{
// Assign
var client = new HttpClient();
// Act
_server = FluentMockServer.Start();
_server.SetMaxRequestLogCount(2);
await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo1");
await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo2");
await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo3");
// Assert
Check.That(_server.LogEntries).HasSize(2);
var requestLoggedA = _server.LogEntries.First();
Check.That(requestLoggedA.RequestMessage.Path).EndsWith("/foo2");
var requestLoggedB = _server.LogEntries.Last();
Check.That(requestLoggedB.RequestMessage.Path).EndsWith("/foo3");
}
public void Dispose()
{
_server?.Stop();

View File

@@ -1,104 +0,0 @@
//using System;
//using System.Collections.Generic;
//using System.Net;
//using System.Net.Http;
//using System.Threading;
//using System.Threading.Tasks;
//using NFluent;
//using Xunit;
//using WireMock.Matchers;
//using WireMock.RequestBuilders;
//using WireMock.ResponseBuilders;
//using WireMock.Server;
//namespace WireMock.Net.Tests
//{
// //[TestFixture]
// public class HttpListenerRequestMapperTests : IDisposable
// {
// private FluentMockServer _server;
// public HttpListenerRequestMapperTests()
// {
// _server = FluentMockServer.Start();
// }
// [Fact]
// public async Task Should_map_uri_from_listener_request()
// {
// // given
// var client = new HttpClient();
// // when
// await client.GetAsync(MapperServer.UrlPrefix + "toto");
// // then
// Check.That(MapperServer.LastRequestMessage).IsNotNull();
// Check.That(MapperServer.LastRequestMessage.Path).IsEqualTo("/toto");
// }
// [Fact]
// public async Task Should_map_verb_from_listener_request()
// {
// // given
// var client = new HttpClient();
// // when
// await client.PutAsync(MapperServer.UrlPrefix, new StringContent("Hello!"));
// // then
// Check.That(MapperServer.LastRequestMessage).IsNotNull();
// Check.That(MapperServer.LastRequestMessage.Method).IsEqualTo("put");
// }
// [Fact]
// public async Task Should_map_body_from_listener_request()
// {
// // given
// var client = new HttpClient();
// // when
// await client.PutAsync(MapperServer.UrlPrefix, new StringContent("Hello!"));
// // then
// Check.That(MapperServer.LastRequestMessage).IsNotNull();
// Check.That(MapperServer.LastRequestMessage.Body).IsEqualTo("Hello!");
// }
// [Fact]
// public async Task Should_map_headers_from_listener_request()
// {
// // given
// var client = new HttpClient();
// client.DefaultRequestHeaders.Add("X-Alex", "1706");
// // when
// await client.GetAsync(MapperServer.UrlPrefix);
// // then
// Check.That(MapperServer.LastRequestMessage).IsNotNull();
// Check.That(MapperServer.LastRequestMessage.Headers).Not.IsNullOrEmpty();
// Check.That(MapperServer.LastRequestMessage.Headers.Contains(new KeyValuePair<string, string>("X-Alex", "1706"))).IsTrue();
// }
// [Fact]
// public async Task Should_map_params_from_listener_request()
// {
// // given
// var client = new HttpClient();
// // when
// await client.GetAsync(MapperServer.UrlPrefix + "index.html?id=toto");
// // then
// Check.That(MapperServer.LastRequestMessage).IsNotNull();
// Check.That(MapperServer.LastRequestMessage.Path).EndsWith("/index.html");
// Check.That(MapperServer.LastRequestMessage.GetParameter("id")).HasSize(1);
// }
// public void Dispose()
// {
// _server.Stop().Wait();
// }
// }
//}

View File

@@ -1,135 +0,0 @@
//using System;
//using System.Net;
//using System.Net.Http;
//using System.Text;
//using System.Threading;
//using System.Threading.Tasks;
//using NFluent;
//using Xunit;
//using WireMock.Http;
//using WireMock.Owin;
//namespace WireMock.Net.Tests
//{
// //[TestFixture]
// public class HttpListenerResponseMapperTests : IDisposable
// {
// private TinyHttpServer _server;
// private Task<HttpResponseMessage> _responseMsgTask;
// [Fact]
// public void Should_map_status_code_from_original_response()
// {
// // given
// var response = new ResponseMessage { StatusCode = 404 };
// var httpListenerResponse = CreateHttpListenerResponse();
// // when
// new HttpListenerResponseMapper().Map(response, httpListenerResponse);
// // then
// Check.That(httpListenerResponse.StatusCode).IsEqualTo(404);
// }
// [Fact]
// public void Should_map_headers_from_original_response()
// {
// // given
// var response = new ResponseMessage();
// response.AddHeader("cache-control", "no-cache");
// var httpListenerResponse = CreateHttpListenerResponse();
// // when
// new HttpListenerResponseMapper().Map(response, httpListenerResponse);
// // then
// Check.That(httpListenerResponse.Headers).HasSize(1);
// Check.That(httpListenerResponse.Headers.Keys).Contains("cache-control");
// Check.That(httpListenerResponse.Headers.Get("cache-control")).Contains("no-cache");
// }
// [Fact]
// public void Should_map_body_from_original_response()
// {
// // given
// var response = new ResponseMessage
// {
// Body = "Hello !!!"
// };
// var httpListenerResponse = CreateHttpListenerResponse();
// // when
// new OwinResponseMapper().Map(response, httpListenerResponse);
// // then
// var responseMessage = ToResponseMessage(httpListenerResponse);
// Check.That(responseMessage).IsNotNull();
// var contentTask = responseMessage.Content.ReadAsStringAsync();
// Check.That(contentTask.Result).IsEqualTo("Hello !!!");
// }
// [Fact]
// public void Should_map_encoded_body_from_original_response()
// {
// // given
// var response = new ResponseMessage
// {
// Body = "Hello !!!",
// BodyEncoding = Encoding.ASCII
// };
// var httpListenerResponse = CreateHttpListenerResponse();
// // when
// new HttpListenerResponseMapper().Map(response, httpListenerResponse);
// // then
// Check.That(httpListenerResponse.ContentEncoding).Equals(Encoding.ASCII);
// var responseMessage = ToResponseMessage(httpListenerResponse);
// Check.That(responseMessage).IsNotNull();
// var contentTask = responseMessage.Content.ReadAsStringAsync();
// Check.That(contentTask.Result).IsEqualTo("Hello !!!");
// }
// //[TearDown]
// public void Dispose()
// {
// _server?.Stop();
// }
// /// <summary>
// /// Dirty HACK to get HttpListenerResponse instances
// /// </summary>
// /// <returns>
// /// The <see cref="HttpListenerResponse"/>.
// /// </returns>
// public HttpListenerResponse CreateHttpListenerResponse()
// {
// var port = PortUtil.FindFreeTcpPort();
// var urlPrefix = "http://localhost:" + port + "/";
// var responseReady = new AutoResetEvent(false);
// HttpListenerResponse response = null;
// _server = new TinyHttpServer(
// (context, token) =>
// {
// response = context.Response;
// responseReady.Set();
// }, urlPrefix);
// _server.Start();
// _responseMsgTask = new HttpClient().GetAsync(urlPrefix);
// responseReady.WaitOne();
// return response;
// }
// public HttpResponseMessage ToResponseMessage(HttpListenerResponse listenerResponse)
// {
// listenerResponse.Close();
// _responseMsgTask.Wait();
// return _responseMsgTask.Result;
// }
// }
//}

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