Compare commits

...

27 Commits

Author SHA1 Message Date
Stef Heyenrath
ad6c59e3b5 1.0.3.3 2018-02-24 09:04:21 +01:00
Stef Heyenrath
0c25b2e9f2 Json fixes (#91) (#92)
* Fixes for JsonPath

* More tests

* Fixes + added tests
2018-02-23 12:29:43 +00:00
Stef Heyenrath
1ffd56701c fix unit-test 2018-02-14 20:38:19 +01:00
Stef Heyenrath
4f87146622 1.0.3.2 2018-02-14 19:35:51 +01:00
Stef Heyenrath
693778659e Concurrent issue (#88) (#90)
* concurrent

* uni tests
2018-02-14 18:30:06 +00:00
Stef Heyenrath
51070dab63 1.0.3.1 releasenotes 2018-02-14 17:17:05 +01:00
Stef Heyenrath
e21582aacf Add log4net logging (#89)
* logging + fixed proxy isses

* Add more logging

* comments

* 1.0.3.1
2018-02-14 16:13:05 +00:00
Stef Heyenrath
9778c5adbe Use EnhancedFileSystemWatcher.cs instead of NuGet 2018-02-05 08:36:50 +01:00
Stef Heyenrath
1caa769618 1.0.3 + release notes 2018-02-04 10:47:33 +01:00
Stef Heyenrath
cf4e83b10b Fix typo 2018-02-04 10:31:11 +01:00
Stef Heyenrath
95a201573a #86 2018-02-04 10:22:56 +01:00
Stef Heyenrath
b248c8c6e5 EnhancedFileSystemWatcher (#86) 2018-02-03 23:22:29 +01:00
Stef Heyenrath
180526c8b4 self-signed-certificate + #83 2018-02-03 12:06:12 +01:00
Stef Heyenrath
361d40189b Fixed https proxy for netstandard 1.3 and netstandard 2.0 (#85) 2018-02-02 22:52:08 +01:00
Stef Heyenrath
061eb93fd0 Refactor HttpClientHandler code 2018-02-02 18:11:37 +01:00
Stef Heyenrath
e5b2ad0543 SetBody (#81) 2018-02-02 15:42:25 +01:00
Stef Heyenrath
66b2ff16de Implemented #82 2018-02-02 11:17:37 +01:00
Stef Heyenrath
40ff8514ac BodyAsJson (#80) 2018-01-31 12:41:10 +01:00
Stef Heyenrath
cdcaaa970a 1.0.2.13 2018-01-23 08:09:19 +01:00
Stef Heyenrath
d07a89c907 Merge remote-tracking branch 'origin/master' 2018-01-23 08:07:00 +01:00
Stef Heyenrath
141ed5d96c code update 2018-01-23 08:06:55 +01:00
vladimir-fed
6d60b3773a Fix missed content headers (#79) 2018-01-23 08:05:12 +01:00
Stef Heyenrath
e6af765777 1.0.2.12 2018-01-16 20:46:43 +01:00
Stef Heyenrath
97d80ada11 Fixed issue #76 (#77)
#76
2018-01-16 20:44:59 +01:00
Stef Heyenrath
51bd9ec186 Add WireMock.Net.WebApplication example (#75) 2018-01-11 19:38:58 +01:00
Stef Heyenrath
da798a59aa Fix wiki link to Stubbing & Request Matching 2018-01-10 16:33:32 +01:00
Stef Heyenrath
71196b51c9 Fixed issue with same Mapping Guid (#73) 2017-12-20 20:51:51 +01:00
116 changed files with 3127 additions and 1102 deletions

168
CHANGELOG.md Normal file
View File

@@ -0,0 +1,168 @@
# 1.0.3.2 (14 February 2018)
- [#90](https://github.com/WireMock-Net/WireMock.Net/pull/90) - Concurrent issue (#88) contributed by Stef Heyenrath ([StefH](https://github.com/StefH))
- [#89](https://github.com/WireMock-Net/WireMock.Net/pull/89) - Add log4net logging contributed by Stef Heyenrath ([StefH](https://github.com/StefH))
- [#88](https://github.com/WireMock-Net/WireMock.Net/issues/88) - Bug: Standalone server throws 500 error when receiving concurrent requests +fix
- [#87](https://github.com/WireMock-Net/WireMock.Net/issues/87) - Feature: Add logging
Commits: 51070dab63...693778659e
# 1.0.3.1 (14 February 2018)
- [#89](https://github.com/WireMock-Net/WireMock.Net/pull/89) - Add log4net logging contributed by Stef Heyenrath ([StefH](https://github.com/StefH))
- [#87](https://github.com/WireMock-Net/WireMock.Net/issues/87) - Feature: Add logging
Commits: ...
# 1.0.3 (04 February 2018)
- [#86](https://github.com/WireMock-Net/WireMock.Net/issues/86) - Feature : Add FileSystemWatcher logic for watching static mapping files +feature
- [#85](https://github.com/WireMock-Net/WireMock.Net/issues/85) - Bug: https for netstandard does not work ? +fix
- [#83](https://github.com/WireMock-Net/WireMock.Net/issues/83) - Feature : Add also a method in IProxyResponseBuilder to provide proxy-settings +feature
- [#82](https://github.com/WireMock-Net/WireMock.Net/issues/82) - Feature: make it possible to ignore some headers when proxying +feature
- [#81](https://github.com/WireMock-Net/WireMock.Net/issues/81) - Feature: When using proxy, only BodyAsBytes in case of binary data?
- [#80](https://github.com/WireMock-Net/WireMock.Net/issues/80) - Feature: When using proxy, in case Content-Type is JSON, use BodyAsJson in Response
Commits: 40ff8514ac...cf4e83b10b
# 1.0.2.13 (23 January 2018)
- [#79](https://github.com/WireMock-Net/WireMock.Net/pull/79) - Fix missed content headers contributed by ([vladimir-fed](https://github.com/vladimir-fed))
- [#78](https://github.com/WireMock-Net/WireMock.Net/issues/78) - WireMock not working when attempting to access from anything other than localhost.
- [#57](https://github.com/WireMock-Net/WireMock.Net/issues/57) - ProxyAndRecord does not save query-parameters, headers and body +fix
Commits: 6d60b3773a...cdcaaa970a
# 1.0.2.12 (16 January 2018)
- [#77](https://github.com/WireMock-Net/WireMock.Net/pull/77) - Fixed issue #76 contributed by Stef Heyenrath ([StefH](https://github.com/StefH))
- [#76](https://github.com/WireMock-Net/WireMock.Net/issues/76) - Bug: IFluentMockServerAdmin is missing content-type for some POST/PUT calls
- [#75](https://github.com/WireMock-Net/WireMock.Net/pull/75) - Add WireMock.Net.WebApplication example contributed by Stef Heyenrath ([StefH](https://github.com/StefH))
- [#74](https://github.com/WireMock-Net/WireMock.Net/pull/74) - Capturing the index of the existing mapping before removing and insert the updated mapping at the same index of the list contributed by ([raghavendrabankapur](https://github.com/raghavendrabankapur))
- [#73](https://github.com/WireMock-Net/WireMock.Net/issues/73) - Updated mapping is not being picked and responded with the response
Commits: da798a59aa...e6af765777
# 1.0.2.11 (20 December 2017)
- [#72](https://github.com/WireMock-Net/WireMock.Net/issues/72) - Matching WithParam on OData End Points
Commits: 71196b51c9...71196b51c9
# 1.0.2.9 (07 December 2017)
- [#71](https://github.com/WireMock-Net/WireMock.Net/pull/71) - Fixed restricted headers on response contributed by Stef Heyenrath ([StefH](https://github.com/StefH))
- [#69](https://github.com/WireMock-Net/WireMock.Net/issues/69) - Instructions are incorrect (?)
Commits: 601af2d6b2...fd5bc203c3
# 1.0.2.10 (12 December 2017)
- [#70](https://github.com/WireMock-Net/WireMock.Net/issues/70) - Proxy/Intercept pattern is throwing a keep alive header error with net461
Commits: d0fc889f42...d0fc889f42
# 1.0.2.8 (23 November 2017)
- [#68](https://github.com/WireMock-Net/WireMock.Net/issues/68) - Full path required in Stub
- [#67](https://github.com/WireMock-Net/WireMock.Net/pull/67) - bug: fix supporting the Patch method and logging the body contributed by Alastair Crabtree ([alastairtree](https://github.com/alastairtree))
- [#65](https://github.com/WireMock-Net/WireMock.Net/pull/65) - bug: Fix admin api client definition returning the wrong types contributed by Alastair Crabtree ([alastairtree](https://github.com/alastairtree))
- [#64](https://github.com/WireMock-Net/WireMock.Net/issues/64) - Pull Requests do not trigger test + codecoverage ?
Commits: d0b48e2967...ea16ee866b
# 1.0.2.7 (18 November 2017)
- [#63](https://github.com/WireMock-Net/WireMock.Net/pull/63) - Fix issue with concurrent logging contributed by ([vladimir-fed](https://github.com/vladimir-fed))
- [#62](https://github.com/WireMock-Net/WireMock.Net/pull/62) - Add the Host, Protocol, Port and Origin to the Request message so they can be used in templating contributed by Alastair Crabtree ([alastairtree](https://github.com/alastairtree))
- [#61](https://github.com/WireMock-Net/WireMock.Net/issues/61) - Partial mapping
- [#53](https://github.com/WireMock-Net/WireMock.Net/issues/53) - New feature request: Access to Owin pipeline
- [#42](https://github.com/WireMock-Net/WireMock.Net/issues/42) - Enhancement - Save/load request logs to/from disk +feature
- [#27](https://github.com/WireMock-Net/WireMock.Net/issues/27) - New feature: Record and Save
Commits: e25c873765...018d2a904d
# 1.0.2.6 (30 October 2017)
- [#60](https://github.com/WireMock-Net/WireMock.Net/pull/60) - Fix proxy headers handling contributed by Oleksandr Liakhevych ([Dreamescaper](https://github.com/Dreamescaper))
- [#59](https://github.com/WireMock-Net/WireMock.Net/pull/59) - Add ability to provide multiple values for headers in response contributed by Oleksandr Liakhevych ([Dreamescaper](https://github.com/Dreamescaper))
- [#58](https://github.com/WireMock-Net/WireMock.Net/issues/58) - Multiple headers with same name +feature
- [#56](https://github.com/WireMock-Net/WireMock.Net/issues/56) - WithBodyFromFile Support +feature
- [#54](https://github.com/WireMock-Net/WireMock.Net/issues/54) - Proxy for AWS: Error unmarshalling response back from AWS +fix
Commits: cbe6a0a2b4...d83f308591
# 1.0.2.5 (24 October 2017)
- [#55](https://github.com/WireMock-Net/WireMock.Net/pull/55) - Fix the problem with headers passthrough contributed by deeptowncitizen ([deeptowncitizen](https://github.com/deeptowncitizen)) +fix
- [#52](https://github.com/WireMock-Net/WireMock.Net/issues/52) - SimMetrics.NET error when trying to install NuGet Package
- [#48](https://github.com/WireMock-Net/WireMock.Net/issues/48) - Stateful support +feature
- [#44](https://github.com/WireMock-Net/WireMock.Net/issues/44) - Bug: Server not listening after Start() returns (on macOS) +fix
Commits: 7c289d44a7...15370a89ca
# 1.0.2.4 (10 October 2017)
- [#51](https://github.com/WireMock-Net/WireMock.Net/pull/51) - Observable logs contributed by deeptowncitizen ([deeptowncitizen](https://github.com/deeptowncitizen))
- [#50](https://github.com/WireMock-Net/WireMock.Net/issues/50) - New Feature: Callbacks
- [#49](https://github.com/WireMock-Net/WireMock.Net/pull/49) - stateful behavior contributed by deeptowncitizen ([deeptowncitizen](https://github.com/deeptowncitizen))
- [#47](https://github.com/WireMock-Net/WireMock.Net/issues/47) - Feature: add matcher details to Request to see which matchers match/not match +feature
- [#46](https://github.com/WireMock-Net/WireMock.Net/issues/46) - Log the ip-address from the client/caller also in the RequestLog +feature
- [#45](https://github.com/WireMock-Net/WireMock.Net/pull/45) - Add RequestLogExpirationDuration and MaxRequestLogCount (#43) contributed by Stef Heyenrath ([StefH](https://github.com/StefH))
- [#43](https://github.com/WireMock-Net/WireMock.Net/issues/43) - Feature: Add RequestLogExpirationDuration and MaxRequestLogCount
- [#41](https://github.com/WireMock-Net/WireMock.Net/pull/41) - Dotnet 20 preview final contributed by Stef Heyenrath ([StefH](https://github.com/StefH)) +feature
- [#40](https://github.com/WireMock-Net/WireMock.Net/pull/40) - Expose more settings to stand-alone app contributed by Stef Heyenrath ([StefH](https://github.com/StefH))
- [#39](https://github.com/WireMock-Net/WireMock.Net/pull/39) - Listen on http://*:9090 contributed by Stef Heyenrath ([StefH](https://github.com/StefH))
- [#38](https://github.com/WireMock-Net/WireMock.Net/issues/38) - Bug: support also listening on *:{port}
- [#37](https://github.com/WireMock-Net/WireMock.Net/issues/37) - Wrong Request Match result is returning
- [#36](https://github.com/WireMock-Net/WireMock.Net/issues/36) - How to implement a request body-dependent response?
- [#35](https://github.com/WireMock-Net/WireMock.Net/pull/35) - Revert changes that were made by mistake in prior PR contributed by ([phillee007](https://github.com/phillee007))
- [#34](https://github.com/WireMock-Net/WireMock.Net/issues/34) - Where is SearchLogsFor method?
- [#33](https://github.com/WireMock-Net/WireMock.Net/issues/33) - Issue with launching sample code (StandAlone server) +fix
- [#32](https://github.com/WireMock-Net/WireMock.Net/pull/32) - [Feature] Add support for client certificate password and test with real services that require client certificate auth contributed by ([phillee007](https://github.com/phillee007)) +feature
- [#31](https://github.com/WireMock-Net/WireMock.Net/issues/31) - Feature request: Nuget package for standalone version +feature
- [#20](https://github.com/WireMock-Net/WireMock.Net/issues/20) - Add client certificate authentication
- [#19](https://github.com/WireMock-Net/WireMock.Net/issues/19) - Is this the same as Mock4Net?
- [#15](https://github.com/WireMock-Net/WireMock.Net/issues/15) - New feature: Proxying +feature
Commits: 538195551d...e87e09e10d
# 1.02.1 (14 June 2017)
- [#30](https://github.com/WireMock-Net/WireMock.Net/issues/30) - [Feature] Disable partial mappings by default in standalone version +fix
- [#29](https://github.com/WireMock-Net/WireMock.Net/issues/29) - Support of .Net 4.0
- [#28](https://github.com/WireMock-Net/WireMock.Net/issues/28) - Facing issue with WildcardMatcher and '?'
Commits: 84db9bbf0d...7111ab384b
# 1.0.2.0 (05 May 2017)
- [#26](https://github.com/WireMock-Net/WireMock.Net/pull/26) - merge netstandard into main contributed by Stef Heyenrath ([StefH](https://github.com/StefH))
- [#25](https://github.com/WireMock-Net/WireMock.Net/issues/25) - Upgrade to vs2017 +feature
- [#23](https://github.com/WireMock-Net/WireMock.Net/issues/23) - Consider port to .Net Core
- [#21](https://github.com/WireMock-Net/WireMock.Net/issues/21) - Admin static json mappings +feature
Commits: b547993415...8d9cef6dd1
# 1.0.1.2 (27 February 2017)
- [#24](https://github.com/WireMock-Net/WireMock.Net/pull/24) - Body Encoding contributed by Sebastian Bebrys ([sbebrys](https://github.com/sbebrys))
- [#22](https://github.com/WireMock-Net/WireMock.Net/issues/22) - Add basic-authentication for accessing admin-interface +feature
- [#8](https://github.com/WireMock-Net/WireMock.Net/issues/8) - admin rest api
Commits: bb35f55bbb...02803562c6

BIN
GitReleaseNotes.exe Normal file

Binary file not shown.

5
GitReleaseNotes.txt Normal file
View File

@@ -0,0 +1,5 @@
https://github.com/GitTools/GitReleaseNotes
GitReleaseNotes.exe . /OutputFile CHANGELOG.md /Version 1.0.3.2
GitReleaseNotes.exe . /OutputFile CHANGELOG.md /allTags

View File

@@ -28,7 +28,7 @@ To build you need:
## Stubbing ## 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). A core feature of WireMock.Net is the ability to return canned/predefined HTTP responses for requests matching criteria, see [Wiki : Stubbing & Request Matching](https://github.com/WireMock-Net/WireMock.Net/wiki/Stubbing-and-Request-Matching).
## Using WireMock in UnitTest framework ## Using WireMock in UnitTest framework
You can use your favorite test framework and use WireMock within your tests, see You can use your favorite test framework and use WireMock within your tests, see
@@ -43,6 +43,16 @@ This is quite straight forward to launch a mock server within a console applicat
### SSL ### SSL
You can start a standalone mock server listening for HTTPS requests. To do so, there is just a flag to set when creating the server: You can start a standalone mock server listening for HTTPS requests. To do so, there is just a flag to set when creating the server:
```csharp ```csharp
var server = FluentMockServer.Start(port: 8443, ssl: true); var server1 = FluentMockServer.Start(port: 8443, ssl: true);
// or like this
var server2 = FluentMockServer.Start(new FluentMockServerSettings
{
Urls = new[] { "http://localhost:9091", "https://localhost:9443" }
});
``` ```
Obviously you need a certificate registered on your box, properly associated with your application and the port number that will be used. This is not really specific to WireMock, not very straightforward and hence the following stackoverflow thread might come handy: [Httplistener with https support](http://stackoverflow.com/questions/11403333/httplistener-with-https-support)
- In case when using **net 4.5.2** or **net 4.6**, you need a certificate registered on your box, properly associated with your application and the port number that will be used. This is not really specific to WireMock.Net, not very straightforward and hence the following stackoverflow thread might come handy: [Httplistener with https support](http://stackoverflow.com/questions/11403333/httplistener-with-https-support).
- When using **netstandard**, WireMock.Net uses a self signed certificate (which can be overriden if you like) to host https urls.

View File

@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.27004.2010 VisualStudioVersion = 15.0.27130.2027
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF242EDF-7133-4277-9A0C-18744DE08707}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF242EDF-7133-4277-9A0C-18744DE08707}"
EndProject EndProject
@@ -9,14 +9,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.runsettings = .runsettings .runsettings = .runsettings
appveyor.yml = appveyor.yml appveyor.yml = appveyor.yml
CHANGELOG.md = CHANGELOG.md
codecov-local.cmd = codecov-local.cmd codecov-local.cmd = codecov-local.cmd
GitReleaseNotes.txt = GitReleaseNotes.txt
README.md = README.md README.md = README.md
EndProjectSection EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{F0C22C47-DF71-463C-9B04-B4E0F3B8708A}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{F0C22C47-DF71-463C-9B04-B4E0F3B8708A}"
ProjectSection(SolutionItems) = preProject
examples\WireMock.Net.Console.Record.NETCoreApp\__admin\mappings\ab38efae-4e4d-4f20-8afe-635533ec2535.json = examples\WireMock.Net.Console.Record.NETCoreApp\__admin\mappings\ab38efae-4e4d-4f20-8afe-635533ec2535.json
EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{890A1DED-C229-4FA1-969E-AAC3BBFC05E5}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{890A1DED-C229-4FA1-969E-AAC3BBFC05E5}"
EndProject EndProject
@@ -26,7 +25,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Tests", "test\
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.NETCoreApp", "examples\WireMock.Net.Console.NETCoreApp\WireMock.Net.Console.NETCoreApp.csproj", "{FE281639-B014-4C8A-96FA-141164A74713}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.NETCoreApp", "examples\WireMock.Net.Console.NETCoreApp\WireMock.Net.Console.NETCoreApp.csproj", "{FE281639-B014-4C8A-96FA-141164A74713}"
EndProject 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}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.Proxy.NETCoreApp", "examples\WireMock.Net.Console.Record.NETCoreApp\WireMock.Net.Console.Proxy.NETCoreApp.csproj", "{1995E414-F197-4AB4-90C2-68D806B5AF59}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Client", "examples\WireMock.Net.Client\WireMock.Net.Client.csproj", "{058D4B6C-C03E-49D0-91DB-A535B058FA0D}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Client", "examples\WireMock.Net.Client\WireMock.Net.Client.csproj", "{058D4B6C-C03E-49D0-91DB-A535B058FA0D}"
EndProject EndProject
@@ -41,6 +40,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.StandAlone.Net
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.NET452", "examples\WireMock.Net.ConsoleApplication\WireMock.Net.Console.NET452.csproj", "{668F689E-57B4-422E-8846-C0FF643CA268}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.NET452", "examples\WireMock.Net.ConsoleApplication\WireMock.Net.Console.NET452.csproj", "{668F689E-57B4-422E-8846-C0FF643CA268}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.WebApplication", "examples\WireMock.Net.WebApplication\WireMock.Net.WebApplication.csproj", "{049539C1-7A66-4559-AD7A-B1C73B97CBB0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.Proxy.Net452", "examples\WireMock.Net.Console.Proxy.Net452\WireMock.Net.Console.Proxy.Net452.csproj", "{26433A8F-BF01-4962-97EB-81BFFBB61096}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.Proxy.NETCoreApp2", "examples\WireMock.Net.Console.Proxy.NETCoreApp2\WireMock.Net.Console.Proxy.NETCoreApp2.csproj", "{23A9AA3C-40FC-42AA-8A5E-05899795A1C6}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -83,6 +88,18 @@ Global
{668F689E-57B4-422E-8846-C0FF643CA268}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{668F689E-57B4-422E-8846-C0FF643CA268}.Release|Any CPU.Build.0 = Release|Any CPU {668F689E-57B4-422E-8846-C0FF643CA268}.Release|Any CPU.Build.0 = Release|Any CPU
{049539C1-7A66-4559-AD7A-B1C73B97CBB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{049539C1-7A66-4559-AD7A-B1C73B97CBB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{049539C1-7A66-4559-AD7A-B1C73B97CBB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{049539C1-7A66-4559-AD7A-B1C73B97CBB0}.Release|Any CPU.Build.0 = Release|Any CPU
{26433A8F-BF01-4962-97EB-81BFFBB61096}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{26433A8F-BF01-4962-97EB-81BFFBB61096}.Debug|Any CPU.Build.0 = Debug|Any CPU
{26433A8F-BF01-4962-97EB-81BFFBB61096}.Release|Any CPU.ActiveCfg = Release|Any CPU
{26433A8F-BF01-4962-97EB-81BFFBB61096}.Release|Any CPU.Build.0 = Release|Any CPU
{23A9AA3C-40FC-42AA-8A5E-05899795A1C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{23A9AA3C-40FC-42AA-8A5E-05899795A1C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23A9AA3C-40FC-42AA-8A5E-05899795A1C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23A9AA3C-40FC-42AA-8A5E-05899795A1C6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -97,6 +114,9 @@ Global
{10E16614-61CA-48D8-8BDD-664C13913DED} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A} {10E16614-61CA-48D8-8BDD-664C13913DED} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{668F689E-57B4-422E-8846-C0FF643CA999} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A} {668F689E-57B4-422E-8846-C0FF643CA999} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{668F689E-57B4-422E-8846-C0FF643CA268} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A} {668F689E-57B4-422E-8846-C0FF643CA268} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{049539C1-7A66-4559-AD7A-B1C73B97CBB0} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{26433A8F-BF01-4962-97EB-81BFFBB61096} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{23A9AA3C-40FC-42AA-8A5E-05899795A1C6} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BF428BCC-C837-433B-87D2-15C7014B73E9} SolutionGuid = {BF428BCC-C837-433B-87D2-15C7014B73E9}

View File

@@ -9,7 +9,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="RestEase" Version="1.4.3" /> <PackageReference Include="RestEase" Version="1.4.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

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

View File

@@ -22,10 +22,17 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" /> <ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" /> <PackageReference Include="log4net" Version="2.0.8" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="log4net.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="nlog.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="__admin\mappings\791a3f31-6946-4ce7-8e6f-0237c7443275.json"> <None Update="__admin\mappings\791a3f31-6946-4ce7-8e6f-0237c7443275.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>

View File

@@ -27,7 +27,7 @@
}, },
"UseTransformer": false, "UseTransformer": false,
"Headers": { "Headers": {
"Date": "Wed, 25 Oct 2017 18:57:40 GMT", "Date": "Wed, 27 Oct 2017 18:57:40 GMT",
"Alt-Svc": "quic=\":443\"; ma=2592000; v=\"39,38,37,35\"", "Alt-Svc": "quic=\":443\"; ma=2592000; v=\"39,38,37,35\"",
"Referrer-Policy": "no-referrer", "Referrer-Policy": "no-referrer",
"Connection": "close" "Connection": "close"

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="System.Configuration.IgnoreSectionHandler" />
</configSections>
<appSettings>
<add key="log4net.Internal.Debug" value="true"/>
</appSettings>
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger{1} - %message%newline" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="ConsoleAppender" />
</root>
</log4net>
</configuration>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="info"
internalLogFile="c:\temp\wiremock-internal-nlog.log">
<targets>
<!-- write logs to file -->
<target xsi:type="File" name="all" fileName="c:\temp\wiremock-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId.Id}|${uppercase:${level}}|${logger}|${message} ${exception}" />
</targets>
<!-- rules to map from logger name to target -->
<rules>
<logger name="*" minlevel="Debug" writeTo="all" />
</rules>
</nlog>

View File

@@ -0,0 +1,36 @@
using Newtonsoft.Json;
using WireMock.Server;
using WireMock.Settings;
namespace WireMock.Net.Console.Proxy.NETCoreApp2
{
class Program
{
static void Main(string[] args)
{
var server = FluentMockServer.Start(new FluentMockServerSettings
{
Urls = new[] { "http://localhost:9091", "https://localhost:9443" },
StartAdminInterface = true,
ReadStaticMappings = false,
ProxyAndRecordSettings = new ProxyAndRecordSettings
{
Url = "https://www.google.com",
//ClientX509Certificate2ThumbprintOrSubjectName = "www.yourclientcertname.com OR yourcertificatethumbprint (only if the service you're proxying to requires it)",
SaveMapping = true,
SaveMappingToFile = false,
BlackListedHeaders = new[] { "dnt", "Content-Length" }
}
});
server.LogEntriesChanged += (sender, eventRecordArgs) =>
{
System.Console.WriteLine(JsonConvert.SerializeObject(eventRecordArgs.NewItems, Formatting.Indented));
};
System.Console.WriteLine("Press any key to stop the server");
System.Console.ReadKey();
server.Stop();
}
}
}

View File

@@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:63377/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"WireMock.Net.Console.Proxy.NETCoreApp2": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:63378/"
}
}
}

View File

@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
</startup>
</configuration>

View File

@@ -0,0 +1,36 @@
using Newtonsoft.Json;
using WireMock.Server;
using WireMock.Settings;
namespace WireMock.Net.Console.Proxy.Net452
{
class Program
{
static void Main(string[] args)
{
var server = FluentMockServer.Start(new FluentMockServerSettings
{
Urls = new[] { "http://localhost:9091/", "https://localhost:9443/" },
StartAdminInterface = true,
ReadStaticMappings = false,
ProxyAndRecordSettings = new ProxyAndRecordSettings
{
Url = "https://www.google.com",
//ClientX509Certificate2ThumbprintOrSubjectName = "www.yourclientcertname.com OR yourcertificatethumbprint (only if the service you're proxying to requires it)",
SaveMapping = true,
SaveMappingToFile = false,
BlackListedHeaders = new[] { "dnt", "Content-Length" }
}
});
server.LogEntriesChanged += (sender, eventRecordArgs) =>
{
System.Console.WriteLine(JsonConvert.SerializeObject(eventRecordArgs.NewItems, Formatting.Indented));
};
System.Console.WriteLine("Press any key to stop the server");
System.Console.ReadKey();
server.Stop();
}
}
}

View File

@@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("WireMock.Net.Console.Proxy.Net452")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("WireMock.Net.Console.Proxy.Net452")]
[assembly: AssemblyCopyright("Copyright © Stef Heyenrath 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("26433a8f-bf01-4962-97eb-81bffbb61096")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{26433A8F-BF01-4962-97EB-81BFFBB61096}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>WireMock.Net.Console.Proxy.Net452</RootNamespace>
<AssemblyName>WireMock.Net.Console.Proxy.Net452</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
</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>
<StartupObject>WireMock.Net.Console.Proxy.Net452.Program</StartupObject>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<None Condition="'$(Platform)' == 'x64'" Include="..\packages\Libuv.1.10.0\runtimes\win7-x64\native\libuv.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>False</Visible>
<Link>libuv.dll</Link>
</None>
<None Condition="'$(Platform)' == 'x86'" Include="..\packages\Libuv.1.10.0\runtimes\win7-x86\native\libuv.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>False</Visible>
<Link>libuv.dll</Link>
</None>
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.Owin.Host.HttpListener, Version=3.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Owin.Host.HttpListener.3.1.0\lib\net45\Microsoft.Owin.Host.HttpListener.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<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" />
</Project>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Owin.Host.HttpListener" version="3.1.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net452" />
</packages>

View File

@@ -2,7 +2,7 @@
using WireMock.Server; using WireMock.Server;
using WireMock.Settings; using WireMock.Settings;
namespace WireMock.Net.Console.Record.NETCoreApp namespace WireMock.Net.Console.Proxy.NETCoreApp
{ {
static class Program static class Program
{ {
@@ -16,9 +16,10 @@ namespace WireMock.Net.Console.Record.NETCoreApp
ProxyAndRecordSettings = new ProxyAndRecordSettings ProxyAndRecordSettings = new ProxyAndRecordSettings
{ {
Url = "https://www.google.com", Url = "https://www.google.com",
//X509Certificate2ThumbprintOrSubjectName = "www.yourclientcertname.com OR yourcertificatethumbprint (only if the service you're proxying to requires it)", //ClientX509Certificate2ThumbprintOrSubjectName = "www.yourclientcertname.com OR yourcertificatethumbprint (only if the service you're proxying to requires it)",
SaveMapping = true, SaveMapping = true,
SaveMappingToFile = false SaveMappingToFile = false,
BlackListedHeaders = new [] { "dnt", "Content-Length" }
} }
}); });

View File

@@ -6,13 +6,9 @@
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon> <ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineArgumentsParser" Version="3.0.10" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" /> <ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -21,6 +21,7 @@ namespace WireMock.Net.ConsoleApplication
Urls = new[] { url1, url2, url3 }, Urls = new[] { url1, url2, url3 },
StartAdminInterface = true, StartAdminInterface = true,
ReadStaticMappings = true, ReadStaticMappings = true,
WatchStaticMappings = true,
//ProxyAndRecordSettings = new ProxyAndRecordSettings //ProxyAndRecordSettings = new ProxyAndRecordSettings
//{ //{
// SaveMapping = true // SaveMapping = true
@@ -28,7 +29,7 @@ namespace WireMock.Net.ConsoleApplication
PreWireMockMiddlewareInit = app => { System.Console.WriteLine($"PreWireMockMiddlewareInit : {app.GetType()}"); }, PreWireMockMiddlewareInit = app => { System.Console.WriteLine($"PreWireMockMiddlewareInit : {app.GetType()}"); },
PostWireMockMiddlewareInit = app => { System.Console.WriteLine($"PostWireMockMiddlewareInit : {app.GetType()}"); } PostWireMockMiddlewareInit = app => { System.Console.WriteLine($"PostWireMockMiddlewareInit : {app.GetType()}"); }
}); });
System.Console.WriteLine("FluentMockServer listening at {0}", string.Join(" and ", server.Urls)); System.Console.WriteLine("FluentMockServer listening at {0}", string.Join(",", server.Urls));
server.SetBasicAuthentication("a", "b"); server.SetBasicAuthentication("a", "b");
@@ -40,6 +41,15 @@ namespace WireMock.Net.ConsoleApplication
// .RespondWith(Response.Create() // .RespondWith(Response.Create()
// .WithProxy("http://restcountries.eu")); // .WithProxy("http://restcountries.eu"));
server
.Given(Request
.Create()
.WithPath("/jsonthings")
.WithBody(new JsonPathMatcher("$.things[?(@.name == 'RequiredThing')]"))
.UsingPut())
.RespondWith(Response.Create()
.WithBody(@"{ ""result"": ""JsonPathMatcher !!!""}"));
server server
.Given(Request .Given(Request
.Create() .Create()
@@ -53,8 +63,9 @@ namespace WireMock.Net.ConsoleApplication
.Given(Request.Create().WithPath("/headers", "/headers_test").UsingPost().WithHeader("Content-Type", "application/json*")) .Given(Request.Create().WithPath("/headers", "/headers_test").UsingPost().WithHeader("Content-Type", "application/json*"))
.RespondWith(Response.Create() .RespondWith(Response.Create()
.WithStatusCode(201) .WithStatusCode(201)
.WithHeader("MyHeader", "application/json", "application/json2") //.WithHeader("MyHeader", "application/json", "application/json2")
.WithBody(@"{ ""result"": ""data posted with 201""}")); .WithHeader("Content-Type", "application/json")
.WithBodyAsJson(new { result = "data:headers posted with 201" }));
server server
.Given(Request.Create().WithPath("/file").UsingGet()) .Given(Request.Create().WithPath("/file").UsingGet())
@@ -116,7 +127,7 @@ namespace WireMock.Net.ConsoleApplication
.RespondWith(Response.Create() .RespondWith(Response.Create()
.WithStatusCode(201) .WithStatusCode(201)
.WithHeader("Content-Type", "application/json") .WithHeader("Content-Type", "application/json")
.WithBody(@"{ ""result"": ""data posted with FUNC 201""}")); .WithBodyAsJson(new { result = "data posted with FUNC 201" }));
server server
.Given(Request.Create().WithPath("/json").UsingPost().WithBody(new JsonPathMatcher("$.things[?(@.name == 'RequiredThing')]"))) .Given(Request.Create().WithPath("/json").UsingPost().WithBody(new JsonPathMatcher("$.things[?(@.name == 'RequiredThing')]")))

View File

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

View File

@@ -36,17 +36,22 @@
<ApplicationIcon>..\..\WireMock.Net-Logo.ico</ApplicationIcon> <ApplicationIcon>..\..\WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Owin.Host.HttpListener, Version=3.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <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> <HintPath>..\..\packages\Microsoft.Owin.Host.HttpListener.3.1.0\lib\net45\Microsoft.Owin.Host.HttpListener.dll</HintPath>
</Reference> </Reference>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath> <HintPath>..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference> </Reference>
<Reference Include="SimMetrics.Net, Version=1.0.3.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="SimMetrics.Net, Version=1.0.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\SimMetrics.Net.1.0.3\lib\net45\SimMetrics.Net.dll</HintPath> <HintPath>..\..\packages\SimMetrics.Net.1.0.4\lib\net45\SimMetrics.Net.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.XML" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="MainApp.cs" /> <Compile Include="MainApp.cs" />
@@ -57,6 +62,9 @@
<None Include="App.config"> <None Include="App.config">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</None> </None>
<None Include="log4net.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="packages.config"> <None Include="packages.config">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</None> </None>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="System.Configuration.IgnoreSectionHandler" />
</configSections>
<appSettings>
<add key="log4net.Internal.Debug" value="true"/>
</appSettings>
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger{1} - %message%newline" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="ConsoleAppender" />
</root>
</log4net>
</configuration>

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="log4net" version="2.0.8" targetFramework="net452" />
<package id="Microsoft.Owin.Host.HttpListener" version="3.1.0" targetFramework="net452" /> <package id="Microsoft.Owin.Host.HttpListener" version="3.1.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net452" /> <package id="Newtonsoft.Json" version="10.0.2" targetFramework="net452" />
<package id="SimMetrics.Net" version="1.0.3" targetFramework="net452" /> <package id="SimMetrics.Net" version="1.0.4" targetFramework="net452" />
</packages> </packages>

View File

@@ -1,21 +1,31 @@
using System; using System;
using System.IO;
using System.Reflection;
using System.Threading; using System.Threading;
using log4net;
using log4net.Config;
using log4net.Repository;
using WireMock.Server; using WireMock.Server;
namespace WireMock.Net.StandAlone.NETCoreApp namespace WireMock.Net.StandAlone.NETCoreApp
{ {
class Program static class Program
{ {
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
// private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
private static int sleepTime = 30000; private static int sleepTime = 30000;
private static FluentMockServer server; private static FluentMockServer _server;
static void Main(string[] args) static void Main(string[] args)
{ {
server = StandAloneApp.Start(args); XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
_server = StandAloneApp.Start(args);
Console.WriteLine($"{DateTime.UtcNow} Press Ctrl+C to shut down"); Console.WriteLine($"{DateTime.UtcNow} Press Ctrl+C to shut down");
System.Console.CancelKeyPress += (s,e) => Console.CancelKeyPress += (s, e) =>
{ {
Stop("CancelKeyPress"); Stop("CancelKeyPress");
}; };
@@ -25,7 +35,7 @@ namespace WireMock.Net.StandAlone.NETCoreApp
Stop("AssemblyLoadContext.Default.Unloading"); Stop("AssemblyLoadContext.Default.Unloading");
}; };
while(true) while (true)
{ {
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server running"); Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server running");
Thread.Sleep(sleepTime); Thread.Sleep(sleepTime);
@@ -35,7 +45,7 @@ namespace WireMock.Net.StandAlone.NETCoreApp
private static void Stop(string why) private static void Stop(string why)
{ {
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopping because '{why}'"); Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopping because '{why}'");
server.Stop(); _server.Stop();
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopped"); Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopped");
} }
} }

View File

@@ -6,8 +6,18 @@
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon> <ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="log4net" Version="2.0.8" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj" /> <ProjectReference Include="..\..\src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="log4net.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="System.Configuration.IgnoreSectionHandler" />
</configSections>
<appSettings>
<add key="log4net.Internal.Debug" value="true"/>
</appSettings>
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger{1} - %message%newline" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="ConsoleAppender" />
</root>
</log4net>
</configuration>

View File

@@ -1,4 +1,6 @@
using System; using System;
using System.IO;
using log4net.Config;
namespace WireMock.Net.StandAlone.Net452 namespace WireMock.Net.StandAlone.Net452
{ {
@@ -6,6 +8,8 @@ namespace WireMock.Net.StandAlone.Net452
{ {
static void Main(params string[] args) static void Main(params string[] args)
{ {
XmlConfigurator.Configure(new FileInfo("log4net.config"));
StandAloneApp.Start(args); StandAloneApp.Start(args);
Console.WriteLine("Press any key to stop the server"); Console.WriteLine("Press any key to stop the server");

View File

@@ -39,6 +39,9 @@
<StartupObject>WireMock.Net.StandAlone.Net452.Program</StartupObject> <StartupObject>WireMock.Net.StandAlone.Net452.Program</StartupObject>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Numerics" /> <Reference Include="System.Numerics" />
@@ -50,6 +53,9 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="log4net.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="System.Configuration.IgnoreSectionHandler" />
</configSections>
<appSettings>
<add key="log4net.Internal.Debug" value="true"/>
</appSettings>
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger{1} - %message%newline" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="ConsoleAppender" />
</root>
</log4net>
</configuration>

View File

@@ -1,3 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="log4net" version="2.0.8" targetFramework="net452" />
</packages> </packages>

View File

@@ -0,0 +1,22 @@
using Microsoft.Extensions.Logging;
namespace WireMock.Net.WebApplication
{
public class App
{
private readonly IWireMockService _service;
private readonly ILogger _logger;
public App(IWireMockService service, ILogger logger)
{
_service = service;
_logger = logger;
}
public void Run()
{
_logger.LogInformation("WireMock.Net App running");
_service.Run();
}
}
}

View File

@@ -0,0 +1,7 @@
namespace WireMock.Net.WebApplication
{
public interface IWireMockService
{
void Run();
}
}

View File

@@ -0,0 +1,57 @@
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using WireMock.Settings;
namespace WireMock.Net.WebApplication
{
public class Program
{
public static void Main(string[] args)
{
// Create service collection
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
// Create service provider
var serviceProvider = serviceCollection.BuildServiceProvider();
// Run app
serviceProvider.GetService<App>().Run();
}
private static void ConfigureServices(IServiceCollection serviceCollection)
{
// Build configuration
var configuration = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables() // <-- this is needed to to override settings via the Azure Portal App Settings
.Build();
// Add LoggerFactory
var factory = new LoggerFactory();
serviceCollection.AddSingleton(factory
.AddConsole(configuration.GetSection("Logging"))
.AddDebug()
.AddAzureWebAppDiagnostics()
);
serviceCollection.AddSingleton(factory.CreateLogger("WireMock.Net Logger"));
// Add access to generic IConfigurationRoot
serviceCollection.AddSingleton(configuration);
// Add access to IFluentMockServerSettings
var settings = configuration.GetSection("FluentMockServerSettings").Get<FluentMockServerSettings>();
serviceCollection.AddSingleton<IFluentMockServerSettings>(settings);
// Add services
serviceCollection.AddTransient<IWireMockService, WireMockService>();
// Add app
serviceCollection.AddTransient<App>();
}
}
}

View File

@@ -0,0 +1,28 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:56513/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchUrl": "__admin/settings",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"WireMock.Net.WebApplication": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "__admin/settings",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:56514/"
}
}
}

View File

@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.2" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,37 @@
using System.Threading;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using WireMock.Net.StandAlone;
using WireMock.Settings;
namespace WireMock.Net.WebApplication
{
public class WireMockService : IWireMockService
{
private static int sleepTime = 30000;
private readonly ILogger _logger;
private readonly IFluentMockServerSettings _settings;
public WireMockService(ILogger logger, IFluentMockServerSettings settings)
{
_logger = logger;
_settings = settings;
}
public void Run()
{
_logger.LogInformation("WireMock.Net server starting");
StandAloneApp.Start(_settings);
_logger.LogInformation($"WireMock.Net server settings {JsonConvert.SerializeObject(_settings)}");
while (true)
{
_logger.LogInformation("WireMock.Net server running");
Thread.Sleep(sleepTime);
}
}
}
}

View File

@@ -0,0 +1,20 @@
{
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Debug"
}
},
"Console": {
"LogLevel": {
"Default": "Debug"
}
}
},
"FluentMockServerSettings": {
"AdminUsername": "a",
"AdminPassword": "a",
"StartAdminInterface": true
}
}

View File

@@ -1,10 +1,9 @@
using System; using System.Linq;
using System.Linq;
using WireMock.Server; using WireMock.Server;
using WireMock.Settings; using WireMock.Settings;
using WireMock.Validation; using WireMock.Validation;
using JetBrains.Annotations; using JetBrains.Annotations;
using Newtonsoft.Json; using log4net;
namespace WireMock.Net.StandAlone namespace WireMock.Net.StandAlone
{ {
@@ -13,12 +12,14 @@ namespace WireMock.Net.StandAlone
/// </summary> /// </summary>
public static class StandAloneApp public static class StandAloneApp
{ {
private static readonly ILog Log = LogManager.GetLogger(typeof(StandAloneApp));
/// <summary> /// <summary>
/// Start WireMock.Net standalone based on the FluentMockServerSettings. /// Start WireMock.Net standalone Server based on the FluentMockServerSettings.
/// </summary> /// </summary>
/// <param name="settings">The FluentMockServerSettings</param> /// <param name="settings">The FluentMockServerSettings</param>
[PublicAPI] [PublicAPI]
public static FluentMockServer Start([NotNull] FluentMockServerSettings settings) public static FluentMockServer Start([NotNull] IFluentMockServerSettings settings)
{ {
Check.NotNull(settings, nameof(settings)); Check.NotNull(settings, nameof(settings));
@@ -26,7 +27,7 @@ namespace WireMock.Net.StandAlone
} }
/// <summary> /// <summary>
/// Start WireMock.Net standalone based on the commandline arguments. /// Start WireMock.Net standalone Server based on the commandline arguments.
/// </summary> /// </summary>
/// <param name="args">The commandline arguments</param> /// <param name="args">The commandline arguments</param>
[PublicAPI] [PublicAPI]
@@ -34,7 +35,7 @@ namespace WireMock.Net.StandAlone
{ {
Check.NotNull(args, nameof(args)); Check.NotNull(args, nameof(args));
Console.WriteLine("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'"))); Log.DebugFormat("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));
var parser = new SimpleCommandLineParser(); var parser = new SimpleCommandLineParser();
parser.Parse(args); parser.Parse(args);
@@ -43,6 +44,7 @@ namespace WireMock.Net.StandAlone
{ {
StartAdminInterface = parser.GetBoolValue("StartAdminInterface", true), StartAdminInterface = parser.GetBoolValue("StartAdminInterface", true),
ReadStaticMappings = parser.GetBoolValue("ReadStaticMappings"), ReadStaticMappings = parser.GetBoolValue("ReadStaticMappings"),
WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"),
AllowPartialMapping = parser.GetBoolValue("AllowPartialMapping", true), AllowPartialMapping = parser.GetBoolValue("AllowPartialMapping", true),
AdminUsername = parser.GetStringValue("AdminUsername"), AdminUsername = parser.GetStringValue("AdminUsername"),
AdminPassword = parser.GetStringValue("AdminPassword"), AdminPassword = parser.GetStringValue("AdminPassword"),
@@ -67,15 +69,14 @@ namespace WireMock.Net.StandAlone
Url = proxyURL, Url = proxyURL,
SaveMapping = parser.GetBoolValue("SaveMapping"), SaveMapping = parser.GetBoolValue("SaveMapping"),
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"), SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
X509Certificate2ThumbprintOrSubjectName = parser.GetStringValue("X509Certificate2ThumbprintOrSubjectName") ClientX509Certificate2ThumbprintOrSubjectName = parser.GetStringValue("ClientX509Certificate2ThumbprintOrSubjectName"),
BlackListedHeaders = parser.GetValues("BlackListedHeaders")
}; };
} }
Console.WriteLine("WireMock.Net server settings {0}", JsonConvert.SerializeObject(settings, Formatting.Indented));
FluentMockServer server = Start(settings); FluentMockServer server = Start(settings);
Console.WriteLine("WireMock.Net server listening at {0}", string.Join(" and ", server.Urls)); Log.InfoFormat("WireMock.Net server listening at {0}", string.Join(",", server.Urls));
return server; return server;
} }

View File

@@ -1,26 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>Lightweight StandAlone Http Mocking Server for .Net.</Description> <Description>Lightweight StandAlone Http Mocking Server for .Net.</Description>
<AssemblyTitle>WireMock.Net.StandAlone</AssemblyTitle> <AssemblyTitle>WireMock.Net.StandAlone</AssemblyTitle>
<Version>1.0.2.10</Version> <Version>1.0.3.3</Version>
<Authors>Stef Heyenrath</Authors> <Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks> <TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>WireMock.Net.StandAlone</AssemblyName> <AssemblyName>WireMock.Net.StandAlone</AssemblyName>
<PackageId>WireMock.Net.StandAlone</PackageId> <PackageId>WireMock.Net.StandAlone</PackageId>
<PackageTags>tdd;mock;http;wiremock;test;server;unittest</PackageTags> <PackageTags>tdd;mock;http;wiremock;test;server;unittest</PackageTags>
<PackageReleaseNotes></PackageReleaseNotes> <PackageReleaseNotes>See CHANGELOG.md</PackageReleaseNotes>
<PackageIconUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/WireMock.Net-Logo.png</PackageIconUrl> <PackageIconUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/WireMock.Net-Logo.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl> <PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
<PackageLicenseUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/LICENSE</PackageLicenseUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl> <RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<DebugType>portable</DebugType>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon> <ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
<RootNamespace>WireMock.Net.StandAlone</RootNamespace> <RootNamespace>WireMock.Net.StandAlone</RootNamespace>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugType>full</DebugType>
<IncludeSource>True</IncludeSource>
<IncludeSymbols>True</IncludeSymbols>
<PathMap>$(MSBuildProjectDirectory)=/</PathMap>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' or '$(TargetFramework)' == 'netstandard2.0' "> <PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' or '$(TargetFramework)' == 'netstandard2.0' ">
<DefineConstants>NETSTANDARD</DefineConstants> <DefineConstants>NETSTANDARD</DefineConstants>
@@ -30,10 +36,7 @@
<PackageReference Include="JetBrains.Annotations" Version="10.4.0"> <PackageReference Include="JetBrains.Annotations" Version="10.4.0">
<PrivateAssets>All</PrivateAssets> <PrivateAssets>All</PrivateAssets>
</PackageReference> </PackageReference>
<!-- <PackageReference Include="CommandLineArgumentsParser" Version="3.0.16" /> --> <PackageReference Include="log4net" Version="2.0.8" />
<!-- <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>
<ItemGroup> <ItemGroup>

View File

@@ -10,29 +10,20 @@ namespace WireMock.Admin.Mappings
/// <summary> /// <summary>
/// Gets or sets the unique identifier. /// Gets or sets the unique identifier.
/// </summary> /// </summary>
/// <value>
/// The unique identifier.
/// </value>
public Guid? Guid { get; set; } public Guid? Guid { get; set; }
/// <summary> /// <summary>
/// Gets or sets the unique title.
/// </summary>
/// <value>
/// The unique title. /// The unique title.
/// </value> /// </summary>
public string Title { get; set; } public string Title { get; set; }
/// <summary> /// <summary>
/// Gets or sets the priority.
/// </summary>
/// <value>
/// The priority. /// The priority.
/// </value> /// </summary>
public int? Priority { get; set; } public int? Priority { get; set; }
/// <summary> /// <summary>
/// Scenario. /// The Scenario.
/// </summary> /// </summary>
public string Scenario { get; set; } public string Scenario { get; set; }
@@ -48,19 +39,13 @@ namespace WireMock.Admin.Mappings
public object SetStateTo { get; set; } public object SetStateTo { get; set; }
/// <summary> /// <summary>
/// Gets or sets the request.
/// </summary>
/// <value>
/// The request. /// The request.
/// </value> /// </summary>
public RequestModel Request { get; set; } public RequestModel Request { get; set; }
/// <summary> /// <summary>
/// Gets or sets the response.
/// </summary>
/// <value>
/// The response. /// The response.
/// </value> /// </summary>
public ResponseModel Response { get; set; } public ResponseModel Response { get; set; }
} }
} }

View File

@@ -8,51 +8,33 @@ namespace WireMock.Admin.Requests
public class LogEntryModel public class LogEntryModel
{ {
/// <summary> /// <summary>
/// Gets or sets the unique identifier.
/// </summary>
/// <value>
/// The unique identifier. /// The unique identifier.
/// </value> /// </summary>
public Guid Guid { get; set; } public Guid Guid { get; set; }
/// <summary> /// <summary>
/// Gets or sets the request.
/// </summary>
/// <value>
/// The request. /// The request.
/// </value> /// </summary>
public LogRequestModel Request { get; set; } public LogRequestModel Request { get; set; }
/// <summary> /// <summary>
/// Gets or sets the response.
/// </summary>
/// <value>
/// The response. /// The response.
/// </value> /// </summary>
public LogResponseModel Response { get; set; } public LogResponseModel Response { get; set; }
/// <summary> /// <summary>
/// Gets or sets the mapping unique identifier.
/// </summary>
/// <value>
/// The mapping unique identifier. /// The mapping unique identifier.
/// </value> /// </summary>
public Guid? MappingGuid { get; set; } public Guid? MappingGuid { get; set; }
/// <summary> /// <summary>
/// Gets or sets the mapping unique title.
/// </summary>
/// <value>
/// The mapping unique title. /// The mapping unique title.
/// </value> /// </summary>
public string MappingTitle { get; set; } public string MappingTitle { get; set; }
/// <summary> /// <summary>
/// Gets or sets the request match result.
/// </summary>
/// <value>
/// The request match result. /// The request match result.
/// </value> /// </summary>
public LogRequestMatchModel RequestMatchResult { get; set; } public LogRequestMatchModel RequestMatchResult { get; set; }
} }
} }

View File

@@ -11,52 +11,62 @@ namespace WireMock.Admin.Requests
public class LogRequestModel public class LogRequestModel
{ {
/// <summary> /// <summary>
/// Gets the Client IP Address. /// The Client IP Address.
/// </summary> /// </summary>
public string ClientIP { get; set; } public string ClientIP { get; set; }
/// <summary> /// <summary>
/// Gets the DateTime. /// The DateTime.
/// </summary> /// </summary>
public DateTime DateTime { get; set; } public DateTime DateTime { get; set; }
/// <summary> /// <summary>
/// Gets or sets the Path. /// The Path.
/// </summary> /// </summary>
public string Path { get; set; } public string Path { get; set; }
/// <summary> /// <summary>
/// Gets or sets the absolete URL. ///The absolete URL.
/// </summary> /// </summary>
public string AbsoluteUrl { get; set; } public string AbsoluteUrl { get; set; }
/// <summary> /// <summary>
/// Gets the query. /// The query.
/// </summary> /// </summary>
public IDictionary<string, WireMockList<string>> Query { get; set; } public IDictionary<string, WireMockList<string>> Query { get; set; }
/// <summary> /// <summary>
/// Gets or sets the method. /// The method.
/// </summary> /// </summary>
public string Method { get; set; } public string Method { get; set; }
/// <summary> /// <summary>
/// Gets or sets the Headers. /// The Headers.
/// </summary> /// </summary>
public IDictionary<string, WireMockList<string>> Headers { get; set; } public IDictionary<string, WireMockList<string>> Headers { get; set; }
/// <summary> /// <summary>
/// Gets or sets the Cookies. /// Tthe Cookies.
/// </summary> /// </summary>
public IDictionary<string, string> Cookies { get; set; } public IDictionary<string, string> Cookies { get; set; }
/// <summary> /// <summary>
/// Gets or sets the body. /// The body (as string).
/// </summary> /// </summary>
public string Body { get; set; } public string Body { get; set; }
/// <summary> /// <summary>
/// Gets or sets the body encoding. /// The body (as JSON object).
/// </summary>
public object BodyAsJson { get; set; }
/// <summary>
/// The body (as bytearray).
/// </summary>
public byte[] BodyAsBytes { get; set; }
/// <summary>
/// The body encoding.
/// </summary> /// </summary>
public EncodingModel BodyEncoding { get; set; } public EncodingModel BodyEncoding { get; set; }
} }

View File

@@ -25,12 +25,17 @@ namespace WireMock.Admin.Requests
public string BodyDestination { get; set; } public string BodyDestination { get; set; }
/// <summary> /// <summary>
/// Gets or sets the body. /// The body (as string).
/// </summary> /// </summary>
public string Body { get; set; } public string Body { get; set; }
/// <summary> /// <summary>
/// Gets or sets the body as bytes. /// The body (as JSON object).
/// </summary>
public object BodyAsJson { get; set; }
/// <summary>
/// The body (as bytearray).
/// </summary> /// </summary>
public byte[] BodyAsBytes { get; set; } public byte[] BodyAsBytes { get; set; }

View File

@@ -55,6 +55,7 @@ namespace WireMock.Client
/// </summary> /// </summary>
/// <param name="mapping">MappingModel</param> /// <param name="mapping">MappingModel</param>
[Post("__admin/mappings")] [Post("__admin/mappings")]
[Header("Content-Type", "application/json")]
Task<string> PostMappingAsync([Body] MappingModel mapping); Task<string> PostMappingAsync([Body] MappingModel mapping);
/// <summary> /// <summary>
@@ -137,6 +138,7 @@ namespace WireMock.Client
/// </summary> /// </summary>
/// <param name="model">The RequestModel</param> /// <param name="model">The RequestModel</param>
[Post("__admin/requests/find")] [Post("__admin/requests/find")]
[Header("Content-Type", "application/json")]
Task<IList<LogEntryModel>> FindRequestsAsync([Body] RequestModel model); Task<IList<LogEntryModel>> FindRequestsAsync([Body] RequestModel model);
/// <summary> /// <summary>

View File

@@ -42,10 +42,10 @@ namespace WireMock
internal class ProxyAsyncResponseProvider : IResponseProvider internal class ProxyAsyncResponseProvider : IResponseProvider
{ {
private readonly Func<RequestMessage, ProxyAndRecordSettings, Task<ResponseMessage>> _responseMessageFunc; private readonly Func<RequestMessage, IProxyAndRecordSettings, Task<ResponseMessage>> _responseMessageFunc;
private readonly ProxyAndRecordSettings _settings; private readonly IProxyAndRecordSettings _settings;
public ProxyAsyncResponseProvider([NotNull] Func<RequestMessage, ProxyAndRecordSettings, Task<ResponseMessage>> responseMessageFunc, [NotNull] ProxyAndRecordSettings settings) public ProxyAsyncResponseProvider([NotNull] Func<RequestMessage, IProxyAndRecordSettings, Task<ResponseMessage>> responseMessageFunc, [NotNull] IProxyAndRecordSettings settings)
{ {
Check.NotNull(responseMessageFunc, nameof(responseMessageFunc)); Check.NotNull(responseMessageFunc, nameof(responseMessageFunc));
Check.NotNull(settings, nameof(settings)); Check.NotNull(settings, nameof(settings));

View File

@@ -1,7 +1,13 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations;
using Newtonsoft.Json;
using WireMock.HttpsCertificate;
using WireMock.Util;
using WireMock.Validation; using WireMock.Validation;
namespace WireMock.Http namespace WireMock.Http
@@ -10,37 +16,28 @@ namespace WireMock.Http
{ {
public static HttpClient CreateHttpClient(string clientX509Certificate2ThumbprintOrSubjectName = null) public static HttpClient CreateHttpClient(string clientX509Certificate2ThumbprintOrSubjectName = null)
{ {
HttpClientHandler handler;
if (string.IsNullOrEmpty(clientX509Certificate2ThumbprintOrSubjectName))
{
handler = new HttpClientHandler();
}
else
{
#if NETSTANDARD || NET46 #if NETSTANDARD || NET46
handler = new HttpClientHandler var handler = new HttpClientHandler
{ {
ClientCertificateOptions = ClientCertificateOption.Manual, CheckCertificateRevocationList = false,
SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls, SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls,
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true,
}; AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
var x509Certificate2 = CertificateUtil.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName);
handler.ClientCertificates.Add(x509Certificate2);
#else #else
var handler = new WebRequestHandler
var webRequestHandler = new WebRequestHandler {
{ ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true,
ClientCertificateOptions = ClientCertificateOption.Manual, AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true, };
AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate
};
var x509Certificate2 = CertificateUtil.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName);
webRequestHandler.ClientCertificates.Add(x509Certificate2);
handler = webRequestHandler;
#endif #endif
if (!string.IsNullOrEmpty(clientX509Certificate2ThumbprintOrSubjectName))
{
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
var x509Certificate2 = ClientCertificateHelper.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName);
handler.ClientCertificates.Add(x509Certificate2);
} }
// For proxy we shouldn't follow auto redirects // For proxy we shouldn't follow auto redirects
@@ -49,23 +46,43 @@ namespace WireMock.Http
// If UseCookies enabled, httpClient ignores Cookie header // If UseCookies enabled, httpClient ignores Cookie header
handler.UseCookies = false; handler.UseCookies = false;
return new HttpClient(handler); var client = new HttpClient(handler);
#if NET452 || NET46
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
#endif
return client;
} }
public static async Task<ResponseMessage> SendAsync(HttpClient client, RequestMessage requestMessage, string url) public static async Task<ResponseMessage> SendAsync([NotNull] HttpClient client, [NotNull] RequestMessage requestMessage, string url)
{ {
Check.NotNull(client, nameof(client)); Check.NotNull(client, nameof(client));
Check.NotNull(requestMessage, nameof(requestMessage));
var originalUri = new Uri(requestMessage.Url); var originalUri = new Uri(requestMessage.Url);
var requiredUri = new Uri(url); var requiredUri = new Uri(url);
var httpRequestMessage = new HttpRequestMessage(new HttpMethod(requestMessage.Method), url); var httpRequestMessage = new HttpRequestMessage(new HttpMethod(requestMessage.Method), url);
WireMockList<string> contentTypeHeader = null;
bool contentTypeHeaderPresent = requestMessage.Headers.Any(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentType, StringComparison.OrdinalIgnoreCase));
if (contentTypeHeaderPresent)
{
contentTypeHeader = requestMessage.Headers[HttpKnownHeaderNames.ContentType];
}
// Set Body if present // Set Body if present
if (requestMessage.BodyAsBytes != null && requestMessage.BodyAsBytes.Length > 0) if (requestMessage.BodyAsBytes != null)
{ {
httpRequestMessage.Content = new ByteArrayContent(requestMessage.BodyAsBytes); httpRequestMessage.Content = new ByteArrayContent(requestMessage.BodyAsBytes);
} }
else if (requestMessage.BodyAsJson != null)
{
httpRequestMessage.Content = new StringContent(JsonConvert.SerializeObject(requestMessage.BodyAsJson), requestMessage.BodyEncoding);
}
else if (requestMessage.Body != null)
{
httpRequestMessage.Content = new StringContent(requestMessage.Body, requestMessage.BodyEncoding);
}
// Overwrite the host header // Overwrite the host header
httpRequestMessage.Headers.Host = requiredUri.Authority; httpRequestMessage.Headers.Host = requiredUri.Authority;
@@ -86,23 +103,25 @@ namespace WireMock.Http
// Call the URL // Call the URL
var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead); var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead);
// Transform response // Create transform response
var responseMessage = new ResponseMessage var responseMessage = new ResponseMessage { StatusCode = (int)httpResponseMessage.StatusCode };
{
StatusCode = (int)httpResponseMessage.StatusCode,
BodyAsBytes = await httpResponseMessage.Content.ReadAsByteArrayAsync(),
Body = await httpResponseMessage.Content.ReadAsStringAsync()
};
// Set both content and response headers, replacing URLs in values // Set both content and response headers, replacing URLs in values
var headers = httpResponseMessage.Content?.Headers.Union(httpResponseMessage.Headers); var headers = (httpResponseMessage.Content?.Headers.Union(httpResponseMessage.Headers) ?? Enumerable.Empty<KeyValuePair<string, IEnumerable<string>>>()).ToArray();
if (httpResponseMessage.Content != null)
{
var stream = await httpResponseMessage.Content.ReadAsStreamAsync();
var body = await BodyParser.Parse(stream, contentTypeHeader?.FirstOrDefault());
responseMessage.Body = body.BodyAsString;
responseMessage.BodyAsJson = body.BodyAsJson;
responseMessage.BodyAsBytes = body.BodyAsBytes;
}
foreach (var header in headers) foreach (var header in headers)
{ {
// if Location header contains absolute redirect URL, and base URL is one that we proxy to, // If Location header contains absolute redirect URL, and base URL is one that we proxy to,
// we need to replace it to original one. // we need to replace it to original one.
if (string.Equals(header.Key, "Location", StringComparison.OrdinalIgnoreCase) if (string.Equals(header.Key, HttpKnownHeaderNames.Location, StringComparison.OrdinalIgnoreCase)
&& Uri.TryCreate(header.Value.First(), UriKind.Absolute, out Uri absoluteLocationUri) && Uri.TryCreate(header.Value.First(), UriKind.Absolute, out Uri absoluteLocationUri)
&& string.Equals(absoluteLocationUri.Host, requiredUri.Host, StringComparison.OrdinalIgnoreCase)) && string.Equals(absoluteLocationUri.Host, requiredUri.Host, StringComparison.OrdinalIgnoreCase))
{ {

View File

@@ -5,18 +5,15 @@ using System.Text.RegularExpressions;
namespace WireMock.Http namespace WireMock.Http
{ {
/// <summary> /// <summary>
/// Utility class /// Port Utility class
/// </summary> /// </summary>
public static class PortUtil public static class PortUtil
{ {
private static readonly Regex UrlDetailsRegex = new Regex(@"^(?<proto>\w+)://[^/]+?(?<port>\d+)?/", RegexOptions.Compiled); private static readonly Regex UrlDetailsRegex = new Regex(@"^(?<proto>\w+)://[^/]+?(?<port>\d+)?/", RegexOptions.Compiled);
/// <summary> /// <summary>
/// The find free TCP port. /// Finds a free TCP port.
/// </summary> /// </summary>
/// <returns>
/// The <see cref="int"/>.
/// </returns>
/// <remarks>see http://stackoverflow.com/questions/138043/find-the-next-tcp-port-in-net.</remarks> /// <remarks>see http://stackoverflow.com/questions/138043/find-the-next-tcp-port-in-net.</remarks>
public static int FindFreeTcpPort() public static int FindFreeTcpPort()
{ {
@@ -40,7 +37,7 @@ namespace WireMock.Http
public static bool TryExtractProtocolAndPort(string url, out string proto, out int port) public static bool TryExtractProtocolAndPort(string url, out string proto, out int port)
{ {
proto = null; proto = null;
port = -1; port = 0;
Match m = UrlDetailsRegex.Match(url); Match m = UrlDetailsRegex.Match(url);
if (m.Success) if (m.Success)

View File

@@ -1,9 +1,9 @@
using System; using System;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
namespace WireMock.Http namespace WireMock.HttpsCertificate
{ {
internal static class CertificateUtil internal static class ClientCertificateHelper
{ {
public static X509Certificate2 GetCertificate(string thumbprintOrSubjectName) public static X509Certificate2 GetCertificate(string thumbprintOrSubjectName)
{ {

View File

@@ -0,0 +1,91 @@
using System;
using System.Security.Cryptography.X509Certificates;
namespace WireMock.HttpsCertificate
{
internal static class PublicCertificateHelper
{
// 1] Generate using https://www.pluralsight.com/blog/software-development/selfcert-create-a-self-signed-certificate-interactively-gui-or-programmatically-in-net
// 2] Converted to Base64
private const string Data = @"MIIQMgIBAzCCD+4GCSqGSIb3DQEHAaCCD98Egg/bMIIP1zCCCogGCSqGSIb3DQEHAaCCCnkEggp1
MIIKcTCCCm0GCyqGSIb3DQEMCgECoIIJfjCCCXowHAYKKoZIhvcNAQwBAzAOBAi1j9x1jTfUewIC
B9AEgglYa48lP16+isiGEVT7zwN3XwaPwPOHZcQ7tRA/DA8LZnZbwU7XhtPObF5bZcHn4engX2An
ISFpe2S5XJ7BfHmsGOO7Bxj6C2IcZIPTefvAd9vWE0WUAGN11SLhJ3fB/ZRt3Nys7JCJzywQCkYK
dCA35V7WfETCLT6+ArtRU4qsjop2YXyUzcLw3OuumBAoRsazgUKz8rkZJbifkSikbdxs+Hupcf2I
NOOuKStKoqouqCO/vmRi8u8g0KQhf2LcQBSqLk6OZ8TQuv07W5tVO2Ky5qCYu6aXBBlHhGSY9fGL
vqaYcxMcVJQpXUUL6nSWCoaLdaAAB+Anw1Tpbd47W7ieTK5Yq2IROPQIr1mk8nvFbxoTcBuIQ6oU
RiiLX+mb3hYgbTL3LqDmmm9FFI8enJu6pUxP8iKKROtCqhYXhF1i3EwReBzJzpDGZ+y4rJxb0Es4
sPVc/TaVPSJCTmgcKzwps7M12uxm8G9Dv3lKgZVmgDRivovCJFxHdCdgCYB08FvNWuFtXO+schsE
N0nY2i07A2joaJC18yvoNGZ+ySBTBPOBN+5XbiQs0vsQ4MfLETb2O2dFjwE/tErgo6RWYg2qQNAe
DSh2wHzI0YJM3PqaUR0Q9KnjEWc92hsLI34KnuNkNkVk4NEjPOetxeIBcYN7CDD6tTxp42sU++bT
o9zyjy1BPS+LuEblxDTSlVPb0dxvkwNBXBi0RXIOWfD15BWcV1Uv972jB6To1XPDIOc+eq7fa5yn
HRW/GYdGPmOYKietdw6V3t2Et9cPlw4v08IelucF06Ju2a73QtidtkA89vxjn7qOEVACAXpsiMsM
4JCcAzF7jh0U2mskSB14I9HcZh1Sei0J2ZULcXNyuIw9nsWp8vrH04OOoUDe7/UpX7c8+A+tqDUy
1W2V1dJDhlwu2SXL5jJFBK4P9p2e+XyHJ+AcYXrHQIKxqoCgvnywT4HnI2bvZ1+lmIR99mp1Cvzj
RUgJaqhI7u6WH3i6fmkA92hF8MP5WDYTGwjsHPrCg6Xkqykuvub4osu+gLq5t+JC9rOczgrRNYXg
54FMzlCyQxTfgY6IRwPH/xYHKGbViFF+jA4ksLMRjI2XOV4swbI232SxMoQQDNsjx1la2nZM3P5z
g7zpmaiyzY44q3kU1viMMR7qM/w1S6nTW0ZkzTXk8Gttor/0JWT1K8KgwK02B6Q9zNwfK60a0cQm
SbA/dXkWapuIzQLz+ZUPyG/1EP5KuKNnsp0hfVi8TTgOFceoV9kyIhrTQNI0o5O91dqkyWd/bMl2
OnrnRnhka6f839zJKUpWPTfRX9RMJTk/5HVcL/qsHYcJedZyPawJjMU+cxW1ZZjr1Lo6M0fjKuTI
Askg0ZKS0FJ60jTKnc1DmldQLtieHh1UTdty+yn1A0rGnrFEyN7if2/d1EduWJaf6bvWzfH+d/36
1gDwp4OXi0qWu7c5zByGZi2j2sFo4v0cGgjPZUJvF5z0V8OkE15DiA4xtTSkIjCnEmOhODViwrfc
ONvHB+inBW9wLp26qFgNcYrYxP2FC54pPCxO+KJaAJrtfE3A4lkboB7V0xFu23ecOy4n2gho39tg
Bxkt1Xj3GCLgeKvcYLzOPytZldXDTtoCcsm5uhCmBHEmPUsnLc7Tt1cFflFtWTOjtyv0Lk8Qu2BU
B/hSSgjZRpqE+hzjtghjXdFOT5wKULvtUz5eu+lH7kjGghQbF962vLcRCsr+tMPba6MFqhy5Q+dM
NcHc+HIx2WiuRZ6jdCZSUpH3f1+kH6XYj2P3/F0kLBRAMPGKbXpIi9Px4AVDEDClL77mDpVgeEoh
kkVh5zvk2PsEPonmTFK0kQE8Q4cYFWTKa9lAE4Wc35EzvpKFdTwQKhr5kN7tEq17n5wJt/499164
ho0+LjzYy62JI/fv3RPISL0gXr2INLW7fgZ5KNjcnu/AITJu3ycw/XH8BKsx4dcbBgBdrKY8afEn
IZ1EIv/TuNvmifmAEGX/DWuVmZIOU6pTystzTQwfz0wko3lUKjkPM40RLN9o6lddV6fM3QNtK0ac
hqOOmG68LzI1U33nvUBol/FeEV0DLvGjvsIRC/TCtDu5Vk2tKS13p9kNj4owJK8d343PZ/eyi/Oe
sNZCCJJuj4iIodekm1hzj7zc6ZLNudgab/WkF7TbWDOhDPwG1gE/McffGNWPFlwsaopoZaH8E8tl
QSOnHqAiNa7B3ifxMrGDWHDlxkWddClbKd9ujL4mgB88Wo8JceLawDOcSVGImvWGxsrK2RX7FK57
GQYuc0zcq3NH4jpoOyS+vpMeigTPDOvQdqjGgEUW3aXdA8Ma0KYVEyAhK/rFXw8FelGREM2ku1kN
kziOTAe52SWqcrv6NUPfo1/Uc6u7KpwQrcDlFZWxPCKomeRQTm6AmP2QHMgl2gaUbmuBF/C9Ccl2
1Iqh8vUdDT3lHdf5I5SjRPJJXX2/0/oUFmwIC5AFp8/XXnIG05BER1X2Y+SL15QHW7lYsvd4ZKTv
tvYaZNWajgAVZl8gJYFUQG+U+cFYcpHhf6SGzcuwcmQJGxptZAVnRtDzNjJ04vJB616/uoI2Qkp2
ysGAjDNlwgeckmU7TSYoYaML29pRupTTQqKItyFDuebUpSKBTxsEFIJBCTErA8TD8I9T5nzT+rTy
+Lpp9mqcxQR1RhxgTx5bE7D4igdblobX0IONARg4EIAk8xj1Ba3k4skdjAQcJOHKd+xVo+vsrIqg
a+ycemROE5F3D3s21ozMOn1Dy8iIeQusJQTSkP82+wHYWXRg59N0cq/40CaJskK+yp7afOWFoCqY
T/D9OGlfeIIiLivwMh8naPZyM4+pd/CFZwcWoGtIno04nWWR/xQVe17jqPMov1xonC2E3AcKxUXl
PMdZoOARkR+KHZnoBZ/vjqxeDPARKupijkw5QQI3jNmd5IELjcL8OraHlo+e884mRsa/66J7p94D
bidzjiVLUhCsZnVks9eZF6PIEg49eq/+w8ph3X7XBNnZYAbgola/0yy/PlPzgJ3p/AgXMoGr+HXO
p2WAs1WRo9mAdeOBMrAMvXKFD04bZPNHke0Ri03O5U7NRRs1T0LnqdZHyF39As8FiktfJl1bn/U0
sUjhc4fDNnDaBN8VF8VsEa7UolRC6NqQ1oHaeEZRoq0li6NbXIXdxIIT4bbqwajqFkvvO6qc6SEG
iAlUzTGB2zATBgkqhkiG9w0BCRUxBgQEAQAAADBXBgkqhkiG9w0BCRQxSh5IADcAOAA1ADAAZABj
ADAAYwAtAGIAZQBmAGIALQA0AGEANQBiAC0AOAA5ADcAMAAtAGIAZAA5ADkAOQAxADEAOQA2AGIA
YgA1MGsGCSsGAQQBgjcRATFeHlwATQBpAGMAcgBvAHMAbwBmAHQAIABFAG4AaABhAG4AYwBlAGQA
IABDAHIAeQBwAHQAbwBnAHIAYQBwAGgAaQBjACAAUAByAG8AdgBpAGQAZQByACAAdgAxAC4AMDCC
BUcGCSqGSIb3DQEHBqCCBTgwggU0AgEAMIIFLQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQMwDgQI
Amt1zAZpKWkCAgfQgIIFAJoBLiXPEZGuUfsyM2ed66sBIirBctHbhyydZsQDT8V2crq6AI3P0dBT
Agf/MouK/JcAfdGEEpU6SKxqZBDZoTRbyK7VdW5YiKGUurFGf66L9K3c4MHhVLMWnSkwK+0gwiIB
RB82Or4ru0cSQbF32vsuJgJY9Ax4YODKokPFUzZjPrmch4AgZWKxslDFDq4xs3tpLIeZbALWYdrR
PUZaReO+NaLfNKwTZsinzjPkCst7R1Jfjf1abikPrOMPgYFUiQL7GNQFbefeJu9SCXlj09u/qw+l
uLrwEyMamG0cgjrWbMOohGtvAuHqeZIIWuLnGE2quq7Ah/U55lliZn3IFgs1n6FalW/XGH0T0aRA
im6f1EgxtJShBNdG6qNrkzYMyY2Xvpk82qtmvMbhJMDstpovsdT1rZ6OGfsyJAlG94BcCWC2wuvW
MYs6H7AirBG/iocNm7JwSBQ7wjl0keH9vuHDQsI+uu9yWGuyZK6MmxDEOEMZRndU0GrlLIp7zQx8
gPeagKqSHhPK0ghHhDilMyToE3Euvi5jneh90eofWK6E4E8KtQTWvFeCe9fYRhMaflH9lwfHfXPo
9J+7GZLRF/MDTlE8jzWoP5csUxV3jnXQkTUOfHvO7QK6POKwLGkCyZ5wKFydyYdTekG+KU2Vml2y
s6pZ+kME1VMYiHRF3CaXX1ZYKikEdnuB1Qp8BE2uKQTaYO4Wns0vQIVRWTrk/Gp5RXu1ihzoTiFb
YqLFROQTM+dVTUk3C6W82OroNwofW5ErZwpbdgJJ83gbPLr9W4KZ6YegvEWFT1MawnIYDC6RSKtg
fI1blSZOo1SIF6+nR0gFPivSEGBJHclIg7TtvGSB1q7TQFS4W5l+AHdgGPqqURnjuJ7/DVFZHqEV
jJM1QEvH/RY1rDXP8FZC6CZR/Le7tVm5o8bHnlU4gyStvNyLXHCP6o3gHmaBcfYmqJr/0ZEKnwnd
MBmdIgwQ3+JnKvjAeDe8Z/l0U03CbzeyN3TgUolDlUGVDF2FdPZqrVsLKw6VJv3hoFZmgEbb1fDq
Jblc82lrYIEW8E7ltRLDyNDmRdyaqkqw0e7SygXXxv6SKerxoJ5E1NyJdHeQXSahRrsj5UZpxlTE
ylb3upIF0F006w2D5J7xUDktPetVgM97dN3whbOrzWPPkn5CTZMaUdYGTYQK3/K/jQSKuXcaM04A
EWmcQOQ/5Quco/aMYWTTwFI9+bKZayZKHqQjNKuwss+iWTW2b26cA2XAr2XWCL9PEybVRK/5n4km
5qkGIMjjczZxNy1+H1QoEOwkGVzQd390ktQajJBFcf9wBO+Ar14EgPKvdL4DRvCvXOK3CtrgbAq6
GK7uULRrz9t/5lu27ba5wcwxg6bFhgCsJsoSCJnLCR7H+QMB4LhnWA10U4hFWCamCKFoGkiXW6yV
OF4x+0D0MmrjrrAGi05KzfrsXNtRG0xbkmvrsjqzmsOKyjvtiBCrR0S6NUtKhyxoiz5bCCm+d8rm
jaPk9q01k52pjJAKYW0f+5+r15LamBecnjXtJ07LCl6cMA1Cj4L0mQUSefyFi666GC3TmhzHwhnj
SV64nTApS0gBsc6c18fUBsMcUj5nCNclIzfxwnARd/30yg22r09nUY2gtQTwk/W6VCpAH+7yZkH1
TLNGa+UmMnPsnBjlAJ6l9VPsa4uJM2DIQKtZXWq4DkhSAYKF6joIP7nKMDswHzAHBgUrDgMCGgQU
wTM1Z+CJZG9xAcf1zAVGl4ggYyYEFGBFyJ8VBwijS2zy1qwN1WYGtcWoAgIH0A==
";
public static X509Certificate2 GetX509Certificate2()
{
byte[] data = Convert.FromBase64String(Data);
return new X509Certificate2(data);
}
}
}

View File

@@ -20,6 +20,11 @@ namespace WireMock
/// </summary> /// </summary>
public string Title { get; } public string Title { get; }
/// <summary>
/// The full filename path for this mapping (only defined for static mappings).
/// </summary>
public string Path { get; set; }
/// <summary> /// <summary>
/// Gets the priority. /// Gets the priority.
/// </summary> /// </summary>
@@ -63,17 +68,19 @@ namespace WireMock
/// Initializes a new instance of the <see cref="Mapping"/> class. /// Initializes a new instance of the <see cref="Mapping"/> class.
/// </summary> /// </summary>
/// <param name="guid">The unique identifier.</param> /// <param name="guid">The unique identifier.</param>
/// <param name="title">The unique title (can be null_.</param> /// <param name="title">The unique title (can be null).</param>
/// <param name="path">The full file path from this mapping title (can be null).</param>
/// <param name="requestMatcher">The request matcher.</param> /// <param name="requestMatcher">The request matcher.</param>
/// <param name="provider">The provider.</param> /// <param name="provider">The provider.</param>
/// <param name="priority">The priority for this mapping.</param> /// <param name="priority">The priority for this mapping.</param>
/// <param name="scenario">The scenario. [Optional]</param> /// <param name="scenario">The scenario. [Optional]</param>
/// <param name="executionConditionState">State in which the current mapping can occur. [Optional]</param> /// <param name="executionConditionState">State in which the current mapping can occur. [Optional]</param>
/// <param name="nextState">The next state which will occur after the current mapping execution. [Optional]</param> /// <param name="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) public Mapping(Guid guid, [CanBeNull] string title, [CanBeNull] string path, IRequestMatcher requestMatcher, IResponseProvider provider, int priority, [CanBeNull] string scenario, [CanBeNull] object executionConditionState, [CanBeNull] object nextState)
{ {
Guid = guid; Guid = guid;
Title = title; Title = title;
Path = path;
RequestMatcher = requestMatcher; RequestMatcher = requestMatcher;
Provider = provider; Provider = provider;
Priority = priority; Priority = priority;

View File

@@ -7,8 +7,8 @@ namespace WireMock.Matchers
/// <summary> /// <summary>
/// ExactMatcher /// ExactMatcher
/// </summary> /// </summary>
/// <seealso cref="IMatcher" /> /// <seealso cref="IStringMatcher" />
public class ExactMatcher : IMatcher public class ExactMatcher : IStringMatcher
{ {
private readonly string[] _values; private readonly string[] _values;
@@ -23,29 +23,19 @@ namespace WireMock.Matchers
_values = values; _values = values;
} }
/// <summary> /// <inheritdoc cref="IStringMatcher.IsMatch"/>
/// Determines whether the specified input is match.
/// </summary>
/// <param name="input">The input.</param>
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
public double IsMatch(string input) public double IsMatch(string input)
{ {
return MatchScores.ToScore(_values.Select(value => value.Equals(input))); return MatchScores.ToScore(_values.Select(value => value.Equals(input)));
} }
/// <summary> /// <inheritdoc cref="IStringMatcher.GetPatterns"/>
/// Gets the value.
/// </summary>
/// <returns>Patterns</returns>
public string[] GetPatterns() public string[] GetPatterns()
{ {
return _values; return _values;
} }
/// <summary> /// <inheritdoc cref="IMatcher.GetName"/>
/// Gets the name.
/// </summary>
/// <returns>Name</returns>
public string GetName() public string GetName()
{ {
return "ExactMatcher"; return "ExactMatcher";

View File

@@ -0,0 +1,46 @@
using System.Linq;
using JetBrains.Annotations;
namespace WireMock.Matchers
{
/// <summary>
/// ExactMatcher
/// </summary>
/// <seealso cref="IObjectMatcher" />
public class ExactObjectMatcher : IObjectMatcher
{
private readonly object _object;
private readonly byte[] _bytes;
/// <summary>
/// Initializes a new instance of the <see cref="ExactMatcher"/> class.
/// </summary>
/// <param name="value">The value.</param>
public ExactObjectMatcher([NotNull] object value)
{
_object = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="ExactMatcher"/> class.
/// </summary>
/// <param name="value">The value.</param>
public ExactObjectMatcher([NotNull] byte[] value)
{
_bytes = value;
}
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
public double IsMatch(object input)
{
bool equals = _object != null ? Equals(_object, input) : _bytes.SequenceEqual((byte[])input);
return MatchScores.ToScore(equals);
}
/// <inheritdoc cref="IMatcher.GetName"/>
public string GetName()
{
return "ExactObjectMatcher";
}
}
}

View File

@@ -5,19 +5,6 @@
/// </summary> /// </summary>
public interface IMatcher public interface IMatcher
{ {
/// <summary>
/// Determines whether the specified input is match.
/// </summary>
/// <param name="input">The input.</param>
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
double IsMatch(string input);
/// <summary>
/// Gets the patterns.
/// </summary>
/// <returns>Patterns</returns>
string[] GetPatterns();
/// <summary> /// <summary>
/// Gets the name. /// Gets the name.
/// </summary> /// </summary>

View File

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

View File

@@ -0,0 +1,21 @@
namespace WireMock.Matchers
{
/// <summary>
/// IStringMatcher
/// </summary>
public interface IStringMatcher : IMatcher
{
/// <summary>
/// Determines whether the specified input is match.
/// </summary>
/// <param name="input">The input.</param>
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
double IsMatch(string input);
/// <summary>
/// Gets the patterns.
/// </summary>
/// <returns>Patterns</returns>
string[] GetPatterns();
}
}

View File

@@ -10,8 +10,9 @@ namespace WireMock.Matchers
/// JSONPathMatcher /// JSONPathMatcher
/// </summary> /// </summary>
/// <seealso cref="IMatcher" /> /// <seealso cref="IMatcher" />
public class JsonPathMatcher : IMatcher public class JsonPathMatcher : IStringMatcher, IObjectMatcher
{ {
// private readonly object _jsonPattern;
private readonly string[] _patterns; private readonly string[] _patterns;
/// <summary> /// <summary>
@@ -25,15 +26,20 @@ namespace WireMock.Matchers
_patterns = patterns; _patterns = patterns;
} }
/// <summary> //public JsonPathMatcher([NotNull] object jsonPattern)
/// Determines whether the specified input is match. //{
/// </summary> // Check.NotNull(jsonPattern, nameof(jsonPattern));
/// <param name="input">The input string</param>
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns> // _jsonPattern = jsonPattern;
//}
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
public double IsMatch(string input) public double IsMatch(string input)
{ {
if (input == null) if (input == null)
{
return MatchScores.Mismatch; return MatchScores.Mismatch;
}
try try
{ {
@@ -47,19 +53,33 @@ namespace WireMock.Matchers
} }
} }
/// <summary> /// <inheritdoc cref="IObjectMatcher.IsMatch"/>
/// Gets the patterns. public double IsMatch(object input)
/// </summary> {
/// <returns>Pattern</returns> if (input == null)
{
return MatchScores.Mismatch;
}
try
{
var o = input as JObject ?? JObject.FromObject(input);
return MatchScores.ToScore(_patterns.Select(p => o.SelectToken(p) != null));
}
catch (Exception)
{
return MatchScores.Mismatch;
}
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public string[] GetPatterns() public string[] GetPatterns()
{ {
return _patterns; return _patterns;
} }
/// <summary> /// <inheritdoc cref="IMatcher.GetName"/>
/// Gets the name.
/// </summary>
/// <returns>Name</returns>
public string GetName() public string GetName()
{ {
return "JsonPathMatcher"; return "JsonPathMatcher";

View File

@@ -9,8 +9,8 @@ namespace WireMock.Matchers
/// <summary> /// <summary>
/// Regular Expression Matcher /// Regular Expression Matcher
/// </summary> /// </summary>
/// <seealso cref="IMatcher" /> /// <seealso cref="IStringMatcher" />
public class RegexMatcher : IMatcher public class RegexMatcher : IStringMatcher
{ {
private readonly string[] _patterns; private readonly string[] _patterns;
private readonly Regex[] _expressions; private readonly Regex[] _expressions;
@@ -37,20 +37,20 @@ namespace WireMock.Matchers
RegexOptions options = RegexOptions.Compiled; RegexOptions options = RegexOptions.Compiled;
if (ignoreCase) if (ignoreCase)
{
options |= RegexOptions.IgnoreCase; options |= RegexOptions.IgnoreCase;
}
_expressions = patterns.Select(p => new Regex(p, options)).ToArray(); _expressions = patterns.Select(p => new Regex(p, options)).ToArray();
} }
/// <summary> /// <inheritdoc cref="IStringMatcher.IsMatch"/>
/// Determines whether the specified input is match.
/// </summary>
/// <param name="input">The input string</param>
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
public double IsMatch(string input) public double IsMatch(string input)
{ {
if (input == null) if (input == null)
{
return MatchScores.Mismatch; return MatchScores.Mismatch;
}
try try
{ {
@@ -62,21 +62,13 @@ namespace WireMock.Matchers
} }
} }
/// <summary> /// <inheritdoc cref="IStringMatcher.GetPatterns"/>
/// Gets the patterns.
/// </summary>
/// <returns>Pattern</returns>
public virtual string[] GetPatterns() public virtual string[] GetPatterns()
{ {
return _patterns; return _patterns;
} }
/// <summary> /// <inheritdoc cref="IMatcher.GetName"/>
/// Gets the name.
/// </summary>
/// <returns>
/// Name
/// </returns>
public virtual string GetName() public virtual string GetName()
{ {
return "RegexMatcher"; return "RegexMatcher";

View File

@@ -9,21 +9,21 @@ namespace WireMock.Matchers.Request
/// </summary> /// </summary>
public class RequestMessageBodyMatcher : IRequestMatcher public class RequestMessageBodyMatcher : IRequestMatcher
{ {
/// <summary>
/// The body as byte[].
/// </summary>
private readonly byte[] _bodyData;
/// <summary> /// <summary>
/// The body function /// The body function
/// </summary> /// </summary>
public Func<string, bool> Func { get; } public Func<string, bool> Func { get; }
/// <summary> /// <summary>
/// The body data function /// The body data function for byte[]
/// </summary> /// </summary>
public Func<byte[], bool> DataFunc { get; } public Func<byte[], bool> DataFunc { get; }
/// <summary>
/// The body data function for json
/// </summary>
public Func<object, bool> JsonFunc { get; }
/// <summary> /// <summary>
/// The matcher. /// The matcher.
/// </summary> /// </summary>
@@ -32,9 +32,7 @@ namespace WireMock.Matchers.Request
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="body"> /// <param name="body">The body.</param>
/// The body Regex pattern.
/// </param>
public RequestMessageBodyMatcher([NotNull] string body) : this(new SimMetricsMatcher(body)) public RequestMessageBodyMatcher([NotNull] string body) : this(new SimMetricsMatcher(body))
{ {
} }
@@ -42,21 +40,23 @@ namespace WireMock.Matchers.Request
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="body"> /// <param name="body">The body.</param>
/// The body Regex pattern. public RequestMessageBodyMatcher([NotNull] byte[] body) : this(new ExactObjectMatcher(body))
/// </param>
public RequestMessageBodyMatcher([NotNull] byte[] body)
{ {
Check.NotNull(body, nameof(body));
_bodyData = body;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="func"> /// <param name="body">The body.</param>
/// The body func. public RequestMessageBodyMatcher([NotNull] object body) : this(new ExactObjectMatcher(body))
/// </param> {
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary>
/// <param name="func">The function.</param>
public RequestMessageBodyMatcher([NotNull] Func<string, bool> func) public RequestMessageBodyMatcher([NotNull] Func<string, bool> func)
{ {
Check.NotNull(func, nameof(func)); Check.NotNull(func, nameof(func));
@@ -66,9 +66,7 @@ namespace WireMock.Matchers.Request
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="func"> /// <param name="func">The function.</param>
/// The body func.
/// </param>
public RequestMessageBodyMatcher([NotNull] Func<byte[], bool> func) public RequestMessageBodyMatcher([NotNull] Func<byte[], bool> func)
{ {
Check.NotNull(func, nameof(func)); Check.NotNull(func, nameof(func));
@@ -78,23 +76,24 @@ namespace WireMock.Matchers.Request
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matcher"> /// <param name="func">The function.</param>
/// The body matcher. public RequestMessageBodyMatcher([NotNull] Func<object, bool> func)
/// </param> {
Check.NotNull(func, nameof(func));
JsonFunc = func;
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary>
/// <param name="matcher">The matcher.</param>
public RequestMessageBodyMatcher([NotNull] IMatcher matcher) public RequestMessageBodyMatcher([NotNull] IMatcher matcher)
{ {
Check.NotNull(matcher, nameof(matcher)); Check.NotNull(matcher, nameof(matcher));
Matcher = matcher; Matcher = matcher;
} }
/// <summary> /// <see cref="IRequestMatcher.GetMatchingScore"/>
/// 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) public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
{ {
double score = IsMatch(requestMessage); double score = IsMatch(requestMessage);
@@ -103,17 +102,43 @@ namespace WireMock.Matchers.Request
private double IsMatch(RequestMessage requestMessage) private double IsMatch(RequestMessage requestMessage)
{ {
if (Matcher != null) if (requestMessage.Body != null)
return Matcher.IsMatch(requestMessage.Body); {
var stringMatcher = Matcher as IStringMatcher;
if (stringMatcher != null)
{
return stringMatcher.IsMatch(requestMessage.Body);
}
}
if (_bodyData != null) var objectMatcher = Matcher as IObjectMatcher;
return MatchScores.ToScore(requestMessage.BodyAsBytes == _bodyData); if (objectMatcher != null)
{
if (requestMessage.BodyAsJson != null)
{
return objectMatcher.IsMatch(requestMessage.BodyAsJson);
}
if (requestMessage.BodyAsBytes != null)
{
return objectMatcher.IsMatch(requestMessage.BodyAsBytes);
}
}
if (Func != null) if (Func != null)
{
return MatchScores.ToScore(requestMessage.Body != null && Func(requestMessage.Body)); return MatchScores.ToScore(requestMessage.Body != null && Func(requestMessage.Body));
}
if (DataFunc != null && requestMessage.BodyAsBytes != null) if (DataFunc != null)
{
return MatchScores.ToScore(requestMessage.BodyAsBytes != null && DataFunc(requestMessage.BodyAsBytes)); return MatchScores.ToScore(requestMessage.BodyAsBytes != null && DataFunc(requestMessage.BodyAsBytes));
}
if (JsonFunc != null)
{
return MatchScores.ToScore(requestMessage.BodyAsJson != null && JsonFunc(requestMessage.BodyAsJson));
}
return MatchScores.Mismatch; return MatchScores.Mismatch;
} }

View File

@@ -14,7 +14,7 @@ namespace WireMock.Matchers.Request
/// <summary> /// <summary>
/// The matchers. /// The matchers.
/// </summary> /// </summary>
public IReadOnlyList<IMatcher> Matchers { get; } public IReadOnlyList<IStringMatcher> Matchers { get; }
/// <summary> /// <summary>
/// The ClientIP functions. /// The ClientIP functions.
@@ -25,7 +25,7 @@ namespace WireMock.Matchers.Request
/// Initializes a new instance of the <see cref="RequestMessageClientIPMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageClientIPMatcher"/> class.
/// </summary> /// </summary>
/// <param name="clientIPs">The clientIPs.</param> /// <param name="clientIPs">The clientIPs.</param>
public RequestMessageClientIPMatcher([NotNull] params string[] clientIPs) : this(clientIPs.Select(ip => new WildcardMatcher(ip)).Cast<IMatcher>().ToArray()) public RequestMessageClientIPMatcher([NotNull] params string[] clientIPs) : this(clientIPs.Select(ip => new WildcardMatcher(ip)).Cast<IStringMatcher>().ToArray())
{ {
} }
@@ -33,7 +33,7 @@ namespace WireMock.Matchers.Request
/// Initializes a new instance of the <see cref="RequestMessageClientIPMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageClientIPMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
public RequestMessageClientIPMatcher([NotNull] params IMatcher[] matchers) public RequestMessageClientIPMatcher([NotNull] params IStringMatcher[] matchers)
{ {
Check.NotNull(matchers, nameof(matchers)); Check.NotNull(matchers, nameof(matchers));
Matchers = matchers; Matchers = matchers;
@@ -59,10 +59,14 @@ namespace WireMock.Matchers.Request
private double IsMatch(RequestMessage requestMessage) private double IsMatch(RequestMessage requestMessage)
{ {
if (Matchers != null) if (Matchers != null)
{
return Matchers.Max(matcher => matcher.IsMatch(requestMessage.ClientIP)); return Matchers.Max(matcher => matcher.IsMatch(requestMessage.ClientIP));
}
if (Funcs != null) if (Funcs != null)
{
return MatchScores.ToScore(requestMessage.ClientIP != null && Funcs.Any(func => func(requestMessage.ClientIP))); return MatchScores.ToScore(requestMessage.ClientIP != null && Funcs.Any(func => func(requestMessage.ClientIP)));
}
return MatchScores.Mismatch; return MatchScores.Mismatch;
} }

View File

@@ -24,7 +24,7 @@ namespace WireMock.Matchers.Request
/// <value> /// <value>
/// The matchers. /// The matchers.
/// </value> /// </value>
public IMatcher[] Matchers { get; } public IStringMatcher[] Matchers { get; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
@@ -38,7 +38,7 @@ namespace WireMock.Matchers.Request
Check.NotNull(pattern, nameof(pattern)); Check.NotNull(pattern, nameof(pattern));
Name = name; Name = name;
Matchers = new IMatcher[] { new WildcardMatcher(pattern, ignoreCase) }; Matchers = new IStringMatcher[] { new WildcardMatcher(pattern, ignoreCase) };
} }
/// <summary> /// <summary>
@@ -46,7 +46,7 @@ namespace WireMock.Matchers.Request
/// </summary> /// </summary>
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
public RequestMessageCookieMatcher([NotNull] string name, [NotNull] params IMatcher[] matchers) public RequestMessageCookieMatcher([NotNull] string name, [NotNull] params IStringMatcher[] matchers)
{ {
Check.NotNull(name, nameof(name)); Check.NotNull(name, nameof(name));
Check.NotNull(matchers, nameof(matchers)); Check.NotNull(matchers, nameof(matchers));
@@ -83,16 +83,24 @@ namespace WireMock.Matchers.Request
private double IsMatch(RequestMessage requestMessage) private double IsMatch(RequestMessage requestMessage)
{ {
if (requestMessage.Cookies == null) if (requestMessage.Cookies == null)
{
return MatchScores.Mismatch; return MatchScores.Mismatch;
}
if (Funcs != null) if (Funcs != null)
{
return MatchScores.ToScore(Funcs.Any(f => f(requestMessage.Cookies))); return MatchScores.ToScore(Funcs.Any(f => f(requestMessage.Cookies)));
}
if (Matchers == null) if (Matchers == null)
{
return MatchScores.Mismatch; return MatchScores.Mismatch;
}
if (!requestMessage.Cookies.ContainsKey(Name)) if (!requestMessage.Cookies.ContainsKey(Name))
{
return MatchScores.Mismatch; return MatchScores.Mismatch;
}
string value = requestMessage.Cookies[Name]; string value = requestMessage.Cookies[Name];
return Matchers.Max(m => m.IsMatch(value)); return Matchers.Max(m => m.IsMatch(value));

View File

@@ -26,7 +26,7 @@ namespace WireMock.Matchers.Request
/// <value> /// <value>
/// The matchers. /// The matchers.
/// </value> /// </value>
public IMatcher[] Matchers { get; } public IStringMatcher[] Matchers { get; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> class.
@@ -40,7 +40,7 @@ namespace WireMock.Matchers.Request
Check.NotNull(pattern, nameof(pattern)); Check.NotNull(pattern, nameof(pattern));
Name = name; Name = name;
Matchers = new IMatcher[] { new WildcardMatcher(pattern, ignoreCase) }; Matchers = new IStringMatcher[] { new WildcardMatcher(pattern, ignoreCase) };
} }
/// <summary> /// <summary>
@@ -55,7 +55,7 @@ namespace WireMock.Matchers.Request
Check.NotNull(patterns, nameof(patterns)); Check.NotNull(patterns, nameof(patterns));
Name = name; Name = name;
Matchers = patterns.Select(pattern => new WildcardMatcher(pattern, ignoreCase)).Cast<IMatcher>().ToArray(); Matchers = patterns.Select(pattern => new WildcardMatcher(pattern, ignoreCase)).Cast<IStringMatcher>().ToArray();
} }
/// <summary> /// <summary>
@@ -63,7 +63,7 @@ namespace WireMock.Matchers.Request
/// </summary> /// </summary>
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
public RequestMessageHeaderMatcher([NotNull] string name, [NotNull] params IMatcher[] matchers) public RequestMessageHeaderMatcher([NotNull] string name, [NotNull] params IStringMatcher[] matchers)
{ {
Check.NotNull(name, nameof(name)); Check.NotNull(name, nameof(name));
Check.NotNull(matchers, nameof(matchers)); Check.NotNull(matchers, nameof(matchers));
@@ -93,16 +93,24 @@ namespace WireMock.Matchers.Request
private double IsMatch(RequestMessage requestMessage) private double IsMatch(RequestMessage requestMessage)
{ {
if (requestMessage.Headers == null) if (requestMessage.Headers == null)
{
return MatchScores.Mismatch; return MatchScores.Mismatch;
}
if (Funcs != null) if (Funcs != null)
{
return MatchScores.ToScore(Funcs.Any(f => f(requestMessage.Headers.ToDictionary(entry => entry.Key, entry => entry.Value.ToArray())))); return MatchScores.ToScore(Funcs.Any(f => f(requestMessage.Headers.ToDictionary(entry => entry.Key, entry => entry.Value.ToArray()))));
}
if (Matchers == null) if (Matchers == null)
{
return MatchScores.Mismatch; return MatchScores.Mismatch;
}
if (!requestMessage.Headers.ContainsKey(Name)) if (!requestMessage.Headers.ContainsKey(Name))
{
return MatchScores.Mismatch; return MatchScores.Mismatch;
}
WireMockList<string> list = requestMessage.Headers[Name]; WireMockList<string> list = requestMessage.Headers[Name];
return Matchers.Max(m => list.Max(value => m.IsMatch(value))); // TODO : is this correct ? return Matchers.Max(m => list.Max(value => m.IsMatch(value))); // TODO : is this correct ?

View File

@@ -14,7 +14,7 @@ namespace WireMock.Matchers.Request
/// <summary> /// <summary>
/// The matcher. /// The matcher.
/// </summary> /// </summary>
public IReadOnlyList<IMatcher> Matchers { get; } public IReadOnlyList<IStringMatcher> Matchers { get; }
/// <summary> /// <summary>
/// The path functions /// The path functions
@@ -25,7 +25,7 @@ namespace WireMock.Matchers.Request
/// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class.
/// </summary> /// </summary>
/// <param name="paths">The paths.</param> /// <param name="paths">The paths.</param>
public RequestMessagePathMatcher([NotNull] params string[] paths) : this(paths.Select(path => new WildcardMatcher(path)).Cast<IMatcher>().ToArray()) public RequestMessagePathMatcher([NotNull] params string[] paths) : this(paths.Select(path => new WildcardMatcher(path)).Cast<IStringMatcher>().ToArray())
{ {
} }
@@ -33,7 +33,7 @@ namespace WireMock.Matchers.Request
/// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
public RequestMessagePathMatcher([NotNull] params IMatcher[] matchers) public RequestMessagePathMatcher([NotNull] params IStringMatcher[] matchers)
{ {
Check.NotNull(matchers, nameof(matchers)); Check.NotNull(matchers, nameof(matchers));
Matchers = matchers; Matchers = matchers;
@@ -59,10 +59,14 @@ namespace WireMock.Matchers.Request
private double IsMatch(RequestMessage requestMessage) private double IsMatch(RequestMessage requestMessage)
{ {
if (Matchers != null) if (Matchers != null)
{
return Matchers.Max(m => m.IsMatch(requestMessage.Path)); return Matchers.Max(m => m.IsMatch(requestMessage.Path));
}
if (Funcs != null) if (Funcs != null)
{
return MatchScores.ToScore(requestMessage.Path != null && Funcs.Any(func => func(requestMessage.Path))); return MatchScores.ToScore(requestMessage.Path != null && Funcs.Any(func => func(requestMessage.Path)));
}
return MatchScores.Mismatch; return MatchScores.Mismatch;
} }

View File

@@ -14,7 +14,7 @@ namespace WireMock.Matchers.Request
/// <summary> /// <summary>
/// The matchers. /// The matchers.
/// </summary> /// </summary>
public IReadOnlyList<IMatcher> Matchers { get; } public IReadOnlyList<IStringMatcher> Matchers { get; }
/// <summary> /// <summary>
/// The url functions. /// The url functions.
@@ -25,7 +25,7 @@ namespace WireMock.Matchers.Request
/// Initializes a new instance of the <see cref="RequestMessageUrlMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageUrlMatcher"/> class.
/// </summary> /// </summary>
/// <param name="urls">The urls.</param> /// <param name="urls">The urls.</param>
public RequestMessageUrlMatcher([NotNull] params string[] urls) : this(urls.Select(url => new WildcardMatcher(url)).Cast<IMatcher>().ToArray()) public RequestMessageUrlMatcher([NotNull] params string[] urls) : this(urls.Select(url => new WildcardMatcher(url)).Cast<IStringMatcher>().ToArray())
{ {
} }
@@ -33,7 +33,7 @@ namespace WireMock.Matchers.Request
/// Initializes a new instance of the <see cref="RequestMessageUrlMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageUrlMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
public RequestMessageUrlMatcher([NotNull] params IMatcher[] matchers) public RequestMessageUrlMatcher([NotNull] params IStringMatcher[] matchers)
{ {
Check.NotNull(matchers, nameof(matchers)); Check.NotNull(matchers, nameof(matchers));
Matchers = matchers; Matchers = matchers;
@@ -59,10 +59,14 @@ namespace WireMock.Matchers.Request
private double IsMatch(RequestMessage requestMessage) private double IsMatch(RequestMessage requestMessage)
{ {
if (Matchers != null) if (Matchers != null)
{
return Matchers.Max(matcher => matcher.IsMatch(requestMessage.Url)); return Matchers.Max(matcher => matcher.IsMatch(requestMessage.Url));
}
if (Funcs != null) if (Funcs != null)
{
return MatchScores.ToScore(requestMessage.Url != null && Funcs.Any(func => func(requestMessage.Url))); return MatchScores.ToScore(requestMessage.Url != null && Funcs.Any(func => func(requestMessage.Url)));
}
return MatchScores.Mismatch; return MatchScores.Mismatch;
} }

View File

@@ -10,8 +10,8 @@ namespace WireMock.Matchers
/// <summary> /// <summary>
/// SimMetricsMatcher /// SimMetricsMatcher
/// </summary> /// </summary>
/// <seealso cref="IMatcher" /> /// <seealso cref="IStringMatcher" />
public class SimMetricsMatcher : IMatcher public class SimMetricsMatcher : IStringMatcher
{ {
private readonly string[] _patterns; private readonly string[] _patterns;
private readonly SimMetricType _simMetricType; private readonly SimMetricType _simMetricType;
@@ -32,17 +32,13 @@ namespace WireMock.Matchers
/// <param name="simMetricType">The SimMetric Type</param> /// <param name="simMetricType">The SimMetric Type</param>
public SimMetricsMatcher([NotNull] string[] patterns, SimMetricType simMetricType = SimMetricType.Levenstein) public SimMetricsMatcher([NotNull] string[] patterns, SimMetricType simMetricType = SimMetricType.Levenstein)
{ {
Check.NotEmpty(patterns, nameof(patterns)); Check.NotNullOrEmpty(patterns, nameof(patterns));
_patterns = patterns; _patterns = patterns;
_simMetricType = simMetricType; _simMetricType = simMetricType;
} }
/// <summary> /// <inheritdoc cref="IStringMatcher.IsMatch"/>
/// Determines whether the specified input is match.
/// </summary>
/// <param name="input">The input string</param>
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
public double IsMatch(string input) public double IsMatch(string input)
{ {
IStringMetric m = GetStringMetricType(); IStringMetric m = GetStringMetricType();
@@ -93,19 +89,13 @@ namespace WireMock.Matchers
} }
} }
/// <summary> /// <inheritdoc cref="IStringMatcher.GetPatterns"/>
/// Gets the pattern.
/// </summary>
/// <returns>Pattern</returns>
public string[] GetPatterns() public string[] GetPatterns()
{ {
return _patterns; return _patterns;
} }
/// <summary> /// <inheritdoc cref="IMatcher.GetName"/>
/// Gets the name.
/// </summary>
/// <returns>Name</returns>
public string GetName() public string GetName()
{ {
return $"SimMetricsMatcher.{_simMetricType}"; return $"SimMetricsMatcher.{_simMetricType}";

View File

@@ -7,7 +7,7 @@ namespace WireMock.Matchers
/// <summary> /// <summary>
/// WildcardMatcher /// WildcardMatcher
/// </summary> /// </summary>
/// <seealso cref="IMatcher" /> /// <seealso cref="RegexMatcher" />
public class WildcardMatcher : RegexMatcher public class WildcardMatcher : RegexMatcher
{ {
private readonly string[] _patterns; private readonly string[] _patterns;
@@ -31,21 +31,13 @@ namespace WireMock.Matchers
_patterns = patterns; _patterns = patterns;
} }
/// <summary> /// <inheritdoc cref="IStringMatcher.GetPatterns"/>
/// Gets the pattern.
/// </summary>
/// <returns>Pattern</returns>
public override string[] GetPatterns() public override string[] GetPatterns()
{ {
return _patterns; return _patterns;
} }
/// <summary> /// <inheritdoc cref="IMatcher.GetName"/>
/// Gets the name.
/// </summary>
/// <returns>
/// Name
/// </returns>
public override string GetName() public override string GetName()
{ {
return "WildcardMatcher"; return "WildcardMatcher";

View File

@@ -12,8 +12,8 @@ namespace WireMock.Matchers
/// <summary> /// <summary>
/// XPath2Matcher /// XPath2Matcher
/// </summary> /// </summary>
/// <seealso cref="WireMock.Matchers.IMatcher" /> /// <seealso cref="IStringMatcher" />
public class XPathMatcher : IMatcher public class XPathMatcher : IStringMatcher
{ {
private readonly string[] _patterns; private readonly string[] _patterns;
@@ -28,15 +28,13 @@ namespace WireMock.Matchers
_patterns = patterns; _patterns = patterns;
} }
/// <summary> /// <inheritdoc cref="IStringMatcher.IsMatch"/>
/// Determines whether the specified input is match.
/// </summary>
/// <param name="input">The input string</param>
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
public double IsMatch(string input) public double IsMatch(string input)
{ {
if (input == null) if (input == null)
{
return MatchScores.Mismatch; return MatchScores.Mismatch;
}
try try
{ {
@@ -53,19 +51,13 @@ namespace WireMock.Matchers
} }
} }
/// <summary> /// <inheritdoc cref="IStringMatcher.GetPatterns"/>
/// Gets the patterns.
/// </summary>
/// <returns>Patterns</returns>
public string[] GetPatterns() public string[] GetPatterns()
{ {
return _patterns; return _patterns;
} }
/// <summary> /// <inheritdoc cref="IMatcher.GetName"/>
/// Gets the name.
/// </summary>
/// <returns>Name</returns>
public string GetName() public string GetName()
{ {
return "XPathMatcher"; return "XPathMatcher";

View File

@@ -1,11 +1,15 @@
#if NETSTANDARD #if NETSTANDARD
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using WireMock.Http; using WireMock.Http;
using WireMock.HttpsCertificate;
using WireMock.Validation; using WireMock.Validation;
namespace WireMock.Owin namespace WireMock.Owin
@@ -14,7 +18,7 @@ namespace WireMock.Owin
{ {
private readonly CancellationTokenSource _cts = new CancellationTokenSource(); private readonly CancellationTokenSource _cts = new CancellationTokenSource();
private readonly WireMockMiddlewareOptions _options; private readonly WireMockMiddlewareOptions _options;
private readonly string[] _uriPrefixes; private readonly string[] _urls;
private IWebHost _host; private IWebHost _host;
@@ -27,7 +31,7 @@ namespace WireMock.Owin
public AspNetCoreSelfHost([NotNull] WireMockMiddlewareOptions options, [NotNull] params string[] uriPrefixes) public AspNetCoreSelfHost([NotNull] WireMockMiddlewareOptions options, [NotNull] params string[] uriPrefixes)
{ {
Check.NotNull(options, nameof(options)); Check.NotNull(options, nameof(options));
Check.NotEmpty(uriPrefixes, nameof(uriPrefixes)); Check.NotNullOrEmpty(uriPrefixes, nameof(uriPrefixes));
foreach (string uriPrefix in uriPrefixes) foreach (string uriPrefix in uriPrefixes)
{ {
@@ -38,7 +42,7 @@ namespace WireMock.Owin
} }
_options = options; _options = options;
_uriPrefixes = uriPrefixes; _urls = uriPrefixes;
} }
public Task StartAsync() public Task StartAsync()
@@ -47,16 +51,45 @@ namespace WireMock.Owin
.Configure(appBuilder => .Configure(appBuilder =>
{ {
appBuilder.UseMiddleware<GlobalExceptionMiddleware>(); appBuilder.UseMiddleware<GlobalExceptionMiddleware>();
_options.PreWireMockMiddlewareInit?.Invoke(appBuilder); _options.PreWireMockMiddlewareInit?.Invoke(appBuilder);
appBuilder.UseMiddleware<WireMockMiddleware>(_options); appBuilder.UseMiddleware<WireMockMiddleware>(_options);
_options.PostWireMockMiddlewareInit?.Invoke(appBuilder); _options.PostWireMockMiddlewareInit?.Invoke(appBuilder);
}) })
.UseKestrel() .UseKestrel(options =>
.UseUrls(_uriPrefixes) {
#if NETSTANDARD1_3
if (_urls.Any(u => u.StartsWith("https://", StringComparison.OrdinalIgnoreCase)))
{
options.UseHttps(PublicCertificateHelper.GetX509Certificate2());
}
#else
// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?tabs=aspnetcore2x
foreach (string url in _urls.Where(u => u.StartsWith("http://", StringComparison.OrdinalIgnoreCase)))
{
PortUtil.TryExtractProtocolAndPort(url, out string host, out int port);
options.Listen(IPAddress.Loopback, port);
}
foreach (string url in _urls.Where(u => u.StartsWith("https://", StringComparison.OrdinalIgnoreCase)))
{
PortUtil.TryExtractProtocolAndPort(url, out string host, out int port);
options.Listen(IPAddress.Loopback, port, listenOptions =>
{
listenOptions.UseHttps(PublicCertificateHelper.GetX509Certificate2());
});
}
#endif
})
#if NETSTANDARD1_3
.UseUrls(_urls)
#endif
.Build(); .Build();
#if NETSTANDARD1_3 #if NETSTANDARD1_3
System.Console.WriteLine("WireMock.Net server using netstandard1.3"); Console.WriteLine("WireMock.Net server using netstandard1.3");
return Task.Run(() => return Task.Run(() =>
{ {
_host.Run(_cts.Token); _host.Run(_cts.Token);
@@ -65,7 +98,11 @@ namespace WireMock.Owin
#else #else
System.Console.WriteLine("WireMock.Net server using netstandard2.0"); System.Console.WriteLine("WireMock.Net server using netstandard2.0");
IsStarted = true; IsStarted = true;
return _host.RunAsync(_cts.Token); return Task.Run(() =>
{
_host.Run();
IsStarted = true;
}, _cts.Token);
#endif #endif
} }

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using log4net;
using Newtonsoft.Json; using Newtonsoft.Json;
#if !NETSTANDARD #if !NETSTANDARD
using Microsoft.Owin; using Microsoft.Owin;
@@ -15,6 +16,7 @@ namespace WireMock.Owin
internal class GlobalExceptionMiddleware internal class GlobalExceptionMiddleware
#endif #endif
{ {
private static readonly ILog Log = LogManager.GetLogger(typeof(GlobalExceptionMiddleware));
#if !NETSTANDARD #if !NETSTANDARD
public GlobalExceptionMiddleware(OwinMiddleware next) : base(next) { } public GlobalExceptionMiddleware(OwinMiddleware next) : base(next) { }
#else #else
@@ -42,6 +44,7 @@ namespace WireMock.Owin
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error("HttpStatusCode set to 500", ex);
await _responseMapper.MapAsync(new ResponseMessage { StatusCode = 500, Body = JsonConvert.SerializeObject(ex) }, ctx.Response); await _responseMapper.MapAsync(new ResponseMessage { StatusCode = 500, Body = JsonConvert.SerializeObject(ex) }, ctx.Response);
} }
} }

View File

@@ -4,6 +4,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using WireMock.Util;
#if !NETSTANDARD #if !NETSTANDARD
using Microsoft.Owin; using Microsoft.Owin;
#else #else
@@ -16,7 +17,7 @@ namespace WireMock.Owin
/// <summary> /// <summary>
/// OwinRequestMapper /// OwinRequestMapper
/// </summary> /// </summary>
public class OwinRequestMapper internal class OwinRequestMapper
{ {
/// <summary> /// <summary>
/// MapAsync IOwinRequest to RequestMessage /// MapAsync IOwinRequest to RequestMessage
@@ -43,20 +44,6 @@ namespace WireMock.Owin
#endif #endif
string method = request.Method; string method = request.Method;
string bodyAsString = null;
byte[] body = null;
Encoding bodyEncoding = null;
if (ParseBody(method) && request.Body != null)
{
using (var streamReader = new StreamReader(request.Body))
{
bodyAsString = await streamReader.ReadToEndAsync();
bodyEncoding = streamReader.CurrentEncoding;
}
body = bodyEncoding.GetBytes(bodyAsString);
}
Dictionary<string, string[]> headers = null; Dictionary<string, string[]> headers = null;
if (request.Headers.Any()) if (request.Headers.Any())
{ {
@@ -77,10 +64,16 @@ namespace WireMock.Owin
} }
} }
return new RequestMessage(url, method, clientIP, body, bodyAsString, bodyEncoding, headers, cookies) { DateTime = DateTime.Now }; BodyData body = null;
if (request.Body != null && ShouldParseBody(method))
{
body = await BodyParser.Parse(request.Body, request.ContentType);
}
return new RequestMessage(url, method, clientIP, body, headers, cookies) { DateTime = DateTime.Now };
} }
private bool ParseBody(string method) private bool ShouldParseBody(string method)
{ {
/* /*
HEAD - No defined body semantics. HEAD - No defined body semantics.

View File

@@ -4,6 +4,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json;
using WireMock.Util; using WireMock.Util;
#if !NETSTANDARD #if !NETSTANDARD
using Microsoft.Owin; using Microsoft.Owin;
@@ -65,7 +66,7 @@ namespace WireMock.Owin
} }
} }
if (responseMessage.Body == null && responseMessage.BodyAsBytes == null && responseMessage.BodyAsFile == null) if (responseMessage.Body == null && responseMessage.BodyAsBytes == null && responseMessage.BodyAsFile == null && responseMessage.BodyAsJson == null)
{ {
return; return;
} }
@@ -84,11 +85,20 @@ namespace WireMock.Owin
return; return;
} }
Encoding encoding = responseMessage.BodyEncoding ?? _utf8NoBom; if (responseMessage.BodyAsJson != null)
using (var writer = new StreamWriter(response.Body, encoding)) {
string jsonBody = JsonConvert.SerializeObject(responseMessage.BodyAsJson, new JsonSerializerSettings { Formatting = Formatting.None, NullValueHandling = NullValueHandling.Ignore });
using (var writer = new StreamWriter(response.Body, responseMessage.BodyEncoding ?? _utf8NoBom))
{
await writer.WriteAsync(jsonBody);
}
return;
}
using (var writer = new StreamWriter(response.Body, responseMessage.BodyEncoding ?? _utf8NoBom))
{ {
await writer.WriteAsync(responseMessage.Body); await writer.WriteAsync(responseMessage.Body);
// TODO : response.ContentLength = responseMessage.Body.Length;
} }
} }
} }

View File

@@ -19,7 +19,7 @@ namespace WireMock.Owin
public OwinSelfHost([NotNull] WireMockMiddlewareOptions options, [NotNull] params string[] uriPrefixes) public OwinSelfHost([NotNull] WireMockMiddlewareOptions options, [NotNull] params string[] uriPrefixes)
{ {
Check.NotNull(options, nameof(options)); Check.NotNull(options, nameof(options));
Check.NotEmpty(uriPrefixes, nameof(uriPrefixes)); Check.NotNullOrEmpty(uriPrefixes, nameof(uriPrefixes));
foreach (string uriPrefix in uriPrefixes) foreach (string uriPrefix in uriPrefixes)
{ {

View File

@@ -3,9 +3,11 @@ using System.Threading.Tasks;
using WireMock.Logging; using WireMock.Logging;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using System.Linq; using System.Linq;
using log4net;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Util; using WireMock.Util;
using Newtonsoft.Json; using Newtonsoft.Json;
using WireMock.Http;
#if !NETSTANDARD #if !NETSTANDARD
using Microsoft.Owin; using Microsoft.Owin;
#else #else
@@ -20,6 +22,7 @@ namespace WireMock.Owin
internal class WireMockMiddleware internal class WireMockMiddleware
#endif #endif
{ {
private static readonly ILog Log = LogManager.GetLogger(typeof(WireMockMiddleware));
private static readonly Task CompletedTask = Task.FromResult(false); private static readonly Task CompletedTask = Task.FromResult(false);
private readonly WireMockMiddlewareOptions _options; private readonly WireMockMiddlewareOptions _options;
@@ -52,7 +55,7 @@ namespace WireMock.Owin
RequestMatchResult requestMatchResult = null; RequestMatchResult requestMatchResult = null;
try try
{ {
foreach (var mapping in _options.Mappings.Where(m => m?.Scenario != null)) foreach (var mapping in _options.Mappings.Values.Where(m => m?.Scenario != null))
{ {
// Set start // Set start
if (!_options.Scenarios.ContainsKey(mapping.Scenario) && mapping.IsStartState) if (!_options.Scenarios.ContainsKey(mapping.Scenario) && mapping.IsStartState)
@@ -61,7 +64,7 @@ namespace WireMock.Owin
} }
} }
var mappings = _options.Mappings var mappings = _options.Mappings.Values
.Select(m => new .Select(m => new
{ {
Mapping = m, Mapping = m,
@@ -95,6 +98,7 @@ namespace WireMock.Owin
if (targetMapping == null) if (targetMapping == null)
{ {
logRequest = true; logRequest = true;
Log.Warn("HttpStatusCode set to 404 : No matching mapping found");
response = new ResponseMessage { StatusCode = 404, Body = "No matching mapping found" }; response = new ResponseMessage { StatusCode = 404, Body = "No matching mapping found" };
return; return;
} }
@@ -103,9 +107,10 @@ namespace WireMock.Owin
if (targetMapping.IsAdminInterface && _options.AuthorizationMatcher != null) if (targetMapping.IsAdminInterface && _options.AuthorizationMatcher != null)
{ {
bool present = request.Headers.TryGetValue("Authorization", out WireMockList<string> authorization); bool present = request.Headers.TryGetValue(HttpKnownHeaderNames.Authorization, out WireMockList<string> authorization);
if (!present || _options.AuthorizationMatcher.IsMatch(authorization.ToString()) < MatchScores.Perfect) if (!present || _options.AuthorizationMatcher.IsMatch(authorization.ToString()) < MatchScores.Perfect)
{ {
Log.Error("HttpStatusCode set to 401");
response = new ResponseMessage { StatusCode = 401 }; response = new ResponseMessage { StatusCode = 401 };
return; return;
} }
@@ -125,6 +130,7 @@ namespace WireMock.Owin
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Error("HttpStatusCode set to 500", ex);
response = new ResponseMessage { StatusCode = 500, Body = JsonConvert.SerializeObject(ex) }; response = new ResponseMessage { StatusCode = 500, Body = JsonConvert.SerializeObject(ex) };
} }
finally finally

View File

@@ -17,11 +17,11 @@ namespace WireMock.Owin
{ {
public TimeSpan? RequestProcessingDelay { get; set; } public TimeSpan? RequestProcessingDelay { get; set; }
public IMatcher AuthorizationMatcher { get; set; } public IStringMatcher AuthorizationMatcher { get; set; }
public bool AllowPartialMapping { get; set; } public bool AllowPartialMapping { get; set; }
public IList<Mapping> Mappings { get; set; } = new List<Mapping>(); public IDictionary<Guid, Mapping> Mappings { get; set; } = new ConcurrentDictionary<Guid, Mapping>();
public ObservableCollection<LogEntry> LogEntries { get; } = new ConcurentObservableCollection<LogEntry>(); public ObservableCollection<LogEntry> LogEntries { get; } = new ConcurentObservableCollection<LogEntry>();

View File

@@ -10,38 +10,52 @@ namespace WireMock.RequestBuilders
public interface IBodyRequestBuilder public interface IBodyRequestBuilder
{ {
/// <summary> /// <summary>
/// The with body. /// WithBody: IMatcher
/// </summary> /// </summary>
/// <param name="matcher">The matcher.</param> /// <param name="matcher">The matcher.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithBody([NotNull] IMatcher matcher); IRequestBuilder WithBody([NotNull] IMatcher matcher);
/// <summary> /// <summary>
/// The with body. /// WithBody: Body as string
/// </summary> /// </summary>
/// <param name="body">The body.</param> /// <param name="body">The body.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithBody(string body); IRequestBuilder WithBody(string body);
/// <summary> /// <summary>
/// The with body byte[]. /// WithBody: Body as byte[]
/// </summary> /// </summary>
/// <param name="body">The body as byte[].</param> /// <param name="body">The body.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithBody(byte[] body); IRequestBuilder WithBody(byte[] body);
/// <summary> /// <summary>
/// The with body string func. /// WithBody: Body as object
/// </summary> /// </summary>
/// <param name="body">The body string function.</param> /// <param name="body">The body.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithBody([NotNull] Func<string, bool> body); IRequestBuilder WithBody(object body);
/// <summary> /// <summary>
/// The with body byte[] func. ///WithBody: func (string)
/// </summary> /// </summary>
/// <param name="body">The body byte[] function.</param> /// <param name="func">The function.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithBody([NotNull] Func<byte[], bool> body); IRequestBuilder WithBody([NotNull] Func<string, bool> func);
/// <summary>
///WithBody: func (byte[])
/// </summary>
/// <param name="func">The function.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithBody([NotNull] Func<byte[], bool> func);
/// <summary>
///WithBody: func (object)
/// </summary>
/// <param name="func">The function.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithBody([NotNull] Func<object, bool> func);
} }
} }

View File

@@ -14,7 +14,7 @@ namespace WireMock.RequestBuilders
/// </summary> /// </summary>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithClientIP([NotNull] params IMatcher[] matchers); IRequestBuilder WithClientIP([NotNull] params IStringMatcher[] matchers);
/// <summary> /// <summary>
/// The with ClientIP. /// The with ClientIP.

View File

@@ -35,7 +35,7 @@ namespace WireMock.RequestBuilders
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithHeader([NotNull] string name, [NotNull] params IMatcher[] matchers); IRequestBuilder WithHeader([NotNull] string name, [NotNull] params IStringMatcher[] matchers);
/// <summary> /// <summary>
/// The with header. /// The with header.
@@ -59,7 +59,7 @@ namespace WireMock.RequestBuilders
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithCookie([NotNull] string name, [NotNull] params IMatcher[] matchers); IRequestBuilder WithCookie([NotNull] string name, [NotNull] params IStringMatcher[] matchers);
/// <summary> /// <summary>
/// The with cookie. /// The with cookie.

View File

@@ -14,7 +14,7 @@ namespace WireMock.RequestBuilders
/// </summary> /// </summary>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithPath([NotNull] params IMatcher[] matchers); IRequestBuilder WithPath([NotNull] params IStringMatcher[] matchers);
/// <summary> /// <summary>
/// The with path. /// The with path.
@@ -35,7 +35,7 @@ namespace WireMock.RequestBuilders
/// </summary> /// </summary>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithUrl([NotNull] params IMatcher[] matchers); IRequestBuilder WithUrl([NotNull] params IStringMatcher[] matchers);
/// <summary> /// <summary>
/// The with url. /// The with url.

View File

@@ -59,9 +59,9 @@ namespace WireMock.RequestBuilders
/// </summary> /// </summary>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithClientIP(params IMatcher[] matchers) public IRequestBuilder WithClientIP(params IStringMatcher[] matchers)
{ {
Check.NotEmpty(matchers, nameof(matchers)); Check.NotNullOrEmpty(matchers, nameof(matchers));
_requestMatchers.Add(new RequestMessageClientIPMatcher(matchers)); _requestMatchers.Add(new RequestMessageClientIPMatcher(matchers));
return this; return this;
@@ -74,7 +74,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithClientIP(params string[] clientIPs) public IRequestBuilder WithClientIP(params string[] clientIPs)
{ {
Check.NotEmpty(clientIPs, nameof(clientIPs)); Check.NotNullOrEmpty(clientIPs, nameof(clientIPs));
_requestMatchers.Add(new RequestMessageClientIPMatcher(clientIPs)); _requestMatchers.Add(new RequestMessageClientIPMatcher(clientIPs));
return this; return this;
@@ -87,7 +87,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithClientIP(params Func<string, bool>[] funcs) public IRequestBuilder WithClientIP(params Func<string, bool>[] funcs)
{ {
Check.NotEmpty(funcs, nameof(funcs)); Check.NotNullOrEmpty(funcs, nameof(funcs));
_requestMatchers.Add(new RequestMessageClientIPMatcher(funcs)); _requestMatchers.Add(new RequestMessageClientIPMatcher(funcs));
return this; return this;
@@ -98,9 +98,9 @@ namespace WireMock.RequestBuilders
/// </summary> /// </summary>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithPath(params IMatcher[] matchers) public IRequestBuilder WithPath(params IStringMatcher[] matchers)
{ {
Check.NotEmpty(matchers, nameof(matchers)); Check.NotNullOrEmpty(matchers, nameof(matchers));
_requestMatchers.Add(new RequestMessagePathMatcher(matchers)); _requestMatchers.Add(new RequestMessagePathMatcher(matchers));
return this; return this;
@@ -113,7 +113,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithPath(params string[] paths) public IRequestBuilder WithPath(params string[] paths)
{ {
Check.NotEmpty(paths, nameof(paths)); Check.NotNullOrEmpty(paths, nameof(paths));
_requestMatchers.Add(new RequestMessagePathMatcher(paths)); _requestMatchers.Add(new RequestMessagePathMatcher(paths));
return this; return this;
@@ -126,7 +126,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithPath(params Func<string, bool>[] funcs) public IRequestBuilder WithPath(params Func<string, bool>[] funcs)
{ {
Check.NotEmpty(funcs, nameof(funcs)); Check.NotNullOrEmpty(funcs, nameof(funcs));
_requestMatchers.Add(new RequestMessagePathMatcher(funcs)); _requestMatchers.Add(new RequestMessagePathMatcher(funcs));
return this; return this;
@@ -137,9 +137,9 @@ namespace WireMock.RequestBuilders
/// </summary> /// </summary>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithUrl(params IMatcher[] matchers) public IRequestBuilder WithUrl(params IStringMatcher[] matchers)
{ {
Check.NotEmpty(matchers, nameof(matchers)); Check.NotNullOrEmpty(matchers, nameof(matchers));
_requestMatchers.Add(new RequestMessageUrlMatcher(matchers)); _requestMatchers.Add(new RequestMessageUrlMatcher(matchers));
return this; return this;
@@ -152,7 +152,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithUrl(params string[] urls) public IRequestBuilder WithUrl(params string[] urls)
{ {
Check.NotEmpty(urls, nameof(urls)); Check.NotNullOrEmpty(urls, nameof(urls));
_requestMatchers.Add(new RequestMessageUrlMatcher(urls)); _requestMatchers.Add(new RequestMessageUrlMatcher(urls));
return this; return this;
@@ -165,7 +165,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithUrl(params Func<string, bool>[] funcs) public IRequestBuilder WithUrl(params Func<string, bool>[] funcs)
{ {
Check.NotEmpty(funcs, nameof(funcs)); Check.NotNullOrEmpty(funcs, nameof(funcs));
_requestMatchers.Add(new RequestMessageUrlMatcher(funcs)); _requestMatchers.Add(new RequestMessageUrlMatcher(funcs));
return this; return this;
@@ -228,73 +228,34 @@ namespace WireMock.RequestBuilders
/// <inheritdoc cref="IMethodRequestBuilder.UsingVerb"/> /// <inheritdoc cref="IMethodRequestBuilder.UsingVerb"/>
public IRequestBuilder UsingVerb(params string[] verbs) public IRequestBuilder UsingVerb(params string[] verbs)
{ {
Check.NotEmpty(verbs, nameof(verbs)); Check.NotNullOrEmpty(verbs, nameof(verbs));
_requestMatchers.Add(new RequestMessageMethodMatcher(verbs)); _requestMatchers.Add(new RequestMessageMethodMatcher(verbs));
return this; return this;
} }
/// <summary> /// <inheritdoc cref="IBodyRequestBuilder.WithBody(string)"/>
/// The with body.
/// </summary>
/// <param name="body">
/// The body.
/// </param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithBody(string body) public IRequestBuilder WithBody(string body)
{ {
_requestMatchers.Add(new RequestMessageBodyMatcher(body)); _requestMatchers.Add(new RequestMessageBodyMatcher(body));
return this; return this;
} }
/// <summary> /// <inheritdoc cref="IBodyRequestBuilder.WithBody(byte[])"/>
/// The with body byte[].
/// </summary>
/// <param name="body">
/// The body as byte[].
/// </param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithBody(byte[] body) public IRequestBuilder WithBody(byte[] body)
{ {
_requestMatchers.Add(new RequestMessageBodyMatcher(body)); _requestMatchers.Add(new RequestMessageBodyMatcher(body));
return this; return this;
} }
/// <summary> /// <inheritdoc cref="IBodyRequestBuilder.WithBody(object)"/>
/// The with body. public IRequestBuilder WithBody(object body)
/// </summary>
/// <param name="func">
/// The body function.
/// </param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithBody(Func<string, bool> func)
{ {
Check.NotNull(func, nameof(func)); _requestMatchers.Add(new RequestMessageBodyMatcher(body));
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
return this; return this;
} }
/// <summary> /// <inheritdoc cref="IBodyRequestBuilder.WithBody(IMatcher)"/>
/// The with body.
/// </summary>
/// <param name="func">
/// The body function.
/// </param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithBody(Func<byte[], bool> func)
{
Check.NotNull(func, nameof(func));
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
return this;
}
/// <summary>
/// The with body.
/// </summary>
/// <param name="matcher">The matcher.</param>
/// <returns>The <see cref="IRequestBuilder" />.</returns>
public IRequestBuilder WithBody(IMatcher matcher) public IRequestBuilder WithBody(IMatcher matcher)
{ {
Check.NotNull(matcher, nameof(matcher)); Check.NotNull(matcher, nameof(matcher));
@@ -303,16 +264,34 @@ namespace WireMock.RequestBuilders
return this; return this;
} }
/// <summary> /// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{string, bool})"/>
/// The with parameters. public IRequestBuilder WithBody(Func<string, bool> func)
/// </summary> {
/// <param name="key"> Check.NotNull(func, nameof(func));
/// The key.
/// </param> _requestMatchers.Add(new RequestMessageBodyMatcher(func));
/// <param name="values"> return this;
/// The values. }
/// </param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{byte[], bool})"/>
public IRequestBuilder WithBody(Func<byte[], bool> func)
{
Check.NotNull(func, nameof(func));
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
return this;
}
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{object, bool})"/>
public IRequestBuilder WithBody(Func<object, bool> func)
{
Check.NotNull(func, nameof(func));
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
return this;
}
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, string[])"/>
public IRequestBuilder WithParam(string key, params string[] values) public IRequestBuilder WithParam(string key, params string[] values)
{ {
Check.NotNull(key, nameof(key)); Check.NotNull(key, nameof(key));
@@ -321,14 +300,10 @@ namespace WireMock.RequestBuilders
return this; return this;
} }
/// <summary> /// <inheritdoc cref="IParamsRequestBuilder.WithParam(Func{IDictionary{string, WireMockList{string}}, bool}[])"/>
/// The with parameters.
/// </summary>
/// <param name="funcs">The funcs.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithParam(params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs) public IRequestBuilder WithParam(params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs)
{ {
Check.NotEmpty(funcs, nameof(funcs)); Check.NotNullOrEmpty(funcs, nameof(funcs));
_requestMatchers.Add(new RequestMessageParamMatcher(funcs)); _requestMatchers.Add(new RequestMessageParamMatcher(funcs));
return this; return this;
@@ -360,10 +335,10 @@ namespace WireMock.RequestBuilders
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithHeader(string name, params IMatcher[] matchers) public IRequestBuilder WithHeader(string name, params IStringMatcher[] matchers)
{ {
Check.NotNull(name, nameof(name)); Check.NotNull(name, nameof(name));
Check.NotEmpty(matchers, nameof(matchers)); Check.NotNullOrEmpty(matchers, nameof(matchers));
_requestMatchers.Add(new RequestMessageHeaderMatcher(name, matchers)); _requestMatchers.Add(new RequestMessageHeaderMatcher(name, matchers));
return this; return this;
@@ -376,7 +351,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithHeader(params Func<IDictionary<string, string[]>, bool>[] funcs) public IRequestBuilder WithHeader(params Func<IDictionary<string, string[]>, bool>[] funcs)
{ {
Check.NotEmpty(funcs, nameof(funcs)); Check.NotNullOrEmpty(funcs, nameof(funcs));
_requestMatchers.Add(new RequestMessageHeaderMatcher(funcs)); _requestMatchers.Add(new RequestMessageHeaderMatcher(funcs));
return this; return this;
@@ -401,9 +376,9 @@ namespace WireMock.RequestBuilders
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithCookie(string name, params IMatcher[] matchers) public IRequestBuilder WithCookie(string name, params IStringMatcher[] matchers)
{ {
Check.NotEmpty(matchers, nameof(matchers)); Check.NotNullOrEmpty(matchers, nameof(matchers));
_requestMatchers.Add(new RequestMessageCookieMatcher(name, matchers)); _requestMatchers.Add(new RequestMessageCookieMatcher(name, matchers));
return this; return this;
@@ -416,7 +391,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithCookie(params Func<IDictionary<string, string>, bool>[] funcs) public IRequestBuilder WithCookie(params Func<IDictionary<string, string>, bool>[] funcs)
{ {
Check.NotEmpty(funcs, nameof(funcs)); Check.NotNullOrEmpty(funcs, nameof(funcs));
_requestMatchers.Add(new RequestMessageCookieMatcher(funcs)); _requestMatchers.Add(new RequestMessageCookieMatcher(funcs));
return this; return this;

View File

@@ -60,15 +60,20 @@ namespace WireMock
public string RawQuery { get; } public string RawQuery { get; }
/// <summary> /// <summary>
/// Gets the bodyAsBytes. /// The body as string.
/// </summary>
public byte[] BodyAsBytes { get; }
/// <summary>
/// Gets the body.
/// </summary> /// </summary>
public string Body { get; } public string Body { get; }
/// <summary>
/// The body (as JSON object).
/// </summary>
public object BodyAsJson { get; set; }
/// <summary>
/// The body (as bytearray).
/// </summary>
public byte[] BodyAsBytes { get; set; }
/// <summary> /// <summary>
/// Gets the Host /// Gets the Host
/// </summary> /// </summary>
@@ -90,10 +95,45 @@ namespace WireMock
public string Origin { get; } public string Origin { get; }
/// <summary> /// <summary>
/// Gets the body encoding. /// The body encoding.
/// </summary> /// </summary>
public Encoding BodyEncoding { get; } public Encoding BodyEncoding { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessage"/> class.
/// </summary>
/// <param name="url">The original url.</param>
/// <param name="method">The HTTP method.</param>
/// <param name="clientIP">The client IP Address.</param>
/// <param name="body">The body.</param>
/// <param name="headers">The headers.</param>
/// <param name="cookies">The cookies.</param>
public RequestMessage([NotNull] Uri url, [NotNull] string method, [NotNull] string clientIP, [CanBeNull] BodyData body, [CanBeNull] IDictionary<string, string[]> headers = null, [CanBeNull] IDictionary<string, string> cookies = null)
{
Check.NotNull(url, nameof(url));
Check.NotNull(method, nameof(method));
Check.NotNull(clientIP, nameof(clientIP));
Url = url.ToString();
Protocol = url.Scheme;
Host = url.Host;
Port = url.Port;
Origin = $"{url.Scheme}://{url.Host}:{url.Port}";
Path = WebUtility.UrlDecode(url.AbsolutePath);
Method = method.ToLower();
ClientIP = clientIP;
Body = body?.BodyAsString;
BodyEncoding = body?.Encoding;
BodyAsJson = body?.BodyAsJson;
BodyAsBytes = body?.BodyAsBytes;
Headers = headers?.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
Cookies = cookies;
RawQuery = WebUtility.UrlDecode(url.Query);
Query = ParseQuery(RawQuery);
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessage"/> class. /// Initializes a new instance of the <see cref="RequestMessage"/> class.
/// </summary> /// </summary>

View File

@@ -1,4 +1,5 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using WireMock.Settings;
namespace WireMock.ResponseBuilders namespace WireMock.ResponseBuilders
{ {
@@ -8,11 +9,18 @@ namespace WireMock.ResponseBuilders
public interface IProxyResponseBuilder : IStatusCodeResponseBuilder public interface IProxyResponseBuilder : IStatusCodeResponseBuilder
{ {
/// <summary> /// <summary>
/// With Proxy URL using X509Certificate2. /// WithProxy URL using Client X509Certificate2.
/// </summary> /// </summary>
/// <param name="proxyUrl">The proxy url.</param> /// <param name="proxyUrl">The proxy url.</param>
/// <param name="clientX509Certificate2ThumbprintOrSubjectName">The X509Certificate2 file to use for client authentication.</param> /// <param name="clientX509Certificate2ThumbprintOrSubjectName">The X509Certificate2 file to use for client authentication.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns> /// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithProxy([NotNull] string proxyUrl, [CanBeNull] string clientX509Certificate2ThumbprintOrSubjectName = null); IResponseBuilder WithProxy([NotNull] string proxyUrl, [CanBeNull] string clientX509Certificate2ThumbprintOrSubjectName = null);
/// <summary>
/// WithProxy using IProxyAndRecordSettings.
/// </summary>
/// <param name="settings">The IProxyAndRecordSettings.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithProxy([NotNull] IProxyAndRecordSettings settings);
} }
} }

View File

@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using Newtonsoft.Json; using Newtonsoft.Json;
using WireMock.Http; using WireMock.Http;
using WireMock.Settings;
using WireMock.Transformers; using WireMock.Transformers;
using WireMock.Util; using WireMock.Util;
using WireMock.Validation; using WireMock.Validation;
@@ -43,7 +44,7 @@ namespace WireMock.ResponseBuilders
/// <summary> /// <summary>
/// The client X509Certificate2 Thumbprint or SubjectName to use. /// The client X509Certificate2 Thumbprint or SubjectName to use.
/// </summary> /// </summary>
public string X509Certificate2ThumbprintOrSubjectName { get; private set; } public string ClientX509Certificate2ThumbprintOrSubjectName { get; private set; }
/// <summary> /// <summary>
/// Gets the response message. /// Gets the response message.
@@ -246,16 +247,9 @@ namespace WireMock.ResponseBuilders
{ {
Check.NotNull(body, nameof(body)); Check.NotNull(body, nameof(body));
string jsonBody = JsonConvert.SerializeObject(body, new JsonSerializerSettings { Formatting = Formatting.None, NullValueHandling = NullValueHandling.Ignore });
if (encoding != null && !encoding.Equals(Encoding.UTF8))
{
jsonBody = encoding.GetString(Encoding.UTF8.GetBytes(jsonBody));
ResponseMessage.BodyEncoding = encoding;
}
ResponseMessage.BodyDestination = null; ResponseMessage.BodyDestination = null;
ResponseMessage.Body = jsonBody; ResponseMessage.BodyAsJson = body;
ResponseMessage.BodyEncoding = encoding;
return this; return this;
} }
@@ -296,22 +290,25 @@ namespace WireMock.ResponseBuilders
return WithDelay(TimeSpan.FromMilliseconds(milliseconds)); return WithDelay(TimeSpan.FromMilliseconds(milliseconds));
} }
/// <summary> /// <inheritdoc cref="IProxyResponseBuilder.WithProxy(string, string)"/>
/// 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) public IResponseBuilder WithProxy(string proxyUrl, string clientX509Certificate2ThumbprintOrSubjectName = null)
{ {
Check.NotEmpty(proxyUrl, nameof(proxyUrl)); Check.NotNullOrEmpty(proxyUrl, nameof(proxyUrl));
ProxyUrl = proxyUrl; ProxyUrl = proxyUrl;
X509Certificate2ThumbprintOrSubjectName = clientX509Certificate2ThumbprintOrSubjectName; ClientX509Certificate2ThumbprintOrSubjectName = clientX509Certificate2ThumbprintOrSubjectName;
_httpClientForProxy = HttpClientHelper.CreateHttpClient(clientX509Certificate2ThumbprintOrSubjectName); _httpClientForProxy = HttpClientHelper.CreateHttpClient(clientX509Certificate2ThumbprintOrSubjectName);
return this; return this;
} }
/// <inheritdoc cref="IProxyResponseBuilder.WithProxy(IProxyAndRecordSettings)"/>
public IResponseBuilder WithProxy(IProxyAndRecordSettings settings)
{
Check.NotNull(settings, nameof(settings));
return WithProxy(settings.Url, settings.ClientX509Certificate2ThumbprintOrSubjectName);
}
/// <summary> /// <summary>
/// The provide response. /// The provide response.
/// </summary> /// </summary>

View File

@@ -37,6 +37,11 @@ namespace WireMock
/// </summary> /// </summary>
public string Body { get; set; } public string Body { get; set; }
/// <summary>
/// Gets or sets the body as a json object.
/// </summary>
public object BodyAsJson { get; set; }
/// <summary> /// <summary>
/// Gets or sets the body as bytes. /// Gets or sets the body as bytes.
/// </summary> /// </summary>
@@ -74,7 +79,7 @@ namespace WireMock
/// <param name="values">The values.</param> /// <param name="values">The values.</param>
public void AddHeader(string name, params string[] values) public void AddHeader(string name, params string[] values)
{ {
Check.NotEmpty(values, nameof(values)); Check.NotNullOrEmpty(values, nameof(values));
var newHeaderValues = Headers.TryGetValue(name, out WireMockList<string> existingValues) var newHeaderValues = Headers.TryGetValue(name, out WireMockList<string> existingValues)
? values.Union(existingValues).ToArray() ? values.Union(existingValues).ToArray()

View File

@@ -97,6 +97,7 @@ namespace WireMock.Serialization
mappingModel.Response.StatusCode = null; mappingModel.Response.StatusCode = null;
mappingModel.Response.Headers = null; mappingModel.Response.Headers = null;
mappingModel.Response.BodyDestination = null; mappingModel.Response.BodyDestination = null;
mappingModel.Response.BodyAsJson = null;
mappingModel.Response.Body = null; mappingModel.Response.Body = null;
mappingModel.Response.BodyAsBytes = null; mappingModel.Response.BodyAsBytes = null;
mappingModel.Response.BodyAsFile = null; mappingModel.Response.BodyAsFile = null;
@@ -110,6 +111,7 @@ namespace WireMock.Serialization
mappingModel.Response.BodyDestination = response.ResponseMessage.BodyDestination; mappingModel.Response.BodyDestination = response.ResponseMessage.BodyDestination;
mappingModel.Response.StatusCode = response.ResponseMessage.StatusCode; mappingModel.Response.StatusCode = response.ResponseMessage.StatusCode;
mappingModel.Response.Headers = Map(response.ResponseMessage.Headers); mappingModel.Response.Headers = Map(response.ResponseMessage.Headers);
mappingModel.Response.BodyAsJson = response.ResponseMessage.BodyAsJson;
mappingModel.Response.Body = response.ResponseMessage.Body; mappingModel.Response.Body = response.ResponseMessage.Body;
mappingModel.Response.BodyAsBytes = response.ResponseMessage.BodyAsBytes; mappingModel.Response.BodyAsBytes = response.ResponseMessage.BodyAsBytes;
mappingModel.Response.BodyAsFile = response.ResponseMessage.BodyAsFile; mappingModel.Response.BodyAsFile = response.ResponseMessage.BodyAsFile;
@@ -150,7 +152,9 @@ namespace WireMock.Serialization
private static MatcherModel[] Map([CanBeNull] IEnumerable<IMatcher> matchers) private static MatcherModel[] Map([CanBeNull] IEnumerable<IMatcher> matchers)
{ {
if (matchers == null || !matchers.Any()) if (matchers == null || !matchers.Any())
{
return null; return null;
}
return matchers.Select(Map).Where(x => x != null).ToArray(); return matchers.Select(Map).Where(x => x != null).ToArray();
} }
@@ -158,9 +162,12 @@ namespace WireMock.Serialization
private static MatcherModel Map([CanBeNull] IMatcher matcher) private static MatcherModel Map([CanBeNull] IMatcher matcher)
{ {
if (matcher == null) if (matcher == null)
{
return null; return null;
}
var patterns = matcher.GetPatterns(); IStringMatcher stringMatcher = matcher as IStringMatcher;
string[] patterns = stringMatcher != null ? stringMatcher.GetPatterns() : new string[0];
return new MatcherModel return new MatcherModel
{ {
@@ -186,7 +193,9 @@ namespace WireMock.Serialization
public static IMatcher Map([CanBeNull] MatcherModel matcher) public static IMatcher Map([CanBeNull] MatcherModel matcher)
{ {
if (matcher == null) if (matcher == null)
{
return null; return null;
}
var parts = matcher.Name.Split('.'); var parts = matcher.Name.Split('.');
string matcherName = parts[0]; string matcherName = parts[0];

View File

@@ -7,6 +7,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using WireMock.Admin.Mappings; using WireMock.Admin.Mappings;
using WireMock.Admin.Requests; using WireMock.Admin.Requests;
using WireMock.Admin.Settings; using WireMock.Admin.Settings;
@@ -29,6 +30,7 @@ namespace WireMock.Server
public partial class FluentMockServer public partial class FluentMockServer
{ {
private static readonly string AdminMappingsFolder = Path.Combine("__admin", "mappings"); private static readonly string AdminMappingsFolder = Path.Combine("__admin", "mappings");
private const string ContentTypeJson = "application/json";
private const string AdminMappings = "/__admin/mappings"; private const string AdminMappings = "/__admin/mappings";
private const string AdminRequests = "/__admin/requests"; private const string AdminRequests = "/__admin/requests";
private const string AdminSettings = "/__admin/settings"; private const string AdminSettings = "/__admin/settings";
@@ -42,56 +44,17 @@ namespace WireMock.Server
NullValueHandling = NullValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
}; };
/// <summary> #region InitAdmin
/// Reads the static mappings from a folder.
/// </summary>
/// <param name="folder">The optional folder. If not defined, use \__admin\mappings\</param>
[PublicAPI]
public void ReadStaticMappings([CanBeNull] string folder = null)
{
if (folder == null)
folder = Path.Combine(Directory.GetCurrentDirectory(), AdminMappingsFolder);
if (!Directory.Exists(folder))
return;
foreach (string filename in Directory.EnumerateFiles(folder).OrderBy(f => f))
{
ReadStaticMapping(filename);
}
}
/// <summary>
/// Reads the static mapping.
/// </summary>
/// <param name="filename">The filename.</param>
[PublicAPI]
public void ReadStaticMapping([NotNull] string filename)
{
Check.NotNull(filename, nameof(filename));
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(filename);
if (Guid.TryParse(filenameWithoutExtension, out var guidFromFilename))
{
DeserializeAndAddMapping(File.ReadAllText(filename), guidFromFilename);
}
else
{
DeserializeAndAddMapping(File.ReadAllText(filename));
}
}
private void InitAdmin() private void InitAdmin()
{ {
// __admin/settings // __admin/settings
Given(Request.Create().WithPath(AdminSettings).UsingGet()).RespondWith(new DynamicResponseProvider(SettingsGet)); Given(Request.Create().WithPath(AdminSettings).UsingGet()).RespondWith(new DynamicResponseProvider(SettingsGet));
Given(Request.Create().WithPath(AdminSettings).UsingVerb("PUT", "POST").WithHeader("Content-Type", "application/json")).RespondWith(new DynamicResponseProvider(SettingsUpdate)); Given(Request.Create().WithPath(AdminSettings).UsingVerb("PUT", "POST").WithHeader(HttpKnownHeaderNames.ContentType, ContentTypeJson)).RespondWith(new DynamicResponseProvider(SettingsUpdate));
// __admin/mappings // __admin/mappings
Given(Request.Create().WithPath(AdminMappings).UsingGet()).RespondWith(new DynamicResponseProvider(MappingsGet)); Given(Request.Create().WithPath(AdminMappings).UsingGet()).RespondWith(new DynamicResponseProvider(MappingsGet));
Given(Request.Create().WithPath(AdminMappings).UsingPost().WithHeader("Content-Type", "application/json")).RespondWith(new DynamicResponseProvider(MappingsPost)); Given(Request.Create().WithPath(AdminMappings).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, ContentTypeJson)).RespondWith(new DynamicResponseProvider(MappingsPost));
Given(Request.Create().WithPath(AdminMappings).UsingDelete()).RespondWith(new DynamicResponseProvider(MappingsDelete)); Given(Request.Create().WithPath(AdminMappings).UsingDelete()).RespondWith(new DynamicResponseProvider(MappingsDelete));
// __admin/mappings/reset // __admin/mappings/reset
@@ -99,7 +62,7 @@ namespace WireMock.Server
// __admin/mappings/{guid} // __admin/mappings/{guid}
Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingGet()).RespondWith(new DynamicResponseProvider(MappingGet)); Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingGet()).RespondWith(new DynamicResponseProvider(MappingGet));
Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingPut().WithHeader("Content-Type", "application/json")).RespondWith(new DynamicResponseProvider(MappingPut)); Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingPut().WithHeader(HttpKnownHeaderNames.ContentType, ContentTypeJson)).RespondWith(new DynamicResponseProvider(MappingPut));
Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingDelete()).RespondWith(new DynamicResponseProvider(MappingDelete)); Given(Request.Create().WithPath(_adminMappingsGuidPathMatcher).UsingDelete()).RespondWith(new DynamicResponseProvider(MappingDelete));
// __admin/mappings/save // __admin/mappings/save
@@ -128,17 +91,114 @@ namespace WireMock.Server
// __admin/scenarios/reset // __admin/scenarios/reset
Given(Request.Create().WithPath(AdminScenarios + "/reset").UsingPost()).RespondWith(new DynamicResponseProvider(ScenariosReset)); Given(Request.Create().WithPath(AdminScenarios + "/reset").UsingPost()).RespondWith(new DynamicResponseProvider(ScenariosReset));
} }
#endregion
#region StaticMappings
/// <summary>
/// Reads the static mappings from a folder.
/// </summary>
/// <param name="folder">The optional folder. If not defined, use \__admin\mappings\</param>
[PublicAPI]
public void ReadStaticMappings([CanBeNull] string folder = null)
{
if (folder == null)
{
folder = Path.Combine(Directory.GetCurrentDirectory(), AdminMappingsFolder);
}
if (!Directory.Exists(folder))
{
return;
}
foreach (string filename in Directory.EnumerateFiles(folder).OrderBy(f => f))
{
Log.InfoFormat("Reading Static MappingFile : '{0}'", filename);
ReadStaticMappingAndAddOrUpdate(filename);
}
}
/// <summary>
/// Watches the static mappings for changes.
/// </summary>
/// <param name="folder">The optional folder. If not defined, use \__admin\mappings\</param>
[PublicAPI]
public void WatchStaticMappings([CanBeNull] string folder = null)
{
if (folder == null)
{
folder = Path.Combine(Directory.GetCurrentDirectory(), AdminMappingsFolder);
}
if (!Directory.Exists(folder))
{
return;
}
Log.InfoFormat("Watching folder '{0}' for new, updated and deleted MappingFiles.", folder);
var watcher = new EnhancedFileSystemWatcher(folder, "*.json", 1000);
watcher.Created += (sender, args) =>
{
Log.InfoFormat("New MappingFile created : '{0}'", args.FullPath);
ReadStaticMappingAndAddOrUpdate(args.FullPath);
};
watcher.Changed += (sender, args) =>
{
Log.InfoFormat("New MappingFile updated : '{0}'", args.FullPath);
ReadStaticMappingAndAddOrUpdate(args.FullPath);
};
watcher.Deleted += (sender, args) =>
{
Log.InfoFormat("New MappingFile deleted : '{0}'", args.FullPath);
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(args.FullPath);
if (Guid.TryParse(filenameWithoutExtension, out Guid guidFromFilename))
{
DeleteMapping(guidFromFilename);
}
else
{
DeleteMapping(args.FullPath);
}
};
watcher.EnableRaisingEvents = true;
}
/// <summary>
/// Reads a static mapping file and adds or updates the mapping.
/// </summary>
/// <param name="path">The path.</param>
[PublicAPI]
public void ReadStaticMappingAndAddOrUpdate([NotNull] string path)
{
Check.NotNull(path, nameof(path));
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(path);
MappingModel mappingModel = JsonConvert.DeserializeObject<MappingModel>(FileHelper.ReadAllText(path));
if (Guid.TryParse(filenameWithoutExtension, out Guid guidFromFilename))
{
DeserializeAndAddOrUpdateMapping(mappingModel, guidFromFilename, path);
}
else
{
DeserializeAndAddOrUpdateMapping(mappingModel, null, path);
}
}
#endregion
#region Proxy and Record #region Proxy and Record
private HttpClient _httpClientForProxy; private HttpClient _httpClientForProxy;
private void InitProxyAndRecord(ProxyAndRecordSettings settings) private void InitProxyAndRecord(IProxyAndRecordSettings settings)
{ {
_httpClientForProxy = HttpClientHelper.CreateHttpClient(settings.X509Certificate2ThumbprintOrSubjectName); _httpClientForProxy = HttpClientHelper.CreateHttpClient(settings.ClientX509Certificate2ThumbprintOrSubjectName);
Given(Request.Create().WithPath("/*").UsingAnyVerb()).RespondWith(new ProxyAsyncResponseProvider(ProxyAndRecordAsync, settings)); Given(Request.Create().WithPath("/*").UsingAnyVerb()).RespondWith(new ProxyAsyncResponseProvider(ProxyAndRecordAsync, settings));
} }
private async Task<ResponseMessage> ProxyAndRecordAsync(RequestMessage requestMessage, ProxyAndRecordSettings settings) private async Task<ResponseMessage> ProxyAndRecordAsync(RequestMessage requestMessage, IProxyAndRecordSettings settings)
{ {
var requestUri = new Uri(requestMessage.Url); var requestUri = new Uri(requestMessage.Url);
var proxyUri = new Uri(settings.Url); var proxyUri = new Uri(settings.Url);
@@ -148,8 +208,8 @@ namespace WireMock.Server
if (settings.SaveMapping) if (settings.SaveMapping)
{ {
var mapping = ToMapping(requestMessage, responseMessage); var mapping = ToMapping(requestMessage, responseMessage, settings.BlackListedHeaders ?? new string[] { });
_options.Mappings.Add(mapping); _options.Mappings.Add(mapping.Guid, mapping);
if (settings.SaveMappingToFile) if (settings.SaveMappingToFile)
{ {
@@ -160,16 +220,23 @@ namespace WireMock.Server
return responseMessage; return responseMessage;
} }
private Mapping ToMapping(RequestMessage requestMessage, ResponseMessage responseMessage) private Mapping ToMapping(RequestMessage requestMessage, ResponseMessage responseMessage, string[] blacklistedHeaders)
{ {
var request = Request.Create(); var request = Request.Create();
request.WithPath(requestMessage.Path); request.WithPath(requestMessage.Path);
request.UsingVerb(requestMessage.Method); request.UsingVerb(requestMessage.Method);
requestMessage.Query.Loop((key, value) => request.WithParam(key, value.ToArray())); requestMessage.Query.Loop((key, value) => request.WithParam(key, value.ToArray()));
requestMessage.Headers.Loop((key, value) => request.WithHeader(key, value.ToArray()));
requestMessage.Cookies.Loop((key, value) => request.WithCookie(key, value)); requestMessage.Cookies.Loop((key, value) => request.WithCookie(key, value));
requestMessage.Headers.Loop((key, value) =>
{
if (!blacklistedHeaders.Any(b => string.Equals(key, b, StringComparison.OrdinalIgnoreCase)))
{
request.WithHeader(key, value.ToArray());
}
});
if (requestMessage.Body != null) if (requestMessage.Body != null)
{ {
request.WithBody(new ExactMatcher(requestMessage.Body)); request.WithBody(new ExactMatcher(requestMessage.Body));
@@ -177,7 +244,7 @@ namespace WireMock.Server
var response = Response.Create(responseMessage); var response = Response.Create(responseMessage);
return new Mapping(Guid.NewGuid(), string.Empty, request, response, 0, null, null, null); return new Mapping(Guid.NewGuid(), string.Empty, null, request, response, 0, null, null, null);
} }
#endregion #endregion
@@ -197,7 +264,7 @@ namespace WireMock.Server
private ResponseMessage SettingsUpdate(RequestMessage requestMessage) private ResponseMessage SettingsUpdate(RequestMessage requestMessage)
{ {
var settings = JsonConvert.DeserializeObject<SettingsModel>(requestMessage.Body); var settings = requestMessage.Body != null ? JsonConvert.DeserializeObject<SettingsModel>(requestMessage.Body) : ((JObject)requestMessage.BodyAsJson).ToObject<SettingsModel>();
if (settings.AllowPartialMapping != null) if (settings.AllowPartialMapping != null)
_options.AllowPartialMapping = settings.AllowPartialMapping.Value; _options.AllowPartialMapping = settings.AllowPartialMapping.Value;
@@ -220,7 +287,10 @@ namespace WireMock.Server
var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid); var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
if (mapping == null) if (mapping == null)
{
Log.Warn("HttpStatusCode set to 404 : Mapping not found");
return new ResponseMessage { StatusCode = 404, Body = "Mapping not found" }; return new ResponseMessage { StatusCode = 404, Body = "Mapping not found" };
}
var model = MappingConverter.ToMappingModel(mapping); var model = MappingConverter.ToMappingModel(mapping);
@@ -230,23 +300,9 @@ namespace WireMock.Server
private ResponseMessage MappingPut(RequestMessage requestMessage) private ResponseMessage MappingPut(RequestMessage requestMessage)
{ {
Guid guid = Guid.Parse(requestMessage.Path.TrimStart(AdminMappings.ToCharArray())); Guid guid = Guid.Parse(requestMessage.Path.TrimStart(AdminMappings.ToCharArray()));
var mappingModel = JsonConvert.DeserializeObject<MappingModel>(requestMessage.Body);
if (mappingModel.Request == null) MappingModel mappingModel = requestMessage.Body != null ? JsonConvert.DeserializeObject<MappingModel>(requestMessage.Body) : ((JObject)requestMessage.BodyAsJson).ToObject<MappingModel>();
return new ResponseMessage { StatusCode = 400, Body = "Request missing" }; DeserializeAndAddOrUpdateMapping(mappingModel, guid);
if (mappingModel.Response == null)
return new ResponseMessage { StatusCode = 400, Body = "Response missing" };
var requestBuilder = InitRequestBuilder(mappingModel.Request);
var responseBuilder = InitResponseBuilder(mappingModel.Response);
IRespondWithAProvider respondProvider = Given(requestBuilder).WithGuid(guid);
if (!string.IsNullOrEmpty(mappingModel.Title))
respondProvider = respondProvider.WithTitle(mappingModel.Title);
respondProvider.RespondWith(responseBuilder);
return new ResponseMessage { Body = "Mapping added or updated" }; return new ResponseMessage { Body = "Mapping added or updated" };
} }
@@ -256,7 +312,9 @@ namespace WireMock.Server
Guid guid = Guid.Parse(requestMessage.Path.Substring(AdminMappings.Length + 1)); Guid guid = Guid.Parse(requestMessage.Path.Substring(AdminMappings.Length + 1));
if (DeleteMapping(guid)) if (DeleteMapping(guid))
{
return new ResponseMessage { Body = "Mapping removed" }; return new ResponseMessage { Body = "Mapping removed" };
}
return new ResponseMessage { Body = "Mapping not found" }; return new ResponseMessage { Body = "Mapping not found" };
} }
@@ -277,13 +335,17 @@ namespace WireMock.Server
{ {
string folder = Path.Combine(Directory.GetCurrentDirectory(), AdminMappingsFolder); string folder = Path.Combine(Directory.GetCurrentDirectory(), AdminMappingsFolder);
if (!Directory.Exists(folder)) if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder); Directory.CreateDirectory(folder);
}
var model = MappingConverter.ToMappingModel(mapping); var model = MappingConverter.ToMappingModel(mapping);
string json = JsonConvert.SerializeObject(model, _settings);
string filename = !string.IsNullOrEmpty(mapping.Title) ? SanitizeFileName(mapping.Title) : mapping.Guid.ToString(); string filename = !string.IsNullOrEmpty(mapping.Title) ? SanitizeFileName(mapping.Title) : mapping.Guid.ToString();
File.WriteAllText(Path.Combine(folder, filename + ".json"), json); string filePath = Path.Combine(folder, filename + ".json");
Log.InfoFormat("Saving Mapping to file {0}", filePath);
File.WriteAllText(filePath, JsonConvert.SerializeObject(model, _settings));
} }
private static string SanitizeFileName(string name, char replaceChar = '_') private static string SanitizeFileName(string name, char replaceChar = '_')
@@ -307,24 +369,25 @@ namespace WireMock.Server
{ {
try try
{ {
DeserializeAndAddMapping(requestMessage.Body); MappingModel mappingModel = requestMessage.Body != null ? JsonConvert.DeserializeObject<MappingModel>(requestMessage.Body) : ((JObject)requestMessage.BodyAsJson).ToObject<MappingModel>();
DeserializeAndAddOrUpdateMapping(mappingModel);
} }
catch (ArgumentException a) catch (ArgumentException a)
{ {
Log.Error("HttpStatusCode set to 400", a);
return new ResponseMessage { StatusCode = 400, Body = a.Message }; return new ResponseMessage { StatusCode = 400, Body = a.Message };
} }
catch (Exception e) catch (Exception e)
{ {
Log.Error("HttpStatusCode set to 500", e);
return new ResponseMessage { StatusCode = 500, Body = e.ToString() }; return new ResponseMessage { StatusCode = 500, Body = e.ToString() };
} }
return new ResponseMessage { StatusCode = 201, Body = "Mapping added" }; return new ResponseMessage { StatusCode = 201, Body = "Mapping added" };
} }
private void DeserializeAndAddMapping(string json, Guid? guid = null) private void DeserializeAndAddOrUpdateMapping(MappingModel mappingModel, Guid? guid = null, string path = null)
{ {
var mappingModel = JsonConvert.DeserializeObject<MappingModel>(json);
Check.NotNull(mappingModel, nameof(mappingModel)); Check.NotNull(mappingModel, nameof(mappingModel));
Check.NotNull(mappingModel.Request, nameof(mappingModel.Request)); Check.NotNull(mappingModel.Request, nameof(mappingModel.Request));
Check.NotNull(mappingModel.Response, nameof(mappingModel.Response)); Check.NotNull(mappingModel.Response, nameof(mappingModel.Response));
@@ -343,11 +406,20 @@ namespace WireMock.Server
respondProvider = respondProvider.WithGuid(mappingModel.Guid.Value); respondProvider = respondProvider.WithGuid(mappingModel.Guid.Value);
} }
if (path != null)
{
respondProvider = respondProvider.WithPath(path);
}
if (!string.IsNullOrEmpty(mappingModel.Title)) if (!string.IsNullOrEmpty(mappingModel.Title))
{
respondProvider = respondProvider.WithTitle(mappingModel.Title); respondProvider = respondProvider.WithTitle(mappingModel.Title);
}
if (mappingModel.Priority != null) if (mappingModel.Priority != null)
{
respondProvider = respondProvider.AtPriority(mappingModel.Priority.Value); respondProvider = respondProvider.AtPriority(mappingModel.Priority.Value);
}
if (mappingModel.Scenario != null) if (mappingModel.Scenario != null)
{ {
@@ -376,7 +448,10 @@ namespace WireMock.Server
var entry = LogEntries.FirstOrDefault(r => !r.RequestMessage.Path.StartsWith("/__admin/") && r.Guid == guid); var entry = LogEntries.FirstOrDefault(r => !r.RequestMessage.Path.StartsWith("/__admin/") && r.Guid == guid);
if (entry == null) if (entry == null)
return new ResponseMessage { StatusCode = 404, Body = "Request not found" }; {
Log.Warn("HttpStatusCode set to 404 : Request not found");
return new ResponseMessage {StatusCode = 404, Body = "Request not found"};
}
var model = ToLogEntryModel(entry); var model = ToLogEntryModel(entry);
@@ -418,6 +493,8 @@ namespace WireMock.Server
Query = logEntry.RequestMessage.Query, Query = logEntry.RequestMessage.Query,
Method = logEntry.RequestMessage.Method, Method = logEntry.RequestMessage.Method,
Body = logEntry.RequestMessage.Body, Body = logEntry.RequestMessage.Body,
BodyAsJson = logEntry.RequestMessage.BodyAsJson,
BodyAsBytes = logEntry.RequestMessage.BodyAsBytes,
Headers = logEntry.RequestMessage.Headers, Headers = logEntry.RequestMessage.Headers,
Cookies = logEntry.RequestMessage.Cookies, Cookies = logEntry.RequestMessage.Cookies,
BodyEncoding = logEntry.RequestMessage.BodyEncoding != null ? new EncodingModel BodyEncoding = logEntry.RequestMessage.BodyEncoding != null ? new EncodingModel
@@ -432,6 +509,7 @@ namespace WireMock.Server
StatusCode = logEntry.ResponseMessage.StatusCode, StatusCode = logEntry.ResponseMessage.StatusCode,
BodyDestination = logEntry.ResponseMessage.BodyDestination, BodyDestination = logEntry.ResponseMessage.BodyDestination,
Body = logEntry.ResponseMessage.Body, Body = logEntry.ResponseMessage.Body,
BodyAsJson = logEntry.ResponseMessage.BodyAsJson,
BodyAsBytes = logEntry.ResponseMessage.BodyAsBytes, BodyAsBytes = logEntry.ResponseMessage.BodyAsBytes,
BodyOriginal = logEntry.ResponseMessage.BodyOriginal, BodyOriginal = logEntry.ResponseMessage.BodyOriginal,
BodyAsFile = logEntry.ResponseMessage.BodyAsFile, BodyAsFile = logEntry.ResponseMessage.BodyAsFile,
@@ -472,7 +550,7 @@ namespace WireMock.Server
#region Requests/find #region Requests/find
private ResponseMessage RequestsFind(RequestMessage requestMessage) private ResponseMessage RequestsFind(RequestMessage requestMessage)
{ {
var requestModel = JsonConvert.DeserializeObject<RequestModel>(requestMessage.Body); var requestModel = requestMessage.Body != null ? JsonConvert.DeserializeObject<RequestModel>(requestMessage.Body) : ((JObject)requestMessage.BodyAsJson).ToObject<RequestModel>();
var request = (Request)InitRequestBuilder(requestModel); var request = (Request)InitRequestBuilder(requestModel);
@@ -528,7 +606,7 @@ namespace WireMock.Server
var clientIPModel = JsonUtils.ParseJTokenToObject<ClientIPModel>(requestModel.ClientIP); var clientIPModel = JsonUtils.ParseJTokenToObject<ClientIPModel>(requestModel.ClientIP);
if (clientIPModel?.Matchers != null) if (clientIPModel?.Matchers != null)
{ {
requestBuilder = requestBuilder.WithPath(clientIPModel.Matchers.Select(MappingConverter.Map).ToArray()); requestBuilder = requestBuilder.WithPath(clientIPModel.Matchers.Select(MappingConverter.Map).Cast<IStringMatcher>().ToArray());
} }
} }
} }
@@ -545,7 +623,7 @@ namespace WireMock.Server
var pathModel = JsonUtils.ParseJTokenToObject<PathModel>(requestModel.Path); var pathModel = JsonUtils.ParseJTokenToObject<PathModel>(requestModel.Path);
if (pathModel?.Matchers != null) if (pathModel?.Matchers != null)
{ {
requestBuilder = requestBuilder.WithPath(pathModel.Matchers.Select(MappingConverter.Map).ToArray()); requestBuilder = requestBuilder.WithPath(pathModel.Matchers.Select(MappingConverter.Map).Cast<IStringMatcher>().ToArray());
} }
} }
} }
@@ -562,7 +640,7 @@ namespace WireMock.Server
var urlModel = JsonUtils.ParseJTokenToObject<UrlModel>(requestModel.Url); var urlModel = JsonUtils.ParseJTokenToObject<UrlModel>(requestModel.Url);
if (urlModel?.Matchers != null) if (urlModel?.Matchers != null)
{ {
requestBuilder = requestBuilder.WithUrl(urlModel.Matchers.Select(MappingConverter.Map).ToArray()); requestBuilder = requestBuilder.WithUrl(urlModel.Matchers.Select(MappingConverter.Map).Cast<IStringMatcher>().ToArray());
} }
} }
} }
@@ -576,7 +654,7 @@ namespace WireMock.Server
{ {
foreach (var headerModel in requestModel.Headers.Where(h => h.Matchers != null)) foreach (var headerModel in requestModel.Headers.Where(h => h.Matchers != null))
{ {
requestBuilder = requestBuilder.WithHeader(headerModel.Name, headerModel.Matchers.Select(MappingConverter.Map).ToArray()); requestBuilder = requestBuilder.WithHeader(headerModel.Name, headerModel.Matchers.Select(MappingConverter.Map).Cast<IStringMatcher>().ToArray());
} }
} }
@@ -584,7 +662,7 @@ namespace WireMock.Server
{ {
foreach (var cookieModel in requestModel.Cookies.Where(c => c.Matchers != null)) foreach (var cookieModel in requestModel.Cookies.Where(c => c.Matchers != null))
{ {
requestBuilder = requestBuilder.WithCookie(cookieModel.Name, cookieModel.Matchers.Select(MappingConverter.Map).ToArray()); requestBuilder = requestBuilder.WithCookie(cookieModel.Name, cookieModel.Matchers.Select(MappingConverter.Map).Cast<IStringMatcher>().ToArray());
} }
} }
@@ -680,7 +758,7 @@ namespace WireMock.Server
{ {
Body = JsonConvert.SerializeObject(result, _settings), Body = JsonConvert.SerializeObject(result, _settings),
StatusCode = 200, StatusCode = 200,
Headers = new Dictionary<string, WireMockList<string>> { { "Content-Type", new WireMockList<string>("application/json") } } Headers = new Dictionary<string, WireMockList<string>> { { HttpKnownHeaderNames.ContentType, new WireMockList<string>("application/json") } }
}; };
} }

View File

@@ -14,7 +14,7 @@ namespace WireMock.Server
public partial class FluentMockServer public partial class FluentMockServer
{ {
/// <summary> /// <summary>
/// Log entries notification handler /// Occurs when [log entries changed].
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public event NotifyCollectionChangedEventHandler LogEntriesChanged public event NotifyCollectionChangedEventHandler LogEntriesChanged
@@ -66,7 +66,7 @@ namespace WireMock.Server
} }
/// <summary> /// <summary>
/// Deletes the mapping. /// Deletes a LogEntry.
/// </summary> /// </summary>
/// <param name="guid">The unique identifier.</param> /// <param name="guid">The unique identifier.</param>
[PublicAPI] [PublicAPI]
@@ -83,4 +83,4 @@ namespace WireMock.Server
return false; return false;
} }
} }
} }

View File

@@ -6,6 +6,8 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using log4net;
using Newtonsoft.Json;
using WireMock.Http; using WireMock.Http;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
@@ -21,16 +23,20 @@ namespace WireMock.Server
/// </summary> /// </summary>
public partial class FluentMockServer : IDisposable public partial class FluentMockServer : IDisposable
{ {
private static readonly ILog Log = LogManager.GetLogger(typeof(FluentMockServer));
private const int ServerStartDelay = 100; private const int ServerStartDelay = 100;
private readonly IOwinSelfHost _httpServer; private readonly IOwinSelfHost _httpServer;
private readonly WireMockMiddlewareOptions _options = new WireMockMiddlewareOptions(); private readonly WireMockMiddlewareOptions _options = new WireMockMiddlewareOptions();
/// <summary>
/// Gets a value indicating whether this server is started.
/// </summary>
[PublicAPI]
public bool IsStarted { get; }
/// <summary> /// <summary>
/// Gets the ports. /// Gets the ports.
/// </summary> /// </summary>
/// <value>
/// The ports.
/// </value>
[PublicAPI] [PublicAPI]
public List<int> Ports { get; } public List<int> Ports { get; }
@@ -44,7 +50,7 @@ namespace WireMock.Server
/// Gets the mappings. /// Gets the mappings.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public IEnumerable<Mapping> Mappings => new ReadOnlyCollection<Mapping>(_options.Mappings); public IEnumerable<Mapping> Mappings => _options.Mappings.Values.ToArray();
/// <summary> /// <summary>
/// Gets the scenarios. /// Gets the scenarios.
@@ -59,7 +65,7 @@ namespace WireMock.Server
/// <param name="settings">The FluentMockServerSettings.</param> /// <param name="settings">The FluentMockServerSettings.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns> /// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI] [PublicAPI]
public static FluentMockServer Start(FluentMockServerSettings settings) public static FluentMockServer Start(IFluentMockServerSettings settings)
{ {
Check.NotNull(settings, nameof(settings)); Check.NotNull(settings, nameof(settings));
@@ -90,7 +96,7 @@ namespace WireMock.Server
[PublicAPI] [PublicAPI]
public static FluentMockServer Start(params string[] urls) public static FluentMockServer Start(params string[] urls)
{ {
Check.NotEmpty(urls, nameof(urls)); Check.NotNullOrEmpty(urls, nameof(urls));
return new FluentMockServer(new FluentMockServerSettings return new FluentMockServer(new FluentMockServerSettings
{ {
@@ -123,7 +129,7 @@ namespace WireMock.Server
[PublicAPI] [PublicAPI]
public static FluentMockServer StartWithAdminInterface(params string[] urls) public static FluentMockServer StartWithAdminInterface(params string[] urls)
{ {
Check.NotEmpty(urls, nameof(urls)); Check.NotNullOrEmpty(urls, nameof(urls));
return new FluentMockServer(new FluentMockServerSettings return new FluentMockServer(new FluentMockServerSettings
{ {
@@ -140,7 +146,7 @@ namespace WireMock.Server
[PublicAPI] [PublicAPI]
public static FluentMockServer StartWithAdminInterfaceAndReadStaticMappings(params string[] urls) public static FluentMockServer StartWithAdminInterfaceAndReadStaticMappings(params string[] urls)
{ {
Check.NotEmpty(urls, nameof(urls)); Check.NotNullOrEmpty(urls, nameof(urls));
return new FluentMockServer(new FluentMockServerSettings return new FluentMockServer(new FluentMockServerSettings
{ {
@@ -150,11 +156,13 @@ namespace WireMock.Server
}); });
} }
private FluentMockServer(FluentMockServerSettings settings) private FluentMockServer(IFluentMockServerSettings settings)
{ {
Log.DebugFormat("WireMock.Net server settings {0}", JsonConvert.SerializeObject(settings, Formatting.Indented));
if (settings.Urls != null) if (settings.Urls != null)
{ {
Urls = settings.Urls; Urls = settings.Urls.Select(u => u.EndsWith("/") ? u : $"{u}/").ToArray();
} }
else else
{ {
@@ -170,6 +178,8 @@ namespace WireMock.Server
#else #else
_httpServer = new OwinSelfHost(_options, Urls); _httpServer = new OwinSelfHost(_options, Urls);
#endif #endif
IsStarted = _httpServer.IsStarted;
Ports = _httpServer.Ports; Ports = _httpServer.Ports;
_httpServer.StartAsync(); _httpServer.StartAsync();
@@ -197,6 +207,11 @@ namespace WireMock.Server
ReadStaticMappings(); ReadStaticMappings();
} }
if (settings.WatchStaticMappings == true)
{
WatchStaticMappings();
}
if (settings.ProxyAndRecordSettings != null) if (settings.ProxyAndRecordSettings != null)
{ {
InitProxyAndRecord(settings.ProxyAndRecordSettings); InitProxyAndRecord(settings.ProxyAndRecordSettings);
@@ -258,7 +273,10 @@ namespace WireMock.Server
[PublicAPI] [PublicAPI]
public void ResetMappings() public void ResetMappings()
{ {
_options.Mappings = _options.Mappings.Where(m => m.IsAdminInterface).ToList(); foreach (var nonAdmin in _options.Mappings.Where(m => !m.Value.IsAdminInterface))
{
_options.Mappings.Remove(nonAdmin);
}
} }
/// <summary> /// <summary>
@@ -269,16 +287,21 @@ namespace WireMock.Server
public bool DeleteMapping(Guid guid) public bool DeleteMapping(Guid guid)
{ {
// Check a mapping exists with the same GUID, if so, remove it. // Check a mapping exists with the same GUID, if so, remove it.
var existingMapping = _options.Mappings.FirstOrDefault(m => m.Guid == guid); if (_options.Mappings.ContainsKey(guid))
if (existingMapping != null)
{ {
_options.Mappings.Remove(existingMapping); return _options.Mappings.Remove(guid);
return true;
} }
return false; return false;
} }
private bool DeleteMapping(string path)
{
// Check a mapping exists with the same path, if so, remove it.
var mapping = _options.Mappings.FirstOrDefault(entry => string.Equals(entry.Value.Path, path, StringComparison.OrdinalIgnoreCase));
return DeleteMapping(mapping.Key);
}
/// <summary> /// <summary>
/// The add request processing delay. /// The add request processing delay.
/// </summary> /// </summary>
@@ -293,9 +316,10 @@ namespace WireMock.Server
/// Allows the partial mapping. /// Allows the partial mapping.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public void AllowPartialMapping() public void AllowPartialMapping(bool allow = true)
{ {
_options.AllowPartialMapping = true; Log.InfoFormat("AllowPartialMapping is set to {0}", allow);
_options.AllowPartialMapping = allow;
} }
/// <summary> /// <summary>
@@ -363,18 +387,17 @@ namespace WireMock.Server
return new RespondWithAProvider(RegisterMapping, requestMatcher); return new RespondWithAProvider(RegisterMapping, requestMatcher);
} }
/// <summary>
/// The register mapping.
/// </summary>
/// <param name="mapping">
/// The mapping.
/// </param>
private void RegisterMapping(Mapping mapping) private void RegisterMapping(Mapping mapping)
{ {
// Check a mapping exists with the same GUID, if so, remove it first. // Check a mapping exists with the same Guid, if so, replace it.
DeleteMapping(mapping.Guid); if (_options.Mappings.ContainsKey(mapping.Guid))
{
_options.Mappings.Add(mapping); _options.Mappings[mapping.Guid] = mapping;
}
else
{
_options.Mappings.Add(mapping.Guid, mapping);
}
} }
} }
} }

View File

@@ -21,6 +21,13 @@ namespace WireMock.Server
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns> /// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithTitle(string title); IRespondWithAProvider WithTitle(string title);
/// <summary>
/// Define the full filepath for this mapping.
/// </summary>
/// <param name="path">The full filepath.</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithPath(string path);
/// <summary> /// <summary>
/// Define a unique identifier for this mapping. /// Define a unique identifier for this mapping.
/// </summary> /// </summary>

View File

@@ -11,18 +11,11 @@ namespace WireMock.Server
private int _priority; private int _priority;
private Guid? _guid; private Guid? _guid;
private string _title; private string _title;
private string _path;
private object _executionConditionState; private object _executionConditionState;
private object _nextState; private object _nextState;
private string _scenario; private string _scenario;
/// <summary>
/// The _registration callback.
/// </summary>
private readonly RegistrationCallback _registrationCallback; private readonly RegistrationCallback _registrationCallback;
/// <summary>
/// The _request matcher.
/// </summary>
private readonly IRequestMatcher _requestMatcher; private readonly IRequestMatcher _requestMatcher;
/// <summary> /// <summary>
@@ -39,30 +32,20 @@ namespace WireMock.Server
/// <summary> /// <summary>
/// The respond with. /// The respond with.
/// </summary> /// </summary>
/// <param name="provider"> /// <param name="provider">The provider.</param>
/// The provider.
/// </param>
public void RespondWith(IResponseProvider provider) public void RespondWith(IResponseProvider provider)
{ {
var mappingGuid = _guid ?? Guid.NewGuid(); var mappingGuid = _guid ?? Guid.NewGuid();
_registrationCallback(new Mapping(mappingGuid, _title, _requestMatcher, provider, _priority, _scenario, _executionConditionState, _nextState)); _registrationCallback(new Mapping(mappingGuid, _title, _path, _requestMatcher, provider, _priority, _scenario, _executionConditionState, _nextState));
} }
/// <summary> /// <see cref="IRespondWithAProvider.WithGuid(string)"/>
/// Define a unique identifier for this mapping.
/// </summary>
/// <param name="guid">The unique identifier.</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
public IRespondWithAProvider WithGuid(string guid) public IRespondWithAProvider WithGuid(string guid)
{ {
return WithGuid(Guid.Parse(guid)); return WithGuid(Guid.Parse(guid));
} }
/// <summary> /// <see cref="IRespondWithAProvider.WithGuid(Guid)"/>
/// Define a unique identifier for this mapping.
/// </summary>
/// <param name="guid">The unique identifier.</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
public IRespondWithAProvider WithGuid(Guid guid) public IRespondWithAProvider WithGuid(Guid guid)
{ {
_guid = guid; _guid = guid;
@@ -70,11 +53,7 @@ namespace WireMock.Server
return this; return this;
} }
/// <summary> /// <see cref="IRespondWithAProvider.WithTitle"/>
/// Define a unique identifier for this mapping.
/// </summary>
/// <param name="title">The unique identifier.</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
public IRespondWithAProvider WithTitle(string title) public IRespondWithAProvider WithTitle(string title)
{ {
_title = title; _title = title;
@@ -82,11 +61,15 @@ namespace WireMock.Server
return this; return this;
} }
/// <summary> /// <see cref="IRespondWithAProvider.WithPath"/>
/// Define the priority for this mapping. public IRespondWithAProvider WithPath(string path)
/// </summary> {
/// <param name="priority">The priority.</param> _path = path;
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
return this;
}
/// <see cref="IRespondWithAProvider.AtPriority"/>
public IRespondWithAProvider AtPriority(int priority) public IRespondWithAProvider AtPriority(int priority)
{ {
_priority = priority; _priority = priority;
@@ -94,6 +77,7 @@ namespace WireMock.Server
return this; return this;
} }
/// <see cref="IRespondWithAProvider.InScenario(string)"/>
public IRespondWithAProvider InScenario(string scenario) public IRespondWithAProvider InScenario(string scenario)
{ {
_scenario = scenario; _scenario = scenario;
@@ -101,6 +85,7 @@ namespace WireMock.Server
return this; return this;
} }
/// <see cref="IRespondWithAProvider.WhenStateIs"/>
public IRespondWithAProvider WhenStateIs(object state) public IRespondWithAProvider WhenStateIs(object state)
{ {
if (string.IsNullOrEmpty(_scenario)) if (string.IsNullOrEmpty(_scenario))
@@ -118,6 +103,7 @@ namespace WireMock.Server
return this; return this;
} }
/// <see cref="IRespondWithAProvider.WillSetStateTo"/>
public IRespondWithAProvider WillSetStateTo(object state) public IRespondWithAProvider WillSetStateTo(object state)
{ {
if (string.IsNullOrEmpty(_scenario)) if (string.IsNullOrEmpty(_scenario))

View File

@@ -1,97 +1,75 @@
using System; using System;
using JetBrains.Annotations; using JetBrains.Annotations;
using Newtonsoft.Json;
namespace WireMock.Settings namespace WireMock.Settings
{ {
/// <summary> /// <summary>
/// FluentMockServerSettings /// FluentMockServerSettings
/// </summary> /// </summary>
public class FluentMockServerSettings public class FluentMockServerSettings : IFluentMockServerSettings
{ {
/// <summary> /// <inheritdoc cref="IFluentMockServerSettings.Port"/>
/// Gets or sets the port.
/// </summary>
[PublicAPI] [PublicAPI]
public int? Port { get; set; } public int? Port { get; set; }
/// <summary> /// <inheritdoc cref="IFluentMockServerSettings.UseSSL"/>
/// Gets or sets the use SSL.
/// </summary>
// ReSharper disable once InconsistentNaming
[PublicAPI] [PublicAPI]
// ReSharper disable once InconsistentNaming
public bool? UseSSL { get; set; } public bool? UseSSL { get; set; }
/// <summary> /// <inheritdoc cref="IFluentMockServerSettings.StartAdminInterface"/>
/// Gets or sets wether to start admin interface.
/// </summary>
[PublicAPI] [PublicAPI]
public bool? StartAdminInterface { get; set; } public bool? StartAdminInterface { get; set; }
/// <summary> /// <inheritdoc cref="IFluentMockServerSettings.ReadStaticMappings"/>
/// Gets or sets if the static mappings should be read at startup.
/// </summary>
[PublicAPI] [PublicAPI]
public bool? ReadStaticMappings { get; set; } public bool? ReadStaticMappings { get; set; }
/// <summary> /// <inheritdoc cref="IFluentMockServerSettings.WatchStaticMappings"/>
/// Gets or sets if the server should record and save requests and responses.
/// </summary>
/// <value>true/false</value>
[PublicAPI] [PublicAPI]
public ProxyAndRecordSettings ProxyAndRecordSettings { get; set; } public bool? WatchStaticMappings { get; set; }
/// <summary> /// <inheritdoc cref="IFluentMockServerSettings.ProxyAndRecordSettings"/>
/// Gets or sets the urls. [PublicAPI]
/// </summary> public IProxyAndRecordSettings ProxyAndRecordSettings { get; set; }
/// <inheritdoc cref="IFluentMockServerSettings.Urls"/>
[PublicAPI] [PublicAPI]
public string[] Urls { get; set; } public string[] Urls { get; set; }
/// <summary> /// <inheritdoc cref="IFluentMockServerSettings.StartTimeout"/>
/// StartTimeout
/// </summary>
[PublicAPI] [PublicAPI]
public int StartTimeout { get; set; } = 10000; public int StartTimeout { get; set; } = 10000;
/// <summary> /// <inheritdoc cref="IFluentMockServerSettings.AllowPartialMapping"/>
/// Allow Partial Mapping (default set to false).
/// </summary>
[PublicAPI] [PublicAPI]
public bool? AllowPartialMapping { get; set; } public bool? AllowPartialMapping { get; set; }
/// <summary> /// <inheritdoc cref="IFluentMockServerSettings.AdminUsername"/>
/// The username needed for __admin access.
/// </summary>
[PublicAPI] [PublicAPI]
public string AdminUsername { get; set; } public string AdminUsername { get; set; }
/// <summary> /// <inheritdoc cref="IFluentMockServerSettings.AdminPassword"/>
/// The password needed for __admin access.
/// </summary>
[PublicAPI] [PublicAPI]
public string AdminPassword { get; set; } public string AdminPassword { get; set; }
/// <summary> /// <inheritdoc cref="IFluentMockServerSettings.RequestLogExpirationDuration"/>
/// The RequestLog expiration in hours (optional).
/// </summary>
[PublicAPI] [PublicAPI]
public int? RequestLogExpirationDuration { get; set; } public int? RequestLogExpirationDuration { get; set; }
/// <summary> /// <inheritdoc cref="IFluentMockServerSettings.MaxRequestLogCount"/>
/// The MaxRequestLog count (optional).
/// </summary>
[PublicAPI] [PublicAPI]
public int? MaxRequestLogCount { get; set; } public int? MaxRequestLogCount { get; set; }
/// <summary> /// <inheritdoc cref="IFluentMockServerSettings.PreWireMockMiddlewareInit"/>
/// Action which is called (with the IAppBuilder or IApplicationBuilder) before the internal WireMockMiddleware is initialized. [Optional]
/// </summary>
[PublicAPI] [PublicAPI]
[JsonIgnore]
public Action<object> PreWireMockMiddlewareInit { get; set; } public Action<object> PreWireMockMiddlewareInit { get; set; }
/// <summary> /// <inheritdoc cref="IFluentMockServerSettings.PostWireMockMiddlewareInit"/>
/// Action which is called (with the IAppBuilder or IApplicationBuilder) after the internal WireMockMiddleware is initialized. [Optional]
/// </summary>
[PublicAPI] [PublicAPI]
[JsonIgnore]
public Action<object> PostWireMockMiddlewareInit { get; set; } public Action<object> PostWireMockMiddlewareInit { get; set; }
} }
} }

View File

@@ -0,0 +1,86 @@
using System;
namespace WireMock.Settings
{
/// <summary>
/// IFluentMockServerSettings
/// </summary>
public interface IFluentMockServerSettings
{
/// <summary>
/// Gets or sets the port.
/// </summary>
int? Port { get; set; }
/// <summary>
/// Gets or sets the use SSL.
/// </summary>
// ReSharper disable once InconsistentNaming
bool? UseSSL { get; set; }
/// <summary>
/// Gets or sets wether to start admin interface.
/// </summary>
bool? StartAdminInterface { get; set; }
/// <summary>
/// Gets or sets if the static mappings should be read at startup.
/// </summary>
bool? ReadStaticMappings { get; set; }
/// <summary>
/// Watch the static mapping files + folder for changes when running.
/// </summary>
bool? WatchStaticMappings { get; set; }
/// <summary>
/// Gets or sets if the proxy and record settings.
/// </summary>
IProxyAndRecordSettings ProxyAndRecordSettings { get; set; }
/// <summary>
/// Gets or sets the urls.
/// </summary>
string[] Urls { get; set; }
/// <summary>
/// StartTimeout
/// </summary>
int StartTimeout { get; set; }
/// <summary>
/// Allow Partial Mapping (default set to false).
/// </summary>
bool? AllowPartialMapping { get; set; }
/// <summary>
/// The username needed for __admin access.
/// </summary>
string AdminUsername { get; set; }
/// <summary>
/// The password needed for __admin access.
/// </summary>
string AdminPassword { get; set; }
/// <summary>
/// The RequestLog expiration in hours (optional).
/// </summary>
int? RequestLogExpirationDuration { get; set; }
/// <summary>
/// The MaxRequestLog count (optional).
/// </summary>
int? MaxRequestLogCount { get; set; }
/// <summary>
/// Action which is called (with the IAppBuilder or IApplicationBuilder) before the internal WireMockMiddleware is initialized. [Optional]
/// </summary>
Action<object> PreWireMockMiddlewareInit { get; set; }
/// <summary>
/// Action which is called (with the IAppBuilder or IApplicationBuilder) after the internal WireMockMiddleware is initialized. [Optional]
/// </summary>
Action<object> PostWireMockMiddlewareInit { get; set; }
}
}

View File

@@ -0,0 +1,34 @@
namespace WireMock.Settings
{
/// <summary>
/// IProxyAndRecordSettings
/// </summary>
public interface IProxyAndRecordSettings
{
/// <summary>
/// The URL to proxy.
/// </summary>
string Url { get; set; }
/// <summary>
/// Save the mapping for each request/response to the internal Mappings.
/// </summary>
bool SaveMapping { get; set; }
/// <summary>
/// Save the mapping for each request/response to also file. (Note that SaveMapping must also be set to true.)
/// </summary>
bool SaveMappingToFile { get; set; }
/// <summary>
/// The clientCertificate thumbprint or subject name fragment to use.
/// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
/// </summary>
string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
/// <summary>
/// Defines a list from headers which will excluded from the saved mappings.
/// </summary>
string[] BlackListedHeaders { get; set; }
}
}

View File

@@ -1,28 +1,30 @@
namespace WireMock.Settings using JetBrains.Annotations;
namespace WireMock.Settings
{ {
/// <summary> /// <summary>
/// RecordAndSaveSettings /// ProxyAndRecordSettings
/// </summary> /// </summary>
public class ProxyAndRecordSettings public class ProxyAndRecordSettings : IProxyAndRecordSettings
{ {
/// <summary> /// <inheritdoc cref="IProxyAndRecordSettings.Url"/>
/// The URL to proxy. [PublicAPI]
/// </summary>
public string Url { get; set; } public string Url { get; set; }
/// <summary> /// <inheritdoc cref="IProxyAndRecordSettings.SaveMapping"/>
/// Save the mapping for each request/response to the internal Mappings. [PublicAPI]
/// </summary>
public bool SaveMapping { get; set; } = true; public bool SaveMapping { get; set; } = true;
/// <summary> /// <inheritdoc cref="IProxyAndRecordSettings.SaveMappingToFile"/>
/// Save the mapping for each request/response to also file. (Note that SaveMapping must also be set to true.) [PublicAPI]
/// </summary>
public bool SaveMappingToFile { get; set; } = true; public bool SaveMappingToFile { get; set; } = true;
/// <summary> /// <inheritdoc cref="IProxyAndRecordSettings.ClientX509Certificate2ThumbprintOrSubjectName"/>
/// The clientCertificate thumbprint or subject name fragment to use. Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com"" [PublicAPI]
/// </summary> public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
public string X509Certificate2ThumbprintOrSubjectName { get; set; }
/// <inheritdoc cref="IProxyAndRecordSettings.BlackListedHeaders"/>
[PublicAPI]
public string[] BlackListedHeaders { get; set; }
} }
} }

View File

@@ -0,0 +1,30 @@
using System.Text;
namespace WireMock.Util
{
/// <summary>
/// BodyData
/// </summary>
public class BodyData
{
/// <summary>
/// The body encoding.
/// </summary>
public Encoding Encoding { get; set; }
/// <summary>
/// The body as string.
/// </summary>
public string BodyAsString { get; set; }
/// <summary>
/// The body (as JSON object).
/// </summary>
public object BodyAsJson { get; set; }
/// <summary>
/// The body (as bytearray).
/// </summary>
public byte[] BodyAsBytes { get; set; }
}
}

View File

@@ -0,0 +1,75 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Newtonsoft.Json;
namespace WireMock.Util
{
internal static class BodyParser
{
private static readonly string[] TextContentTypes = { "text/", "application/xml", "application/javascript", "application/typescript", "application/xhtml+xml" };
private static async Task<Tuple<string, Encoding>> ReadStringAsync(Stream stream)
{
using (var streamReader = new StreamReader(stream))
{
string content = await streamReader.ReadToEndAsync();
return new Tuple<string, Encoding>(content, streamReader.CurrentEncoding);
}
}
private static async Task<byte[]> ReadBytesAsync(Stream stream)
{
using (var memoryStream = new MemoryStream())
{
await stream.CopyToAsync(memoryStream);
return memoryStream.ToArray();
}
}
public static async Task<BodyData> Parse([NotNull] Stream stream, [CanBeNull] string contentTypeHeaderValue)
{
var data = new BodyData();
if (contentTypeHeaderValue != null && TextContentTypes.Any(t => contentTypeHeaderValue.StartsWith(t, StringComparison.OrdinalIgnoreCase)))
{
try
{
var stringData = await ReadStringAsync(stream);
data.BodyAsString = stringData.Item1;
data.Encoding = stringData.Item2;
}
catch
{
// Reading as string failed, just get the ByteArray.
data.BodyAsBytes = await ReadBytesAsync(stream);
}
}
else if (contentTypeHeaderValue != null && contentTypeHeaderValue.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
{
var stringData = await ReadStringAsync(stream);
data.Encoding = stringData.Item2;
try
{
data.BodyAsJson = JsonConvert.DeserializeObject(stringData.Item1, new JsonSerializerSettings { Formatting = Formatting.Indented });
}
catch
{
// JsonConvert failed, just set the Body as string.
data.BodyAsString = stringData.Item1;
}
}
else
{
data.BodyAsBytes = await ReadBytesAsync(stream);
}
return data;
}
}
}

View File

@@ -0,0 +1,265 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using JetBrains.Annotations;
using WireMock.Validation;
namespace WireMock.Util
{
/// <summary>
/// An EnhancedFileSystemWatcher, which can be used to suppress duplicate events that fire on a single change to the file.
/// </summary>
/// <seealso cref="System.IO.FileSystemWatcher" />
/// <seealso cref="System.IDisposable" />
public class EnhancedFileSystemWatcher : FileSystemWatcher, IDisposable
{
#region Private Members
// Default Watch Interval in Milliseconds
private const int DefaultWatchInterval = 100;
// This Dictionary keeps the track of when an event occured last for a particular file
private ConcurrentDictionary<string, DateTime> _lastFileEvent;
// Watch Interval in Milliseconds
private int _interval;
// Timespan created when interval is set
private TimeSpan _recentTimeSpan;
#endregion
#region Public Properties
/// <summary>
/// Interval, in milliseconds, within which events are considered "recent".
/// </summary>
[PublicAPI]
public int Interval
{
get => _interval;
set
{
_interval = value;
// Set timespan based on the value passed
_recentTimeSpan = new TimeSpan(0, 0, 0, 0, value);
}
}
/// <summary>
/// Allows user to set whether to filter recent events.
/// If this is set a false, this class behaves like System.IO.FileSystemWatcher class.
/// </summary>
[PublicAPI]
public bool FilterRecentEvents { get; set; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="EnhancedFileSystemWatcher"/> class.
/// </summary>
/// <param name="interval">The interval.</param>
public EnhancedFileSystemWatcher(int interval = DefaultWatchInterval)
{
Check.Condition(interval, i => i >= 0, nameof(interval));
InitializeMembers(interval);
}
/// <summary>
/// Initializes a new instance of the <see cref="EnhancedFileSystemWatcher"/> class.
/// </summary>
/// <param name="path">The directory to monitor, in standard or Universal Naming Convention (UNC) notation.</param>
/// <param name="interval">The interval.</param>
public EnhancedFileSystemWatcher([NotNull] string path, int interval = DefaultWatchInterval) : base(path)
{
Check.NotNullOrEmpty(path, nameof(path));
Check.Condition(interval, i => i >= 0, nameof(interval));
InitializeMembers(interval);
}
/// <summary>
/// Initializes a new instance of the <see cref="EnhancedFileSystemWatcher"/> class.
/// </summary>
/// <param name="path">The directory to monitor, in standard or Universal Naming Convention (UNC) notation.</param>
/// <param name="filter">The type of files to watch. For example, "*.txt" watches for changes to all text files.</param>
/// <param name="interval">The interval.</param>
public EnhancedFileSystemWatcher([NotNull] string path, [NotNull] string filter, int interval = DefaultWatchInterval) : base(path, filter)
{
Check.NotNullOrEmpty(path, nameof(path));
Check.NotNullOrEmpty(filter, nameof(filter));
Check.Condition(interval, i => i >= 0, nameof(interval));
InitializeMembers(interval);
}
#endregion
#region Events
// These events hide the events from the base class.
// We want to raise these events appropriately and we do not want the
// users of this class subscribing to these events of the base class accidentally
/// <summary>
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is changed.
/// </summary>
public new event FileSystemEventHandler Changed;
/// <summary>
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is created.
/// </summary>
public new event FileSystemEventHandler Created;
/// <summary>
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is deleted.
/// </summary>
public new event FileSystemEventHandler Deleted;
/// <summary>
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is renamed.
/// </summary>
public new event RenamedEventHandler Renamed;
#endregion
#region Protected Methods to raise the Events for this class
/// <summary>
/// Raises the <see cref="E:System.IO.FileSystemWatcher.Changed" /> event.
/// </summary>
/// <param name="e">A <see cref="T:System.IO.FileSystemEventArgs" /> that contains the event data.</param>
protected new virtual void OnChanged(FileSystemEventArgs e)
{
Changed?.Invoke(this, e);
}
/// <summary>
/// Raises the <see cref="E:System.IO.FileSystemWatcher.Created" /> event.
/// </summary>
/// <param name="e">A <see cref="T:System.IO.FileSystemEventArgs" /> that contains the event data.</param>
protected new virtual void OnCreated(FileSystemEventArgs e)
{
Created?.Invoke(this, e);
}
/// <summary>
/// Raises the <see cref="E:System.IO.FileSystemWatcher.Deleted" /> event.
/// </summary>
/// <param name="e">A <see cref="T:System.IO.FileSystemEventArgs" /> that contains the event data.</param>
protected new virtual void OnDeleted(FileSystemEventArgs e)
{
Deleted?.Invoke(this, e);
}
/// <summary>
/// Raises the <see cref="E:System.IO.FileSystemWatcher.Renamed" /> event.
/// </summary>
/// <param name="e">A <see cref="T:System.IO.RenamedEventArgs" /> that contains the event data.</param>
protected new virtual void OnRenamed(RenamedEventArgs e)
{
Renamed?.Invoke(this, e);
}
#endregion
#region Private Methods
/// <summary>
/// This Method Initializes the private members.
/// Interval is set to its default value of 100 millisecond.
/// FilterRecentEvents is set to true, _lastFileEvent dictionary is initialized.
/// We subscribe to the base class events.
/// </summary>
private void InitializeMembers(int interval = 100)
{
Interval = interval;
FilterRecentEvents = true;
_lastFileEvent = new ConcurrentDictionary<string, DateTime>();
base.Created += OnCreated;
base.Changed += OnChanged;
base.Deleted += OnDeleted;
base.Renamed += OnRenamed;
}
/// <summary>
/// This method searches the dictionary to find out when the last event occured
/// for a particular file. If that event occured within the specified timespan
/// it returns true, else false
/// </summary>
/// <param name="fileName">The filename to be checked</param>
/// <returns>True if an event has occured within the specified interval, False otherwise</returns>
private bool HasAnotherFileEventOccuredRecently(string fileName)
{
// Check dictionary only if user wants to filter recent events otherwise return value stays false.
if (!FilterRecentEvents)
{
return false;
}
bool retVal = false;
if (_lastFileEvent.ContainsKey(fileName))
{
// If dictionary contains the filename, check how much time has elapsed
// since the last event occured. If the timespan is less that the
// specified interval, set return value to true
// and store current datetime in dictionary for this file
DateTime lastEventTime = _lastFileEvent[fileName];
DateTime currentTime = DateTime.Now;
TimeSpan timeSinceLastEvent = currentTime - lastEventTime;
retVal = timeSinceLastEvent < _recentTimeSpan;
_lastFileEvent[fileName] = currentTime;
}
else
{
// If dictionary does not contain the filename,
// no event has occured in past for this file, so set return value to false
// and append filename along with current datetime to the dictionary
_lastFileEvent.TryAdd(fileName, DateTime.Now);
}
return retVal;
}
#region FileSystemWatcher EventHandlers
// Base class Event Handlers. Check if an event has occured recently and call method
// to raise appropriate event only if no recent event is detected
private void OnChanged(object sender, FileSystemEventArgs e)
{
if (!HasAnotherFileEventOccuredRecently(e.FullPath))
{
OnChanged(e);
}
}
private void OnCreated(object sender, FileSystemEventArgs e)
{
if (!HasAnotherFileEventOccuredRecently(e.FullPath))
{
OnCreated(e);
}
}
private void OnDeleted(object sender, FileSystemEventArgs e)
{
if (!HasAnotherFileEventOccuredRecently(e.FullPath))
{
OnDeleted(e);
}
}
private void OnRenamed(object sender, RenamedEventArgs e)
{
if (!HasAnotherFileEventOccuredRecently(e.OldFullPath))
{
OnRenamed(e);
}
}
#endregion
#endregion
#region IDisposable Members
/// <summary>
/// Releases all resources used by the <see cref="T:System.ComponentModel.Component" />.
/// </summary>
public new void Dispose()
{
base.Dispose();
}
#endregion
}
}

View File

@@ -0,0 +1,29 @@
using System.IO;
using System.Threading;
namespace WireMock.Util
{
internal static class FileHelper
{
private const int NumberOfRetries = 3;
private const int DelayOnRetry = 500;
public static string ReadAllText(string path)
{
for (int i = 1; i <= NumberOfRetries; ++i)
{
try
{
return File.ReadAllText(path);
}
catch
{
// You may check error code to filter some exceptions, not every error can be recovered.
Thread.Sleep(DelayOnRetry);
}
}
throw new IOException();
}
}
}

View File

@@ -23,7 +23,7 @@ namespace WireMock.Validation
if (!condition(value)) if (!condition(value))
{ {
NotEmpty(parameterName, nameof(parameterName)); NotNullOrEmpty(parameterName, nameof(parameterName));
throw new ArgumentOutOfRangeException(parameterName); throw new ArgumentOutOfRangeException(parameterName);
} }
@@ -36,7 +36,7 @@ namespace WireMock.Validation
{ {
if (ReferenceEquals(value, null)) if (ReferenceEquals(value, null))
{ {
NotEmpty(parameterName, nameof(parameterName)); NotNullOrEmpty(parameterName, nameof(parameterName));
throw new ArgumentNullException(parameterName); throw new ArgumentNullException(parameterName);
} }
@@ -45,15 +45,12 @@ namespace WireMock.Validation
} }
[ContractAnnotation("value:null => halt")] [ContractAnnotation("value:null => halt")]
public static T NotNull<T>( public static T NotNull<T>([NoEnumeration] T value, [InvokerParameterName] [NotNull] string parameterName, [NotNull] string propertyName)
[NoEnumeration] T value,
[InvokerParameterName] [NotNull] string parameterName,
[NotNull] string propertyName)
{ {
if (ReferenceEquals(value, null)) if (ReferenceEquals(value, null))
{ {
NotEmpty(parameterName, nameof(parameterName)); NotNullOrEmpty(parameterName, nameof(parameterName));
NotEmpty(propertyName, nameof(propertyName)); NotNullOrEmpty(propertyName, nameof(propertyName));
throw new ArgumentException(CoreStrings.ArgumentPropertyNull(propertyName, parameterName)); throw new ArgumentException(CoreStrings.ArgumentPropertyNull(propertyName, parameterName));
} }
@@ -62,13 +59,13 @@ namespace WireMock.Validation
} }
[ContractAnnotation("value:null => halt")] [ContractAnnotation("value:null => halt")]
public static IList<T> NotEmpty<T>(IList<T> value, [InvokerParameterName] [NotNull] string parameterName) public static IList<T> NotNullOrEmpty<T>(IList<T> value, [InvokerParameterName] [NotNull] string parameterName)
{ {
NotNull(value, parameterName); NotNull(value, parameterName);
if (value.Count == 0) if (value.Count == 0)
{ {
NotEmpty(parameterName, nameof(parameterName)); NotNullOrEmpty(parameterName, nameof(parameterName));
throw new ArgumentException(CoreStrings.CollectionArgumentIsEmpty(parameterName)); throw new ArgumentException(CoreStrings.CollectionArgumentIsEmpty(parameterName));
} }
@@ -77,7 +74,7 @@ namespace WireMock.Validation
} }
[ContractAnnotation("value:null => halt")] [ContractAnnotation("value:null => halt")]
public static string NotEmpty(string value, [InvokerParameterName] [NotNull] string parameterName) public static string NotNullOrEmpty(string value, [InvokerParameterName] [NotNull] string parameterName)
{ {
Exception e = null; Exception e = null;
if (ReferenceEquals(value, null)) if (ReferenceEquals(value, null))
@@ -91,7 +88,7 @@ namespace WireMock.Validation
if (e != null) if (e != null)
{ {
NotEmpty(parameterName, nameof(parameterName)); NotNullOrEmpty(parameterName, nameof(parameterName));
throw e; throw e;
} }
@@ -104,7 +101,7 @@ namespace WireMock.Validation
if (!ReferenceEquals(value, null) if (!ReferenceEquals(value, null)
&& (value.Length == 0)) && (value.Length == 0))
{ {
NotEmpty(parameterName, nameof(parameterName)); NotNullOrEmpty(parameterName, nameof(parameterName));
throw new ArgumentException(CoreStrings.ArgumentIsEmpty(parameterName)); throw new ArgumentException(CoreStrings.ArgumentIsEmpty(parameterName));
} }
@@ -119,7 +116,7 @@ namespace WireMock.Validation
if (value.Any(e => e == null)) if (value.Any(e => e == null))
{ {
NotEmpty(parameterName, nameof(parameterName)); NotNullOrEmpty(parameterName, nameof(parameterName));
throw new ArgumentException(parameterName); throw new ArgumentException(parameterName);
} }
@@ -131,7 +128,7 @@ namespace WireMock.Validation
{ {
if (!value.GetTypeInfo().IsClass) if (!value.GetTypeInfo().IsClass)
{ {
NotEmpty(parameterName, nameof(parameterName)); NotNullOrEmpty(parameterName, nameof(parameterName));
throw new ArgumentException(CoreStrings.InvalidEntityType(value, parameterName)); throw new ArgumentException(CoreStrings.InvalidEntityType(value, parameterName));
} }

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