Compare commits

..

49 Commits

Author SHA1 Message Date
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
Stef Heyenrath
d0fc889f42 Fix for url encoded path + params (1.0.2.10) - #72
Fix for url encoded path + params (1.0.2.10)
2017-12-12 18:18:04 +01:00
Stef Heyenrath
fd5bc203c3 1.0.2.9 2017-12-07 22:17:35 +01:00
Stef Heyenrath
601af2d6b2 Fixed restricted headers on response (#71)
* Fixed restricted headers on response

* appveyor
2017-12-07 22:16:19 +01:00
Stef Heyenrath
ea16ee866b 1.0.2.8 2017-11-23 20:54:30 +01:00
Stef Heyenrath
ee0cb6e670 Added unit-test for #68
Added unit-test for #68
2017-11-22 21:33:40 +01:00
Alastair Crabtree
798603118c bug: fix supporting the Patch method and logging the body (#67)
* bug: fix supporting the Patch method and logging the body

- Patch not configured to support Body
- Add a unit test

* chore: typo fixed

* Added / reordered tests for PATCH method
2017-11-20 19:32:36 +01:00
Alastair Crabtree
208303729e bug: Fix admin api client definition returning the wrong types (#65)
* bug: Fix admin api client definition returning the wrong types

- IFluentMockServerAdmin get, find and get all requests should return a log entry model
- Add tests for get and find using the rest ease api client

* Fix Build status and rename tests
2017-11-19 20:44:05 +01:00
Stef Heyenrath
d39e9ef7fa Fixed readme 2017-11-19 11:47:42 +01:00
Stef Heyenrath
431e5656ca <DebugType>full</DebugType> 2017-11-19 11:37:50 +01:00
Stef Heyenrath
fa919343ac <DebugType>portable</DebugType> 2017-11-19 11:32:39 +01:00
Stef Heyenrath
d0b48e2967 DebugType, IncludeSource and IncludeSymbols set to 'true' 2017-11-19 10:39:11 +01:00
Stef Heyenrath
018d2a904d 1.0.2.7 2017-11-18 13:49:22 +01:00
Stef Heyenrath
939a5a61ab 1.0.2.7 2017-11-18 13:48:12 +01:00
vladimir-fed
a15e6747b0 Fix issue with concurrent logging (#63)
* Fix issue with concurrent logging

* Fix namespace + code format/cleanup
2017-11-18 12:22:19 +01:00
Alastair Crabtree
6c38400827 Add the Host, Protocol, Port and Origin to the Request message so they can be used in templating (#62)
* feat: Add the Host, Protocol Port and Origin to the Request message

- Add new fields during request message creation, derived from the Uri object
- Allow the new fields to be accessed in the templating engine
- Add tests for the new fields

* source code file reformat

* appveyor
2017-11-18 12:03:13 +01:00
Stef Heyenrath
cb1117fdaa IHeadersAndCookiesRequestBuilder 2017-11-11 10:09:41 +01:00
Stef Heyenrath
e25c873765 Update ProxyAndRecord
ProxyAndRecord does not save query-parameters, headers and body (#57)
2017-11-07 21:52:30 +01:00
Stef Heyenrath
d83f308591 1.0.2.6 2017-10-30 08:49:16 +01:00
Oleksandr Liakhevych
c8c9ab99c5 Fix proxy headers handling (#60)
* Fix proxy headers handling

* Small refactor and moved proxy tests to new test-file.
2017-10-28 18:59:34 +02:00
Oleksandr Liakhevych
d134684bcb Add ability to provide multiple values for headers in response (#59)
* Add ability to provide multiple values for headers

* Updated json model
2017-10-27 21:49:03 +02:00
Stef Heyenrath
a96b7bca1e WithBodyFromFile (relative) 2017-10-27 12:12:13 +02:00
Stef Heyenrath
cbe6a0a2b4 WithBodyFromFile (#56) 2017-10-25 21:33:57 +02:00
Stef Heyenrath
15370a89ca 1.0.2.5 2017-10-24 18:12:39 +02:00
Stef Heyenrath
656f7f5432 HttpClientHelper : BodyAsBytes 2017-10-23 20:56:01 +02:00
deeptowncitizen
bcdc3d646a fix header (#55) 2017-10-23 20:25:06 +02:00
Stef Heyenrath
10350fac04 Merge branch 'appbuilder' 2017-10-18 20:19:03 +02:00
Stef Heyenrath
37244d5c2f PreWireMockMiddlewareInit / PostWireMockMiddlewareInit 2017-10-17 20:47:19 +02:00
Stef Heyenrath
07f03997c0 Fix issues with Bytes for Response 2017-10-14 10:48:58 +02:00
Stef Heyenrath
7c289d44a7 Update client interface for Scenarios 2017-10-10 18:21:22 +02:00
106 changed files with 4455 additions and 1820 deletions

150
CHANGELOG.md Normal file
View File

@@ -0,0 +1,150 @@
# 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
GitReleaseNotes.exe . /OutputFile CHANGELOG.md /allTags

View File

@@ -3,9 +3,9 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w
[![Build status](https://ci.appveyor.com/api/projects/status/b3n6q3ygbww4lyls?svg=true)](https://ci.appveyor.com/project/StefH/wiremock-net)
[![codecov](https://codecov.io/gh/WireMock-Net/WireMock.Net/branch/master/graph/badge.svg)](https://codecov.io/gh/WireMock-Net/WireMock.Net)
[![Coverage Status](https://coveralls.io/repos/github/StefH/WireMock.Net/badge.svg?branch=master)](https://coveralls.io/github/StefH/WireMock.Net?branch=master)
[![GitHub issues](https://img.shields.io/github/issues/StefH/WireMock.Net.svg)](https://github.com/StefH/WireMock.Net/issues)
[![GitHub stars](https://img.shields.io/github/stars/StefH/WireMock.Net.svg)](https://github.com/StefH/WireMock.Net/stargazers)
[![Coverage Status](https://coveralls.io/repos/github/WireMock-Net/WireMock.Net/badge.svg?branch=master)](https://coveralls.io/github/WireMock-Net/WireMock.Net?branch=master)
[![GitHub issues](https://img.shields.io/github/issues/WireMock-Net/WireMock.Net.svg)](https://github.com/WireMock-Net/WireMock.Net/issues)
[![GitHub stars](https://img.shields.io/github/stars/WireMock-Net/WireMock.Net.svg)](https://github.com/WireMock-Net/WireMock.Net/stargazers)
| Name | NuGet |
| ---- | ----- |
@@ -28,7 +28,7 @@ To build you need:
## 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
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
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
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
# Visual Studio 15
VisualStudioVersion = 15.0.26430.13
VisualStudioVersion = 15.0.27130.2027
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF242EDF-7133-4277-9A0C-18744DE08707}"
EndProject
@@ -9,7 +9,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject
.runsettings = .runsettings
appveyor.yml = appveyor.yml
CHANGELOG.md = CHANGELOG.md
codecov-local.cmd = codecov-local.cmd
GitReleaseNotes.txt = GitReleaseNotes.txt
README.md = README.md
EndProjectSection
EndProject
@@ -21,11 +23,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net", "src\WireMoc
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Tests", "test\WireMock.Net.Tests\WireMock.Net.Tests.csproj", "{31DC2EF8-C3FE-467D-84BE-FB5D956E612E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.NETCoreApp", "examples\WireMock.Net.Console.NETCoreApp\WireMock.Net.Console.NETCoreApp.csproj", "{FE281639-B014-4C8A-96FA-141164A74713}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.NETCoreApp", "examples\WireMock.Net.Console.NETCoreApp\WireMock.Net.Console.NETCoreApp.csproj", "{FE281639-B014-4C8A-96FA-141164A74713}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.Record.NETCoreApp", "examples\WireMock.Net.Console.Record.NETCoreApp\WireMock.Net.Console.Record.NETCoreApp.csproj", "{1995E414-F197-4AB4-90C2-68D806B5AF59}"
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
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "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
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.StandAlone", "src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj", "{B6269AAC-170A-43D5-8B9A-579DED3D9A95}"
EndProject
@@ -36,6 +38,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.StandAlone.Net
{B6269AAC-170A-43D5-8B9A-579DED3D9A95} = {B6269AAC-170A-43D5-8B9A-579DED3D9A95}
EndProjectSection
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}"
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
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -74,6 +84,22 @@ Global
{668F689E-57B4-422E-8846-C0FF643CA999}.Debug|Any CPU.Build.0 = Debug|Any CPU
{668F689E-57B4-422E-8846-C0FF643CA999}.Release|Any CPU.ActiveCfg = Release|Any CPU
{668F689E-57B4-422E-8846-C0FF643CA999}.Release|Any CPU.Build.0 = Release|Any CPU
{668F689E-57B4-422E-8846-C0FF643CA268}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{668F689E-57B4-422E-8846-C0FF643CA268}.Debug|Any CPU.Build.0 = Debug|Any CPU
{668F689E-57B4-422E-8846-C0FF643CA268}.Release|Any CPU.ActiveCfg = Release|Any CPU
{668F689E-57B4-422E-8846-C0FF643CA268}.Release|Any CPU.Build.0 = Release|Any CPU
{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
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -87,5 +113,12 @@ Global
{B6269AAC-170A-43D5-8B9A-579DED3D9A95} = {EF242EDF-7133-4277-9A0C-18744DE08707}
{10E16614-61CA-48D8-8BDD-664C13913DED} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{668F689E-57B4-422E-8846-C0FF643CA999} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{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
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BF428BCC-C837-433B-87D2-15C7014B73E9}
EndGlobalSection
EndGlobal

View File

@@ -21,7 +21,6 @@ environment:
before_build:
- dotnet restore .\src\WireMock.Net\WireMock.Net.csproj
- dotnet restore .\src\WireMock.Net.Standalone\WireMock.Net.Standalone.csproj
- nuget restore .\examples\WireMock.Net.ConsoleApplication\WireMock.Net.ConsoleApplication.csproj -PackagesDirectory packages
build_script:
# build WireMock.Net
@@ -39,6 +38,6 @@ test_script:
- nuget.exe install coveralls.net -ExcludeVersion
- pip install codecov
- cmd: '"OpenCover\tools\OpenCover.Console.exe" -register:user -target:dotnet.exe -targetargs:"test test\WireMock.Net.Tests\WireMock.Net.Tests.csproj --no-build" -returntargetcode -filter:"+[WireMock.Net]* -[WireMock.Net.Tests*]*" -output:coverage.xml -oldstyle -searchdirs:".\test\WireMock.Net.Tests\bin\%CONFIGURATION%\net452"'
- cmd: '"OpenCover\tools\OpenCover.Console.exe" -target:dotnet.exe -targetargs:"test test\WireMock.Net.Tests\WireMock.Net.Tests.csproj --no-build" -output:coverage.xml -returntargetcode -register:user -filter:"+[WireMock.Net]* -[WireMock.Net.Tests*]*" -nodefaultfilters -returntargetcode -oldstyle -searchdirs:".\test\WireMock.Net.Tests\bin\%CONFIGURATION%\net452"'
- codecov -f "coverage.xml"
- coveralls.net\tools\csmacnz.Coveralls.exe --opencover -i .\coverage.xml

View File

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

View File

@@ -18,14 +18,20 @@
<Content Include="__admin\mappings\11111110-a633-40e8-a244-5cb80bc0ab66.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="__admin\mappings\826aff7c-6208-4a3c-923d-575248907db4.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
</ItemGroup>
<ItemGroup>
<None Update="__admin\mappings\791a3f31-6946-4ce7-8e6f-0237c7443275.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="__admin\mappings\791a3f31-6946-4ce7-8e6f-0237c7443275.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -14,8 +14,9 @@
},
"Response": {
"BodyAsJson": { "body": "static mapping" },
"Headers": {
"Content-Type": "application/json"
}
"Headers": {
"Content-Type": "application/json",
"Test-X": [ "test 1", "test 2" ]
}
}
}

View File

@@ -0,0 +1,36 @@
{
"Guid": "791a3f31-6946-4ce7-8e6f-0237c7443275",
"Title": "",
"Priority": 0,
"Request": {
"Path": {
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "/proxy-google-test-post"
}
]
},
"Methods": [
"post"
],
"Body": {}
},
"Response": {
"StatusCode": 404,
"Body": "<!DOCTYPE html>\n<html lang=en>\n <meta charset=utf-8>\n <meta name=viewport content=\"initial-scale=1, minimum-scale=1, width=device-width\">\n <title>Error 404 (Not Found)!!1</title>\n <style>\n *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}\n </style>\n <a href=//www.google.com/><span id=logo aria-label=Google></span></a>\n <p><b>404.</b> <ins>Thats an error.</ins>\n <p>The requested URL <code>/proxy-google-test-post</code> was not found on this server. <ins>Thats all we know.</ins>\n",
"BodyAsBytes": "PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ZW4+CiAgPG1ldGEgY2hhcnNldD11dGYtOD4KICA8bWV0YSBuYW1lPXZpZXdwb3J0IGNvbnRlbnQ9ImluaXRpYWwtc2NhbGU9MSwgbWluaW11bS1zY2FsZT0xLCB3aWR0aD1kZXZpY2Utd2lkdGgiPgogIDx0aXRsZT5FcnJvciA0MDQgKE5vdCBGb3VuZCkhITE8L3RpdGxlPgogIDxzdHlsZT4KICAgICp7bWFyZ2luOjA7cGFkZGluZzowfWh0bWwsY29kZXtmb250OjE1cHgvMjJweCBhcmlhbCxzYW5zLXNlcmlmfWh0bWx7YmFja2dyb3VuZDojZmZmO2NvbG9yOiMyMjI7cGFkZGluZzoxNXB4fWJvZHl7bWFyZ2luOjclIGF1dG8gMDttYXgtd2lkdGg6MzkwcHg7bWluLWhlaWdodDoxODBweDtwYWRkaW5nOjMwcHggMCAxNXB4fSogPiBib2R5e2JhY2tncm91bmQ6dXJsKC8vd3d3Lmdvb2dsZS5jb20vaW1hZ2VzL2Vycm9ycy9yb2JvdC5wbmcpIDEwMCUgNXB4IG5vLXJlcGVhdDtwYWRkaW5nLXJpZ2h0OjIwNXB4fXB7bWFyZ2luOjExcHggMCAyMnB4O292ZXJmbG93OmhpZGRlbn1pbnN7Y29sb3I6Izc3Nzt0ZXh0LWRlY29yYXRpb246bm9uZX1hIGltZ3tib3JkZXI6MH1AbWVkaWEgc2NyZWVuIGFuZCAobWF4LXdpZHRoOjc3MnB4KXtib2R5e2JhY2tncm91bmQ6bm9uZTttYXJnaW4tdG9wOjA7bWF4LXdpZHRoOm5vbmU7cGFkZGluZy1yaWdodDowfX0jbG9nb3tiYWNrZ3JvdW5kOnVybCgvL3d3dy5nb29nbGUuY29tL2ltYWdlcy9icmFuZGluZy9nb29nbGVsb2dvLzF4L2dvb2dsZWxvZ29fY29sb3JfMTUweDU0ZHAucG5nKSBuby1yZXBlYXQ7bWFyZ2luLWxlZnQ6LTVweH1AbWVkaWEgb25seSBzY3JlZW4gYW5kIChtaW4tcmVzb2x1dGlvbjoxOTJkcGkpeyNsb2dve2JhY2tncm91bmQ6dXJsKC8vd3d3Lmdvb2dsZS5jb20vaW1hZ2VzL2JyYW5kaW5nL2dvb2dsZWxvZ28vMngvZ29vZ2xlbG9nb19jb2xvcl8xNTB4NTRkcC5wbmcpIG5vLXJlcGVhdCAwJSAwJS8xMDAlIDEwMCU7LW1vei1ib3JkZXItaW1hZ2U6dXJsKC8vd3d3Lmdvb2dsZS5jb20vaW1hZ2VzL2JyYW5kaW5nL2dvb2dsZWxvZ28vMngvZ29vZ2xlbG9nb19jb2xvcl8xNTB4NTRkcC5wbmcpIDB9fUBtZWRpYSBvbmx5IHNjcmVlbiBhbmQgKC13ZWJraXQtbWluLWRldmljZS1waXhlbC1yYXRpbzoyKXsjbG9nb3tiYWNrZ3JvdW5kOnVybCgvL3d3dy5nb29nbGUuY29tL2ltYWdlcy9icmFuZGluZy9nb29nbGVsb2dvLzJ4L2dvb2dsZWxvZ29fY29sb3JfMTUweDU0ZHAucG5nKSBuby1yZXBlYXQ7LXdlYmtpdC1iYWNrZ3JvdW5kLXNpemU6MTAwJSAxMDAlfX0jbG9nb3tkaXNwbGF5OmlubGluZS1ibG9jaztoZWlnaHQ6NTRweDt3aWR0aDoxNTBweH0KICA8L3N0eWxlPgogIDxhIGhyZWY9Ly93d3cuZ29vZ2xlLmNvbS8+PHNwYW4gaWQ9bG9nbyBhcmlhLWxhYmVsPUdvb2dsZT48L3NwYW4+PC9hPgogIDxwPjxiPjQwNC48L2I+IDxpbnM+VGhhdOKAmXMgYW4gZXJyb3IuPC9pbnM+CiAgPHA+VGhlIHJlcXVlc3RlZCBVUkwgPGNvZGU+L3Byb3h5LWdvb2dsZS10ZXN0LXBvc3Q8L2NvZGU+IHdhcyBub3QgZm91bmQgb24gdGhpcyBzZXJ2ZXIuICA8aW5zPlRoYXTigJlzIGFsbCB3ZSBrbm93LjwvaW5zPgo=",
"BodyEncoding": {
"CodePage": 65001,
"EncodingName": "Unicode (UTF-8)",
"WebName": "utf-8"
},
"UseTransformer": false,
"Headers": {
"Date": "Wed, 27 Oct 2017 18:57:40 GMT",
"Alt-Svc": "quic=\":443\"; ma=2592000; v=\"39,38,37,35\"",
"Referrer-Policy": "no-referrer",
"Connection": "close"
}
}
}

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.Settings;
namespace WireMock.Net.Console.Record.NETCoreApp
namespace WireMock.Net.Console.Proxy.NETCoreApp
{
static class Program
{
@@ -10,13 +10,16 @@ namespace WireMock.Net.Console.Record.NETCoreApp
{
var server = FluentMockServer.Start(new FluentMockServerSettings
{
Urls = new[] { "http://localhost:9095/", "https://localhost:9096/" },
Urls = new[] { "http://localhost:9091/", "https://localhost:9443/" },
StartAdminInterface = true,
ReadStaticMappings = false,
ProxyAndRecordSettings = new ProxyAndRecordSettings
{
Url = "https://www.msn.com",
//X509Certificate2ThumbprintOrSubjectName = "www.yourclientcertname.com OR yourcertificatethumbprint (only if the service you're proxying to requires it)",
SaveMapping = true
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" }
}
});

View File

@@ -7,12 +7,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineArgumentsParser" Version="3.0.10" />
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.1" />
<Folder Include="__admin\mappings\" />
</ItemGroup>
</Project>

View File

@@ -1,5 +1,4 @@
using System;
using System.Linq;
using Newtonsoft.Json;
using WireMock.Matchers;
using WireMock.RequestBuilders;
@@ -21,13 +20,75 @@ namespace WireMock.Net.ConsoleApplication
{
Urls = new[] { url1, url2, url3 },
StartAdminInterface = true,
ReadStaticMappings = true
ReadStaticMappings = true,
WatchStaticMappings = true,
//ProxyAndRecordSettings = new ProxyAndRecordSettings
//{
// SaveMapping = true
//},
PreWireMockMiddlewareInit = app => { System.Console.WriteLine($"PreWireMockMiddlewareInit : {app.GetType()}"); },
PostWireMockMiddlewareInit = app => { System.Console.WriteLine($"PostWireMockMiddlewareInit : {app.GetType()}"); }
});
System.Console.WriteLine("FluentMockServer listening at {0}", string.Join(" and ", server.Urls));
System.Console.WriteLine("FluentMockServer listening at {0}", string.Join(",", server.Urls));
server.SetBasicAuthentication("a", "b");
// server.AllowPartialMapping();
server.AllowPartialMapping();
// .WithHeader("Stef", "Stef")
//server
// .Given(Request.Create().WithPath("*"))
// .RespondWith(Response.Create()
// .WithProxy("http://restcountries.eu"));
server
.Given(Request
.Create()
.WithPath(new WildcardMatcher("/navision/OData/Company('My Company')/School*", true))
.WithParam("$filter", "(substringof(Code, 'WA')")
.UsingGet())
.RespondWith(Response.Create()
.WithBody(@"{ ""result"": ""odata""}"));
server
.Given(Request.Create().WithPath("/headers", "/headers_test").UsingPost().WithHeader("Content-Type", "application/json*"))
.RespondWith(Response.Create()
.WithStatusCode(201)
.WithHeader("MyHeader", "application/json", "application/json2")
.WithBody(@"{ ""result"": ""data posted with 201""}"));
server
.Given(Request.Create().WithPath("/file").UsingGet())
.RespondWith(Response.Create()
.WithBodyFromFile(@"c:\temp\x.json", false)
);
server
.Given(Request.Create().WithPath("/filecache").UsingGet())
.RespondWith(Response.Create()
.WithBodyFromFile(@"c:\temp\x.json")
);
server
.Given(Request.Create().WithPath("/file_rel").UsingGet())
.RespondWith(Response.Create()
.WithBodyFromFile("Program.cs", false)
);
server
.Given(Request.Create().WithHeader("ProxyThis", "true")
.UsingGet())
.RespondWith(Response.Create()
.WithProxy("http://www.google.com")
);
server
.Given(Request.Create().WithPath("/bodyasbytes.png")
.UsingGet())
.RespondWith(Response.Create()
.WithHeader("Content-Type", "image/png")
.WithBody(Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTczbp9jAAAAJ0lEQVQoU2NgUPuPD6Hz0RCEAtJoiAxpCCBXGgmRIo0TofORkdp/AMiMdRVnV6O0AAAAAElFTkSuQmCC"))
);
server
.Given(Request.Create().WithPath("/oauth2/access").UsingPost().WithBody("grant_type=password;username=u;password=p"))
@@ -58,13 +119,6 @@ namespace WireMock.Net.ConsoleApplication
.WithHeader("Content-Type", "application/json")
.WithBody(@"{ ""result"": ""data posted with FUNC 201""}"));
server
.Given(Request.Create().WithPath("/data", "/ax").UsingPost().WithHeader("Content-Type", "application/json*"))
.RespondWith(Response.Create()
.WithStatusCode(201)
.WithHeader("Content-Type", "application/json")
.WithBody(@"{ ""result"": ""data posted with 201""}"));
server
.Given(Request.Create().WithPath("/json").UsingPost().WithBody(new JsonPathMatcher("$.things[?(@.name == 'RequiredThing')]")))
.RespondWith(Response.Create()

View File

@@ -47,9 +47,6 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="WireMock.Net">
<HintPath>..\..\src\WireMock.Net\bin\$(Configuration)\net45\WireMock.Net.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="MainApp.cs" />
@@ -67,6 +64,16 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj">
<Project>{b6269aac-170a-43d5-8b9a-579ded3d9a95}</Project>
<Name>WireMock.Net.StandAlone</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj">
<Project>{d3804228-91f4-4502-9595-39584e5a01ad}</Project>
<Name>WireMock.Net</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -7,15 +7,15 @@ namespace WireMock.Net.StandAlone.NETCoreApp
class Program
{
private static int sleepTime = 30000;
private static FluentMockServer server;
private static FluentMockServer _server;
static void Main(string[] args)
{
server = StandAloneApp.Start(args);
_server = StandAloneApp.Start(args);
Console.WriteLine($"{DateTime.UtcNow} Press Ctrl+C to shut down");
System.Console.CancelKeyPress += (s,e) =>
Console.CancelKeyPress += (s, e) =>
{
Stop("CancelKeyPress");
};
@@ -25,7 +25,7 @@ namespace WireMock.Net.StandAlone.NETCoreApp
Stop("AssemblyLoadContext.Default.Unloading");
};
while(true)
while (true)
{
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server running");
Thread.Sleep(sleepTime);
@@ -35,7 +35,7 @@ namespace WireMock.Net.StandAlone.NETCoreApp
private static void Stop(string why)
{
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopping because '{why}'");
server.Stop();
_server.Stop();
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopped");
}
}

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

@@ -18,7 +18,7 @@ namespace WireMock.Net.StandAlone
/// </summary>
/// <param name="settings">The FluentMockServerSettings</param>
[PublicAPI]
public static FluentMockServer Start([NotNull] FluentMockServerSettings settings)
public static FluentMockServer Start([NotNull] IFluentMockServerSettings settings)
{
Check.NotNull(settings, nameof(settings));
@@ -43,6 +43,7 @@ namespace WireMock.Net.StandAlone
{
StartAdminInterface = parser.GetBoolValue("StartAdminInterface", true),
ReadStaticMappings = parser.GetBoolValue("ReadStaticMappings"),
WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"),
AllowPartialMapping = parser.GetBoolValue("AllowPartialMapping", true),
AdminUsername = parser.GetStringValue("AdminUsername"),
AdminPassword = parser.GetStringValue("AdminPassword"),
@@ -66,7 +67,9 @@ namespace WireMock.Net.StandAlone
{
Url = proxyURL,
SaveMapping = parser.GetBoolValue("SaveMapping"),
X509Certificate2ThumbprintOrSubjectName = parser.GetStringValue("X509Certificate2ThumbprintOrSubjectName")
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
ClientX509Certificate2ThumbprintOrSubjectName = parser.GetStringValue("ClientX509Certificate2ThumbprintOrSubjectName"),
BlackListedHeaders = parser.GetValues("BlackListedHeaders")
};
}
@@ -74,7 +77,7 @@ namespace WireMock.Net.StandAlone
FluentMockServer server = Start(settings);
Console.WriteLine("WireMock.Net server listening at {0}", string.Join(" and ", server.Urls));
Console.WriteLine("WireMock.Net server listening at {0}", string.Join(",", server.Urls));
return server;
}

View File

@@ -1,27 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Lightweight StandAlone Http Mocking Server for .Net.</Description>
<AssemblyTitle>WireMock.Net.StandAlone</AssemblyTitle>
<Version>1.0.2.4</Version>
<Version>1.0.3</Version>
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<!-- <TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks> -->
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>WireMock.Net.StandAlone</AssemblyName>
<PackageId>WireMock.Net.StandAlone</PackageId>
<PackageTags>tdd;mock;http;wiremock;test;server;unittest</PackageTags>
<PackageReleaseNotes></PackageReleaseNotes>
<PackageReleaseNotes>See CHANGELOG.md</PackageReleaseNotes>
<PackageIconUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/WireMock.Net-Logo.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
<PackageLicenseUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/LICENSE</PackageLicenseUrl>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<DebugType>portable</DebugType>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
<RootNamespace>WireMock.Net.StandAlone</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition=" '$(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' ">
<DefineConstants>NETSTANDARD</DefineConstants>

View File

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

View File

@@ -10,79 +10,71 @@ namespace WireMock.Admin.Mappings
/// <summary>
/// Gets or sets the HTTP status.
/// </summary>
/// <value>
/// The HTTP status.
/// </value>
public int? StatusCode { get; set; }
/// <summary>
/// Gets or sets the body destination (SameAsSource, String or Bytes).
/// </summary>
public string BodyDestination { get; set; }
/// <summary>
/// Gets or sets the body.
/// </summary>
/// <value>
/// The body.
/// </value>
public string Body { get; set; }
/// <summary>
/// Gets or sets the body.
/// </summary>
/// <value>
/// The body.
/// </value>
public string BodyAsBase64 { get; set; }
public string BodyFromBase64 { get; set; }
/// <summary>
/// Gets or sets the body (as JSON object).
/// </summary>
/// <value>
/// The body.
/// </value>
public object BodyAsJson { get; set; }
/// <summary>
/// Gets or sets the body (as bytearray).
/// </summary>
public byte[] BodyAsBytes { get; set; }
/// <summary>
/// Gets or sets the body as a file.
/// </summary>
public string BodyAsFile { get; set; }
/// <summary>
/// Is the body as file cached?
/// </summary>
public bool? BodyAsFileIsCached { get; set; }
/// <summary>
/// Gets or sets the body encoding.
/// </summary>
/// <value>
/// The body encoding.
/// </value>
public EncodingModel BodyEncoding { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [use transformer].
/// </summary>
/// <value>
/// <c>true</c> if [use transformer]; otherwise, <c>false</c>.
/// </value>
public bool UseTransformer { get; set; }
/// <summary>
/// Gets or sets the headers.
/// </summary>
/// <value>
/// The headers.
/// </value>
public IDictionary<string, string> Headers { get; set; }
public IDictionary<string, object> Headers { get; set; }
/// <summary>
/// Gets or sets the Headers (Raw).
/// </summary>
/// <value>
/// The Headers (Raw).
/// </value>
public string HeadersRaw { get; set; }
/// <summary>
/// Gets or sets the delay in milliseconds.
/// </summary>
/// <value>
/// The delay in milliseconds.
/// </value>
public int? Delay { get; set; }
/// <summary>
/// Gets or sets the Proxy URL.
/// </summary>
/// <value>ProxyUrl</value>
public string ProxyUrl { get; set; }
/// <summary>

View File

@@ -23,17 +23,11 @@ namespace WireMock.Admin.Requests
/// <summary>
/// Gets or sets the Path.
/// </summary>
/// <value>
/// The Path.
/// </value>
public string Path { get; set; }
/// <summary>
/// Gets or sets the absolete URL.
/// </summary>
/// <value>
/// The absolute URL.
/// </value>
public string AbsoluteUrl { get; set; }
/// <summary>
@@ -44,41 +38,26 @@ namespace WireMock.Admin.Requests
/// <summary>
/// Gets or sets the method.
/// </summary>
/// <value>
/// The method.
/// </value>
public string Method { get; set; }
/// <summary>
/// Gets or sets the Headers.
/// </summary>
/// <value>
/// The Headers.
/// </value>
public IDictionary<string, string> Headers { get; set; }
public IDictionary<string, WireMockList<string>> Headers { get; set; }
/// <summary>
/// Gets or sets the Cookies.
/// </summary>
/// <value>
/// The Cookies.
/// </value>
public IDictionary<string, string> Cookies { get; set; }
/// <summary>
/// Gets or sets the body.
/// </summary>
/// <value>
/// The body.
/// </value>
public string Body { get; set; }
/// <summary>
/// Gets or sets the body encoding.
/// </summary>
/// <value>
/// The body encoding.
/// </value>
public EncodingModel BodyEncoding { get; set; }
}
}

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using WireMock.Admin.Mappings;
using WireMock.Util;
namespace WireMock.Admin.Requests
{
@@ -16,13 +17,33 @@ namespace WireMock.Admin.Requests
/// <summary>
/// Gets the headers.
/// </summary>
public IDictionary<string, string> Headers { get; set; }
public IDictionary<string, WireMockList<string>> Headers { get; set; }
/// <summary>
/// Gets or sets the body destination (SameAsSource, String or Bytes).
/// </summary>
public string BodyDestination { get; set; }
/// <summary>
/// Gets or sets the body.
/// </summary>
public string Body { get; set; }
/// <summary>
/// Gets or sets the body as bytes.
/// </summary>
public byte[] BodyAsBytes { get; set; }
/// <summary>
/// Gets or sets the body as file.
/// </summary>
public string BodyAsFile { get; set; }
/// <summary>
/// Is the body as file cached?
/// </summary>
public bool? BodyAsFileIsCached { get; set; }
/// <summary>
/// Gets or sets the original body.
/// </summary>

View File

@@ -55,6 +55,7 @@ namespace WireMock.Client
/// </summary>
/// <param name="mapping">MappingModel</param>
[Post("__admin/mappings")]
[Header("Content-Type", "application/json")]
Task<string> PostMappingAsync([Body] MappingModel mapping);
/// <summary>
@@ -103,7 +104,7 @@ namespace WireMock.Client
/// </summary>
/// <returns>LogRequestModels</returns>
[Get("__admin/requests")]
Task<IList<LogRequestModel>> GetRequestsAsync();
Task<IList<LogEntryModel>> GetRequestsAsync();
/// <summary>
/// Delete all requests.
@@ -123,7 +124,7 @@ namespace WireMock.Client
/// <param name="guid">The Guid</param>
/// <returns>MappingModel</returns>
[Get("__admin/requests/{guid}")]
Task<LogRequestModel> GetRequestAsync([Path] Guid guid);
Task<LogEntryModel> GetRequestAsync([Path] Guid guid);
/// <summary>
/// Delete a request based on the guid
@@ -137,6 +138,25 @@ namespace WireMock.Client
/// </summary>
/// <param name="model">The RequestModel</param>
[Post("__admin/requests/find")]
Task<IList<LogRequestModel>> FindRequestsAsync([Body] RequestModel model);
[Header("Content-Type", "application/json")]
Task<IList<LogEntryModel>> FindRequestsAsync([Body] RequestModel model);
/// <summary>
/// Get all scenarios
/// </summary>
[Get("__admin/scenarios")]
Task<string> GetScenariosAsync();
/// <summary>
/// Delete (reset) all scenarios
/// </summary>
[Delete("__admin/scenarios")]
Task<string> DeleteScenariosAsync();
/// <summary>
/// Delete (reset) all scenarios
/// </summary>
[Post("__admin/scenarios")]
Task<string> ResetScenariosAsync();
}
}

View File

@@ -42,10 +42,10 @@ namespace WireMock
internal class ProxyAsyncResponseProvider : IResponseProvider
{
private readonly Func<RequestMessage, ProxyAndRecordSettings, Task<ResponseMessage>> _responseMessageFunc;
private readonly ProxyAndRecordSettings _settings;
private readonly Func<RequestMessage, IProxyAndRecordSettings, Task<ResponseMessage>> _responseMessageFunc;
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(settings, nameof(settings));

View File

@@ -1,90 +1,155 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Newtonsoft.Json;
using WireMock.HttpsCertificate;
using WireMock.Validation;
namespace WireMock.Http
{
internal static class HttpClientHelper
{
private static HttpClient CreateHttpClient(string clientX509Certificate2ThumbprintOrSubjectName = null)
public static HttpClient CreateHttpClient(string clientX509Certificate2ThumbprintOrSubjectName = null)
{
#if NETSTANDARD || NET46
var handler = new HttpClientHandler
{
CheckCertificateRevocationList = false,
SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls,
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
#else
var handler = new WebRequestHandler
{
ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
#endif
if (!string.IsNullOrEmpty(clientX509Certificate2ThumbprintOrSubjectName))
{
#if NETSTANDARD || NET46
var handler = new HttpClientHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual,
SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls,
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
};
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
var x509Certificate2 = CertificateUtil.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName);
var x509Certificate2 = ClientCertificateHelper.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName);
handler.ClientCertificates.Add(x509Certificate2);
#else
var handler = new WebRequestHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual,
ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
var x509Certificate2 = CertificateUtil.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName);
handler.ClientCertificates.Add(x509Certificate2);
return new HttpClient(handler);
#endif
}
return new HttpClient();
// For proxy we shouldn't follow auto redirects
handler.AllowAutoRedirect = false;
// If UseCookies enabled, httpClient ignores Cookie header
handler.UseCookies = false;
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(RequestMessage requestMessage, string url, string clientX509Certificate2ThumbprintOrSubjectName = null)
public static async Task<ResponseMessage> SendAsync(HttpClient client, RequestMessage requestMessage, string url)
{
var client = CreateHttpClient(clientX509Certificate2ThumbprintOrSubjectName);
Check.NotNull(client, nameof(client));
var originalUri = new Uri(requestMessage.Url);
var requiredUri = new Uri(url);
var httpRequestMessage = new HttpRequestMessage(new HttpMethod(requestMessage.Method), url);
// Set Body if present
if (requestMessage.BodyAsBytes != null)
{
httpRequestMessage.Content = new ByteArrayContent(requestMessage.BodyAsBytes);
}
// Overwrite the host header
httpRequestMessage.Headers.Host = new Uri(url).Authority;
httpRequestMessage.Headers.Host = requiredUri.Authority;
// Set headers if present
if (requestMessage.Headers != null)
{
foreach (var headerName in requestMessage.Headers.Keys.Where(k => k.ToUpper() != "HOST"))
foreach (var header in requestMessage.Headers.Where(header => !string.Equals(header.Key, "HOST", StringComparison.OrdinalIgnoreCase)))
{
httpRequestMessage.Headers.Add(headerName, new[] { requestMessage.Headers[headerName] });
// Try to add to request headers. If failed - try to add to content headers
if (!httpRequestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value))
{
httpRequestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
}
}
// Set Body if present
if (requestMessage.BodyAsBytes != null && requestMessage.BodyAsBytes.Length > 0)
{
httpRequestMessage.Content = new ByteArrayContent(requestMessage.BodyAsBytes);
}
// Call the URL
var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead);
// Create transform response
var responseMessage = new ResponseMessage { StatusCode = (int)httpResponseMessage.StatusCode };
// Transform response
var responseMessage = new ResponseMessage
// Set both content and response headers, replacing URLs in values
var headers = (httpResponseMessage.Content?.Headers.Union(httpResponseMessage.Headers) ?? Enumerable.Empty<KeyValuePair<string, IEnumerable<string>>>()).ToArray();
var contentTypeHeader = headers.FirstOrDefault(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentType, StringComparison.OrdinalIgnoreCase));
if (httpResponseMessage.Content != null)
{
StatusCode = (int)httpResponseMessage.StatusCode,
Body = await httpResponseMessage.Content.ReadAsStringAsync()
};
SetBody(httpResponseMessage.Content, contentTypeHeader, responseMessage);
}
foreach (var header in httpResponseMessage.Headers)
foreach (var header in headers)
{
responseMessage.AddHeader(header.Key, header.Value.FirstOrDefault());
// If Location header contains absolute redirect URL, and base URL is one that we proxy to,
// we need to replace it to original one.
if (string.Equals(header.Key, HttpKnownHeaderNames.Location, StringComparison.OrdinalIgnoreCase)
&& Uri.TryCreate(header.Value.First(), UriKind.Absolute, out Uri absoluteLocationUri)
&& string.Equals(absoluteLocationUri.Host, requiredUri.Host, StringComparison.OrdinalIgnoreCase))
{
var replacedLocationUri = new Uri(originalUri, absoluteLocationUri.PathAndQuery);
responseMessage.AddHeader(header.Key, replacedLocationUri.ToString());
}
else
{
responseMessage.AddHeader(header.Key, header.Value.ToArray());
}
}
return responseMessage;
}
private static async void SetBody(HttpContent content, KeyValuePair<string, IEnumerable<string>> contentTypeHeader, ResponseMessage responseMessage)
{
bool contentTypeIsDefault = contentTypeHeader.Equals(default(KeyValuePair<string, IEnumerable<string>>));
string[] textContentTypes = { "text/", "application/xml", "application/javascript", "application/typescript", "application/xhtml+xml" };
if (!contentTypeIsDefault && contentTypeHeader.Value.Any(value => textContentTypes.Any(t => value != null && value.StartsWith(t, StringComparison.OrdinalIgnoreCase))))
{
try
{
responseMessage.Body = await content.ReadAsStringAsync();
}
catch
{
// Reading as string failed, just get the ByteArray.
responseMessage.BodyAsBytes = await content.ReadAsByteArrayAsync();
}
}
else if (!contentTypeIsDefault && contentTypeHeader.Value.Any(value => value != null && value.StartsWith("application/json", StringComparison.OrdinalIgnoreCase)))
{
string stringContent = await content.ReadAsStringAsync();
try
{
responseMessage.BodyAsJson = JsonConvert.DeserializeObject(stringContent, new JsonSerializerSettings { Formatting = Formatting.Indented });
}
catch
{
// JsonConvert failed, just set the Body as string.
responseMessage.Body = stringContent;
}
}
else
{
responseMessage.BodyAsBytes = await content.ReadAsByteArrayAsync();
}
}
}
}

View File

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

View File

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

View File

@@ -1,9 +1,9 @@
using System;
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)
{

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

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

View File

@@ -25,7 +25,7 @@ namespace WireMock.Matchers.Request
/// Initializes a new instance of the <see cref="RequestMessageClientIPMatcher"/> class.
/// </summary>
/// <param name="clientIPs">The clientIPs.</param>
public RequestMessageClientIPMatcher([NotNull] params string[] clientIPs) : this(clientIPs.Select(ip => new WildcardMatcher(ip)).ToArray())
public RequestMessageClientIPMatcher([NotNull] params string[] clientIPs) : this(clientIPs.Select(ip => new WildcardMatcher(ip)).Cast<IMatcher>().ToArray())
{
}
@@ -49,14 +49,7 @@ namespace WireMock.Matchers.Request
Funcs = funcs;
}
/// <summary>
/// Determines whether the specified RequestMessage is match.
/// </summary>
/// <param name="requestMessage">The RequestMessage.</param>
/// <param name="requestMatchResult">The RequestMatchResult.</param>
/// <returns>
/// A value between 0.0 - 1.0 of the similarity.
/// </returns>
/// <inheritdoc cref="IRequestMatcher.GetMatchingScore"/>
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using WireMock.Util;
using WireMock.Validation;
namespace WireMock.Matchers.Request
@@ -9,12 +10,13 @@ namespace WireMock.Matchers.Request
/// <summary>
/// The request header matcher.
/// </summary>
/// <inheritdoc cref="IRequestMatcher"/>
public class RequestMessageHeaderMatcher : IRequestMatcher
{
/// <summary>
/// The functions
/// </summary>
public Func<IDictionary<string, string>, bool>[] Funcs { get; }
public Func<IDictionary<string, string[]>, bool>[] Funcs { get; }
/// <summary>
/// The name
@@ -31,7 +33,7 @@ namespace WireMock.Matchers.Request
/// </summary>
/// <param name="name">The name.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">The ignoreCase.</param>
/// <param name="ignoreCase">if set to <c>true</c> [ignore case].</param>
public RequestMessageHeaderMatcher([NotNull] string name, [NotNull] string pattern, bool ignoreCase = true)
{
Check.NotNull(name, nameof(name));
@@ -41,6 +43,21 @@ namespace WireMock.Matchers.Request
Matchers = new IMatcher[] { new WildcardMatcher(pattern, ignoreCase) };
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> class.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">if set to <c>true</c> [ignore case].</param>
public RequestMessageHeaderMatcher([NotNull] string name, [NotNull] string[] patterns, bool ignoreCase = true)
{
Check.NotNull(name, nameof(name));
Check.NotNull(patterns, nameof(patterns));
Name = name;
Matchers = patterns.Select(pattern => new WildcardMatcher(pattern, ignoreCase)).Cast<IMatcher>().ToArray();
}
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> class.
/// </summary>
@@ -59,21 +76,14 @@ namespace WireMock.Matchers.Request
/// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> class.
/// </summary>
/// <param name="funcs">The funcs.</param>
public RequestMessageHeaderMatcher([NotNull] params Func<IDictionary<string, string>, bool>[] funcs)
public RequestMessageHeaderMatcher([NotNull] params Func<IDictionary<string, string[]>, bool>[] funcs)
{
Check.NotNull(funcs, nameof(funcs));
Funcs = funcs;
}
/// <summary>
/// Determines whether the specified RequestMessage is match.
/// </summary>
/// <param name="requestMessage">The RequestMessage.</param>
/// <param name="requestMatchResult">The RequestMatchResult.</param>
/// <returns>
/// A value between 0.0 - 1.0 of the similarity.
/// </returns>
/// <inheritdoc cref="IRequestMatcher.GetMatchingScore"/>
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);
@@ -86,7 +96,7 @@ namespace WireMock.Matchers.Request
return MatchScores.Mismatch;
if (Funcs != null)
return MatchScores.ToScore(Funcs.Any(f => f(requestMessage.Headers)));
return MatchScores.ToScore(Funcs.Any(f => f(requestMessage.Headers.ToDictionary(entry => entry.Key, entry => entry.Value.ToArray()))));
if (Matchers == null)
return MatchScores.Mismatch;
@@ -94,8 +104,8 @@ namespace WireMock.Matchers.Request
if (!requestMessage.Headers.ContainsKey(Name))
return MatchScores.Mismatch;
string value = requestMessage.Headers[Name];
return Matchers.Max(m => m.IsMatch(value));
WireMockList<string> list = requestMessage.Headers[Name];
return Matchers.Max(m => list.Max(value => m.IsMatch(value))); // TODO : is this correct ?
}
}
}

View File

@@ -30,12 +30,8 @@ namespace WireMock.Matchers.Request
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageParamMatcher"/> class.
/// </summary>
/// <param name="key">
/// The key.
/// </param>
/// <param name="values">
/// The values.
/// </param>
/// <param name="key">The key.</param>
/// <param name="values">The values.</param>
public RequestMessageParamMatcher([NotNull] string key, [CanBeNull] IEnumerable<string> values)
{
Check.NotNull(key, nameof(key));
@@ -51,17 +47,11 @@ namespace WireMock.Matchers.Request
public RequestMessageParamMatcher([NotNull] params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs)
{
Check.NotNull(funcs, nameof(funcs));
Funcs = funcs;
}
/// <summary>
/// Determines whether the specified RequestMessage is match.
/// </summary>
/// <param name="requestMessage">The RequestMessage.</param>
/// <param name="requestMatchResult">The RequestMatchResult.</param>
/// <returns>
/// A value between 0.0 - 1.0 of the similarity.
/// </returns>
/// <inheritdoc cref="IRequestMatcher.GetMatchingScore"/>
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);
@@ -71,7 +61,9 @@ namespace WireMock.Matchers.Request
private double IsMatch(RequestMessage requestMessage)
{
if (Funcs != null)
{
return MatchScores.ToScore(requestMessage.Query != null && Funcs.Any(f => f(requestMessage.Query)));
}
List<string> values = requestMessage.GetParameter(Key);

View File

@@ -25,7 +25,7 @@ namespace WireMock.Matchers.Request
/// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class.
/// </summary>
/// <param name="paths">The paths.</param>
public RequestMessagePathMatcher([NotNull] params string[] paths) : this(paths.Select(path => new WildcardMatcher(path)).ToArray())
public RequestMessagePathMatcher([NotNull] params string[] paths) : this(paths.Select(path => new WildcardMatcher(path)).Cast<IMatcher>().ToArray())
{
}
@@ -49,14 +49,7 @@ namespace WireMock.Matchers.Request
Funcs = funcs;
}
/// <summary>
/// Determines whether the specified RequestMessage is match.
/// </summary>
/// <param name="requestMessage">The RequestMessage.</param>
/// <param name="requestMatchResult">The RequestMatchResult.</param>
/// <returns>
/// A value between 0.0 - 1.0 of the similarity.
/// </returns>
/// <inheritdoc cref="IRequestMatcher.GetMatchingScore"/>
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);

View File

@@ -25,7 +25,7 @@ namespace WireMock.Matchers.Request
/// Initializes a new instance of the <see cref="RequestMessageUrlMatcher"/> class.
/// </summary>
/// <param name="urls">The urls.</param>
public RequestMessageUrlMatcher([NotNull] params string[] urls) : this(urls.Select(url => new WildcardMatcher(url)).ToArray())
public RequestMessageUrlMatcher([NotNull] params string[] urls) : this(urls.Select(url => new WildcardMatcher(url)).Cast<IMatcher>().ToArray())
{
}
@@ -49,14 +49,7 @@ namespace WireMock.Matchers.Request
Funcs = funcs;
}
/// <summary>
/// Determines whether the specified RequestMessage is match.
/// </summary>
/// <param name="requestMessage">The RequestMessage.</param>
/// <param name="requestMatchResult">The RequestMatchResult.</param>
/// <returns>
/// A value between 0.0 - 1.0 of the similarity.
/// </returns>
/// <inheritdoc cref="IRequestMatcher.GetMatchingScore"/>
public double GetMatchingScore(RequestMessage requestMessage, RequestMatchResult requestMatchResult)
{
double score = IsMatch(requestMessage);

View File

@@ -32,7 +32,7 @@ namespace WireMock.Matchers
/// <param name="simMetricType">The SimMetric Type</param>
public SimMetricsMatcher([NotNull] string[] patterns, SimMetricType simMetricType = SimMetricType.Levenstein)
{
Check.NotEmpty(patterns, nameof(patterns));
Check.NotNullOrEmpty(patterns, nameof(patterns));
_patterns = patterns;
_simMetricType = simMetricType;

View File

@@ -1,11 +1,15 @@
#if NETSTANDARD
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using WireMock.Http;
using WireMock.HttpsCertificate;
using WireMock.Validation;
namespace WireMock.Owin
@@ -14,7 +18,7 @@ namespace WireMock.Owin
{
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
private readonly WireMockMiddlewareOptions _options;
private readonly string[] _uriPrefixes;
private readonly string[] _urls;
private IWebHost _host;
@@ -27,7 +31,7 @@ namespace WireMock.Owin
public AspNetCoreSelfHost([NotNull] WireMockMiddlewareOptions options, [NotNull] params string[] uriPrefixes)
{
Check.NotNull(options, nameof(options));
Check.NotEmpty(uriPrefixes, nameof(uriPrefixes));
Check.NotNullOrEmpty(uriPrefixes, nameof(uriPrefixes));
foreach (string uriPrefix in uriPrefixes)
{
@@ -38,7 +42,7 @@ namespace WireMock.Owin
}
_options = options;
_uriPrefixes = uriPrefixes;
_urls = uriPrefixes;
}
public Task StartAsync()
@@ -46,14 +50,46 @@ namespace WireMock.Owin
_host = new WebHostBuilder()
.Configure(appBuilder =>
{
appBuilder.UseMiddleware<GlobalExceptionMiddleware>();
_options.PreWireMockMiddlewareInit?.Invoke(appBuilder);
appBuilder.UseMiddleware<WireMockMiddleware>(_options);
_options.PostWireMockMiddlewareInit?.Invoke(appBuilder);
})
.UseKestrel()
.UseUrls(_uriPrefixes)
.UseKestrel(options =>
{
#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();
#if NETSTANDARD1_3
System.Console.WriteLine("WireMock.Net server using netstandard1.3");
Console.WriteLine("WireMock.Net server using netstandard1.3");
return Task.Run(() =>
{
_host.Run(_cts.Token);
@@ -62,7 +98,11 @@ namespace WireMock.Owin
#else
System.Console.WriteLine("WireMock.Net server using netstandard2.0");
IsStarted = true;
return _host.RunAsync(_cts.Token);
return Task.Run(() =>
{
_host.Run();
IsStarted = true;
}, _cts.Token);
#endif
}

View File

@@ -0,0 +1,49 @@
using System;
using System.Threading.Tasks;
using Newtonsoft.Json;
#if !NETSTANDARD
using Microsoft.Owin;
#else
using Microsoft.AspNetCore.Http;
#endif
namespace WireMock.Owin
{
#if !NETSTANDARD
internal class GlobalExceptionMiddleware : OwinMiddleware
#else
internal class GlobalExceptionMiddleware
#endif
{
#if !NETSTANDARD
public GlobalExceptionMiddleware(OwinMiddleware next) : base(next) { }
#else
public GlobalExceptionMiddleware(RequestDelegate next)
{
Next = next;
}
#endif
#if NETSTANDARD
public RequestDelegate Next { get; }
#endif
private readonly OwinResponseMapper _responseMapper = new OwinResponseMapper();
#if !NETSTANDARD
public override async Task Invoke(IOwinContext ctx)
#else
public async Task Invoke(HttpContext ctx)
#endif
{
try
{
await Next?.Invoke(ctx);
}
catch (Exception ex)
{
await _responseMapper.MapAsync(new ResponseMessage { StatusCode = 500, Body = JsonConvert.SerializeObject(ex) }, ctx.Response);
}
}
}
}

View File

@@ -46,7 +46,7 @@ namespace WireMock.Owin
string bodyAsString = null;
byte[] body = null;
Encoding bodyEncoding = null;
if (request.Body != null)
if (ParseBody(method) && request.Body != null)
{
using (var streamReader = new StreamReader(request.Body))
{
@@ -57,18 +57,43 @@ namespace WireMock.Owin
body = bodyEncoding.GetBytes(bodyAsString);
}
var listenerHeaders = request.Headers;
Dictionary<string, string[]> headers = null;
if (request.Headers.Any())
{
headers = new Dictionary<string, string[]>();
foreach (var header in request.Headers)
{
headers.Add(header.Key, header.Value);
}
}
var headers = new Dictionary<string, string>();
foreach (var header in listenerHeaders)
headers.Add(header.Key, header.Value.FirstOrDefault());
var cookies = new Dictionary<string, string>();
foreach (var cookie in request.Cookies)
cookies.Add(cookie.Key, cookie.Value);
IDictionary<string, string> cookies = null;
if (request.Cookies.Any())
{
cookies = new Dictionary<string, string>();
foreach (var cookie in request.Cookies)
{
cookies.Add(cookie.Key, cookie.Value);
}
}
return new RequestMessage(url, method, clientIP, body, bodyAsString, bodyEncoding, headers, cookies) { DateTime = DateTime.Now };
}
private bool ParseBody(string method)
{
/*
HEAD - No defined body semantics.
GET - No defined body semantics.
PUT - Body supported.
POST - Body supported.
DELETE - No defined body semantics.
TRACE - Body not supported.
OPTIONS - Body supported but no semantics on usage (maybe in the future).
CONNECT - No defined body semantics
PATCH - Body supported.
*/
return new[] { "PUT", "POST", "OPTIONS", "PATCH" }.Contains(method.ToUpper());
}
}
}

View File

@@ -1,7 +1,11 @@
using System.IO;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using WireMock.Util;
#if !NETSTANDARD
using Microsoft.Owin;
#else
@@ -17,6 +21,19 @@ namespace WireMock.Owin
{
private readonly Encoding _utf8NoBom = new UTF8Encoding(false);
// https://stackoverflow.com/questions/239725/cannot-set-some-http-headers-when-using-system-net-webrequest
#if !NETSTANDARD
private static readonly IDictionary<string, Action<IOwinResponse, WireMockList<string>>> RestrictedResponseHeaders = new Dictionary<string, Action<IOwinResponse, WireMockList<string>>>(StringComparer.OrdinalIgnoreCase) {
#else
private static readonly IDictionary<string, Action<HttpResponse, WireMockList<string>>> RestrictedResponseHeaders = new Dictionary<string, Action<HttpResponse, WireMockList<string>>>(StringComparer.OrdinalIgnoreCase) {
#endif
{ "Content-Length", null },
{ "Content-Type", (r, v) => r.ContentType = v.FirstOrDefault() },
{ "Keep-Alive", null },
{ "Transfer-Encoding", null },
{ "WWW-Authenticate", null }
};
/// <summary>
/// MapAsync ResponseMessage to OwinResponse
/// </summary>
@@ -32,13 +49,54 @@ namespace WireMock.Owin
{
response.StatusCode = responseMessage.StatusCode;
responseMessage.Headers.ToList().ForEach(pair => response.Headers.Append(pair.Key, pair.Value));
// Set headers
foreach (var pair in responseMessage.Headers)
{
if (RestrictedResponseHeaders.ContainsKey(pair.Key))
{
RestrictedResponseHeaders[pair.Key]?.Invoke(response, pair.Value);
}
else
{
#if !NETSTANDARD
response.Headers.AppendValues(pair.Key, pair.Value.ToArray());
#else
response.Headers.Append(pair.Key, pair.Value.ToArray());
#endif
}
}
if (responseMessage.Body == null)
if (responseMessage.Body == null && responseMessage.BodyAsBytes == null && responseMessage.BodyAsFile == null && responseMessage.BodyAsJson == null)
{
return;
}
Encoding encoding = responseMessage.BodyEncoding ?? _utf8NoBom;
using (var writer = new StreamWriter(response.Body, encoding))
if (responseMessage.BodyAsBytes != null)
{
await response.Body.WriteAsync(responseMessage.BodyAsBytes, 0, responseMessage.BodyAsBytes.Length);
return;
}
if (responseMessage.BodyAsFile != null)
{
byte[] bytes = File.ReadAllBytes(responseMessage.BodyAsFile);
await response.Body.WriteAsync(bytes, 0, bytes.Length);
return;
}
if (responseMessage.BodyAsJson != null)
{
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);
}

View File

@@ -19,7 +19,7 @@ namespace WireMock.Owin
public OwinSelfHost([NotNull] WireMockMiddlewareOptions options, [NotNull] params string[] uriPrefixes)
{
Check.NotNull(options, nameof(options));
Check.NotEmpty(uriPrefixes, nameof(uriPrefixes));
Check.NotNullOrEmpty(uriPrefixes, nameof(uriPrefixes));
foreach (string uriPrefix in uriPrefixes)
{
@@ -57,11 +57,14 @@ namespace WireMock.Owin
private void StartServers()
{
System.Console.WriteLine("WireMock.Net server using .net 4.5.x or .net 4.6.x");
Console.WriteLine("WireMock.Net server using .net 4.5.x or higher");
Action<IAppBuilder> startup = app =>
{
app.Use<GlobalExceptionMiddleware>();
_options.PreWireMockMiddlewareInit?.Invoke(app);
app.Use<WireMockMiddleware>(_options);
_options.PostWireMockMiddlewareInit?.Invoke(app);
};
var servers = new List<IDisposable>();

View File

@@ -4,6 +4,8 @@ using WireMock.Logging;
using WireMock.Matchers.Request;
using System.Linq;
using WireMock.Matchers;
using WireMock.Util;
using Newtonsoft.Json;
#if !NETSTANDARD
using Microsoft.Owin;
#else
@@ -50,7 +52,7 @@ namespace WireMock.Owin
RequestMatchResult requestMatchResult = null;
try
{
foreach (var mapping in _options.Mappings.Where(m => m.Scenario != null))
foreach (var mapping in _options.Mappings.Where(m => m?.Scenario != null))
{
// Set start
if (!_options.Scenarios.ContainsKey(mapping.Scenario) && mapping.IsStartState)
@@ -101,8 +103,8 @@ namespace WireMock.Owin
if (targetMapping.IsAdminInterface && _options.AuthorizationMatcher != null)
{
bool present = request.Headers.TryGetValue("Authorization", out string authorization);
if (!present || _options.AuthorizationMatcher.IsMatch(authorization) < MatchScores.Perfect)
bool present = request.Headers.TryGetValue("Authorization", out WireMockList<string> authorization);
if (!present || _options.AuthorizationMatcher.IsMatch(authorization.ToString()) < MatchScores.Perfect)
{
response = new ResponseMessage { StatusCode = 401 };
return;
@@ -123,7 +125,7 @@ namespace WireMock.Owin
}
catch (Exception ex)
{
response = new ResponseMessage { StatusCode = 500, Body = ex.ToString() };
response = new ResponseMessage { StatusCode = 500, Body = JsonConvert.SerializeObject(ex) };
}
finally
{

View File

@@ -4,6 +4,12 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using WireMock.Logging;
using WireMock.Matchers;
using WireMock.Util;
#if !NETSTANDARD
using Owin;
#else
using Microsoft.AspNetCore.Builder;
#endif
namespace WireMock.Owin
{
@@ -17,12 +23,22 @@ namespace WireMock.Owin
public IList<Mapping> Mappings { get; set; } = new List<Mapping>();
public ObservableCollection<LogEntry> LogEntries { get; } = new ObservableCollection<LogEntry>();
public ObservableCollection<LogEntry> LogEntries { get; } = new ConcurentObservableCollection<LogEntry>();
public int? RequestLogExpirationDuration { get; set; }
public int? MaxRequestLogCount { get; set; }
public IDictionary<string, object> Scenarios { get; } = new ConcurrentDictionary<string, object>();
#if !NETSTANDARD
public Action<IAppBuilder> PreWireMockMiddlewareInit { get; set; }
public Action<IAppBuilder> PostWireMockMiddlewareInit { get; set; }
#else
public Action<IApplicationBuilder> PreWireMockMiddlewareInit { get; set; }
public Action<IApplicationBuilder> PostWireMockMiddlewareInit { get; set; }
#endif
}
}

View File

@@ -12,7 +12,7 @@ namespace WireMock.RequestBuilders
public interface IHeadersAndCookiesRequestBuilder : IBodyRequestBuilder, IRequestMatcher, IParamsRequestBuilder
{
/// <summary>
/// The with header.
/// Add Header matching based on name, pattern and ignoreCase.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="pattern">The pattern.</param>
@@ -20,6 +20,15 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithHeader([NotNull] string name, string pattern, bool ignoreCase = true);
/// <summary>
/// Add Header matching based on name, patterns and ignoreCase.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">ignore Case</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithHeader([NotNull] string name, string[] patterns, bool ignoreCase = true);
/// <summary>
/// The with header.
/// </summary>
@@ -33,7 +42,7 @@ namespace WireMock.RequestBuilders
/// </summary>
/// <param name="funcs">The headers funcs.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithHeader([NotNull] params Func<IDictionary<string, string>, bool>[] funcs);
IRequestBuilder WithHeader([NotNull] params Func<IDictionary<string, string[]>, bool>[] funcs);
/// <summary>
/// The with cookie.

View File

@@ -7,6 +7,14 @@ namespace WireMock.RequestBuilders
/// </summary>
public interface IMethodRequestBuilder : IHeadersAndCookiesRequestBuilder
{
/// <summary>
/// The using delete.
/// </summary>
/// <returns>
/// The <see cref="IRequestBuilder"/>.
/// </returns>
IRequestBuilder UsingDelete();
/// <summary>
/// The using get.
/// </summary>
@@ -15,6 +23,14 @@ namespace WireMock.RequestBuilders
/// </returns>
IRequestBuilder UsingGet();
/// <summary>
/// The using head.
/// </summary>
/// <returns>
/// The <see cref="IRequestBuilder"/>.
/// </returns>
IRequestBuilder UsingHead();
/// <summary>
/// The using post.
/// </summary>
@@ -24,12 +40,12 @@ namespace WireMock.RequestBuilders
IRequestBuilder UsingPost();
/// <summary>
/// The using delete.
/// The using patch.
/// </summary>
/// <returns>
/// The <see cref="IRequestBuilder"/>.
/// </returns>
IRequestBuilder UsingDelete();
IRequestBuilder UsingPatch();
/// <summary>
/// The using put.
@@ -39,14 +55,6 @@ namespace WireMock.RequestBuilders
/// </returns>
IRequestBuilder UsingPut();
/// <summary>
/// The using head.
/// </summary>
/// <returns>
/// The <see cref="IRequestBuilder"/>.
/// </returns>
IRequestBuilder UsingHead();
/// <summary>
/// The using any verb.
/// </summary>

View File

@@ -61,7 +61,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithClientIP(params IMatcher[] matchers)
{
Check.NotEmpty(matchers, nameof(matchers));
Check.NotNullOrEmpty(matchers, nameof(matchers));
_requestMatchers.Add(new RequestMessageClientIPMatcher(matchers));
return this;
@@ -74,7 +74,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithClientIP(params string[] clientIPs)
{
Check.NotEmpty(clientIPs, nameof(clientIPs));
Check.NotNullOrEmpty(clientIPs, nameof(clientIPs));
_requestMatchers.Add(new RequestMessageClientIPMatcher(clientIPs));
return this;
@@ -87,7 +87,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithClientIP(params Func<string, bool>[] funcs)
{
Check.NotEmpty(funcs, nameof(funcs));
Check.NotNullOrEmpty(funcs, nameof(funcs));
_requestMatchers.Add(new RequestMessageClientIPMatcher(funcs));
return this;
@@ -100,7 +100,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithPath(params IMatcher[] matchers)
{
Check.NotEmpty(matchers, nameof(matchers));
Check.NotNullOrEmpty(matchers, nameof(matchers));
_requestMatchers.Add(new RequestMessagePathMatcher(matchers));
return this;
@@ -113,7 +113,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithPath(params string[] paths)
{
Check.NotEmpty(paths, nameof(paths));
Check.NotNullOrEmpty(paths, nameof(paths));
_requestMatchers.Add(new RequestMessagePathMatcher(paths));
return this;
@@ -126,7 +126,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithPath(params Func<string, bool>[] funcs)
{
Check.NotEmpty(funcs, nameof(funcs));
Check.NotNullOrEmpty(funcs, nameof(funcs));
_requestMatchers.Add(new RequestMessagePathMatcher(funcs));
return this;
@@ -139,7 +139,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithUrl(params IMatcher[] matchers)
{
Check.NotEmpty(matchers, nameof(matchers));
Check.NotNullOrEmpty(matchers, nameof(matchers));
_requestMatchers.Add(new RequestMessageUrlMatcher(matchers));
return this;
@@ -152,7 +152,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithUrl(params string[] urls)
{
Check.NotEmpty(urls, nameof(urls));
Check.NotNullOrEmpty(urls, nameof(urls));
_requestMatchers.Add(new RequestMessageUrlMatcher(urls));
return this;
@@ -165,76 +165,55 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithUrl(params Func<string, bool>[] funcs)
{
Check.NotEmpty(funcs, nameof(funcs));
Check.NotNullOrEmpty(funcs, nameof(funcs));
_requestMatchers.Add(new RequestMessageUrlMatcher(funcs));
return this;
}
/// <summary>
/// The using get.
/// </summary>
/// <returns>
/// The <see cref="IRequestBuilder"/>.
/// </returns>
public IRequestBuilder UsingGet()
{
_requestMatchers.Add(new RequestMessageMethodMatcher("get"));
return this;
}
/// <summary>
/// The using post.
/// </summary>
/// <returns>
/// The <see cref="IRequestBuilder"/>.
/// </returns>
public IRequestBuilder UsingPost()
{
_requestMatchers.Add(new RequestMessageMethodMatcher("post"));
return this;
}
/// <summary>
/// The using put.
/// </summary>
/// <returns>
/// The <see cref="IRequestBuilder"/>.
/// </returns>
public IRequestBuilder UsingPut()
{
_requestMatchers.Add(new RequestMessageMethodMatcher("put"));
return this;
}
/// <summary>
/// The using delete.
/// </summary>
/// <returns>
/// The <see cref="IRequestBuilder"/>.
/// </returns>
/// <inheritdoc cref="IMethodRequestBuilder.UsingDelete"/>
public IRequestBuilder UsingDelete()
{
_requestMatchers.Add(new RequestMessageMethodMatcher("delete"));
return this;
}
/// <summary>
/// The using head.
/// </summary>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
/// <inheritdoc cref="IMethodRequestBuilder.UsingGet"/>
public IRequestBuilder UsingGet()
{
_requestMatchers.Add(new RequestMessageMethodMatcher("get"));
return this;
}
/// <inheritdoc cref="IMethodRequestBuilder.UsingHead"/>
public IRequestBuilder UsingHead()
{
_requestMatchers.Add(new RequestMessageMethodMatcher("head"));
return this;
}
/// <summary>
/// The using any verb.
/// </summary>
/// <returns>
/// The <see cref="IRequestBuilder"/>.
/// </returns>
/// <inheritdoc cref="IMethodRequestBuilder.UsingPost"/>
public IRequestBuilder UsingPost()
{
_requestMatchers.Add(new RequestMessageMethodMatcher("post"));
return this;
}
/// <inheritdoc cref="IMethodRequestBuilder.UsingPatch"/>
public IRequestBuilder UsingPatch()
{
_requestMatchers.Add(new RequestMessageMethodMatcher("patch"));
return this;
}
/// <inheritdoc cref="IMethodRequestBuilder.UsingPut"/>
public IRequestBuilder UsingPut()
{
_requestMatchers.Add(new RequestMessageMethodMatcher("put"));
return this;
}
/// <inheritdoc cref="IMethodRequestBuilder.UsingAnyVerb"/>
public IRequestBuilder UsingAnyVerb()
{
var matchers = _requestMatchers.Where(m => m is RequestMessageMethodMatcher).ToList();
@@ -246,14 +225,10 @@ namespace WireMock.RequestBuilders
return this;
}
/// <summary>
/// The using verb.
/// </summary>
/// <param name="verbs">The verbs.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
/// <inheritdoc cref="IMethodRequestBuilder.UsingVerb"/>
public IRequestBuilder UsingVerb(params string[] verbs)
{
Check.NotEmpty(verbs, nameof(verbs));
Check.NotNullOrEmpty(verbs, nameof(verbs));
_requestMatchers.Add(new RequestMessageMethodMatcher(verbs));
return this;
@@ -353,19 +328,13 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
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));
return this;
}
/// <summary>
/// With header.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">if set to <c>true</c> [ignore case].</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
/// <inheritdoc cref="IHeadersAndCookiesRequestBuilder.WithHeader(string,string,bool)"/>
public IRequestBuilder WithHeader(string name, string pattern, bool ignoreCase = true)
{
Check.NotNull(name, nameof(name));
@@ -375,6 +344,16 @@ namespace WireMock.RequestBuilders
return this;
}
/// <inheritdoc cref="IHeadersAndCookiesRequestBuilder.WithHeader(string,string[],bool)"/>
public IRequestBuilder WithHeader(string name, string[] patterns, bool ignoreCase = true)
{
Check.NotNull(name, nameof(name));
Check.NotNull(patterns, nameof(patterns));
_requestMatchers.Add(new RequestMessageHeaderMatcher(name, patterns, ignoreCase));
return this;
}
/// <summary>
/// With header.
/// </summary>
@@ -384,7 +363,7 @@ namespace WireMock.RequestBuilders
public IRequestBuilder WithHeader(string name, params IMatcher[] matchers)
{
Check.NotNull(name, nameof(name));
Check.NotEmpty(matchers, nameof(matchers));
Check.NotNullOrEmpty(matchers, nameof(matchers));
_requestMatchers.Add(new RequestMessageHeaderMatcher(name, matchers));
return this;
@@ -395,9 +374,9 @@ namespace WireMock.RequestBuilders
/// </summary>
/// <param name="funcs">The funcs.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithHeader(params Func<IDictionary<string, string>, bool>[] funcs)
public IRequestBuilder WithHeader(params Func<IDictionary<string, string[]>, bool>[] funcs)
{
Check.NotEmpty(funcs, nameof(funcs));
Check.NotNullOrEmpty(funcs, nameof(funcs));
_requestMatchers.Add(new RequestMessageHeaderMatcher(funcs));
return this;
@@ -424,7 +403,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithCookie(string name, params IMatcher[] matchers)
{
Check.NotEmpty(matchers, nameof(matchers));
Check.NotNullOrEmpty(matchers, nameof(matchers));
_requestMatchers.Add(new RequestMessageCookieMatcher(name, matchers));
return this;
@@ -437,7 +416,7 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
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));
return this;

View File

@@ -1,137 +1,177 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using WireMock.Util;
using WireMock.Validation;
using System.Text;
namespace WireMock
{
/// <summary>
/// The request.
/// </summary>
public class RequestMessage
{
/// <summary>
/// Gets the Client IP Address.
/// </summary>
public string ClientIP { get; }
/// <summary>
/// Gets the url.
/// </summary>
public string Url { get; }
/// <summary>
/// Gets the DateTime.
/// </summary>
public DateTime DateTime { get; set; }
/// <summary>
/// Gets the path.
/// </summary>
public string Path { get; }
/// <summary>
/// Gets the method.
/// </summary>
public string Method { get; }
/// <summary>
/// Gets the headers.
/// </summary>
public IDictionary<string, string> Headers { get; }
/// <summary>
/// Gets the cookies.
/// </summary>
public IDictionary<string, string> Cookies { get; }
/// <summary>
/// Gets the query.
/// </summary>
public IDictionary<string, WireMockList<string>> Query { get; } = new Dictionary<string, WireMockList<string>>();
/// <summary>
/// Gets the bodyAsBytes.
/// </summary>
public byte[] BodyAsBytes { get; }
/// <summary>
/// Gets the body.
/// </summary>
public string Body { get; }
/// <summary>
/// Gets the body encoding.
/// </summary>
public Encoding BodyEncoding { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessage"/> class.
/// </summary>
/// <param name="url">The original url.</param>
/// <param name="method">The HTTP method.</param>
/// <param name="clientIP">The client IP Address.</param>
/// <param name="bodyAsBytes">The bodyAsBytes byte[].</param>
/// <param name="body">The body string.</param>
/// <param name="bodyEncoding">The body encoding</param>
/// <param name="headers">The headers.</param>
/// <param name="cookies">The cookies.</param>
public RequestMessage([NotNull] Uri url, [NotNull] string method, [NotNull] string clientIP, [CanBeNull] byte[] bodyAsBytes = null, [CanBeNull] string body = null, [CanBeNull] Encoding bodyEncoding = null, [CanBeNull] IDictionary<string, string> headers = null, [CanBeNull] IDictionary<string, string> cookies = null)
{
Check.NotNull(url, nameof(url));
Check.NotNull(method, nameof(method));
Check.NotNull(clientIP, nameof(clientIP));
Url = url.ToString();
Path = url.AbsolutePath;
Method = method.ToLower();
ClientIP = clientIP;
BodyAsBytes = bodyAsBytes;
Body = body;
BodyEncoding = bodyEncoding;
Headers = headers;
Cookies = cookies;
string query = url.Query;
if (!string.IsNullOrEmpty(query))
{
if (query.StartsWith("?"))
{
query = query.Substring(1);
}
Query = query.Split('&').Aggregate(
new Dictionary<string, WireMockList<string>>(),
(dict, term) =>
{
var parts = term.Split('=');
string key = parts[0];
if (!dict.ContainsKey(key))
{
dict.Add(key, new WireMockList<string>());
}
if (parts.Length == 2)
{
dict[key].Add(parts[1]);
}
return dict;
});
}
}
/// <summary>
/// The get a query parameter.
/// </summary>
/// <param name="key">The key.</param>
/// <returns>The query parameter.</returns>
public List<string> GetParameter(string key)
{
return Query.ContainsKey(key) ? Query[key] : null;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using JetBrains.Annotations;
using WireMock.Util;
using WireMock.Validation;
namespace WireMock
{
/// <summary>
/// The request.
/// </summary>
public class RequestMessage
{
/// <summary>
/// Gets the Client IP Address.
/// </summary>
public string ClientIP { get; }
/// <summary>
/// Gets the url.
/// </summary>
public string Url { get; }
/// <summary>
/// Gets the DateTime.
/// </summary>
public DateTime DateTime { get; set; }
/// <summary>
/// Gets the path.
/// </summary>
public string Path { get; }
/// <summary>
/// Gets the method.
/// </summary>
public string Method { get; }
/// <summary>
/// Gets the headers.
/// </summary>
public IDictionary<string, WireMockList<string>> Headers { get; }
/// <summary>
/// Gets the cookies.
/// </summary>
public IDictionary<string, string> Cookies { get; }
/// <summary>
/// Gets the query.
/// </summary>
public IDictionary<string, WireMockList<string>> Query { get; }
/// <summary>
/// Gets the raw query.
/// </summary>
public string RawQuery { get; }
/// <summary>
/// Gets the bodyAsBytes.
/// </summary>
public byte[] BodyAsBytes { get; }
/// <summary>
/// Gets the body.
/// </summary>
public string Body { get; }
/// <summary>
/// Gets the Host
/// </summary>
public string Host { get; }
/// <summary>
/// Gets the protocol
/// </summary>
public string Protocol { get; }
/// <summary>
/// Gets the port
/// </summary>
public int Port { get; }
/// <summary>
/// Gets the origin
/// </summary>
public string Origin { get; }
/// <summary>
/// Gets the body encoding.
/// </summary>
public Encoding BodyEncoding { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessage"/> class.
/// </summary>
/// <param name="url">The original url.</param>
/// <param name="method">The HTTP method.</param>
/// <param name="clientIP">The client IP Address.</param>
/// <param name="bodyAsBytes">The bodyAsBytes byte[].</param>
/// <param name="body">The body string.</param>
/// <param name="bodyEncoding">The body encoding</param>
/// <param name="headers">The headers.</param>
/// <param name="cookies">The cookies.</param>
public RequestMessage([NotNull] Uri url, [NotNull] string method, [NotNull] string clientIP, [CanBeNull] byte[] bodyAsBytes = null, [CanBeNull] string body = null, [CanBeNull] Encoding bodyEncoding = null, [CanBeNull] IDictionary<string, string[]> headers = null, [CanBeNull] IDictionary<string, string> cookies = null)
{
Check.NotNull(url, nameof(url));
Check.NotNull(method, nameof(method));
Check.NotNull(clientIP, nameof(clientIP));
Url = url.ToString();
Protocol = url.Scheme;
Host = url.Host;
Port = url.Port;
Origin = $"{url.Scheme}://{url.Host}:{url.Port}";
Path = WebUtility.UrlDecode(url.AbsolutePath);
Method = method.ToLower();
ClientIP = clientIP;
BodyAsBytes = bodyAsBytes;
Body = body;
BodyEncoding = bodyEncoding;
Headers = headers?.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
Cookies = cookies;
RawQuery = WebUtility.UrlDecode(url.Query);
Query = ParseQuery(RawQuery);
}
private static IDictionary<string, WireMockList<string>> ParseQuery(string queryString)
{
if (string.IsNullOrEmpty(queryString))
{
return null;
}
if (queryString.StartsWith("?"))
{
queryString = queryString.Substring(1);
}
return queryString.Split('&').Aggregate(new Dictionary<string, WireMockList<string>>(),
(dict, term) =>
{
var parts = term.Split('=');
string key = parts[0];
if (!dict.ContainsKey(key))
{
dict.Add(key, new WireMockList<string>());
}
if (parts.Length == 2)
{
dict[key].Add(parts[1]);
}
return dict;
});
}
/// <summary>
/// Get a query parameter.
/// </summary>
/// <param name="key">The key.</param>
/// <returns>The query parameter.</returns>
public List<string> GetParameter(string key)
{
if (Query == null)
{
return null;
}
return Query.ContainsKey(key) ? Query[key] : null;
}
}
}

View File

@@ -0,0 +1,23 @@
namespace WireMock.ResponseBuilders
{
/// <summary>
/// Defines the BodyDestinationFormat
/// </summary>
public static class BodyDestinationFormat
{
/// <summary>
/// Same as source (no conversion)
/// </summary>
public const string SameAsSource = "SameAsSource";
/// <summary>
/// Convert to string
/// </summary>
public const string String = "String";
/// <summary>
/// Convert to bytes
/// </summary>
public const string Bytes = "Bytes";
}
}

View File

@@ -1,4 +1,5 @@
using System.Text;
using System;
using System.Text;
using JetBrains.Annotations;
namespace WireMock.ResponseBuilders
@@ -9,15 +10,25 @@ namespace WireMock.ResponseBuilders
public interface IBodyResponseBuilder : ITransformResponseBuilder
{
/// <summary>
/// The with body.
/// WithBody : Create a ... response based on a string.
/// </summary>
/// <param name="body">The body.</param>
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
/// <param name="encoding">The body encoding.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBody([NotNull] string body, [CanBeNull] Encoding encoding = null);
IResponseBuilder WithBody([NotNull] string body, [CanBeNull] string destination = BodyDestinationFormat.SameAsSource, [CanBeNull] Encoding encoding = null);
/// <summary>
/// The with body as Json.
/// WithBody : Create a ... response based on a bytearray.
/// </summary>
/// <param name="body">The body.</param>
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
/// <param name="encoding">The body encoding.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBody([NotNull] byte[] body, [CanBeNull] string destination = BodyDestinationFormat.SameAsSource, [CanBeNull] Encoding encoding = null);
/// <summary>
/// WithBody : Create a string response based on a object (which will be converted to a JSON string).
/// </summary>
/// <param name="body">The body.</param>
/// <param name="encoding">The body encoding.</param>
@@ -25,11 +36,20 @@ namespace WireMock.ResponseBuilders
IResponseBuilder WithBodyAsJson([NotNull] object body, [CanBeNull] Encoding encoding = null);
/// <summary>
/// The with body as base64.
/// WithBody : Create a string response based on a Base64 string (which will be decoded to a normal string).
/// </summary>
/// <param name="bodyAsbase64">The body asbase64.</param>
/// <param name="bodyAsbase64">The body.</param>
/// <param name="encoding">The Encoding.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBodyAsBase64([NotNull] string bodyAsbase64, [CanBeNull] Encoding encoding = null);
[Obsolete]
IResponseBuilder WithBodyFromBase64([NotNull] string bodyAsbase64, [CanBeNull] Encoding encoding = null);
/// <summary>
/// WithBodyFromFile : Create a ... response based on a File.
/// </summary>
/// <param name="filename">The filename.</param>
/// <param name="cache">Defines if this file is cached in memory or retrieved from disk everytime the response is created.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBodyFromFile([NotNull] string filename, bool cache = true);
}
}

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using WireMock.Util;
namespace WireMock.ResponseBuilders
{
@@ -12,15 +13,29 @@ namespace WireMock.ResponseBuilders
/// The with header.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="value">The value.</param>
/// <param name="values">The values.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithHeader([NotNull] string name, string value);
IResponseBuilder WithHeader([NotNull] string name, params string[] values);
/// <summary>
/// The with headers.
/// </summary>
/// <param name="headers">The headers.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithHeaders([NotNull] IDictionary<string,string> headers);
IResponseBuilder WithHeaders([NotNull] IDictionary<string, string> headers);
/// <summary>
/// The with headers.
/// </summary>
/// <param name="headers">The headers.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithHeaders([NotNull] IDictionary<string, string[]> headers);
/// <summary>
/// The with headers.
/// </summary>
/// <param name="headers">The headers.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithHeaders([NotNull] IDictionary<string, WireMockList<string>> headers);
}
}

View File

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

View File

@@ -1,13 +1,18 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Newtonsoft.Json;
using WireMock.Validation;
using WireMock.Http;
using WireMock.Settings;
using WireMock.Transformers;
using WireMock.Util;
using WireMock.Validation;
namespace WireMock.ResponseBuilders
{
@@ -16,6 +21,8 @@ namespace WireMock.ResponseBuilders
/// </summary>
public class Response : IResponseBuilder
{
private HttpClient _httpClientForProxy;
/// <summary>
/// The delay
/// </summary>
@@ -37,7 +44,7 @@ namespace WireMock.ResponseBuilders
/// <summary>
/// The client X509Certificate2 Thumbprint or SubjectName to use.
/// </summary>
public string X509Certificate2ThumbprintOrSubjectName { get; private set; }
public string ClientX509Certificate2ThumbprintOrSubjectName { get; private set; }
/// <summary>
/// Gets the response message.
@@ -125,53 +132,117 @@ namespace WireMock.ResponseBuilders
return WithStatusCode((int)HttpStatusCode.NotFound);
}
/// <summary>
/// The with header.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="value">The value.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
public IResponseBuilder WithHeader(string name, string value)
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeader(string, string[])"/>
public IResponseBuilder WithHeader(string name, params string[] values)
{
Check.NotNull(name, nameof(name));
ResponseMessage.AddHeader(name, value);
ResponseMessage.AddHeader(name, values);
return this;
}
/// <summary>
/// The with headers.
/// </summary>
/// <param name="headers">The headers.</param>
/// <returns></returns>
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeaders(IDictionary{string, string})"/>
public IResponseBuilder WithHeaders(IDictionary<string, string> headers)
{
Check.NotNull(headers, nameof(headers));
ResponseMessage.Headers = headers.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
return this;
}
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeaders(IDictionary{string, string[]})"/>
public IResponseBuilder WithHeaders(IDictionary<string, string[]> headers)
{
Check.NotNull(headers, nameof(headers));
ResponseMessage.Headers = headers.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
return this;
}
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeaders(IDictionary{string, WireMockList{string}})"/>
public IResponseBuilder WithHeaders(IDictionary<string, WireMockList<string>> headers)
{
ResponseMessage.Headers = headers;
return this;
}
/// <summary>
/// The with body.
/// </summary>
/// <param name="body">The body.</param>
/// <param name="encoding">The body encoding.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
public IResponseBuilder WithBody(string body, Encoding encoding = null)
/// <inheritdoc cref="IBodyResponseBuilder.WithBody(byte[], string, Encoding)"/>
public IResponseBuilder WithBody(byte[] body, string destination, Encoding encoding = null)
{
Check.NotNull(body, nameof(body));
ResponseMessage.Body = body;
ResponseMessage.BodyEncoding = encoding ?? Encoding.UTF8;
ResponseMessage.BodyDestination = destination;
switch (destination)
{
case BodyDestinationFormat.String:
var enc = encoding ?? Encoding.UTF8;
ResponseMessage.BodyAsBytes = null;
ResponseMessage.Body = enc.GetString(body);
ResponseMessage.BodyEncoding = enc;
break;
default:
ResponseMessage.BodyAsBytes = body;
ResponseMessage.BodyEncoding = null;
break;
}
return this;
}
/// <summary>
/// The with body (AsJson object).
/// </summary>
/// <param name="body">The body.</param>
/// <param name="encoding">The body encoding.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
/// <inheritdoc cref="IBodyResponseBuilder.WithBodyFromFile"/>
public IResponseBuilder WithBodyFromFile(string filename, bool cache = true)
{
Check.NotNull(filename, nameof(filename));
ResponseMessage.BodyEncoding = null;
ResponseMessage.BodyAsFileIsCached = cache;
if (cache)
{
ResponseMessage.Body = null;
ResponseMessage.BodyAsBytes = File.ReadAllBytes(filename);
ResponseMessage.BodyAsFile = null;
}
else
{
ResponseMessage.Body = null;
ResponseMessage.BodyAsBytes = null;
ResponseMessage.BodyAsFile = filename;
}
return this;
}
/// <inheritdoc cref="IBodyResponseBuilder.WithBody(string, string, Encoding)"/>
public IResponseBuilder WithBody(string body, string destination = BodyDestinationFormat.SameAsSource, Encoding encoding = null)
{
Check.NotNull(body, nameof(body));
encoding = encoding ?? Encoding.UTF8;
ResponseMessage.BodyDestination = destination;
switch (destination)
{
case BodyDestinationFormat.Bytes:
ResponseMessage.Body = null;
ResponseMessage.BodyAsBytes = encoding.GetBytes(body);
ResponseMessage.BodyEncoding = encoding;
break;
default:
ResponseMessage.Body = body;
ResponseMessage.BodyAsBytes = null;
ResponseMessage.BodyEncoding = encoding;
break;
}
return this;
}
/// <inheritdoc cref="IBodyResponseBuilder.WithBodyAsJson"/>
public IResponseBuilder WithBodyAsJson(object body, Encoding encoding = null)
{
Check.NotNull(body, nameof(body));
@@ -184,78 +255,67 @@ namespace WireMock.ResponseBuilders
ResponseMessage.BodyEncoding = encoding;
}
ResponseMessage.BodyDestination = null;
ResponseMessage.Body = jsonBody;
return this;
}
/// <summary>
/// The with body as base64.
/// </summary>
/// <param name="bodyAsbase64">The body asbase64.</param>
/// <param name="encoding">The Encoding.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
public IResponseBuilder WithBodyAsBase64(string bodyAsbase64, Encoding encoding = null)
/// <inheritdoc cref="IBodyResponseBuilder.WithBodyFromBase64"/>
public IResponseBuilder WithBodyFromBase64(string bodyAsbase64, Encoding encoding = null)
{
Check.NotNull(bodyAsbase64, nameof(bodyAsbase64));
encoding = encoding ?? Encoding.UTF8;
ResponseMessage.BodyDestination = null;
ResponseMessage.Body = encoding.GetString(Convert.FromBase64String(bodyAsbase64));
ResponseMessage.BodyEncoding = encoding;
return this;
}
/// <summary>
/// The with transformer.
/// </summary>
/// <returns>
/// The <see cref="IResponseBuilder"/>.
/// </returns>
/// <inheritdoc cref="ITransformResponseBuilder.WithTransformer"/>
public IResponseBuilder WithTransformer()
{
UseTransformer = true;
return this;
}
/// <summary>
/// The with delay.
/// </summary>
/// <param name="delay">The TimeSpan to delay.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
/// <inheritdoc cref="IDelayResponseBuilder.WithDelay(TimeSpan)"/>
public IResponseBuilder WithDelay(TimeSpan delay)
{
Check.Condition(delay, d => d > TimeSpan.Zero, nameof(delay));
Delay = delay;
return this;
}
/// <summary>
/// The with delay.
/// </summary>
/// <param name="milliseconds">The milliseconds to delay.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
/// <inheritdoc cref="IDelayResponseBuilder.WithDelay(int)"/>
public IResponseBuilder WithDelay(int milliseconds)
{
return WithDelay(TimeSpan.FromMilliseconds(milliseconds));
}
/// <summary>
/// With Proxy URL.
/// </summary>
/// <param name="proxyUrl">The proxy url.</param>
/// <param name="clientX509Certificate2ThumbprintOrSubjectName">The X509Certificate2 file to use for client authentication.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
/// <inheritdoc cref="IProxyResponseBuilder.WithProxy(string, string)"/>
public IResponseBuilder WithProxy(string proxyUrl, string clientX509Certificate2ThumbprintOrSubjectName = null)
{
Check.NotEmpty(proxyUrl, nameof(proxyUrl));
Check.NotNullOrEmpty(proxyUrl, nameof(proxyUrl));
ProxyUrl = proxyUrl;
X509Certificate2ThumbprintOrSubjectName = clientX509Certificate2ThumbprintOrSubjectName;
ClientX509Certificate2ThumbprintOrSubjectName = clientX509Certificate2ThumbprintOrSubjectName;
_httpClientForProxy = HttpClientHelper.CreateHttpClient(clientX509Certificate2ThumbprintOrSubjectName);
return this;
}
/// <inheritdoc cref="IProxyResponseBuilder.WithProxy(IProxyAndRecordSettings)"/>
public IResponseBuilder WithProxy(IProxyAndRecordSettings settings)
{
Check.NotNull(settings, nameof(settings));
return WithProxy(settings.Url, settings.ClientX509Certificate2ThumbprintOrSubjectName);
}
/// <summary>
/// The provide response.
/// </summary>
@@ -266,14 +326,17 @@ namespace WireMock.ResponseBuilders
Check.NotNull(requestMessage, nameof(requestMessage));
if (Delay != null)
{
await Task.Delay(Delay.Value);
}
if (ProxyUrl != null)
if (ProxyUrl != null && _httpClientForProxy != null)
{
var requestUri = new Uri(requestMessage.Url);
var proxyUri = new Uri(ProxyUrl);
var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery);
return await HttpClientHelper.SendAsync(requestMessage, proxyUriWithRequestPathAndQuery.AbsoluteUri, X509Certificate2ThumbprintOrSubjectName);
return await HttpClientHelper.SendAsync(_httpClientForProxy, requestMessage, proxyUriWithRequestPathAndQuery.AbsoluteUri);
}
if (UseTransformer)

View File

@@ -1,6 +1,9 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WireMock.Util;
using WireMock.Validation;
namespace WireMock
{
@@ -12,7 +15,7 @@ namespace WireMock
/// <summary>
/// Gets the headers.
/// </summary>
public IDictionary<string, string> Headers { get; set; } = new ConcurrentDictionary<string, string>();
public IDictionary<string, WireMockList<string>> Headers { get; set; } = new ConcurrentDictionary<string, WireMockList<string>>();
/// <summary>
/// Gets or sets the status code.
@@ -25,27 +28,64 @@ namespace WireMock
public string BodyOriginal { get; set; }
/// <summary>
/// Gets or sets the body.
/// Gets or sets the body destination (SameAsSource, String or Bytes).
/// </summary>
public string BodyDestination { get; set; }
/// <summary>
/// Gets or sets the body as a string.
/// </summary>
public string Body { get; set; }
/// <summary>
/// Gets or sets the body as a json object.
/// </summary>
public object BodyAsJson { get; set; }
/// <summary>
/// Gets or sets the body as bytes.
/// </summary>
public byte[] BodyAsBytes { get; set; }
/// <summary>
/// Gets or sets the body as a file.
/// </summary>
public string BodyAsFile { get; set; }
/// <summary>
/// Is the body as file cached?
/// </summary>
public bool? BodyAsFileIsCached { get; set; }
/// <summary>
/// Gets or sets the body encoding.
/// </summary>
public Encoding BodyEncoding { get; set; } = new UTF8Encoding(false);
/// <summary>
/// The add header.
/// Adds the header.
/// </summary>
/// <param name="name">
/// The name.
/// </param>
/// <param name="value">
/// The value.
/// </param>
/// <param name="name">The name.</param>
/// <param name="value">The value.</param>
public void AddHeader(string name, string value)
{
Headers.Add(name, value);
Headers.Add(name, new WireMockList<string>(value));
}
/// <summary>
/// Adds the header.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="values">The values.</param>
public void AddHeader(string name, params string[] values)
{
Check.NotNullOrEmpty(values, nameof(values));
var newHeaderValues = Headers.TryGetValue(name, out WireMockList<string> existingValues)
? values.Union(existingValues).ToArray()
: values;
Headers[name] = new WireMockList<string>(newHeaderValues);
}
}
}

View File

@@ -8,6 +8,7 @@ using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Util;
namespace WireMock.Serialization
{
@@ -78,7 +79,7 @@ namespace WireMock.Serialization
Funcs = Map(pm.Funcs)
}).ToList() : null,
Body = methodMatcher?.Methods != null && methodMatcher.Methods.Count(m => m == "get") == 1 ? null : new BodyModel
Body = methodMatcher?.Methods != null && methodMatcher.Methods.Any(m => m == "get") ? null : new BodyModel
{
Matcher = bodyMatcher != null ? Map(bodyMatcher.Matcher) : null,
Func = bodyMatcher != null ? Map(bodyMatcher.Func) : null,
@@ -95,42 +96,75 @@ namespace WireMock.Serialization
{
mappingModel.Response.StatusCode = null;
mappingModel.Response.Headers = null;
mappingModel.Response.BodyDestination = null;
mappingModel.Response.BodyAsJson = null;
mappingModel.Response.Body = null;
mappingModel.Response.BodyAsBytes = null;
mappingModel.Response.BodyAsFile = null;
mappingModel.Response.BodyAsFileIsCached = null;
mappingModel.Response.UseTransformer = false;
mappingModel.Response.BodyEncoding = null;
mappingModel.Response.ProxyUrl = response.ProxyUrl;
}
else
{
mappingModel.Response.BodyDestination = response.ResponseMessage.BodyDestination;
mappingModel.Response.StatusCode = response.ResponseMessage.StatusCode;
mappingModel.Response.Headers = response.ResponseMessage.Headers;
mappingModel.Response.Headers = Map(response.ResponseMessage.Headers);
mappingModel.Response.BodyAsJson = response.ResponseMessage.BodyAsJson;
mappingModel.Response.Body = response.ResponseMessage.Body;
mappingModel.Response.BodyAsBytes = response.ResponseMessage.BodyAsBytes;
mappingModel.Response.BodyAsFile = response.ResponseMessage.BodyAsFile;
mappingModel.Response.BodyAsFileIsCached = response.ResponseMessage.BodyAsFileIsCached;
mappingModel.Response.UseTransformer = response.UseTransformer;
mappingModel.Response.BodyEncoding = response.ResponseMessage.BodyEncoding != null
? new EncodingModel
if (response.ResponseMessage.BodyEncoding != null)
{
mappingModel.Response.BodyEncoding = new EncodingModel
{
EncodingName = response.ResponseMessage.BodyEncoding.EncodingName,
CodePage = response.ResponseMessage.BodyEncoding.CodePage,
WebName = response.ResponseMessage.BodyEncoding.WebName
}
: null;
};
}
}
return mappingModel;
}
public static MatcherModel[] Map([CanBeNull] IEnumerable<IMatcher> matchers)
private static IDictionary<string, object> Map(IDictionary<string, WireMockList<string>> dictionary)
{
if (dictionary == null)
{
return null;
}
var newDictionary = new Dictionary<string, object>();
foreach (var entry in dictionary)
{
object value = entry.Value.Count == 1 ? (object)entry.Value.ToString() : entry.Value;
newDictionary.Add(entry.Key, value);
}
return newDictionary;
}
private static MatcherModel[] Map([CanBeNull] IEnumerable<IMatcher> matchers)
{
if (matchers == null || !matchers.Any())
{
return null;
}
return matchers.Select(Map).Where(x => x != null).ToArray();
}
public static MatcherModel Map([CanBeNull] IMatcher matcher)
private static MatcherModel Map([CanBeNull] IMatcher matcher)
{
if (matcher == null)
{
return null;
}
var patterns = matcher.GetPatterns();
@@ -142,7 +176,7 @@ namespace WireMock.Serialization
};
}
public static string[] Map<T>([CanBeNull] IEnumerable<Func<T, bool>> funcs)
private static string[] Map<T>([CanBeNull] IEnumerable<Func<T, bool>> funcs)
{
if (funcs == null || !funcs.Any())
return null;
@@ -150,7 +184,7 @@ namespace WireMock.Serialization
return funcs.Select(Map).Where(x => x != null).ToArray();
}
public static string Map<T>([CanBeNull] Func<T, bool> func)
private static string Map<T>([CanBeNull] Func<T, bool> func)
{
return func?.ToString();
}

View File

@@ -2,23 +2,24 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Newtonsoft.Json;
using WireMock.Admin.Mappings;
using WireMock.Admin.Requests;
using WireMock.Admin.Settings;
using WireMock.Http;
using WireMock.Logging;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Serialization;
using WireMock.Settings;
using WireMock.Util;
using WireMock.Validation;
using WireMock.Http;
using System.Threading.Tasks;
using WireMock.Settings;
using WireMock.Serialization;
namespace WireMock.Server
{
@@ -28,6 +29,7 @@ namespace WireMock.Server
public partial class FluentMockServer
{
private static readonly string AdminMappingsFolder = Path.Combine("__admin", "mappings");
private const string ContentTypeJson = "application/json";
private const string AdminMappings = "/__admin/mappings";
private const string AdminRequests = "/__admin/requests";
private const string AdminSettings = "/__admin/settings";
@@ -41,56 +43,17 @@ namespace WireMock.Server
NullValueHandling = NullValueHandling.Ignore,
};
/// <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))
{
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));
}
}
#region InitAdmin
private void InitAdmin()
{
// __admin/settings
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
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));
// __admin/mappings/reset
@@ -98,7 +61,7 @@ namespace WireMock.Server
// __admin/mappings/{guid}
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));
// __admin/mappings/save
@@ -127,39 +90,153 @@ namespace WireMock.Server
// __admin/scenarios/reset
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))
{
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;
}
var watcher = new EnhancedFileSystemWatcher(folder, "*.json", 500);
watcher.Created += (sender, args) =>
{
ReadStaticMappingAndAddOrUpdate(args.FullPath);
};
watcher.Changed += (sender, args) =>
{
ReadStaticMappingAndAddOrUpdate(args.FullPath);
};
watcher.Deleted += (sender, args) =>
{
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);
if (Guid.TryParse(filenameWithoutExtension, out Guid guidFromFilename))
{
DeserializeAndAddOrUpdateMapping(FileHelper.ReadAllText(path), guidFromFilename, path);
}
else
{
DeserializeAndAddOrUpdateMapping(FileHelper.ReadAllText(path), null, path);
}
}
#endregion
#region Proxy and Record
private void InitProxyAndRecord(ProxyAndRecordSettings settings)
private HttpClient _httpClientForProxy;
private void InitProxyAndRecord(IProxyAndRecordSettings settings)
{
_httpClientForProxy = HttpClientHelper.CreateHttpClient(settings.ClientX509Certificate2ThumbprintOrSubjectName);
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 proxyUri = new Uri(settings.Url);
var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery);
var responseMessage = await HttpClientHelper.SendAsync(requestMessage, proxyUriWithRequestPathAndQuery.AbsoluteUri, settings.X509Certificate2ThumbprintOrSubjectName);
var responseMessage = await HttpClientHelper.SendAsync(_httpClientForProxy, requestMessage, proxyUriWithRequestPathAndQuery.AbsoluteUri);
if (settings.SaveMapping)
{
var mapping = ToMapping(requestMessage, responseMessage);
SaveMappingToFile(mapping);
var mapping = ToMapping(requestMessage, responseMessage, settings.BlackListedHeaders ?? new string[] { });
_options.Mappings.Add(mapping);
if (settings.SaveMappingToFile)
{
SaveMappingToFile(mapping);
}
}
return responseMessage;
}
private Mapping ToMapping(RequestMessage requestMessage, ResponseMessage responseMessage)
private Mapping ToMapping(RequestMessage requestMessage, ResponseMessage responseMessage, string[] blacklistedHeaders)
{
var request = (Request)Request.Create();
var request = Request.Create();
request.WithPath(requestMessage.Path);
request.UsingVerb(requestMessage.Method);
var response = (Response)Response.Create(responseMessage);
requestMessage.Query.Loop((key, value) => request.WithParam(key, value.ToArray()));
requestMessage.Cookies.Loop((key, value) => request.WithCookie(key, value));
return new Mapping(Guid.NewGuid(), string.Empty, request, response, 0, null, null, null);
requestMessage.Headers.Loop((key, value) =>
{
if (!blacklistedHeaders.Any(b => string.Equals(key, b, StringComparison.OrdinalIgnoreCase)))
{
request.WithHeader(key, value.ToArray());
}
});
if (requestMessage.Body != null)
{
request.WithBody(new ExactMatcher(requestMessage.Body));
}
var response = Response.Create(responseMessage);
return new Mapping(Guid.NewGuid(), string.Empty, null, request, response, 0, null, null, null);
}
#endregion
@@ -202,7 +279,9 @@ namespace WireMock.Server
var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
if (mapping == null)
{
return new ResponseMessage { StatusCode = 404, Body = "Mapping not found" };
}
var model = MappingConverter.ToMappingModel(mapping);
@@ -212,23 +291,8 @@ namespace WireMock.Server
private ResponseMessage MappingPut(RequestMessage requestMessage)
{
Guid guid = Guid.Parse(requestMessage.Path.TrimStart(AdminMappings.ToCharArray()));
var mappingModel = JsonConvert.DeserializeObject<MappingModel>(requestMessage.Body);
if (mappingModel.Request == null)
return new ResponseMessage { StatusCode = 400, Body = "Request missing" };
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);
DeserializeAndAddOrUpdateMapping(requestMessage.Body, guid);
return new ResponseMessage { Body = "Mapping added or updated" };
}
@@ -238,7 +302,9 @@ namespace WireMock.Server
Guid guid = Guid.Parse(requestMessage.Path.Substring(AdminMappings.Length + 1));
if (DeleteMapping(guid))
{
return new ResponseMessage { Body = "Mapping removed" };
}
return new ResponseMessage { Body = "Mapping not found" };
}
@@ -259,7 +325,9 @@ namespace WireMock.Server
{
string folder = Path.Combine(Directory.GetCurrentDirectory(), AdminMappingsFolder);
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
var model = MappingConverter.ToMappingModel(mapping);
string json = JsonConvert.SerializeObject(model, _settings);
@@ -289,7 +357,7 @@ namespace WireMock.Server
{
try
{
DeserializeAndAddMapping(requestMessage.Body);
DeserializeAndAddOrUpdateMapping(requestMessage.Body);
}
catch (ArgumentException a)
{
@@ -303,7 +371,7 @@ namespace WireMock.Server
return new ResponseMessage { StatusCode = 201, Body = "Mapping added" };
}
private void DeserializeAndAddMapping(string json, Guid? guid = null)
private void DeserializeAndAddOrUpdateMapping(string json, Guid? guid = null, string path = null)
{
var mappingModel = JsonConvert.DeserializeObject<MappingModel>(json);
@@ -325,11 +393,20 @@ namespace WireMock.Server
respondProvider = respondProvider.WithGuid(mappingModel.Guid.Value);
}
if (path != null)
{
respondProvider = respondProvider.WithPath(path);
}
if (!string.IsNullOrEmpty(mappingModel.Title))
{
respondProvider = respondProvider.WithTitle(mappingModel.Title);
}
if (mappingModel.Priority != null)
{
respondProvider = respondProvider.AtPriority(mappingModel.Priority.Value);
}
if (mappingModel.Scenario != null)
{
@@ -412,8 +489,12 @@ namespace WireMock.Server
Response = new LogResponseModel
{
StatusCode = logEntry.ResponseMessage.StatusCode,
BodyDestination = logEntry.ResponseMessage.BodyDestination,
Body = logEntry.ResponseMessage.Body,
BodyAsBytes = logEntry.ResponseMessage.BodyAsBytes,
BodyOriginal = logEntry.ResponseMessage.BodyOriginal,
BodyAsFile = logEntry.ResponseMessage.BodyAsFile,
BodyAsFileIsCached = logEntry.ResponseMessage.BodyAsFileIsCached,
Headers = logEntry.ResponseMessage.Headers,
BodyEncoding = logEntry.ResponseMessage.BodyEncoding != null ? new EncodingModel
{
@@ -464,7 +545,7 @@ namespace WireMock.Server
}
}
var result = dict.OrderBy(x => x.Value.AverageTotalScore).Select(x => x.Key);
var result = dict.OrderBy(x => x.Value.AverageTotalScore).Select(x => x.Key).Select(ToLogEntryModel);
return ToJson(result);
}
@@ -515,12 +596,16 @@ namespace WireMock.Server
{
string path = requestModel.Path as string;
if (path != null)
{
requestBuilder = requestBuilder.WithPath(path);
}
else
{
var pathModel = JsonUtils.ParseJTokenToObject<PathModel>(requestModel.Path);
if (pathModel?.Matchers != null)
{
requestBuilder = requestBuilder.WithPath(pathModel.Matchers.Select(MappingConverter.Map).ToArray());
}
}
}
@@ -528,17 +613,23 @@ namespace WireMock.Server
{
string url = requestModel.Url as string;
if (url != null)
{
requestBuilder = requestBuilder.WithUrl(url);
}
else
{
var urlModel = JsonUtils.ParseJTokenToObject<UrlModel>(requestModel.Url);
if (urlModel?.Matchers != null)
{
requestBuilder = requestBuilder.WithUrl(urlModel.Matchers.Select(MappingConverter.Map).ToArray());
}
}
}
if (requestModel.Methods != null)
{
requestBuilder = requestBuilder.UsingVerb(requestModel.Methods);
}
if (requestModel.Headers != null)
{
@@ -599,7 +690,12 @@ namespace WireMock.Server
if (responseModel.Headers != null)
{
responseBuilder = responseBuilder.WithHeaders(responseModel.Headers);
foreach (var entry in responseModel.Headers)
{
responseBuilder = entry.Value is string value ?
responseBuilder.WithHeader(entry.Key, value) :
responseBuilder.WithHeader(entry.Key, JsonUtils.ParseJTokenToObject<string[]>(entry.Value));
}
}
else if (responseModel.HeadersRaw != null)
{
@@ -612,17 +708,21 @@ namespace WireMock.Server
}
}
if (responseModel.Body != null)
if (responseModel.BodyAsBytes != null)
{
responseBuilder = responseBuilder.WithBody(responseModel.Body, ToEncoding(responseModel.BodyEncoding));
responseBuilder = responseBuilder.WithBody(responseModel.BodyAsBytes, responseModel.BodyDestination, ToEncoding(responseModel.BodyEncoding));
}
else if (responseModel.Body != null)
{
responseBuilder = responseBuilder.WithBody(responseModel.Body, responseModel.BodyDestination, ToEncoding(responseModel.BodyEncoding));
}
else if (responseModel.BodyAsJson != null)
{
responseBuilder = responseBuilder.WithBodyAsJson(responseModel.BodyAsJson, ToEncoding(responseModel.BodyEncoding));
}
else if (responseModel.BodyAsBase64 != null)
else if (responseModel.BodyFromBase64 != null)
{
responseBuilder = responseBuilder.WithBodyAsBase64(responseModel.BodyAsBase64, ToEncoding(responseModel.BodyEncoding));
responseBuilder = responseBuilder.WithBodyFromBase64(responseModel.BodyFromBase64, ToEncoding(responseModel.BodyEncoding));
}
if (responseModel.UseTransformer)
@@ -639,7 +739,7 @@ namespace WireMock.Server
{
Body = JsonConvert.SerializeObject(result, _settings),
StatusCode = 200,
Headers = new Dictionary<string, string> { { "Content-Type", "application/json" } }
Headers = new Dictionary<string, WireMockList<string>> { { HttpKnownHeaderNames.ContentType, new WireMockList<string>("application/json") } }
};
}

View File

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

View File

@@ -13,6 +13,7 @@ using WireMock.RequestBuilders;
using WireMock.Settings;
using WireMock.Validation;
using WireMock.Owin;
using WireMock.Serialization;
namespace WireMock.Server
{
@@ -25,12 +26,15 @@ namespace WireMock.Server
private readonly IOwinSelfHost _httpServer;
private readonly WireMockMiddlewareOptions _options = new WireMockMiddlewareOptions();
/// <summary>
/// Gets a value indicating whether this server is started.
/// </summary>
[PublicAPI]
public bool IsStarted { get; }
/// <summary>
/// Gets the ports.
/// </summary>
/// <value>
/// The ports.
/// </value>
[PublicAPI]
public List<int> Ports { get; }
@@ -59,7 +63,7 @@ namespace WireMock.Server
/// <param name="settings">The FluentMockServerSettings.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer Start(FluentMockServerSettings settings)
public static FluentMockServer Start(IFluentMockServerSettings settings)
{
Check.NotNull(settings, nameof(settings));
@@ -90,7 +94,7 @@ namespace WireMock.Server
[PublicAPI]
public static FluentMockServer Start(params string[] urls)
{
Check.NotEmpty(urls, nameof(urls));
Check.NotNullOrEmpty(urls, nameof(urls));
return new FluentMockServer(new FluentMockServerSettings
{
@@ -123,7 +127,7 @@ namespace WireMock.Server
[PublicAPI]
public static FluentMockServer StartWithAdminInterface(params string[] urls)
{
Check.NotEmpty(urls, nameof(urls));
Check.NotNullOrEmpty(urls, nameof(urls));
return new FluentMockServer(new FluentMockServerSettings
{
@@ -140,7 +144,7 @@ namespace WireMock.Server
[PublicAPI]
public static FluentMockServer StartWithAdminInterfaceAndReadStaticMappings(params string[] urls)
{
Check.NotEmpty(urls, nameof(urls));
Check.NotNullOrEmpty(urls, nameof(urls));
return new FluentMockServer(new FluentMockServerSettings
{
@@ -150,11 +154,11 @@ namespace WireMock.Server
});
}
private FluentMockServer(FluentMockServerSettings settings)
private FluentMockServer(IFluentMockServerSettings settings)
{
if (settings.Urls != null)
{
Urls = settings.Urls;
Urls = settings.Urls.Select(u => u.EndsWith("/") ? u : $"{u}/").ToArray();
}
else
{
@@ -162,11 +166,16 @@ namespace WireMock.Server
Urls = new[] { (settings.UseSSL == true ? "https" : "http") + "://localhost:" + port + "/" };
}
_options.PreWireMockMiddlewareInit = settings.PreWireMockMiddlewareInit;
_options.PostWireMockMiddlewareInit = settings.PostWireMockMiddlewareInit;
#if NETSTANDARD
_httpServer = new AspNetCoreSelfHost(_options, Urls);
#else
_httpServer = new OwinSelfHost(_options, Urls);
#endif
IsStarted = _httpServer.IsStarted;
Ports = _httpServer.Ports;
_httpServer.StartAsync();
@@ -194,6 +203,11 @@ namespace WireMock.Server
ReadStaticMappings();
}
if (settings.WatchStaticMappings == true)
{
WatchStaticMappings();
}
if (settings.ProxyAndRecordSettings != null)
{
InitProxyAndRecord(settings.ProxyAndRecordSettings);
@@ -266,7 +280,18 @@ namespace WireMock.Server
public bool DeleteMapping(Guid guid)
{
// Check a mapping exists with the same GUID, if so, remove it.
var existingMapping = _options.Mappings.FirstOrDefault(m => m.Guid == guid);
return DeleteMapping(m => m.Guid == guid);
}
private bool DeleteMapping(string path)
{
// Check a mapping exists with the same path, if so, remove it.
return DeleteMapping(m => string.Equals(m.Path, path, StringComparison.OrdinalIgnoreCase));
}
private bool DeleteMapping(Func<Mapping, bool> predicate)
{
var existingMapping = _options.Mappings.FirstOrDefault(predicate);
if (existingMapping != null)
{
_options.Mappings.Remove(existingMapping);
@@ -360,18 +385,18 @@ namespace WireMock.Server
return new RespondWithAProvider(RegisterMapping, requestMatcher);
}
/// <summary>
/// The register mapping.
/// </summary>
/// <param name="mapping">
/// The mapping.
/// </param>
private void RegisterMapping(Mapping mapping)
{
// Check a mapping exists with the same GUID, if so, remove it first.
DeleteMapping(mapping.Guid);
_options.Mappings.Add(mapping);
// Check a mapping exists with the same Guid, if so, replace it.
var existingMapping = _options.Mappings.FirstOrDefault(m => m.Guid == mapping.Guid);
if (existingMapping != null)
{
_options.Mappings[_options.Mappings.IndexOf(existingMapping)] = mapping;
}
else
{
_options.Mappings.Add(mapping);
}
}
}
}

View File

@@ -21,6 +21,13 @@ namespace WireMock.Server
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
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>
/// Define a unique identifier for this mapping.
/// </summary>

View File

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

View File

@@ -1,83 +1,72 @@
namespace WireMock.Settings
using System;
using JetBrains.Annotations;
namespace WireMock.Settings
{
/// <summary>
/// FluentMockServerSettings
/// </summary>
public class FluentMockServerSettings
public class FluentMockServerSettings : IFluentMockServerSettings
{
/// <summary>
/// Gets or sets the port.
/// </summary>
/// <value>
/// The port.
/// </value>
/// <inheritdoc cref="IFluentMockServerSettings.Port"/>
[PublicAPI]
public int? Port { get; set; }
/// <summary>
/// Gets or sets the use SSL.
/// </summary>
/// <value>
/// The use SSL.
/// </value>
/// <inheritdoc cref="IFluentMockServerSettings.UseSSL"/>
[PublicAPI]
// ReSharper disable once InconsistentNaming
public bool? UseSSL { get; set; }
/// <summary>
/// Gets or sets the start admin interface.
/// </summary>
/// <value>
/// The start admin interface.
/// </value>
/// <inheritdoc cref="IFluentMockServerSettings.StartAdminInterface"/>
[PublicAPI]
public bool? StartAdminInterface { get; set; }
/// <summary>
/// Gets or sets if the static mappings should be read at startup.
/// </summary>
/// <value>true/false</value>
/// <inheritdoc cref="IFluentMockServerSettings.ReadStaticMappings"/>
[PublicAPI]
public bool? ReadStaticMappings { get; set; }
/// <summary>
/// Gets or sets if the server should record and save requests and responses.
/// </summary>
/// <value>true/false</value>
public ProxyAndRecordSettings ProxyAndRecordSettings { get; set; }
/// <inheritdoc cref="IFluentMockServerSettings.WatchStaticMappings"/>
[PublicAPI]
public bool? WatchStaticMappings { get; set; }
/// <summary>
/// Gets or sets the urls.
/// </summary>
/// <value>
/// The urls.
/// </value>
/// <inheritdoc cref="IFluentMockServerSettings.ProxyAndRecordSettings"/>
[PublicAPI]
public IProxyAndRecordSettings ProxyAndRecordSettings { get; set; }
/// <inheritdoc cref="IFluentMockServerSettings.Urls"/>
[PublicAPI]
public string[] Urls { get; set; }
/// <summary>
/// StartTimeout
/// </summary>
/// <inheritdoc cref="IFluentMockServerSettings.StartTimeout"/>
[PublicAPI]
public int StartTimeout { get; set; } = 10000;
/// <summary>
/// Allow Partial Mapping (default set to false).
/// </summary>
/// <inheritdoc cref="IFluentMockServerSettings.AllowPartialMapping"/>
[PublicAPI]
public bool? AllowPartialMapping { get; set; }
/// <summary>
/// The username needed for __admin access.
/// </summary>
/// <inheritdoc cref="IFluentMockServerSettings.AdminUsername"/>
[PublicAPI]
public string AdminUsername { get; set; }
/// <summary>
/// The password needed for __admin access.
/// </summary>
/// <inheritdoc cref="IFluentMockServerSettings.AdminPassword"/>
[PublicAPI]
public string AdminPassword { get; set; }
/// <summary>
/// The RequestLog expiration in hours (optional).
/// </summary>
/// <inheritdoc cref="IFluentMockServerSettings.RequestLogExpirationDuration"/>
[PublicAPI]
public int? RequestLogExpirationDuration { get; set; }
/// <summary>
/// The MaxRequestLog count (optional).
/// </summary>
/// <inheritdoc cref="IFluentMockServerSettings.MaxRequestLogCount"/>
[PublicAPI]
public int? MaxRequestLogCount { get; set; }
/// <inheritdoc cref="IFluentMockServerSettings.PreWireMockMiddlewareInit"/>
[PublicAPI]
public Action<object> PreWireMockMiddlewareInit { get; set; }
/// <inheritdoc cref="IFluentMockServerSettings.PostWireMockMiddlewareInit"/>
[PublicAPI]
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,23 +1,30 @@
namespace WireMock.Settings
using JetBrains.Annotations;
namespace WireMock.Settings
{
/// <summary>
/// RecordAndSaveSettings
/// ProxyAndRecordSettings
/// </summary>
public class ProxyAndRecordSettings
public class ProxyAndRecordSettings : IProxyAndRecordSettings
{
/// <summary>
/// The URL to proxy.
/// </summary>
/// <inheritdoc cref="IProxyAndRecordSettings.Url"/>
[PublicAPI]
public string Url { get; set; }
/// <summary>
/// Save the mapping for each request/response.
/// </summary>
/// <inheritdoc cref="IProxyAndRecordSettings.SaveMapping"/>
[PublicAPI]
public bool SaveMapping { get; set; } = true;
/// <summary>
/// The clientCertificate thumbprint or subject name fragment to use. Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
/// </summary>
public string X509Certificate2ThumbprintOrSubjectName { get; set; }
/// <inheritdoc cref="IProxyAndRecordSettings.SaveMappingToFile"/>
[PublicAPI]
public bool SaveMappingToFile { get; set; } = true;
/// <inheritdoc cref="IProxyAndRecordSettings.ClientX509Certificate2ThumbprintOrSubjectName"/>
[PublicAPI]
public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
/// <inheritdoc cref="IProxyAndRecordSettings.BlackListedHeaders"/>
[PublicAPI]
public string[] BlackListedHeaders { get; set; }
}
}

View File

@@ -1,5 +1,7 @@
using HandlebarsDotNet;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using HandlebarsDotNet;
using WireMock.Util;
namespace WireMock.Transformers
{
@@ -16,13 +18,16 @@ namespace WireMock.Transformers
responseMessage.Body = templateBody(template);
// Headers
var newHeaders = new Dictionary<string, string>();
var newHeaders = new Dictionary<string, WireMockList<string>>();
foreach (var header in original.Headers)
{
var templateHeaderKey = Handlebars.Compile(header.Key);
var templateHeaderValue = Handlebars.Compile(header.Value);
var templateHeaderValues = header.Value
.Select(Handlebars.Compile)
.Select(func => func(template))
.ToArray();
newHeaders.Add(templateHeaderKey(template), templateHeaderValue(template));
newHeaders.Add(templateHeaderKey(template), new WireMockList<string>(templateHeaderValues));
}
responseMessage.Headers = newHeaders;

View File

@@ -0,0 +1,77 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace WireMock.Util
{
/// <summary>
/// A special Collection that overrides methods of <see cref="ObservableCollection{T}"/> to make them thread safe
/// </summary>
/// <typeparam name="T">The type of elements in the collection.</typeparam>
/// <inheritdoc cref="ObservableCollection{T}" />
public class ConcurentObservableCollection<T> : ObservableCollection<T>
{
private readonly object _lockObject = new object();
/// <summary>
/// Initializes a new instance of the <see cref="T:WireMock.Util.ConcurentObservableCollection`1" /> class.
/// </summary>
public ConcurentObservableCollection() { }
/// <summary>
/// Initializes a new instance of the <see cref="ConcurentObservableCollection{T}"/> class that contains elements copied from the specified list.
/// </summary>
/// <param name="list">The list from which the elements are copied.</param>
public ConcurentObservableCollection(List<T> list) : base(list) { }
/// <summary>
/// Initializes a new instance of the <see cref="ConcurentObservableCollection{T}"/> class that contains elements copied from the specified collection.
/// </summary>
/// <param name="collection">The collection from which the elements are copied.</param>
public ConcurentObservableCollection(IEnumerable<T> collection) : base(collection) { }
/// <inheritdoc cref="ObservableCollection{T}.ClearItems"/>
protected override void ClearItems()
{
lock (_lockObject)
{
base.ClearItems();
}
}
/// <inheritdoc cref="ObservableCollection{T}.RemoveItem"/>
protected override void RemoveItem(int index)
{
lock (_lockObject)
{
base.RemoveItem(index);
}
}
/// <inheritdoc cref="ObservableCollection{T}.InsertItem"/>
protected override void InsertItem(int index, T item)
{
lock (_lockObject)
{
base.InsertItem(index, item);
}
}
/// <inheritdoc cref="ObservableCollection{T}.SetItem"/>
protected override void SetItem(int index, T item)
{
lock (_lockObject)
{
base.SetItem(index, item);
}
}
/// <inheritdoc cref="ObservableCollection{T}.MoveItem"/>
protected override void MoveItem(int oldIndex, int newIndex)
{
lock (_lockObject)
{
base.MoveItem(oldIndex, newIndex);
}
}
}
}

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using WireMock.Validation;
namespace WireMock.Util
{
/// <summary>
/// Some IDictionary Extensions
/// </summary>
public static class DictionaryExtensions
{
/// <summary>
/// Loops the dictionary and executes the specified action.
/// </summary>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <param name="dictionary">The dictionary to loop (can be null).</param>
/// <param name="action">The action.</param>
public static void Loop<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, [NotNull] Action<TKey, TValue> action)
{
Check.NotNull(action, nameof(action));
if (dictionary != null)
{
foreach (var entry in dictionary)
{
action(entry.Key, entry.Value);
}
}
}
}
}

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

@@ -7,13 +7,12 @@ namespace WireMock.Util
public static T ParseJTokenToObject<T>(object value)
{
if (value == null)
{
return default(T);
}
JToken token = value as JToken;
if (token == null)
return default(T);
return token.ToObject<T>();
var token = value as JToken;
return token == null ? default(T) : token.ToObject<T>();
}
}
}

View File

@@ -74,8 +74,7 @@ namespace WireMock.Util
public void RemoveLock(string name)
{
ReaderWriterLockSlim o;
_lockDict.TryRemove(name, out o);
_lockDict.TryRemove(name, out _);
}
}
}

View File

@@ -36,9 +36,6 @@ namespace WireMock.Util
/// <summary>
/// Returns a <see cref="string" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="string" /> that represents this instance.
/// </returns>
public override string ToString()
{
if (this != null && this.Any())

View File

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

View File

@@ -1,6 +1,4 @@
using System;
using System.Globalization;
using JetBrains.Annotations;
// copied from https://github.com/aspnet/EntityFramework/blob/dev/src/Microsoft.EntityFrameworkCore/Properties/CoreStrings.resx
namespace WireMock.Validation
@@ -11,33 +9,33 @@ namespace WireMock.Validation
/// <summary>
/// The property '{property}' of the argument '{argument}' cannot be null.
/// </summary>
public static string ArgumentPropertyNull([CanBeNull] string property, [CanBeNull] string argument)
public static string ArgumentPropertyNull(string property, string argument)
{
return string.Format(CultureInfo.CurrentCulture, $"The property '{property}' of the argument '{argument}' cannot be null.", property, argument);
return $"The property '{property}' of the argument '{argument}' cannot be null.";
}
/// <summary>
/// The string argument '{argumentName}' cannot be empty.
/// </summary>
public static string ArgumentIsEmpty([CanBeNull] string argumentName)
public static string ArgumentIsEmpty(string argumentName)
{
return string.Format(CultureInfo.CurrentCulture, $"The string argument '{argumentName}' cannot be empty.", argumentName);
return $"The string argument '{argumentName}' cannot be empty.";
}
/// <summary>
/// The entity type '{type}' provided for the argument '{argumentName}' must be a reference type.
/// </summary>
public static string InvalidEntityType([CanBeNull] Type type, [CanBeNull] string argumentName)
public static string InvalidEntityType(Type type, string argumentName)
{
return string.Format(CultureInfo.CurrentCulture, $"The entity type '{type}' provided for the argument '{argumentName}' must be a reference type.", type, argumentName);
return $"The entity type '{type}' provided for the argument '{argumentName}' must be a reference type.";
}
/// <summary>
/// The collection argument '{argumentName}' must contain at least one element.
/// </summary>
public static string CollectionArgumentIsEmpty([CanBeNull] string argumentName)
public static string CollectionArgumentIsEmpty(string argumentName)
{
return string.Format(CultureInfo.CurrentCulture, $"The collection argument '{argumentName}' must contain at least one element.", argumentName);
return $"The collection argument '{argumentName}' must contain at least one element.";
}
}
}

View File

@@ -3,38 +3,45 @@
<PropertyGroup>
<Description>Lightweight Http Mocking Server for .Net, inspired by WireMock from the Java landscape.</Description>
<AssemblyTitle>WireMock.Net</AssemblyTitle>
<Version>1.0.2.4</Version>
<Version>1.0.3</Version>
<Authors>Alexandre Victoor;Stef Heyenrath</Authors>
<TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>WireMock.Net</AssemblyName>
<PackageId>WireMock.Net</PackageId>
<PackageTags>tdd;mock;http;wiremock;test;server;unittest</PackageTags>
<PackageReleaseNotes></PackageReleaseNotes>
<PackageReleaseNotes>See CHANGELOG.md</PackageReleaseNotes>
<PackageIconUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/WireMock.Net-Logo.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
<PackageLicenseUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/LICENSE</PackageLicenseUrl>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<DebugType>portable</DebugType>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
<RootNamespace>WireMock</RootNamespace>
</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'">
<DefineConstants>NETSTANDARD</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="10.4.0">
<PackageReference Include="JetBrains.Annotations" Version="11.1.0">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="Handlebars.Net" Version="1.9.0" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.1" />
<PackageReference Include="SimMetrics.Net" Version="1.0.3" />
<PackageReference Include="System.Net.Http" Version="4.3.2" />
<PackageReference Include="RestEase" Version="1.4.2" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="SimMetrics.Net" Version="1.0.4" />
<PackageReference Include="System.Net.Http" Version="4.3.3" />
<PackageReference Include="RestEase" Version="1.4.4" />
<!--<PackageReference Include="OpenSSL.X509Certificate2.Provider" Version="1.0.2" />-->
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net452' ">
@@ -49,7 +56,8 @@
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.5" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" Version="1.1.3" />
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
<PackageReference Include="System.Xml.XPath.XmlDocument" Version="4.3.0" />
</ItemGroup>

View File

@@ -0,0 +1,48 @@
using System.Linq;
using System.Threading.Tasks;
using NFluent;
using RestEase;
using WireMock.Admin.Mappings;
using WireMock.Client;
using WireMock.Server;
using Xunit;
namespace WireMock.Net.Tests
{
public class ClientTests
{
[Fact]
public async Task Client_IFluentMockServerAdmin_PostMappingAsync()
{
// Assign
var server = FluentMockServer.StartWithAdminInterface();
var api = RestClient.For<IFluentMockServerAdmin>(server.Urls[0]);
// Act
var model = new MappingModel
{
Request = new RequestModel
{
Path = "/1"
},
Response = new ResponseModel
{
Body = "txt",
StatusCode = 200
},
Priority = 500,
Title = "test"
};
string result = await api.PostMappingAsync(model);
// Assert
Check.That(result).IsNotNull();
var mapping = server.Mappings.Single(m => m.Priority == 500);
Check.That(mapping).IsNotNull();
Check.That(mapping.Title).Equals("test");
server.Stop();
}
}
}

View File

@@ -0,0 +1,64 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using NFluent;
using RestEase;
using WireMock.Admin.Mappings;
using WireMock.Client;
using WireMock.Server;
using WireMock.Settings;
using Xunit;
namespace WireMock.Net.Tests
{
public class FluentMockServerAdminRestClientTests : IDisposable
{
public void Dispose()
{
_server?.Stop();
}
private FluentMockServer _server;
[Fact]
public async Task IFluentMockServerAdmin_FindRequestsAsync()
{
// given
_server = FluentMockServer.Start(new FluentMockServerSettings { StartAdminInterface = true });
var serverUrl = "http://localhost:" + _server.Ports[0];
await new HttpClient().GetAsync(serverUrl + "/foo");
var api = RestClient.For<IFluentMockServerAdmin>(serverUrl);
// when
var requests = await api.FindRequestsAsync(new RequestModel { Methods = new[] { "get" } });
// then
Check.That(requests).HasSize(1);
var requestLogged = requests.First();
Check.That(requestLogged.Request.Method).IsEqualTo("get");
Check.That(requestLogged.Request.Body).IsNull();
Check.That(requestLogged.Request.Path).IsEqualTo("/foo");
}
[Fact]
public async Task IFluentMockServerAdmin_GetRequestsAsync()
{
// given
_server = FluentMockServer.Start(new FluentMockServerSettings { StartAdminInterface = true });
var serverUrl = "http://localhost:" + _server.Ports[0];
await new HttpClient().GetAsync(serverUrl + "/foo");
var api = RestClient.For<IFluentMockServerAdmin>(serverUrl);
// when
var requests = await api.GetRequestsAsync();
// then
Check.That(requests).HasSize(1);
var requestLogged = requests.First();
Check.That(requestLogged.Request.Method).IsEqualTo("get");
Check.That(requestLogged.Request.Body).IsNull();
Check.That(requestLogged.Request.Path).IsEqualTo("/foo");
}
}
}

View File

@@ -0,0 +1,278 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using NFluent;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
using WireMock.Settings;
using Xunit;
namespace WireMock.Net.Tests
{
public partial class FluentMockServerTests
{
private FluentMockServer _serverForProxyForwarding;
[Fact]
public async Task FluentMockServer_Proxy_Should_proxy_responses()
{
// given
_server = FluentMockServer.Start();
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create().WithProxy("http://www.google.com"));
// when
var result = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/search?q=test");
// then
Check.That(result).Contains("google");
}
[Fact]
public async Task FluentMockServer_Proxy_Should_preserve_content_header_in_proxied_request()
{
// given
_serverForProxyForwarding = FluentMockServer.Start();
_serverForProxyForwarding
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create());
var settings = new FluentMockServerSettings
{
ProxyAndRecordSettings = new ProxyAndRecordSettings
{
Url = _serverForProxyForwarding.Urls[0],
SaveMapping = true,
SaveMappingToFile = false
}
};
_server = FluentMockServer.Start(settings);
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create().WithProxy(_serverForProxyForwarding.Urls[0]));
// when
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(_server.Urls[0]),
Content = new StringContent("stringContent")
};
requestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
requestMessage.Content.Headers.Add("bbb", "test");
await new HttpClient().SendAsync(requestMessage);
// then
var receivedRequest = _serverForProxyForwarding.LogEntries.First().RequestMessage;
Check.That(receivedRequest.Body).IsEqualTo("stringContent");
Check.That(receivedRequest.Headers).ContainsKey("Content-Type");
Check.That(receivedRequest.Headers["Content-Type"]).ContainsExactly("text/plain");
Check.That(receivedRequest.Headers).ContainsKey("bbb");
var mapping = _server.Mappings.Last();
var matcher = ((Request) mapping.RequestMatcher).GetRequestMessageMatchers<RequestMessageHeaderMatcher>().FirstOrDefault(m => m.Name == "bbb");
Check.That(matcher).IsNotNull();
}
[Fact]
public async Task FluentMockServer_Proxy_Should_exclude_blacklisted_content_header_in_mapping()
{
// given
_serverForProxyForwarding = FluentMockServer.Start();
_serverForProxyForwarding
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create());
var settings = new FluentMockServerSettings
{
ProxyAndRecordSettings = new ProxyAndRecordSettings
{
Url = _serverForProxyForwarding.Urls[0],
SaveMapping = true,
SaveMappingToFile = false,
BlackListedHeaders = new[] { "bbb" }
}
};
_server = FluentMockServer.Start(settings);
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create());
// when
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(_server.Urls[0]),
Content = new StringContent("stringContent")
};
requestMessage.Content.Headers.Add("bbb", "test");
await new HttpClient().SendAsync(requestMessage);
// then
var receivedRequest = _serverForProxyForwarding.LogEntries.First().RequestMessage;
Check.That(receivedRequest.Headers).ContainsKey("bbb");
var mapping = _server.Mappings.Last();
var matcher = ((Request)mapping.RequestMatcher).GetRequestMessageMatchers<RequestMessageHeaderMatcher>().FirstOrDefault(m => m.Name == "bbb");
Check.That(matcher).IsNull();
}
[Fact]
public async Task FluentMockServer_Proxy_Should_preserve_content_header_in_proxied_request_with_empty_content()
{
// given
_serverForProxyForwarding = FluentMockServer.Start();
_serverForProxyForwarding
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create());
_server = FluentMockServer.Start();
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create().WithProxy(_serverForProxyForwarding.Urls[0]));
// when
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(_server.Urls[0]),
Content = new StringContent("")
};
requestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
await new HttpClient().SendAsync(requestMessage);
// then
var receivedRequest = _serverForProxyForwarding.LogEntries.First().RequestMessage;
Check.That(receivedRequest.Body).IsEqualTo("");
Check.That(receivedRequest.Headers).ContainsKey("Content-Type");
Check.That(receivedRequest.Headers["Content-Type"]).ContainsExactly("text/plain");
}
[Fact]
public async Task FluentMockServer_Proxy_Should_preserve_content_header_in_proxied_response()
{
// given
_serverForProxyForwarding = FluentMockServer.Start();
_serverForProxyForwarding
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create()
.WithBody("body")
.WithHeader("Content-Type", "text/plain"));
_server = FluentMockServer.Start();
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create().WithProxy(_serverForProxyForwarding.Urls[0]));
// when
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(_server.Urls[0])
};
var response = await new HttpClient().SendAsync(requestMessage);
// then
Check.That(await response.Content.ReadAsStringAsync()).IsEqualTo("body");
Check.That(response.Content.Headers.Contains("Content-Type")).IsTrue();
Check.That(response.Content.Headers.GetValues("Content-Type")).ContainsExactly("text/plain");
}
[Fact]
public async Task FluentMockServer_Proxy_Should_change_absolute_location_header_in_proxied_response()
{
// given
_serverForProxyForwarding = FluentMockServer.Start();
_serverForProxyForwarding
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create()
.WithStatusCode(HttpStatusCode.Redirect)
.WithHeader("Location", _serverForProxyForwarding.Urls[0] + "testpath"));
_server = FluentMockServer.Start();
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create().WithProxy(_serverForProxyForwarding.Urls[0]));
// when
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(_server.Urls[0])
};
var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false };
var response = await new HttpClient(httpClientHandler).SendAsync(requestMessage);
// then
Check.That(response.Headers.Contains("Location")).IsTrue();
Check.That(response.Headers.GetValues("Location")).ContainsExactly(_server.Urls[0] + "testpath");
}
[Fact]
public async Task FluentMockServer_Proxy_Should_preserve_cookie_header_in_proxied_request()
{
// given
_serverForProxyForwarding = FluentMockServer.Start();
_serverForProxyForwarding
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create());
_server = FluentMockServer.Start();
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create().WithProxy(_serverForProxyForwarding.Urls[0]));
// when
var requestUri = new Uri(_server.Urls[0]);
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = requestUri
};
var clientHandler = new HttpClientHandler();
clientHandler.CookieContainer.Add(requestUri, new Cookie("name", "value"));
await new HttpClient(clientHandler).SendAsync(requestMessage);
// then
var receivedRequest = _serverForProxyForwarding.LogEntries.First().RequestMessage;
Check.That(receivedRequest.Cookies).IsNotNull();
Check.That(receivedRequest.Cookies).ContainsPair("name", "value");
}
[Fact]
public async Task FluentMockServer_Proxy_Should_set_BodyAsJson_in_proxied_response()
{
// Assign
_serverForProxyForwarding = FluentMockServer.Start();
_serverForProxyForwarding
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create()
.WithBodyAsJson(new { i = 42 })
.WithHeader("Content-Type", "application/json; charset=utf-8"));
_server = FluentMockServer.Start();
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create().WithProxy(_serverForProxyForwarding.Urls[0]));
// Act
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(_server.Urls[0])
};
var response = await new HttpClient().SendAsync(requestMessage);
// Assert
string content = await response.Content.ReadAsStringAsync();
Check.That(content).IsEqualTo("{\"i\":42}");
Check.That(response.Content.Headers.GetValues("Content-Type")).ContainsExactly("application/json; charset=utf-8");
}
}
}

View File

@@ -1,417 +1,449 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using NFluent;
using Xunit;
using WireMock.Matchers;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
namespace WireMock.Net.Tests
{
//[TestFixture]
//[Timeout(5000)]
public class FluentMockServerTests : IDisposable
{
private FluentMockServer _server;
// For for AppVeyor + OpenCover
private string GetCurrentFolder()
{
string current = Directory.GetCurrentDirectory();
//if (!current.EndsWith("WireMock.Net.Tests"))
// return Path.Combine(current, "test", "WireMock.Net.Tests");
return current;
}
[Fact]
public void FluentMockServer_StartStop()
{
var server1 = FluentMockServer.Start("http://localhost:9091/");
server1.Stop();
var server2 = FluentMockServer.Start("http://localhost:9091/");
server2.Stop();
}
[Fact]
public void FluentMockServer_ReadStaticMapping_WithNonGuidFilename()
{
var guid = Guid.Parse("04ee4872-9efd-4770-90d3-88d445265d0d");
string title = "documentdb_root_title";
_server = FluentMockServer.Start();
string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings", "documentdb_root.json");
_server.ReadStaticMapping(folder);
var mappings = _server.Mappings.ToArray();
Check.That(mappings).HasSize(1);
Check.That(mappings.First().RequestMatcher).IsNotNull();
Check.That(mappings.First().Provider).IsNotNull();
Check.That(mappings.First().Guid).Equals(guid);
Check.That(mappings.First().Title).Equals(title);
}
[Fact]
public void FluentMockServer_ReadStaticMapping_WithGuidFilename()
{
string guid = "00000002-ee28-4f29-ae63-1ac9b0802d86";
_server = FluentMockServer.Start();
string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings", guid + ".json");
_server.ReadStaticMapping(folder);
var mappings = _server.Mappings.ToArray();
Check.That(mappings).HasSize(1);
Check.That(mappings.First().RequestMatcher).IsNotNull();
Check.That(mappings.First().Provider).IsNotNull();
Check.That(mappings.First().Guid).Equals(Guid.Parse(guid));
Check.That(mappings.First().Title).IsNullOrEmpty();
}
[Fact]
public void FluentMockServer_ReadStaticMappings()
{
_server = FluentMockServer.Start();
string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings");
_server.ReadStaticMappings(folder);
var mappings = _server.Mappings.ToArray();
Check.That(mappings).HasSize(2);
}
[Fact]
public void FluentMockServer_Admin_Mappings_Get()
{
var guid = Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f05");
_server = FluentMockServer.Start();
_server.Given(Request.Create().WithPath("/foo1").UsingGet())
.WithGuid(guid)
.RespondWith(Response.Create().WithStatusCode(201).WithBody("1"));
_server.Given(Request.Create().WithPath("/foo2").UsingGet())
.RespondWith(Response.Create().WithStatusCode(202).WithBody("2"));
var mappings = _server.Mappings.ToArray();
Check.That(mappings).HasSize(2);
Check.That(mappings.First().RequestMatcher).IsNotNull();
Check.That(mappings.First().Provider).IsNotNull();
Check.That(mappings.First().Guid).Equals(guid);
Check.That(mappings[1].Guid).Not.Equals(guid);
}
[Fact]
public void FluentMockServer_Admin_Mappings_Add_SameGuid()
{
var guid = Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f05");
_server = FluentMockServer.Start();
_server.Given(Request.Create().WithPath("/1").UsingGet())
.WithGuid(guid)
.RespondWith(Response.Create().WithStatusCode(500));
var mappings = _server.Mappings.ToArray();
Check.That(mappings).HasSize(1);
Check.That(mappings.First().Guid).Equals(guid);
_server.Given(Request.Create().WithPath("/2").UsingGet())
.WithGuid(guid)
.RespondWith(Response.Create().WithStatusCode(500));
Check.That(mappings).HasSize(1);
Check.That(mappings.First().Guid).Equals(guid);
}
[Fact]
public async Task FluentMockServer_Admin_Mappings_AtPriority()
{
_server = FluentMockServer.Start();
// given
_server.Given(Request.Create().WithPath("/1").UsingGet())
.AtPriority(2)
.RespondWith(Response.Create().WithStatusCode(200));
_server.Given(Request.Create().WithPath("/1").UsingGet())
.AtPriority(1)
.RespondWith(Response.Create().WithStatusCode(400));
var mappings = _server.Mappings.ToArray();
Check.That(mappings).HasSize(2);
Check.That(mappings[0].Priority).Equals(2);
Check.That(mappings[1].Priority).Equals(1);
// when
var response = await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/1");
// then
Check.That((int)response.StatusCode).IsEqualTo(400);
}
[Fact]
public async Task FluentMockServer_Admin_Requests_Get()
{
// given
_server = FluentMockServer.Start();
// when
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
// then
Check.That(_server.LogEntries).HasSize(1);
var requestLogged = _server.LogEntries.First();
Check.That(requestLogged.RequestMessage.Method).IsEqualTo("get");
Check.That(requestLogged.RequestMessage.BodyAsBytes).IsEmpty();
}
[Fact]
public async Task Should_respond_to_request()
{
// given
_server = FluentMockServer.Start();
_server
.Given(Request.Create()
.WithPath("/foo")
.UsingGet())
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithBody(@"{ msg: ""Hello world!""}"));
// when
var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
// then
Check.That(response).IsEqualTo(@"{ msg: ""Hello world!""}");
}
[Fact]
public async Task Should_respond_to_request_bodyAsBase64()
{
// given
_server = FluentMockServer.Start();
_server.Given(Request.Create().WithPath("/foo").UsingGet()).RespondWith(Response.Create().WithBodyAsBase64("SGVsbG8gV29ybGQ/"));
// when
var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
// then
Check.That(response).IsEqualTo("Hello World?");
}
[Fact]
public async Task Should_respond_404_for_unexpected_request()
{
// given
_server = FluentMockServer.Start();
// when
var response = await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
// then
Check.That(response.StatusCode).IsEqualTo(HttpStatusCode.NotFound);
Check.That((int)response.StatusCode).IsEqualTo(404);
}
[Fact]
public async Task Should_find_a_request_satisfying_a_request_spec()
{
// given
_server = FluentMockServer.Start();
// when
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/bar");
// then
var result = _server.FindLogEntries(Request.Create().WithPath(new RegexMatcher("^/b.*"))).ToList();
Check.That(result).HasSize(1);
var requestLogged = result.First();
Check.That(requestLogged.RequestMessage.Path).IsEqualTo("/bar");
Check.That(requestLogged.RequestMessage.Url).IsEqualTo("http://localhost:" + _server.Ports[0] + "/bar");
}
[Fact]
public async Task Should_reset_requestlogs()
{
// given
_server = FluentMockServer.Start();
// when
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
_server.ResetLogEntries();
// then
Check.That(_server.LogEntries).IsEmpty();
}
[Fact]
public void Should_reset_mappings()
{
// given
_server = FluentMockServer.Start();
_server
.Given(Request.Create()
.WithPath("/foo")
.UsingGet())
.RespondWith(Response.Create()
.WithBody(@"{ msg: ""Hello world!""}"));
// when
_server.ResetMappings();
// then
Check.That(_server.Mappings).IsEmpty();
Check.ThatAsyncCode(() => new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo"))
.ThrowsAny();
}
[Fact]
public async Task Should_respond_a_redirect_without_body()
{
// given
_server = FluentMockServer.Start();
_server
.Given(Request.Create()
.WithPath("/foo")
.UsingGet())
.RespondWith(Response.Create()
.WithStatusCode(307)
.WithHeader("Location", "/bar"));
_server
.Given(Request.Create()
.WithPath("/bar")
.UsingGet())
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithBody("REDIRECT SUCCESSFUL"));
// when
var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
// then
Check.That(response).IsEqualTo("REDIRECT SUCCESSFUL");
}
[Fact]
public async Task Should_delay_responses_for_a_given_route()
{
// given
_server = FluentMockServer.Start();
_server
.Given(Request.Create()
.WithPath("/*"))
.RespondWith(Response.Create()
.WithBody(@"{ msg: ""Hello world!""}")
.WithDelay(TimeSpan.FromMilliseconds(200)));
// when
var watch = new Stopwatch();
watch.Start();
await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
watch.Stop();
// then
Check.That(watch.ElapsedMilliseconds).IsStrictlyGreaterThan(200);
}
[Fact]
public async Task Should_delay_responses()
{
// given
_server = FluentMockServer.Start();
_server.AddGlobalProcessingDelay(TimeSpan.FromMilliseconds(200));
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create().WithBody(@"{ msg: ""Hello world!""}"));
// when
var watch = new Stopwatch();
watch.Start();
await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
watch.Stop();
// then
Check.That(watch.ElapsedMilliseconds).IsStrictlyGreaterThan(200);
}
[Fact]
public async Task Should_proxy_responses()
{
// given
_server = FluentMockServer.Start();
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create().WithProxy("http://www.google.com"));
// when
var result = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/search?q=test");
// then
Check.That(result).Contains("google");
}
//Leaving commented as this requires an actual certificate with password, along with a service that expects a client certificate
//[Fact]
//public async Task Should_proxy_responses_with_client_certificate()
//{
// // given
// _server = FluentMockServer.Start();
// _server
// .Given(Request.Create().WithPath("/*"))
// .RespondWith(Response.Create().WithProxy("https://server-that-expects-a-client-certificate", @"\\yourclientcertificatecontainingprivatekey.pfx", "yourclientcertificatepassword"));
// // when
// var result = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/someurl?someQuery=someValue");
// // then
// Check.That(result).Contains("google");
//}
[Fact]
public async Task FluentMockServer_Logging_SetMaxRequestLogCount()
{
// Assign
var client = new HttpClient();
// Act
_server = FluentMockServer.Start();
_server.SetMaxRequestLogCount(2);
await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo1");
await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo2");
await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo3");
// Assert
Check.That(_server.LogEntries).HasSize(2);
var requestLoggedA = _server.LogEntries.First();
Check.That(requestLoggedA.RequestMessage.Path).EndsWith("/foo2");
var requestLoggedB = _server.LogEntries.Last();
Check.That(requestLoggedB.RequestMessage.Path).EndsWith("/foo3");
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using NFluent;
using WireMock.Matchers;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
using Xunit;
namespace WireMock.Net.Tests
{
public partial class FluentMockServerTests : IDisposable
{
private FluentMockServer _server;
// For for AppVeyor + OpenCover
private string GetCurrentFolder()
{
string current = Directory.GetCurrentDirectory();
//if (!current.EndsWith("WireMock.Net.Tests"))
// return Path.Combine(current, "test", "WireMock.Net.Tests");
return current;
}
public void Dispose()
{
_server?.Stop();
}
}
[Fact]
public void FluentMockServer_StartStop()
{
var server1 = FluentMockServer.Start("http://localhost:9091/");
server1.Stop();
var server2 = FluentMockServer.Start("http://localhost:9091/");
server2.Stop();
}
[Fact]
public void FluentMockServer_ReadStaticMapping_WithNonGuidFilename()
{
var guid = Guid.Parse("04ee4872-9efd-4770-90d3-88d445265d0d");
string title = "documentdb_root_title";
_server = FluentMockServer.Start();
string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings", "documentdb_root.json");
_server.ReadStaticMappingAndAddOrUpdate(folder);
var mappings = _server.Mappings.ToArray();
Check.That(mappings).HasSize(1);
Check.That(mappings.First().RequestMatcher).IsNotNull();
Check.That(mappings.First().Provider).IsNotNull();
Check.That(mappings.First().Guid).Equals(guid);
Check.That(mappings.First().Title).Equals(title);
}
[Fact]
public void FluentMockServer_ReadStaticMapping_WithGuidFilename()
{
string guid = "00000002-ee28-4f29-ae63-1ac9b0802d86";
_server = FluentMockServer.Start();
string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings", guid + ".json");
_server.ReadStaticMappingAndAddOrUpdate(folder);
var mappings = _server.Mappings.ToArray();
Check.That(mappings).HasSize(1);
Check.That(mappings.First().RequestMatcher).IsNotNull();
Check.That(mappings.First().Provider).IsNotNull();
Check.That(mappings.First().Guid).Equals(Guid.Parse(guid));
Check.That(mappings.First().Title).IsNullOrEmpty();
}
[Fact]
public void FluentMockServer_ReadStaticMappings()
{
_server = FluentMockServer.Start();
string folder = Path.Combine(GetCurrentFolder(), "__admin", "mappings");
_server.ReadStaticMappings(folder);
var mappings = _server.Mappings.ToArray();
Check.That(mappings).HasSize(2);
}
[Fact]
public void FluentMockServer_Admin_Mappings_Get()
{
var guid = Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f05");
_server = FluentMockServer.Start();
_server.Given(Request.Create().WithPath("/foo1").UsingGet())
.WithGuid(guid)
.RespondWith(Response.Create().WithStatusCode(201).WithBody("1"));
_server.Given(Request.Create().WithPath("/foo2").UsingGet())
.RespondWith(Response.Create().WithStatusCode(202).WithBody("2"));
var mappings = _server.Mappings.ToArray();
Check.That(mappings).HasSize(2);
Check.That(mappings.First().RequestMatcher).IsNotNull();
Check.That(mappings.First().Provider).IsNotNull();
Check.That(mappings.First().Guid).Equals(guid);
Check.That(mappings[1].Guid).Not.Equals(guid);
}
[Fact]
public void FluentMockServer_Admin_Mappings_Add_SameGuid()
{
var guid = Guid.Parse("90356dba-b36c-469a-a17e-669cd84f1f05");
_server = FluentMockServer.Start();
var response1 = Response.Create().WithStatusCode(500);
_server.Given(Request.Create().UsingGet())
.WithGuid(guid)
.RespondWith(response1);
var mappings1 = _server.Mappings.ToArray();
Check.That(mappings1).HasSize(1);
Check.That(mappings1.First().Guid).Equals(guid);
var response2 = Response.Create().WithStatusCode(400);
_server.Given(Request.Create().WithPath("/2").UsingGet())
.WithGuid(guid)
.RespondWith(response2);
var mappings2 = _server.Mappings.ToArray();
Check.That(mappings2).HasSize(1);
Check.That(mappings2.First().Guid).Equals(guid);
Check.That(mappings2.First().Provider).Equals(response2);
}
[Fact]
public async Task FluentMockServer_Admin_Mappings_AtPriority()
{
_server = FluentMockServer.Start();
// given
_server.Given(Request.Create().WithPath("/1").UsingGet())
.AtPriority(2)
.RespondWith(Response.Create().WithStatusCode(200));
_server.Given(Request.Create().WithPath("/1").UsingGet())
.AtPriority(1)
.RespondWith(Response.Create().WithStatusCode(400));
var mappings = _server.Mappings.ToArray();
Check.That(mappings).HasSize(2);
Check.That(mappings[0].Priority).Equals(2);
Check.That(mappings[1].Priority).Equals(1);
// when
var response = await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/1");
// then
Check.That((int)response.StatusCode).IsEqualTo(400);
}
[Fact]
public async Task FluentMockServer_Admin_Requests_Get()
{
// given
_server = FluentMockServer.Start();
// when
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
// then
Check.That(_server.LogEntries).HasSize(1);
var requestLogged = _server.LogEntries.First();
Check.That(requestLogged.RequestMessage.Method).IsEqualTo("get");
Check.That(requestLogged.RequestMessage.BodyAsBytes).IsNull();
}
[Fact]
public async Task FluentMockServer_Should_respond_to_request_methodPatch()
{
// given
_server = FluentMockServer.Start();
_server.Given(Request.Create().WithPath("/foo").UsingVerb("patch"))
.RespondWith(Response.Create().WithBody("hello patch"));
// when
var msg = new HttpRequestMessage(new HttpMethod("patch"), new Uri("http://localhost:" + _server.Ports[0] + "/foo"))
{
Content = new StringContent("{\"data\": {\"attr\":\"value\"}}")
};
var response = await new HttpClient().SendAsync(msg);
// then
Check.That(response.StatusCode).IsEqualTo(HttpStatusCode.OK);
var responseBody = await response.Content.ReadAsStringAsync();
Check.That(responseBody).IsEqualTo("hello patch");
Check.That(_server.LogEntries).HasSize(1);
var requestLogged = _server.LogEntries.First();
Check.That(requestLogged.RequestMessage.Method).IsEqualTo("patch");
Check.That(requestLogged.RequestMessage.Body).IsNotNull();
Check.That(requestLogged.RequestMessage.Body).IsEqualTo("{\"data\": {\"attr\":\"value\"}}");
}
[Fact]
public async Task FluentMockServer_Should_respond_to_request_bodyAsString()
{
// given
_server = FluentMockServer.Start();
_server
.Given(Request.Create()
.WithPath("/foo")
.UsingGet())
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithBody("Hello world!"));
// when
var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
// then
Check.That(response).IsEqualTo("Hello world!");
}
[Fact]
public async Task FluentMockServer_Should_respond_to_request_bodyAsBase64()
{
// given
_server = FluentMockServer.Start();
_server.Given(Request.Create().WithPath("/foo").UsingGet()).RespondWith(Response.Create().WithBodyFromBase64("SGVsbG8gV29ybGQ/"));
// when
var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
// then
Check.That(response).IsEqualTo("Hello World?");
}
[Fact]
public async Task FluentMockServer_Should_respond_to_request_bodyAsBytes()
{
// given
_server = FluentMockServer.Start();
_server.Given(Request.Create().WithPath("/foo").UsingGet()).RespondWith(Response.Create().WithBody(new byte[] { 48, 49 }));
// when
var responseAsString = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
var responseAsBytes = await new HttpClient().GetByteArrayAsync("http://localhost:" + _server.Ports[0] + "/foo");
// then
Check.That(responseAsString).IsEqualTo("01");
Check.That(responseAsBytes).ContainsExactly(new byte[] { 48, 49 });
}
[Fact]
public async Task FluentMockServer_Should_respond_404_for_unexpected_request()
{
// given
_server = FluentMockServer.Start();
// when
var response = await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
// then
Check.That(response.StatusCode).IsEqualTo(HttpStatusCode.NotFound);
Check.That((int)response.StatusCode).IsEqualTo(404);
}
[Fact]
public async Task FluentMockServer_Should_find_a_request_satisfying_a_request_spec()
{
// given
_server = FluentMockServer.Start();
// when
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/bar");
// then
var result = _server.FindLogEntries(Request.Create().WithPath(new RegexMatcher("^/b.*"))).ToList();
Check.That(result).HasSize(1);
var requestLogged = result.First();
Check.That(requestLogged.RequestMessage.Path).IsEqualTo("/bar");
Check.That(requestLogged.RequestMessage.Url).IsEqualTo("http://localhost:" + _server.Ports[0] + "/bar");
}
[Fact]
public async Task FluentMockServer_Should_reset_requestlogs()
{
// given
_server = FluentMockServer.Start();
// when
await new HttpClient().GetAsync("http://localhost:" + _server.Ports[0] + "/foo");
_server.ResetLogEntries();
// then
Check.That(_server.LogEntries).IsEmpty();
}
[Fact]
public void FluentMockServer_Should_reset_mappings()
{
// given
_server = FluentMockServer.Start();
_server
.Given(Request.Create()
.WithPath("/foo")
.UsingGet())
.RespondWith(Response.Create()
.WithBody(@"{ msg: ""Hello world!""}"));
// when
_server.ResetMappings();
// then
Check.That(_server.Mappings).IsEmpty();
Check.ThatAsyncCode(() => new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo"))
.ThrowsAny();
}
[Fact]
public async Task FluentMockServer_Should_respond_a_redirect_without_body()
{
// given
_server = FluentMockServer.Start();
_server
.Given(Request.Create()
.WithPath("/foo")
.UsingGet())
.RespondWith(Response.Create()
.WithStatusCode(307)
.WithHeader("Location", "/bar"));
_server
.Given(Request.Create()
.WithPath("/bar")
.UsingGet())
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithBody("REDIRECT SUCCESSFUL"));
// when
var response = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
// then
Check.That(response).IsEqualTo("REDIRECT SUCCESSFUL");
}
[Fact]
public async Task FluentMockServer_Should_delay_responses_for_a_given_route()
{
// given
_server = FluentMockServer.Start();
_server
.Given(Request.Create()
.WithPath("/*"))
.RespondWith(Response.Create()
.WithBody(@"{ msg: ""Hello world!""}")
.WithDelay(TimeSpan.FromMilliseconds(200)));
// when
var watch = new Stopwatch();
watch.Start();
await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
watch.Stop();
// then
Check.That(watch.ElapsedMilliseconds).IsStrictlyGreaterThan(200);
}
[Fact]
public async Task FluentMockServer_Should_delay_responses()
{
// given
_server = FluentMockServer.Start();
_server.AddGlobalProcessingDelay(TimeSpan.FromMilliseconds(200));
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create().WithBody(@"{ msg: ""Hello world!""}"));
// when
var watch = new Stopwatch();
watch.Start();
await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/foo");
watch.Stop();
// then
Check.That(watch.ElapsedMilliseconds).IsStrictlyGreaterThan(200);
}
//Leaving commented as this requires an actual certificate with password, along with a service that expects a client certificate
//[Fact]
//public async Task Should_proxy_responses_with_client_certificate()
//{
// // given
// _server = FluentMockServer.Start();
// _server
// .Given(Request.Create().WithPath("/*"))
// .RespondWith(Response.Create().WithProxy("https://server-that-expects-a-client-certificate", @"\\yourclientcertificatecontainingprivatekey.pfx", "yourclientcertificatepassword"));
// // when
// var result = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/someurl?someQuery=someValue");
// // then
// Check.That(result).Contains("google");
//}
[Fact]
public async Task FluentMockServer_Logging_SetMaxRequestLogCount()
{
// Assign
var client = new HttpClient();
// Act
_server = FluentMockServer.Start();
_server.SetMaxRequestLogCount(2);
await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo1");
await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo2");
await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo3");
// Assert
Check.That(_server.LogEntries).HasSize(2);
var requestLoggedA = _server.LogEntries.First();
Check.That(requestLoggedA.RequestMessage.Path).EndsWith("/foo2");
var requestLoggedB = _server.LogEntries.Last();
Check.That(requestLoggedB.RequestMessage.Path).EndsWith("/foo3");
}
public void Dispose()
{
_server?.Stop();
_serverForProxyForwarding?.Stop();
}
}
}

View File

@@ -1,5 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using NFluent;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
@@ -8,12 +13,12 @@ using Xunit;
namespace WireMock.Net.Tests
{
public class ObservableLogEntriesTest: IDisposable
public class ObservableLogEntriesTest : IDisposable
{
private FluentMockServer _server;
[Fact]
public async void Test()
public async void FluentMockServer_LogEntriesChanged()
{
// Assign
_server = FluentMockServer.Start();
@@ -35,9 +40,45 @@ namespace WireMock.Net.Tests
Check.That(count).Equals(1);
}
[Fact]
public async Task FluentMockServer_LogEntriesChanged_Parallel()
{
int expectedCount = 10;
// Assign
_server = FluentMockServer.Start();
_server
.Given(Request.Create()
.WithPath("/foo")
.UsingGet())
.RespondWith(Response.Create()
.WithDelay(6)
.WithSuccess());
int count = 0;
_server.LogEntriesChanged += (sender, args) => count++;
var http = new HttpClient();
// Act
var listOfTasks = new List<Task<HttpResponseMessage>>();
for (var i = 0; i < expectedCount; i++)
{
Thread.Sleep(100);
listOfTasks.Add(http.GetAsync($"{_server.Urls[0]}/foo"));
}
var responses = await Task.WhenAll(listOfTasks);
var countResponsesWithStatusNotOk = responses.Count(r => r.StatusCode != HttpStatusCode.OK);
// Assert
Check.That(countResponsesWithStatusNotOk).Equals(0);
Check.That(count).Equals(expectedCount);
}
public void Dispose()
{
_server?.Dispose();
}
}
}
}

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using NFluent;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using Xunit;
namespace WireMock.Net.Tests
{
public class RequestCookieTests
{
private const string ClientIp = "::1";
[Fact]
public void Request_WithCookie_OK()
{
// given
var spec = Request.Create().UsingAnyVerb().WithCookie("session", "a*");
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp, null, null, null, null, new Dictionary<string, string> { { "session", "abc" } });
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
}
}

View File

@@ -8,13 +8,13 @@ namespace WireMock.Net.Tests
//[TestFixture]
public class RequestMessageTests
{
private const string clientIP = "::1";
private const string ClientIp = "::1";
[Fact]
public void Should_handle_empty_query()
{
// given
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp);
// then
Check.That(request.GetParameter("not_there")).IsNull();
@@ -26,7 +26,7 @@ namespace WireMock.Net.Tests
// given
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost?foo=bar&multi=1&multi=2"), "POST", clientIP, body, bodyAsString, Encoding.UTF8);
var request = new RequestMessage(new Uri("http://localhost?foo=bar&multi=1&multi=2"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
// then
Check.That(request.GetParameter("foo")).Contains("bar");

View File

@@ -4,184 +4,13 @@ using System.Text;
using NFluent;
using Xunit;
using WireMock.RequestBuilders;
using WireMock.Matchers;
using WireMock.Matchers.Request;
namespace WireMock.Net.Tests
{
//[TestFixture]
public partial class RequestTests
public class RequestTests
{
private const string clientIP = "::1";
[Fact]
public void Should_specify_requests_matching_given_path()
{
// given
var spec = Request.Create().WithPath("/foo");
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "blabla", clientIP);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_paths()
{
var requestBuilder = Request.Create().WithPath("/x1", "/x2");
var request1 = new RequestMessage(new Uri("http://localhost/x1"), "blabla", clientIP);
var request2 = new RequestMessage(new Uri("http://localhost/x2"), "blabla", clientIP);
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request1, requestMatchResult)).IsEqualTo(1.0);
Check.That(requestBuilder.GetMatchingScore(request2, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_pathFuncs()
{
// given
var spec = Request.Create().WithPath(url => url.EndsWith("/foo"));
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "blabla", clientIP);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_path_prefix()
{
// given
var spec = Request.Create().WithPath(new RegexMatcher("^/foo"));
// when
var request = new RequestMessage(new Uri("http://localhost/foo/bar"), "blabla", clientIP);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_exclude_requests_not_matching_given_path()
{
// given
var spec = Request.Create().WithPath("/foo");
// when
var request = new RequestMessage(new Uri("http://localhost/bar"), "blabla", clientIP);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_url()
{
// given
var spec = Request.Create().WithUrl("*/foo");
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "blabla", clientIP);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_path_and_method_put()
{
// given
var spec = Request.Create().WithPath("/foo").UsingPut();
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_path_and_method_post()
{
// given
var spec = Request.Create().WithPath("/foo").UsingPost();
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_path_and_method_get()
{
// given
var spec = Request.Create().WithPath("/foo").UsingGet();
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "GET", clientIP);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_path_and_method_delete()
{
// given
var spec = Request.Create().WithPath("/foo").UsingDelete();
// when
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "Delete", clientIP, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_path_and_method_head()
{
// given
var spec = Request.Create().WithPath("/foo").UsingHead();
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "HEAD", clientIP);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_exclude_requests_matching_given_path_but_not_http_method()
{
// given
var spec = Request.Create().WithPath("/foo").UsingPut();
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "HEAD", clientIP);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
}
private const string ClientIp = "::1";
[Fact]
public void Should_exclude_requests_matching_given_http_method_but_not_url()
@@ -190,29 +19,13 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithPath("/bar").UsingPut();
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_path_and_headers()
{
// given
var spec = Request.Create().WithPath("/foo").UsingAnyVerb().WithHeader("X-toto", "tata");
// when
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP, body, bodyAsString, Encoding.UTF8, new Dictionary<string, string> { { "X-toto", "tata" } });
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_exclude_requests_not_matching_given_headers()
{
@@ -222,7 +35,7 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP, body, bodyAsString, Encoding.UTF8, new Dictionary<string, string> { { "X-toto", "tata" } });
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp, body, bodyAsString, Encoding.UTF8, new Dictionary<string, string[]> { { "X-toto", new[] { "tata" } } });
// then
var requestMatchResult = new RequestMatchResult();
@@ -238,7 +51,7 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP, body, bodyAsString, Encoding.UTF8, new Dictionary<string, string> { { "X-toto", "ABC" } });
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp, body, bodyAsString, Encoding.UTF8, new Dictionary<string, string[]> { { "X-toto", new[] { "ABC" } } });
// then
var requestMatchResult = new RequestMatchResult();
@@ -254,26 +67,14 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP, body, bodyAsString, Encoding.UTF8, new Dictionary<string, string> { { "X-toto", "TaTa" } });
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp, body, bodyAsString, Encoding.UTF8, new Dictionary<string, string[]> { { "X-toto", new[] { "TaTa" } } });
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_cookies()
{
// given
var spec = Request.Create().UsingAnyVerb().WithCookie("session", "a*");
// when
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP, null, null, null, null, new Dictionary<string, string> { { "session", "abc" } });
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_body()
@@ -284,199 +85,14 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "Hello world!";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP, body, bodyAsString, Encoding.UTF8);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_body_using_ExactMatcher_true()
{
// given
var requestBuilder = Request.Create().UsingAnyVerb().WithBody(new ExactMatcher("cat"));
// when
string bodyAsString = "cat";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_body_using_ExactMatcher_multiplePatterns()
{
// given
var requestBuilder = Request.Create().UsingAnyVerb().WithBody(new ExactMatcher("cat", "dog"));
// when
string bodyAsString = "cat";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(0.5);
}
[Fact]
public void Should_specify_requests_matching_given_body_using_ExactMatcher_false()
{
// given
var requestBuilder = Request.Create().UsingAnyVerb().WithBody(new ExactMatcher("cat"));
// when
string bodyAsString = "caR";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsStrictlyLessThan(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_body_using_SimMetricsMatcher1()
{
// given
var requestBuilder = Request.Create().UsingAnyVerb().WithBody(new SimMetricsMatcher("The cat walks in the street."));
// when
string bodyAsString = "The car drives in the street.";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsStrictlyLessThan(1.0).And.IsStrictlyGreaterThan(0.5);
}
[Fact]
public void Should_specify_requests_matching_given_body_using_SimMetricsMatcher2()
{
// given
var requestBuilder = Request.Create().UsingAnyVerb().WithBody(new SimMetricsMatcher("The cat walks in the street."));
// when
string bodyAsString = "Hello";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsStrictlyLessThan(0.1).And.IsStrictlyGreaterThan(0.05);
}
[Fact]
public void Should_specify_requests_matching_given_body_using_WildcardMatcher()
{
// given
var spec = Request.Create().WithPath("/foo").UsingAnyVerb().WithBody(new WildcardMatcher("H*o*"));
// when
string bodyAsString = "Hello world!";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP, body, bodyAsString, Encoding.UTF8, new Dictionary<string, string> { { "X-toto", "tatata" } });
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_body_using_RegexMatcher()
{
// given
var spec = Request.Create().UsingAnyVerb().WithBody(new RegexMatcher("H.*o"));
// when
string bodyAsString = "Hello world!";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_body_using_XPathMatcher_true()
{
// given
var spec = Request.Create().UsingAnyVerb().WithBody(new XPathMatcher("/todo-list[count(todo-item) = 3]"));
// when
string xmlBodyAsString = @"
<todo-list>
<todo-item id='a1'>abc</todo-item>
<todo-item id='a2'>def</todo-item>
<todo-item id='a3'>xyz</todo-item>
</todo-list>";
byte[] body = Encoding.UTF8.GetBytes(xmlBodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP, body, xmlBodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_body_using_XPathMatcher_false()
{
// given
var spec = Request.Create().UsingAnyVerb().WithBody(new XPathMatcher("/todo-list[count(todo-item) = 99]"));
// when
string xmlBodyAsString = @"
<todo-list>
<todo-item id='a1'>abc</todo-item>
<todo-item id='a2'>def</todo-item>
<todo-item id='a3'>xyz</todo-item>
</todo-list>";
byte[] body = Encoding.UTF8.GetBytes(xmlBodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP, body, xmlBodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_body_using_JsonPathMatcher_true()
{
// given
var spec = Request.Create().UsingAnyVerb().WithBody(new JsonPathMatcher("$.things[?(@.name == 'RequiredThing')]"));
// when
string bodyAsString = "{ \"things\": [ { \"name\": \"RequiredThing\" }, { \"name\": \"Wiremock\" } ] }";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Should_specify_requests_matching_given_body_using_JsonPathMatcher_false()
{
// given
var spec = Request.Create().UsingAnyVerb().WithBody(new JsonPathMatcher("$.things[?(@.name == 'RequiredThing')]"));
// when
string bodyAsString = "{ \"things\": { \"name\": \"Wiremock\" } }";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
}
[Fact]
public void Should_exclude_requests_not_matching_given_body()
{
@@ -486,7 +102,7 @@ namespace WireMock.Net.Tests
// when
string bodyAsString = "xxx";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP, body, bodyAsString, Encoding.UTF8, new Dictionary<string, string> { { "X-toto", "tatata" } });
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp, body, bodyAsString, Encoding.UTF8, new Dictionary<string, string[]> { { "X-toto", new[] { "tata" } } });
// then
var requestMatchResult = new RequestMatchResult();
@@ -500,7 +116,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithParam("bar", "1", "2");
// when
var request = new RequestMessage(new Uri("http://localhost/foo?bar=1&bar=2"), "PUT", clientIP);
var request = new RequestMessage(new Uri("http://localhost/foo?bar=1&bar=2"), "PUT", ClientIp);
// then
var requestMatchResult = new RequestMatchResult();
@@ -514,7 +130,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithParam("bar");
// when
var request = new RequestMessage(new Uri("http://localhost/foo?bar"), "PUT", clientIP);
var request = new RequestMessage(new Uri("http://localhost/foo?bar"), "PUT", ClientIp);
// then
var requestMatchResult = new RequestMatchResult();
@@ -528,7 +144,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().UsingAnyVerb().WithParam(p => p.ContainsKey("bar"));
// when
var request = new RequestMessage(new Uri("http://localhost/foo?bar=1&bar=2"), "PUT", clientIP);
var request = new RequestMessage(new Uri("http://localhost/foo?bar=1&bar=2"), "PUT", ClientIp);
// then
var requestMatchResult = new RequestMatchResult();
@@ -542,7 +158,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithParam("bar", "1");
// when
var request = new RequestMessage(new Uri("http://localhost/test=7"), "PUT", clientIP);
var request = new RequestMessage(new Uri("http://localhost/test=7"), "PUT", ClientIp);
// then
var requestMatchResult = new RequestMatchResult();

View File

@@ -0,0 +1,202 @@
using System;
using System.Collections.Generic;
using System.Text;
using NFluent;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using Xunit;
namespace WireMock.Net.Tests
{
public class RequestWithBodyTests
{
private const string ClientIp = "::1";
[Fact]
public void Request_WithBodyExactMatcher()
{
// given
var requestBuilder = Request.Create().UsingAnyVerb().WithBody(new ExactMatcher("cat"));
// when
string bodyAsString = "cat";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Request_WithBodyExactMatcher_multiplePatterns()
{
// given
var requestBuilder = Request.Create().UsingAnyVerb().WithBody(new ExactMatcher("cat", "dog"));
// when
string bodyAsString = "cat";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(0.5);
}
[Fact]
public void Request_WithBodyExactMatcher_false()
{
// given
var requestBuilder = Request.Create().UsingAnyVerb().WithBody(new ExactMatcher("cat"));
// when
string bodyAsString = "caR";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsStrictlyLessThan(1.0);
}
[Fact]
public void Request_WithBodySimMetricsMatcher1()
{
// given
var requestBuilder = Request.Create().UsingAnyVerb().WithBody(new SimMetricsMatcher("The cat walks in the street."));
// when
string bodyAsString = "The car drives in the street.";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsStrictlyLessThan(1.0).And.IsStrictlyGreaterThan(0.5);
}
[Fact]
public void Request_WithBodySimMetricsMatcher2()
{
// given
var requestBuilder = Request.Create().UsingAnyVerb().WithBody(new SimMetricsMatcher("The cat walks in the street."));
// when
string bodyAsString = "Hello";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsStrictlyLessThan(0.1).And.IsStrictlyGreaterThan(0.05);
}
[Fact]
public void Request_WithBodyWildcardMatcher()
{
// given
var spec = Request.Create().WithPath("/foo").UsingAnyVerb().WithBody(new WildcardMatcher("H*o*"));
// when
string bodyAsString = "Hello world!";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp, body, bodyAsString, Encoding.UTF8, new Dictionary<string, string[]> { { "X-toto", new[] { "tatata" } } });
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Request_WithBodyRegexMatcher()
{
// given
var spec = Request.Create().UsingAnyVerb().WithBody(new RegexMatcher("H.*o"));
// when
string bodyAsString = "Hello world!";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Request_WithBodyXPathMatcher_true()
{
// given
var spec = Request.Create().UsingAnyVerb().WithBody(new XPathMatcher("/todo-list[count(todo-item) = 3]"));
// when
string xmlBodyAsString = @"
<todo-list>
<todo-item id='a1'>abc</todo-item>
<todo-item id='a2'>def</todo-item>
<todo-item id='a3'>xyz</todo-item>
</todo-list>";
byte[] body = Encoding.UTF8.GetBytes(xmlBodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp, body, xmlBodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Request_WithBodyXPathMatcher_false()
{
// given
var spec = Request.Create().UsingAnyVerb().WithBody(new XPathMatcher("/todo-list[count(todo-item) = 99]"));
// when
string xmlBodyAsString = @"
<todo-list>
<todo-item id='a1'>abc</todo-item>
<todo-item id='a2'>def</todo-item>
<todo-item id='a3'>xyz</todo-item>
</todo-list>";
byte[] body = Encoding.UTF8.GetBytes(xmlBodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp, body, xmlBodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
}
[Fact]
public void Request_WithBodyJsonPathMatcher_true()
{
// given
var spec = Request.Create().UsingAnyVerb().WithBody(new JsonPathMatcher("$.things[?(@.name == 'RequiredThing')]"));
// when
string bodyAsString = "{ \"things\": [ { \"name\": \"RequiredThing\" }, { \"name\": \"Wiremock\" } ] }";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Request_WithBodyJsonPathMatcher_false()
{
// given
var spec = Request.Create().UsingAnyVerb().WithBody(new JsonPathMatcher("$.things[?(@.name == 'RequiredThing')]"));
// when
string bodyAsString = "{ \"things\": { \"name\": \"Wiremock\" } }";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp, body, bodyAsString, Encoding.UTF8);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
}
}
}

View File

@@ -1,41 +1,39 @@
using System;
using NFluent;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using Xunit;
namespace WireMock.Net.Tests
{
//[TestFixture]
public partial class RequestTests
{
[Fact]
public void Request_WithClientIP_Match_Ok()
{
// given
var spec = Request.Create().WithClientIP("127.0.0.2", "1.1.1.1");
// when
var request = new RequestMessage(new Uri("http://localhost"), "GET", "127.0.0.2");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Request_WithClientIP_Match_Fail()
{
// given
var spec = Request.Create().WithClientIP("127.0.0.2");
// when
var request = new RequestMessage(new Uri("http://localhost"), "GET", "192.1.1.1");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(0.0);
}
}
using System;
using NFluent;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using Xunit;
namespace WireMock.Net.Tests
{
public class RequestWithClientIPTests
{
[Fact]
public void Request_WithClientIP_Match_Ok()
{
// given
var spec = Request.Create().WithClientIP("127.0.0.2", "1.1.1.1");
// when
var request = new RequestMessage(new Uri("http://localhost"), "GET", "127.0.0.2");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact]
public void Request_WithClientIP_Match_Fail()
{
// given
var spec = Request.Create().WithClientIP("127.0.0.2");
// when
var request = new RequestMessage(new Uri("http://localhost"), "GET", "192.1.1.1");
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(0.0);
}
}
}

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