Compare commits

...

18 Commits

Author SHA1 Message Date
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
60 changed files with 1976 additions and 1019 deletions

View File

@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.26430.13 VisualStudioVersion = 15.0.27004.2005
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF242EDF-7133-4277-9A0C-18744DE08707}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF242EDF-7133-4277-9A0C-18744DE08707}"
EndProject EndProject
@@ -14,6 +14,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProjectSection EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{F0C22C47-DF71-463C-9B04-B4E0F3B8708A}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{F0C22C47-DF71-463C-9B04-B4E0F3B8708A}"
ProjectSection(SolutionItems) = preProject
examples\WireMock.Net.Console.Record.NETCoreApp\__admin\mappings\ab38efae-4e4d-4f20-8afe-635533ec2535.json = examples\WireMock.Net.Console.Record.NETCoreApp\__admin\mappings\ab38efae-4e4d-4f20-8afe-635533ec2535.json
EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{890A1DED-C229-4FA1-969E-AAC3BBFC05E5}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{890A1DED-C229-4FA1-969E-AAC3BBFC05E5}"
EndProject EndProject
@@ -21,11 +24,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net", "src\WireMoc
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Tests", "test\WireMock.Net.Tests\WireMock.Net.Tests.csproj", "{31DC2EF8-C3FE-467D-84BE-FB5D956E612E}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Tests", "test\WireMock.Net.Tests\WireMock.Net.Tests.csproj", "{31DC2EF8-C3FE-467D-84BE-FB5D956E612E}"
EndProject 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 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.Record.NETCoreApp", "examples\WireMock.Net.Console.Record.NETCoreApp\WireMock.Net.Console.Record.NETCoreApp.csproj", "{1995E414-F197-4AB4-90C2-68D806B5AF59}"
EndProject 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 EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.StandAlone", "src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj", "{B6269AAC-170A-43D5-8B9A-579DED3D9A95}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.StandAlone", "src\WireMock.Net.StandAlone\WireMock.Net.StandAlone.csproj", "{B6269AAC-170A-43D5-8B9A-579DED3D9A95}"
EndProject EndProject
@@ -36,6 +39,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.StandAlone.Net
{B6269AAC-170A-43D5-8B9A-579DED3D9A95} = {B6269AAC-170A-43D5-8B9A-579DED3D9A95} {B6269AAC-170A-43D5-8B9A-579DED3D9A95} = {B6269AAC-170A-43D5-8B9A-579DED3D9A95}
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.ConsoleApplication", "examples\WireMock.Net.ConsoleApplication\WireMock.Net.ConsoleApplication.csproj", "{668F689E-57B4-422E-8846-C0FF643CA268}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -74,6 +79,10 @@ Global
{668F689E-57B4-422E-8846-C0FF643CA999}.Debug|Any CPU.Build.0 = Debug|Any CPU {668F689E-57B4-422E-8846-C0FF643CA999}.Debug|Any CPU.Build.0 = Debug|Any CPU
{668F689E-57B4-422E-8846-C0FF643CA999}.Release|Any CPU.ActiveCfg = Release|Any CPU {668F689E-57B4-422E-8846-C0FF643CA999}.Release|Any CPU.ActiveCfg = Release|Any CPU
{668F689E-57B4-422E-8846-C0FF643CA999}.Release|Any CPU.Build.0 = Release|Any CPU {668F689E-57B4-422E-8846-C0FF643CA999}.Release|Any CPU.Build.0 = Release|Any CPU
{668F689E-57B4-422E-8846-C0FF643CA268}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{668F689E-57B4-422E-8846-C0FF643CA268}.Debug|Any CPU.Build.0 = Debug|Any CPU
{668F689E-57B4-422E-8846-C0FF643CA268}.Release|Any CPU.ActiveCfg = Release|Any CPU
{668F689E-57B4-422E-8846-C0FF643CA268}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -87,5 +96,9 @@ Global
{B6269AAC-170A-43D5-8B9A-579DED3D9A95} = {EF242EDF-7133-4277-9A0C-18744DE08707} {B6269AAC-170A-43D5-8B9A-579DED3D9A95} = {EF242EDF-7133-4277-9A0C-18744DE08707}
{10E16614-61CA-48D8-8BDD-664C13913DED} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A} {10E16614-61CA-48D8-8BDD-664C13913DED} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{668F689E-57B4-422E-8846-C0FF643CA999} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A} {668F689E-57B4-422E-8846-C0FF643CA999} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
{668F689E-57B4-422E-8846-C0FF643CA268} = {F0C22C47-DF71-463C-9B04-B4E0F3B8708A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BF428BCC-C837-433B-87D2-15C7014B73E9}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View File

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

View File

@@ -18,9 +18,6 @@
<Content Include="__admin\mappings\11111110-a633-40e8-a244-5cb80bc0ab66.json"> <Content Include="__admin\mappings\11111110-a633-40e8-a244-5cb80bc0ab66.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="__admin\mappings\826aff7c-6208-4a3c-923d-575248907db4.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -28,4 +25,13 @@
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
</ItemGroup> </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> </Project>

View File

@@ -14,8 +14,9 @@
}, },
"Response": { "Response": {
"BodyAsJson": { "body": "static mapping" }, "BodyAsJson": { "body": "static mapping" },
"Headers": { "Headers": {
"Content-Type": "application/json" "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, 25 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

@@ -10,13 +10,15 @@ namespace WireMock.Net.Console.Record.NETCoreApp
{ {
var server = FluentMockServer.Start(new FluentMockServerSettings var server = FluentMockServer.Start(new FluentMockServerSettings
{ {
Urls = new[] { "http://localhost:9095/", "https://localhost:9096/" }, Urls = new[] { "http://localhost:9091/", "https://localhost:9443/" },
StartAdminInterface = true, StartAdminInterface = true,
ReadStaticMappings = false,
ProxyAndRecordSettings = new ProxyAndRecordSettings ProxyAndRecordSettings = new ProxyAndRecordSettings
{ {
Url = "https://www.msn.com", Url = "https://www.google.com",
//X509Certificate2ThumbprintOrSubjectName = "www.yourclientcertname.com OR yourcertificatethumbprint (only if the service you're proxying to requires it)", //X509Certificate2ThumbprintOrSubjectName = "www.yourclientcertname.com OR yourcertificatethumbprint (only if the service you're proxying to requires it)",
SaveMapping = true SaveMapping = true,
SaveMappingToFile = false
} }
}); });

View File

@@ -15,4 +15,8 @@
<PackageReference Include="Newtonsoft.Json" Version="10.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="10.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="__admin\mappings\" />
</ItemGroup>
</Project> </Project>

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.RequestBuilders; using WireMock.RequestBuilders;
@@ -21,7 +20,9 @@ namespace WireMock.Net.ConsoleApplication
{ {
Urls = new[] { url1, url2, url3 }, Urls = new[] { url1, url2, url3 },
StartAdminInterface = true, StartAdminInterface = true,
ReadStaticMappings = true ReadStaticMappings = true,
PreWireMockMiddlewareInit = app => { System.Console.WriteLine($"PreWireMockMiddlewareInit : {app.GetType()}"); },
PostWireMockMiddlewareInit = app => { System.Console.WriteLine($"PostWireMockMiddlewareInit : {app.GetType()}"); }
}); });
System.Console.WriteLine("FluentMockServer listening at {0}", string.Join(" and ", server.Urls)); System.Console.WriteLine("FluentMockServer listening at {0}", string.Join(" and ", server.Urls));
@@ -29,6 +30,46 @@ namespace WireMock.Net.ConsoleApplication
// server.AllowPartialMapping(); // server.AllowPartialMapping();
server
.Given(Request.Create().WithPath("/headers", "/headers_test").UsingPost().WithHeader("Content-Type", "application/json*"))
.RespondWith(Response.Create()
.WithStatusCode(201)
.WithHeader("MyHeader", "application/json", "application/json2")
.WithBody(@"{ ""result"": ""data posted with 201""}"));
server
.Given(Request.Create().WithPath("/file").UsingGet())
.RespondWith(Response.Create()
.WithBodyFromFile(@"c:\temp\x.json", false)
);
server
.Given(Request.Create().WithPath("/filecache").UsingGet())
.RespondWith(Response.Create()
.WithBodyFromFile(@"c:\temp\x.json")
);
server
.Given(Request.Create().WithPath("/file_rel").UsingGet())
.RespondWith(Response.Create()
.WithBodyFromFile("Program.cs", false)
);
server
.Given(Request.Create().WithHeader("ProxyThis", "true")
.UsingGet())
.RespondWith(Response.Create()
.WithProxy("http://www.google.com")
);
server
.Given(Request.Create().WithPath("/bodyasbytes.png")
.UsingGet())
.RespondWith(Response.Create()
.WithHeader("Content-Type", "image/png")
.WithBody(Convert.FromBase64String("iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTczbp9jAAAAJ0lEQVQoU2NgUPuPD6Hz0RCEAtJoiAxpCCBXGgmRIo0TofORkdp/AMiMdRVnV6O0AAAAAElFTkSuQmCC"))
);
server server
.Given(Request.Create().WithPath("/oauth2/access").UsingPost().WithBody("grant_type=password;username=u;password=p")) .Given(Request.Create().WithPath("/oauth2/access").UsingPost().WithBody("grant_type=password;username=u;password=p"))
.RespondWith(Response.Create() .RespondWith(Response.Create()
@@ -58,13 +99,6 @@ namespace WireMock.Net.ConsoleApplication
.WithHeader("Content-Type", "application/json") .WithHeader("Content-Type", "application/json")
.WithBody(@"{ ""result"": ""data posted with FUNC 201""}")); .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 server
.Given(Request.Create().WithPath("/json").UsingPost().WithBody(new JsonPathMatcher("$.things[?(@.name == 'RequiredThing')]"))) .Given(Request.Create().WithPath("/json").UsingPost().WithBody(new JsonPathMatcher("$.things[?(@.name == 'RequiredThing')]")))
.RespondWith(Response.Create() .RespondWith(Response.Create()

View File

@@ -47,9 +47,6 @@
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="WireMock.Net">
<HintPath>..\..\src\WireMock.Net\bin\$(Configuration)\net45\WireMock.Net.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="MainApp.cs" /> <Compile Include="MainApp.cs" />
@@ -67,6 +64,16 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </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" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -66,6 +66,7 @@ namespace WireMock.Net.StandAlone
{ {
Url = proxyURL, Url = proxyURL,
SaveMapping = parser.GetBoolValue("SaveMapping"), SaveMapping = parser.GetBoolValue("SaveMapping"),
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
X509Certificate2ThumbprintOrSubjectName = parser.GetStringValue("X509Certificate2ThumbprintOrSubjectName") X509Certificate2ThumbprintOrSubjectName = parser.GetStringValue("X509Certificate2ThumbprintOrSubjectName")
}; };
} }

View File

@@ -3,10 +3,9 @@
<PropertyGroup> <PropertyGroup>
<Description>Lightweight StandAlone Http Mocking Server for .Net.</Description> <Description>Lightweight StandAlone Http Mocking Server for .Net.</Description>
<AssemblyTitle>WireMock.Net.StandAlone</AssemblyTitle> <AssemblyTitle>WireMock.Net.StandAlone</AssemblyTitle>
<Version>1.0.2.4</Version> <Version>1.0.2.7</Version>
<Authors>Stef Heyenrath</Authors> <Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks> <TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<!-- <TargetFrameworks>netstandard1.3;netstandard2.0</TargetFrameworks> -->
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>WireMock.Net.StandAlone</AssemblyName> <AssemblyName>WireMock.Net.StandAlone</AssemblyName>
<PackageId>WireMock.Net.StandAlone</PackageId> <PackageId>WireMock.Net.StandAlone</PackageId>

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using WireMock.Admin.Mappings; using WireMock.Admin.Mappings;
using WireMock.Util;
namespace WireMock.Admin.Requests namespace WireMock.Admin.Requests
{ {
@@ -16,13 +17,33 @@ namespace WireMock.Admin.Requests
/// <summary> /// <summary>
/// Gets the headers. /// Gets the headers.
/// </summary> /// </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> /// <summary>
/// Gets or sets the body. /// Gets or sets the body.
/// </summary> /// </summary>
public string Body { get; set; } 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> /// <summary>
/// Gets or sets the original body. /// Gets or sets the original body.
/// </summary> /// </summary>

View File

@@ -138,5 +138,23 @@ namespace WireMock.Client
/// <param name="model">The RequestModel</param> /// <param name="model">The RequestModel</param>
[Post("__admin/requests/find")] [Post("__admin/requests/find")]
Task<IList<LogRequestModel>> FindRequestsAsync([Body] RequestModel model); Task<IList<LogRequestModel>> FindRequestsAsync([Body] RequestModel model);
/// <summary>
/// Get all scenarios
/// </summary>
[Get("__admin/scenarios")]
Task<string> GetScenariosAsync();
/// <summary>
/// Delete (reset) all scenarios
/// </summary>
[Delete("__admin/scenarios")]
Task<string> DeleteScenariosAsync();
/// <summary>
/// Delete (reset) all scenarios
/// </summary>
[Post("__admin/scenarios")]
Task<string> ResetScenariosAsync();
} }
} }

View File

@@ -1,22 +1,25 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks; using System.Threading.Tasks;
using WireMock.Validation;
namespace WireMock.Http namespace WireMock.Http
{ {
internal static class HttpClientHelper internal static class HttpClientHelper
{ {
public static HttpClient CreateHttpClient(string clientX509Certificate2ThumbprintOrSubjectName = null)
private static HttpClient CreateHttpClient(string clientX509Certificate2ThumbprintOrSubjectName = null)
{ {
if (!string.IsNullOrEmpty(clientX509Certificate2ThumbprintOrSubjectName)) HttpClientHandler handler;
if (string.IsNullOrEmpty(clientX509Certificate2ThumbprintOrSubjectName))
{
handler = new HttpClientHandler();
}
else
{ {
#if NETSTANDARD || NET46 #if NETSTANDARD || NET46
var handler = new HttpClientHandler handler = new HttpClientHandler
{ {
ClientCertificateOptions = ClientCertificateOption.Manual, ClientCertificateOptions = ClientCertificateOption.Manual,
SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls, SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls,
@@ -25,63 +28,91 @@ namespace WireMock.Http
var x509Certificate2 = CertificateUtil.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName); var x509Certificate2 = CertificateUtil.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName);
handler.ClientCertificates.Add(x509Certificate2); handler.ClientCertificates.Add(x509Certificate2);
#else #else
var handler = new WebRequestHandler
var webRequestHandler = new WebRequestHandler
{ {
ClientCertificateOptions = ClientCertificateOption.Manual, ClientCertificateOptions = ClientCertificateOption.Manual,
ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true, ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate
}; };
var x509Certificate2 = CertificateUtil.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName); var x509Certificate2 = CertificateUtil.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName);
handler.ClientCertificates.Add(x509Certificate2); webRequestHandler.ClientCertificates.Add(x509Certificate2);
return new HttpClient(handler); handler = webRequestHandler;
#endif #endif
} }
return new HttpClient(); // For proxy we shouldn't follow auto redirects
handler.AllowAutoRedirect = false;
// If UseCookies enabled, httpClient ignores Cookie header
handler.UseCookies = false;
return new HttpClient(handler);
} }
public static async Task<ResponseMessage> SendAsync(RequestMessage requestMessage, string url, string 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); var httpRequestMessage = new HttpRequestMessage(new HttpMethod(requestMessage.Method), url);
// Overwrite the host header
httpRequestMessage.Headers.Host = new Uri(url).Authority;
// Set headers if present
if (requestMessage.Headers != null)
{
foreach (var headerName in requestMessage.Headers.Keys.Where(k => k.ToUpper() != "HOST"))
{
httpRequestMessage.Headers.Add(headerName, new[] { requestMessage.Headers[headerName] });
}
}
// Set Body if present // Set Body if present
if (requestMessage.BodyAsBytes != null && requestMessage.BodyAsBytes.Length > 0) if (requestMessage.BodyAsBytes != null && requestMessage.BodyAsBytes.Length > 0)
{ {
httpRequestMessage.Content = new ByteArrayContent(requestMessage.BodyAsBytes); httpRequestMessage.Content = new ByteArrayContent(requestMessage.BodyAsBytes);
} }
// Overwrite the host header
httpRequestMessage.Headers.Host = requiredUri.Authority;
// Set headers if present
if (requestMessage.Headers != null)
{
foreach (var header in requestMessage.Headers.Where(header => !string.Equals(header.Key, "HOST", StringComparison.OrdinalIgnoreCase)))
{
// Try to add to request headers. If failed - try to add to content headers
if (!httpRequestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value))
{
httpRequestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
}
}
// Call the URL // Call the URL
var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead); var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead);
// Transform response // Transform response
var responseMessage = new ResponseMessage var responseMessage = new ResponseMessage
{ {
StatusCode = (int)httpResponseMessage.StatusCode, StatusCode = (int)httpResponseMessage.StatusCode,
BodyAsBytes = await httpResponseMessage.Content.ReadAsByteArrayAsync(),
Body = await httpResponseMessage.Content.ReadAsStringAsync() Body = await httpResponseMessage.Content.ReadAsStringAsync()
}; };
foreach (var header in httpResponseMessage.Headers) // Set both content and response headers, replacing URLs in values
var headers = httpResponseMessage.Content?.Headers.Union(httpResponseMessage.Headers);
foreach (var header in headers)
{ {
responseMessage.AddHeader(header.Key, header.Value.FirstOrDefault()); // if Location header contains absolute redirect URL, and base URL is one that we proxy to,
// we need to replace it to original one.
if (string.Equals(header.Key, "Location", StringComparison.OrdinalIgnoreCase)
&& Uri.TryCreate(header.Value.First(), UriKind.Absolute, out Uri absoluteLocationUri)
&& string.Equals(absoluteLocationUri.Host, requiredUri.Host, StringComparison.OrdinalIgnoreCase))
{
var replacedLocationUri = new Uri(originalUri, absoluteLocationUri.PathAndQuery);
responseMessage.AddHeader(header.Key, replacedLocationUri.ToString());
}
else
{
responseMessage.AddHeader(header.Key, header.Value.ToArray());
}
} }
return responseMessage; return responseMessage;

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

@@ -13,25 +13,16 @@ namespace WireMock
/// <summary> /// <summary>
/// Gets the unique identifier. /// Gets the unique identifier.
/// </summary> /// </summary>
/// <value>
/// The unique identifier.
/// </value>
public Guid Guid { get; } public Guid Guid { get; }
/// <summary> /// <summary>
/// Gets the unique title. /// Gets the unique title.
/// </summary> /// </summary>
/// <value>
/// The unique title.
/// </value>
public string Title { get; } public string Title { get; }
/// <summary> /// <summary>
/// Gets the priority. /// Gets the priority.
/// </summary> /// </summary>
/// <value>
/// The priority.
/// </value>
public int Priority { get; } public int Priority { get; }
/// <summary> /// <summary>

View File

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

View File

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

View File

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

View File

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

View File

@@ -46,7 +46,10 @@ namespace WireMock.Owin
_host = new WebHostBuilder() _host = new WebHostBuilder()
.Configure(appBuilder => .Configure(appBuilder =>
{ {
appBuilder.UseMiddleware<GlobalExceptionMiddleware>();
_options.PreWireMockMiddlewareInit?.Invoke(appBuilder);
appBuilder.UseMiddleware<WireMockMiddleware>(_options); appBuilder.UseMiddleware<WireMockMiddleware>(_options);
_options.PostWireMockMiddlewareInit?.Invoke(appBuilder);
}) })
.UseKestrel() .UseKestrel()
.UseUrls(_uriPrefixes) .UseUrls(_uriPrefixes)

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; private set; }
#endif
private readonly OwinResponseMapper _responseMapper = new OwinResponseMapper();
#if !NETSTANDARD
public override async Task Invoke(IOwinContext ctx)
#else
public async Task Invoke(HttpContext ctx)
#endif
{
try
{
await Next?.Invoke(ctx);
}
catch (Exception ex)
{
await _responseMapper.MapAsync(new ResponseMessage { StatusCode = 500, Body = JsonConvert.SerializeObject(ex) }, ctx.Response);
}
}
}
}

View File

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

View File

@@ -2,6 +2,7 @@
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using WireMock.Http;
#if !NETSTANDARD #if !NETSTANDARD
using Microsoft.Owin; using Microsoft.Owin;
#else #else
@@ -32,15 +33,43 @@ namespace WireMock.Owin
{ {
response.StatusCode = responseMessage.StatusCode; response.StatusCode = responseMessage.StatusCode;
responseMessage.Headers.ToList().ForEach(pair => response.Headers.Append(pair.Key, pair.Value)); if (responseMessage.Headers.ContainsKey(HttpKnownHeaderNames.ContentType))
{
response.ContentType = responseMessage.Headers[HttpKnownHeaderNames.ContentType].FirstOrDefault();
}
if (responseMessage.Body == null) var headers = responseMessage.Headers.Where(h => h.Key != HttpKnownHeaderNames.ContentType).ToList();
#if !NETSTANDARD
headers.ForEach(pair => response.Headers.AppendValues(pair.Key, pair.Value.ToArray()));
#else
headers.ForEach(pair => response.Headers.Append(pair.Key, pair.Value.ToArray()));
#endif
if (responseMessage.Body == null && responseMessage.BodyAsBytes == null && responseMessage.BodyAsFile == null)
{
return; return;
}
if (responseMessage.BodyAsBytes != null)
{
await response.Body.WriteAsync(responseMessage.BodyAsBytes, 0, responseMessage.BodyAsBytes.Length);
return;
}
if (responseMessage.BodyAsFile != null)
{
byte[] bytes = File.ReadAllBytes(responseMessage.BodyAsFile);
await response.Body.WriteAsync(bytes, 0, bytes.Length);
return;
}
Encoding encoding = responseMessage.BodyEncoding ?? _utf8NoBom; Encoding encoding = responseMessage.BodyEncoding ?? _utf8NoBom;
using (var writer = new StreamWriter(response.Body, encoding)) using (var writer = new StreamWriter(response.Body, encoding))
{ {
await writer.WriteAsync(responseMessage.Body); await writer.WriteAsync(responseMessage.Body);
// TODO : response.ContentLength = responseMessage.Body.Length;
} }
} }
} }

View File

@@ -57,11 +57,14 @@ namespace WireMock.Owin
private void StartServers() 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 .net 4.6.x");
Action<IAppBuilder> startup = app => Action<IAppBuilder> startup = app =>
{ {
app.Use<GlobalExceptionMiddleware>();
_options.PreWireMockMiddlewareInit?.Invoke(app);
app.Use<WireMockMiddleware>(_options); app.Use<WireMockMiddleware>(_options);
_options.PostWireMockMiddlewareInit?.Invoke(app);
}; };
var servers = new List<IDisposable>(); var servers = new List<IDisposable>();

View File

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

View File

@@ -4,6 +4,12 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using WireMock.Logging; using WireMock.Logging;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Util;
#if !NETSTANDARD
using Owin;
#else
using Microsoft.AspNetCore.Builder;
#endif
namespace WireMock.Owin namespace WireMock.Owin
{ {
@@ -17,12 +23,22 @@ namespace WireMock.Owin
public IList<Mapping> Mappings { get; set; } = new List<Mapping>(); 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? RequestLogExpirationDuration { get; set; }
public int? MaxRequestLogCount { get; set; } public int? MaxRequestLogCount { get; set; }
public IDictionary<string, object> Scenarios { get; } = new ConcurrentDictionary<string, object>(); 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 public interface IHeadersAndCookiesRequestBuilder : IBodyRequestBuilder, IRequestMatcher, IParamsRequestBuilder
{ {
/// <summary> /// <summary>
/// The with header. /// Add Header matching based on name, pattern and ignoreCase.
/// </summary> /// </summary>
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <param name="pattern">The pattern.</param> /// <param name="pattern">The pattern.</param>
@@ -20,6 +20,15 @@ namespace WireMock.RequestBuilders
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithHeader([NotNull] string name, string pattern, bool ignoreCase = true); 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> /// <summary>
/// The with header. /// The with header.
/// </summary> /// </summary>
@@ -33,7 +42,7 @@ namespace WireMock.RequestBuilders
/// </summary> /// </summary>
/// <param name="funcs">The headers funcs.</param> /// <param name="funcs">The headers funcs.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <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> /// <summary>
/// The with cookie. /// The with cookie.

View File

@@ -359,13 +359,7 @@ namespace WireMock.RequestBuilders
return this; return this;
} }
/// <summary> /// <inheritdoc cref="IHeadersAndCookiesRequestBuilder.WithHeader(string,string,bool)"/>
/// 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>
public IRequestBuilder WithHeader(string name, string pattern, bool ignoreCase = true) public IRequestBuilder WithHeader(string name, string pattern, bool ignoreCase = true)
{ {
Check.NotNull(name, nameof(name)); Check.NotNull(name, nameof(name));
@@ -375,6 +369,16 @@ namespace WireMock.RequestBuilders
return this; 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> /// <summary>
/// With header. /// With header.
/// </summary> /// </summary>
@@ -395,7 +399,7 @@ namespace WireMock.RequestBuilders
/// </summary> /// </summary>
/// <param name="funcs">The funcs.</param> /// <param name="funcs">The funcs.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns> /// <returns>The <see cref="IRequestBuilder"/>.</returns>
public IRequestBuilder WithHeader(params Func<IDictionary<string, string>, bool>[] funcs) public IRequestBuilder WithHeader(params Func<IDictionary<string, string[]>, bool>[] funcs)
{ {
Check.NotEmpty(funcs, nameof(funcs)); Check.NotEmpty(funcs, nameof(funcs));

View File

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

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; using JetBrains.Annotations;
namespace WireMock.ResponseBuilders namespace WireMock.ResponseBuilders
@@ -9,15 +10,25 @@ namespace WireMock.ResponseBuilders
public interface IBodyResponseBuilder : ITransformResponseBuilder public interface IBodyResponseBuilder : ITransformResponseBuilder
{ {
/// <summary> /// <summary>
/// The with body. /// WithBody : Create a ... response based on a string.
/// </summary> /// </summary>
/// <param name="body">The body.</param> /// <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> /// <param name="encoding">The body encoding.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns> /// <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> /// <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> /// </summary>
/// <param name="body">The body.</param> /// <param name="body">The body.</param>
/// <param name="encoding">The body encoding.</param> /// <param name="encoding">The body encoding.</param>
@@ -25,11 +36,20 @@ namespace WireMock.ResponseBuilders
IResponseBuilder WithBodyAsJson([NotNull] object body, [CanBeNull] Encoding encoding = null); IResponseBuilder WithBodyAsJson([NotNull] object body, [CanBeNull] Encoding encoding = null);
/// <summary> /// <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> /// </summary>
/// <param name="bodyAsbase64">The body asbase64.</param> /// <param name="bodyAsbase64">The body.</param>
/// <param name="encoding">The Encoding.</param> /// <param name="encoding">The Encoding.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns> /// <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 System.Collections.Generic;
using JetBrains.Annotations; using JetBrains.Annotations;
using WireMock.Util;
namespace WireMock.ResponseBuilders namespace WireMock.ResponseBuilders
{ {
@@ -12,15 +13,29 @@ namespace WireMock.ResponseBuilders
/// The with header. /// The with header.
/// </summary> /// </summary>
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <param name="value">The value.</param> /// <param name="values">The values.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns> /// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithHeader([NotNull] string name, string value); IResponseBuilder WithHeader([NotNull] string name, params string[] values);
/// <summary> /// <summary>
/// The with headers. /// The with headers.
/// </summary> /// </summary>
/// <param name="headers">The headers.</param> /// <param name="headers">The headers.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns> /// <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,13 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net; using System.Net;
using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using Newtonsoft.Json; using Newtonsoft.Json;
using WireMock.Validation;
using WireMock.Http; using WireMock.Http;
using WireMock.Transformers; using WireMock.Transformers;
using WireMock.Util;
using WireMock.Validation;
namespace WireMock.ResponseBuilders namespace WireMock.ResponseBuilders
{ {
@@ -16,6 +20,8 @@ namespace WireMock.ResponseBuilders
/// </summary> /// </summary>
public class Response : IResponseBuilder public class Response : IResponseBuilder
{ {
private HttpClient httpClientForProxy;
/// <summary> /// <summary>
/// The delay /// The delay
/// </summary> /// </summary>
@@ -125,53 +131,117 @@ namespace WireMock.ResponseBuilders
return WithStatusCode((int)HttpStatusCode.NotFound); return WithStatusCode((int)HttpStatusCode.NotFound);
} }
/// <summary> /// <inheritdoc cref="IHeadersResponseBuilder.WithHeader(string, string[])"/>
/// The with header. public IResponseBuilder WithHeader(string name, params string[] values)
/// </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)
{ {
Check.NotNull(name, nameof(name)); Check.NotNull(name, nameof(name));
ResponseMessage.AddHeader(name, value); ResponseMessage.AddHeader(name, values);
return this; return this;
} }
/// <summary> /// <inheritdoc cref="IHeadersResponseBuilder.WithHeaders(IDictionary{string, string})"/>
/// The with headers.
/// </summary>
/// <param name="headers">The headers.</param>
/// <returns></returns>
public IResponseBuilder WithHeaders(IDictionary<string, string> headers) 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; ResponseMessage.Headers = headers;
return this; return this;
} }
/// <summary> /// <inheritdoc cref="IBodyResponseBuilder.WithBody(byte[], string, Encoding)"/>
/// The with body. public IResponseBuilder WithBody(byte[] body, string destination, Encoding encoding = null)
/// </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)
{ {
Check.NotNull(body, nameof(body)); Check.NotNull(body, nameof(body));
ResponseMessage.Body = body; ResponseMessage.BodyDestination = destination;
ResponseMessage.BodyEncoding = encoding ?? Encoding.UTF8;
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; return this;
} }
/// <summary> /// <inheritdoc cref="IBodyResponseBuilder.WithBodyFromFile"/>
/// The with body (AsJson object). public IResponseBuilder WithBodyFromFile(string filename, bool cache = true)
/// </summary> {
/// <param name="body">The body.</param> Check.NotNull(filename, nameof(filename));
/// <param name="encoding">The body encoding.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns> 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) public IResponseBuilder WithBodyAsJson(object body, Encoding encoding = null)
{ {
Check.NotNull(body, nameof(body)); Check.NotNull(body, nameof(body));
@@ -184,23 +254,20 @@ namespace WireMock.ResponseBuilders
ResponseMessage.BodyEncoding = encoding; ResponseMessage.BodyEncoding = encoding;
} }
ResponseMessage.BodyDestination = null;
ResponseMessage.Body = jsonBody; ResponseMessage.Body = jsonBody;
return this; return this;
} }
/// <summary> /// <inheritdoc cref="IBodyResponseBuilder.WithBodyFromBase64"/>
/// The with body as base64. public IResponseBuilder WithBodyFromBase64(string bodyAsbase64, Encoding encoding = null)
/// </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)
{ {
Check.NotNull(bodyAsbase64, nameof(bodyAsbase64)); Check.NotNull(bodyAsbase64, nameof(bodyAsbase64));
encoding = encoding ?? Encoding.UTF8; encoding = encoding ?? Encoding.UTF8;
ResponseMessage.BodyDestination = null;
ResponseMessage.Body = encoding.GetString(Convert.FromBase64String(bodyAsbase64)); ResponseMessage.Body = encoding.GetString(Convert.FromBase64String(bodyAsbase64));
ResponseMessage.BodyEncoding = encoding; ResponseMessage.BodyEncoding = encoding;
@@ -253,6 +320,7 @@ namespace WireMock.ResponseBuilders
ProxyUrl = proxyUrl; ProxyUrl = proxyUrl;
X509Certificate2ThumbprintOrSubjectName = clientX509Certificate2ThumbprintOrSubjectName; X509Certificate2ThumbprintOrSubjectName = clientX509Certificate2ThumbprintOrSubjectName;
httpClientForProxy = HttpClientHelper.CreateHttpClient(clientX509Certificate2ThumbprintOrSubjectName);
return this; return this;
} }
@@ -268,12 +336,12 @@ namespace WireMock.ResponseBuilders
if (Delay != null) if (Delay != null)
await Task.Delay(Delay.Value); await Task.Delay(Delay.Value);
if (ProxyUrl != null) if (ProxyUrl != null && httpClientForProxy != null)
{ {
var requestUri = new Uri(requestMessage.Url); var requestUri = new Uri(requestMessage.Url);
var proxyUri = new Uri(ProxyUrl); var proxyUri = new Uri(ProxyUrl);
var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery); 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) if (UseTransformer)

View File

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

View File

@@ -8,6 +8,7 @@ using WireMock.Matchers;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.RequestBuilders; using WireMock.RequestBuilders;
using WireMock.ResponseBuilders; using WireMock.ResponseBuilders;
using WireMock.Util;
namespace WireMock.Serialization namespace WireMock.Serialization
{ {
@@ -78,7 +79,7 @@ namespace WireMock.Serialization
Funcs = Map(pm.Funcs) Funcs = Map(pm.Funcs)
}).ToList() : null, }).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, Matcher = bodyMatcher != null ? Map(bodyMatcher.Matcher) : null,
Func = bodyMatcher != null ? Map(bodyMatcher.Func) : null, Func = bodyMatcher != null ? Map(bodyMatcher.Func) : null,
@@ -95,31 +96,58 @@ namespace WireMock.Serialization
{ {
mappingModel.Response.StatusCode = null; mappingModel.Response.StatusCode = null;
mappingModel.Response.Headers = null; mappingModel.Response.Headers = null;
mappingModel.Response.BodyDestination = null;
mappingModel.Response.Body = null; mappingModel.Response.Body = null;
mappingModel.Response.BodyAsBytes = null;
mappingModel.Response.BodyAsFile = null;
mappingModel.Response.BodyAsFileIsCached = null;
mappingModel.Response.UseTransformer = false; mappingModel.Response.UseTransformer = false;
mappingModel.Response.BodyEncoding = null; mappingModel.Response.BodyEncoding = null;
mappingModel.Response.ProxyUrl = response.ProxyUrl; mappingModel.Response.ProxyUrl = response.ProxyUrl;
} }
else else
{ {
mappingModel.Response.BodyDestination = response.ResponseMessage.BodyDestination;
mappingModel.Response.StatusCode = response.ResponseMessage.StatusCode; mappingModel.Response.StatusCode = response.ResponseMessage.StatusCode;
mappingModel.Response.Headers = response.ResponseMessage.Headers; mappingModel.Response.Headers = Map(response.ResponseMessage.Headers);
mappingModel.Response.Body = response.ResponseMessage.Body; mappingModel.Response.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.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, EncodingName = response.ResponseMessage.BodyEncoding.EncodingName,
CodePage = response.ResponseMessage.BodyEncoding.CodePage, CodePage = response.ResponseMessage.BodyEncoding.CodePage,
WebName = response.ResponseMessage.BodyEncoding.WebName WebName = response.ResponseMessage.BodyEncoding.WebName
} };
: null; }
} }
return mappingModel; 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()) if (matchers == null || !matchers.Any())
return null; return null;
@@ -127,7 +155,7 @@ namespace WireMock.Serialization
return matchers.Select(Map).Where(x => x != null).ToArray(); 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) if (matcher == null)
return null; return null;
@@ -142,7 +170,7 @@ namespace WireMock.Serialization
}; };
} }
public static string[] Map<T>([CanBeNull] IEnumerable<Func<T, bool>> funcs) private static string[] Map<T>([CanBeNull] IEnumerable<Func<T, bool>> funcs)
{ {
if (funcs == null || !funcs.Any()) if (funcs == null || !funcs.Any())
return null; return null;
@@ -150,7 +178,7 @@ namespace WireMock.Serialization
return funcs.Select(Map).Where(x => x != null).ToArray(); 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(); return func?.ToString();
} }

View File

@@ -2,23 +2,24 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using Newtonsoft.Json; using Newtonsoft.Json;
using WireMock.Admin.Mappings; using WireMock.Admin.Mappings;
using WireMock.Admin.Requests; using WireMock.Admin.Requests;
using WireMock.Admin.Settings; using WireMock.Admin.Settings;
using WireMock.Http;
using WireMock.Logging; using WireMock.Logging;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.RequestBuilders; using WireMock.RequestBuilders;
using WireMock.ResponseBuilders; using WireMock.ResponseBuilders;
using WireMock.Serialization;
using WireMock.Settings;
using WireMock.Util; using WireMock.Util;
using WireMock.Validation; using WireMock.Validation;
using WireMock.Http;
using System.Threading.Tasks;
using WireMock.Settings;
using WireMock.Serialization;
namespace WireMock.Server namespace WireMock.Server
{ {
@@ -129,8 +130,11 @@ namespace WireMock.Server
} }
#region Proxy and Record #region Proxy and Record
private HttpClient _httpClientForProxy;
private void InitProxyAndRecord(ProxyAndRecordSettings settings) private void InitProxyAndRecord(ProxyAndRecordSettings settings)
{ {
_httpClientForProxy = HttpClientHelper.CreateHttpClient(settings.X509Certificate2ThumbprintOrSubjectName);
Given(Request.Create().WithPath("/*").UsingAnyVerb()).RespondWith(new ProxyAsyncResponseProvider(ProxyAndRecordAsync, settings)); Given(Request.Create().WithPath("/*").UsingAnyVerb()).RespondWith(new ProxyAsyncResponseProvider(ProxyAndRecordAsync, settings));
} }
@@ -140,12 +144,17 @@ namespace WireMock.Server
var proxyUri = new Uri(settings.Url); var proxyUri = new Uri(settings.Url);
var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery); 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) if (settings.SaveMapping)
{ {
var mapping = ToMapping(requestMessage, responseMessage); var mapping = ToMapping(requestMessage, responseMessage);
SaveMappingToFile(mapping); _options.Mappings.Add(mapping);
if (settings.SaveMappingToFile)
{
SaveMappingToFile(mapping);
}
} }
return responseMessage; return responseMessage;
@@ -153,11 +162,20 @@ namespace WireMock.Server
private Mapping ToMapping(RequestMessage requestMessage, ResponseMessage responseMessage) private Mapping ToMapping(RequestMessage requestMessage, ResponseMessage responseMessage)
{ {
var request = (Request)Request.Create(); var request = Request.Create();
request.WithPath(requestMessage.Path); request.WithPath(requestMessage.Path);
request.UsingVerb(requestMessage.Method); request.UsingVerb(requestMessage.Method);
var response = (Response)Response.Create(responseMessage); requestMessage.Query.Loop((key, value) => request.WithParam(key, value.ToArray()));
requestMessage.Headers.Loop((key, value) => request.WithHeader(key, value.ToArray()));
requestMessage.Cookies.Loop((key, value) => request.WithCookie(key, value));
if (requestMessage.Body != null)
{
request.WithBody(new ExactMatcher(requestMessage.Body));
}
var response = Response.Create(responseMessage);
return new Mapping(Guid.NewGuid(), string.Empty, request, response, 0, null, null, null); return new Mapping(Guid.NewGuid(), string.Empty, request, response, 0, null, null, null);
} }
@@ -412,8 +430,12 @@ namespace WireMock.Server
Response = new LogResponseModel Response = new LogResponseModel
{ {
StatusCode = logEntry.ResponseMessage.StatusCode, StatusCode = logEntry.ResponseMessage.StatusCode,
BodyDestination = logEntry.ResponseMessage.BodyDestination,
Body = logEntry.ResponseMessage.Body, Body = logEntry.ResponseMessage.Body,
BodyAsBytes = logEntry.ResponseMessage.BodyAsBytes,
BodyOriginal = logEntry.ResponseMessage.BodyOriginal, BodyOriginal = logEntry.ResponseMessage.BodyOriginal,
BodyAsFile = logEntry.ResponseMessage.BodyAsFile,
BodyAsFileIsCached = logEntry.ResponseMessage.BodyAsFileIsCached,
Headers = logEntry.ResponseMessage.Headers, Headers = logEntry.ResponseMessage.Headers,
BodyEncoding = logEntry.ResponseMessage.BodyEncoding != null ? new EncodingModel BodyEncoding = logEntry.ResponseMessage.BodyEncoding != null ? new EncodingModel
{ {
@@ -515,12 +537,16 @@ namespace WireMock.Server
{ {
string path = requestModel.Path as string; string path = requestModel.Path as string;
if (path != null) if (path != null)
{
requestBuilder = requestBuilder.WithPath(path); requestBuilder = requestBuilder.WithPath(path);
}
else else
{ {
var pathModel = JsonUtils.ParseJTokenToObject<PathModel>(requestModel.Path); var pathModel = JsonUtils.ParseJTokenToObject<PathModel>(requestModel.Path);
if (pathModel?.Matchers != null) if (pathModel?.Matchers != null)
{
requestBuilder = requestBuilder.WithPath(pathModel.Matchers.Select(MappingConverter.Map).ToArray()); requestBuilder = requestBuilder.WithPath(pathModel.Matchers.Select(MappingConverter.Map).ToArray());
}
} }
} }
@@ -528,17 +554,23 @@ namespace WireMock.Server
{ {
string url = requestModel.Url as string; string url = requestModel.Url as string;
if (url != null) if (url != null)
{
requestBuilder = requestBuilder.WithUrl(url); requestBuilder = requestBuilder.WithUrl(url);
}
else else
{ {
var urlModel = JsonUtils.ParseJTokenToObject<UrlModel>(requestModel.Url); var urlModel = JsonUtils.ParseJTokenToObject<UrlModel>(requestModel.Url);
if (urlModel?.Matchers != null) if (urlModel?.Matchers != null)
{
requestBuilder = requestBuilder.WithUrl(urlModel.Matchers.Select(MappingConverter.Map).ToArray()); requestBuilder = requestBuilder.WithUrl(urlModel.Matchers.Select(MappingConverter.Map).ToArray());
}
} }
} }
if (requestModel.Methods != null) if (requestModel.Methods != null)
{
requestBuilder = requestBuilder.UsingVerb(requestModel.Methods); requestBuilder = requestBuilder.UsingVerb(requestModel.Methods);
}
if (requestModel.Headers != null) if (requestModel.Headers != null)
{ {
@@ -599,7 +631,12 @@ namespace WireMock.Server
if (responseModel.Headers != null) 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) else if (responseModel.HeadersRaw != null)
{ {
@@ -612,17 +649,21 @@ namespace WireMock.Server
} }
} }
if (responseModel.Body != null) if (responseModel.BodyAsBytes != null)
{ {
responseBuilder = responseBuilder.WithBody(responseModel.Body, ToEncoding(responseModel.BodyEncoding)); responseBuilder = responseBuilder.WithBody(responseModel.BodyAsBytes, responseModel.BodyDestination, ToEncoding(responseModel.BodyEncoding));
}
else if (responseModel.Body != null)
{
responseBuilder = responseBuilder.WithBody(responseModel.Body, responseModel.BodyDestination, ToEncoding(responseModel.BodyEncoding));
} }
else if (responseModel.BodyAsJson != null) else if (responseModel.BodyAsJson != null)
{ {
responseBuilder = responseBuilder.WithBodyAsJson(responseModel.BodyAsJson, ToEncoding(responseModel.BodyEncoding)); 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) if (responseModel.UseTransformer)
@@ -639,7 +680,7 @@ namespace WireMock.Server
{ {
Body = JsonConvert.SerializeObject(result, _settings), Body = JsonConvert.SerializeObject(result, _settings),
StatusCode = 200, StatusCode = 200,
Headers = new Dictionary<string, string> { { "Content-Type", "application/json" } } Headers = new Dictionary<string, WireMockList<string>> { { "Content-Type", new WireMockList<string>("application/json") } }
}; };
} }

View File

@@ -162,6 +162,9 @@ namespace WireMock.Server
Urls = new[] { (settings.UseSSL == true ? "https" : "http") + "://localhost:" + port + "/" }; Urls = new[] { (settings.UseSSL == true ? "https" : "http") + "://localhost:" + port + "/" };
} }
_options.PreWireMockMiddlewareInit = settings.PreWireMockMiddlewareInit;
_options.PostWireMockMiddlewareInit = settings.PostWireMockMiddlewareInit;
#if NETSTANDARD #if NETSTANDARD
_httpServer = new AspNetCoreSelfHost(_options, Urls); _httpServer = new AspNetCoreSelfHost(_options, Urls);
#else #else

View File

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

View File

@@ -11,10 +11,15 @@
public string Url { get; set; } public string Url { get; set; }
/// <summary> /// <summary>
/// Save the mapping for each request/response. /// Save the mapping for each request/response to the internal Mappings.
/// </summary> /// </summary>
public bool SaveMapping { get; set; } = true; public bool SaveMapping { get; set; } = true;
/// <summary>
/// Save the mapping for each request/response to also file. (Note that SaveMapping must also be set to true.)
/// </summary>
public bool SaveMappingToFile { get; set; } = true;
/// <summary> /// <summary>
/// The clientCertificate thumbprint or subject name fragment to use. Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com"" /// The clientCertificate thumbprint or subject name fragment to use. Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
/// </summary> /// </summary>

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 namespace WireMock.Transformers
{ {
@@ -16,13 +18,16 @@ namespace WireMock.Transformers
responseMessage.Body = templateBody(template); responseMessage.Body = templateBody(template);
// Headers // Headers
var newHeaders = new Dictionary<string, string>(); var newHeaders = new Dictionary<string, WireMockList<string>>();
foreach (var header in original.Headers) foreach (var header in original.Headers)
{ {
var templateHeaderKey = Handlebars.Compile(header.Key); 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; 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

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

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<Description>Lightweight Http Mocking Server for .Net, inspired by WireMock from the Java landscape.</Description> <Description>Lightweight Http Mocking Server for .Net, inspired by WireMock from the Java landscape.</Description>
<AssemblyTitle>WireMock.Net</AssemblyTitle> <AssemblyTitle>WireMock.Net</AssemblyTitle>
<Version>1.0.2.4</Version> <Version>1.0.2.7</Version>
<Authors>Alexandre Victoor;Stef Heyenrath</Authors> <Authors>Alexandre Victoor;Stef Heyenrath</Authors>
<TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks> <TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>

View File

@@ -0,0 +1,157 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using NFluent;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
using Xunit;
namespace WireMock.Net.Tests
{
public partial class FluentMockServerTests
{
private FluentMockServer _serverForProxyForwarding;
[Fact]
public async Task FluentMockServer_Should_proxy_responses()
{
// given
_server = FluentMockServer.Start();
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create().WithProxy("http://www.google.com"));
// when
var result = await new HttpClient().GetStringAsync("http://localhost:" + _server.Ports[0] + "/search?q=test");
// then
Check.That(result).Contains("google");
}
[Fact]
public async Task FluentMockServer_Should_preserve_content_header_in_proxied_request()
{
// given
_serverForProxyForwarding = FluentMockServer.Start();
_serverForProxyForwarding
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create());
_server = FluentMockServer.Start();
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create().WithProxy(_serverForProxyForwarding.Urls[0]));
// when
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(_server.Urls[0]),
Content = new StringContent("stringContent")
};
requestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
await new HttpClient().SendAsync(requestMessage);
// then
var receivedRequest = _serverForProxyForwarding.LogEntries.First().RequestMessage;
Check.That(receivedRequest.Body).IsEqualTo("stringContent");
Check.That(receivedRequest.Headers).ContainsKey("Content-Type");
Check.That(receivedRequest.Headers["Content-Type"]).ContainsExactly("text/plain");
}
[Fact]
public async Task FluentMockServer_Should_preserve_content_header_in_proxied_response()
{
// given
_serverForProxyForwarding = FluentMockServer.Start();
_serverForProxyForwarding
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create()
.WithBody("body")
.WithHeader("Content-Type", "text/plain"));
_server = FluentMockServer.Start();
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create().WithProxy(_serverForProxyForwarding.Urls[0]));
// when
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(_server.Urls[0])
};
var response = await new HttpClient().SendAsync(requestMessage);
// then
Check.That(await response.Content.ReadAsStringAsync()).IsEqualTo("body");
Check.That(response.Content.Headers.Contains("Content-Type")).IsTrue();
Check.That(response.Content.Headers.GetValues("Content-Type")).ContainsExactly("text/plain");
}
[Fact]
public async Task FluentMockServer_Should_change_absolute_location_header_in_proxied_response()
{
// given
_serverForProxyForwarding = FluentMockServer.Start();
_serverForProxyForwarding
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create()
.WithStatusCode(HttpStatusCode.Redirect)
.WithHeader("Location", _serverForProxyForwarding.Urls[0] + "testpath"));
_server = FluentMockServer.Start();
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create().WithProxy(_serverForProxyForwarding.Urls[0]));
// when
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(_server.Urls[0])
};
var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false };
var response = await new HttpClient(httpClientHandler).SendAsync(requestMessage);
// then
Check.That(response.Headers.Contains("Location")).IsTrue();
Check.That(response.Headers.GetValues("Location")).ContainsExactly(_server.Urls[0] + "testpath");
}
[Fact]
public async Task FluentMockServer_Should_preserve_cookie_header_in_proxied_request()
{
// given
_serverForProxyForwarding = FluentMockServer.Start();
_serverForProxyForwarding
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create());
_server = FluentMockServer.Start();
_server
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create().WithProxy(_serverForProxyForwarding.Urls[0]));
// when
var requestUri = new Uri(_server.Urls[0]);
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = requestUri
};
var clientHandler = new HttpClientHandler();
clientHandler.CookieContainer.Add(requestUri, new Cookie("name", "value"));
await new HttpClient(clientHandler).SendAsync(requestMessage);
// then
var receivedRequest = _serverForProxyForwarding.LogEntries.First().RequestMessage;
Check.That(receivedRequest.Cookies).IsNotNull();
Check.That(receivedRequest.Cookies).ContainsPair("name", "value");
}
}
}

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,3 @@
using System; using System;
using NFluent; using NFluent;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;

View File

@@ -12,7 +12,7 @@ namespace WireMock.Net.Tests
//[TestFixture] //[TestFixture]
public partial class RequestTests public partial class RequestTests
{ {
private const string clientIP = "::1"; private const string ClientIp = "::1";
[Fact] [Fact]
public void Should_specify_requests_matching_given_path() public void Should_specify_requests_matching_given_path()
@@ -21,7 +21,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithPath("/foo"); var spec = Request.Create().WithPath("/foo");
// when // when
var request = new RequestMessage(new Uri("http://localhost/foo"), "blabla", clientIP); var request = new RequestMessage(new Uri("http://localhost/foo"), "blabla", ClientIp);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -33,8 +33,8 @@ namespace WireMock.Net.Tests
{ {
var requestBuilder = Request.Create().WithPath("/x1", "/x2"); var requestBuilder = Request.Create().WithPath("/x1", "/x2");
var request1 = new RequestMessage(new Uri("http://localhost/x1"), "blabla", clientIP); var request1 = new RequestMessage(new Uri("http://localhost/x1"), "blabla", ClientIp);
var request2 = new RequestMessage(new Uri("http://localhost/x2"), "blabla", clientIP); var request2 = new RequestMessage(new Uri("http://localhost/x2"), "blabla", ClientIp);
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(requestBuilder.GetMatchingScore(request1, requestMatchResult)).IsEqualTo(1.0); Check.That(requestBuilder.GetMatchingScore(request1, requestMatchResult)).IsEqualTo(1.0);
@@ -48,7 +48,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithPath(url => url.EndsWith("/foo")); var spec = Request.Create().WithPath(url => url.EndsWith("/foo"));
// when // when
var request = new RequestMessage(new Uri("http://localhost/foo"), "blabla", clientIP); var request = new RequestMessage(new Uri("http://localhost/foo"), "blabla", ClientIp);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -62,7 +62,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithPath(new RegexMatcher("^/foo")); var spec = Request.Create().WithPath(new RegexMatcher("^/foo"));
// when // when
var request = new RequestMessage(new Uri("http://localhost/foo/bar"), "blabla", clientIP); var request = new RequestMessage(new Uri("http://localhost/foo/bar"), "blabla", ClientIp);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -76,7 +76,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithPath("/foo"); var spec = Request.Create().WithPath("/foo");
// when // when
var request = new RequestMessage(new Uri("http://localhost/bar"), "blabla", clientIP); var request = new RequestMessage(new Uri("http://localhost/bar"), "blabla", ClientIp);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -90,7 +90,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithUrl("*/foo"); var spec = Request.Create().WithUrl("*/foo");
// when // when
var request = new RequestMessage(new Uri("http://localhost/foo"), "blabla", clientIP); var request = new RequestMessage(new Uri("http://localhost/foo"), "blabla", ClientIp);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -104,7 +104,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithPath("/foo").UsingPut(); var spec = Request.Create().WithPath("/foo").UsingPut();
// when // when
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP); var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -118,7 +118,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithPath("/foo").UsingPost(); var spec = Request.Create().WithPath("/foo").UsingPost();
// when // when
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP); var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -132,7 +132,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithPath("/foo").UsingGet(); var spec = Request.Create().WithPath("/foo").UsingGet();
// when // when
var request = new RequestMessage(new Uri("http://localhost/foo"), "GET", clientIP); var request = new RequestMessage(new Uri("http://localhost/foo"), "GET", ClientIp);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -148,7 +148,7 @@ namespace WireMock.Net.Tests
// when // when
string bodyAsString = "whatever"; string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "Delete", clientIP, body, bodyAsString, Encoding.UTF8); var request = new RequestMessage(new Uri("http://localhost/foo"), "Delete", ClientIp, body, bodyAsString, Encoding.UTF8);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -162,7 +162,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithPath("/foo").UsingHead(); var spec = Request.Create().WithPath("/foo").UsingHead();
// when // when
var request = new RequestMessage(new Uri("http://localhost/foo"), "HEAD", clientIP); var request = new RequestMessage(new Uri("http://localhost/foo"), "HEAD", ClientIp);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -176,7 +176,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithPath("/foo").UsingPut(); var spec = Request.Create().WithPath("/foo").UsingPut();
// when // when
var request = new RequestMessage(new Uri("http://localhost/foo"), "HEAD", clientIP); var request = new RequestMessage(new Uri("http://localhost/foo"), "HEAD", ClientIp);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -190,7 +190,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithPath("/bar").UsingPut(); var spec = Request.Create().WithPath("/bar").UsingPut();
// when // when
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP); var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -206,7 +206,7 @@ namespace WireMock.Net.Tests
// when // when
string bodyAsString = "whatever"; string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); 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 // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -222,7 +222,7 @@ namespace WireMock.Net.Tests
// when // when
string bodyAsString = "whatever"; string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); 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 // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -238,7 +238,7 @@ namespace WireMock.Net.Tests
// when // when
string bodyAsString = "whatever"; string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); 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 // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -254,7 +254,7 @@ namespace WireMock.Net.Tests
// when // when
string bodyAsString = "whatever"; string bodyAsString = "whatever";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); 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 // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -268,7 +268,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().UsingAnyVerb().WithCookie("session", "a*"); var spec = Request.Create().UsingAnyVerb().WithCookie("session", "a*");
// when // when
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP, null, null, null, null, new Dictionary<string, string> { { "session", "abc" } }); var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp, null, null, null, null, new Dictionary<string, string> { { "session", "abc" } });
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -284,7 +284,7 @@ namespace WireMock.Net.Tests
// when // when
string bodyAsString = "Hello world!"; string bodyAsString = "Hello world!";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); 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 // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -300,7 +300,7 @@ namespace WireMock.Net.Tests
// when // when
string bodyAsString = "cat"; string bodyAsString = "cat";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP, body, bodyAsString, Encoding.UTF8); var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -316,7 +316,7 @@ namespace WireMock.Net.Tests
// when // when
string bodyAsString = "cat"; string bodyAsString = "cat";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP, body, bodyAsString, Encoding.UTF8); var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -332,7 +332,7 @@ namespace WireMock.Net.Tests
// when // when
string bodyAsString = "caR"; string bodyAsString = "caR";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP, body, bodyAsString, Encoding.UTF8); var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -348,7 +348,7 @@ namespace WireMock.Net.Tests
// when // when
string bodyAsString = "The car drives in the street."; string bodyAsString = "The car drives in the street.";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP, body, bodyAsString, Encoding.UTF8); var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -364,7 +364,7 @@ namespace WireMock.Net.Tests
// when // when
string bodyAsString = "Hello"; string bodyAsString = "Hello";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP, body, bodyAsString, Encoding.UTF8); var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -380,7 +380,7 @@ namespace WireMock.Net.Tests
// when // when
string bodyAsString = "Hello world!"; string bodyAsString = "Hello world!";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); 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[] { "tatata" } } });
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -396,7 +396,7 @@ namespace WireMock.Net.Tests
// when // when
string bodyAsString = "Hello world!"; string bodyAsString = "Hello world!";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); 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 // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -417,7 +417,7 @@ namespace WireMock.Net.Tests
<todo-item id='a3'>xyz</todo-item> <todo-item id='a3'>xyz</todo-item>
</todo-list>"; </todo-list>";
byte[] body = Encoding.UTF8.GetBytes(xmlBodyAsString); byte[] body = Encoding.UTF8.GetBytes(xmlBodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP, body, xmlBodyAsString, Encoding.UTF8); var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp, body, xmlBodyAsString, Encoding.UTF8);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -438,7 +438,7 @@ namespace WireMock.Net.Tests
<todo-item id='a3'>xyz</todo-item> <todo-item id='a3'>xyz</todo-item>
</todo-list>"; </todo-list>";
byte[] body = Encoding.UTF8.GetBytes(xmlBodyAsString); byte[] body = Encoding.UTF8.GetBytes(xmlBodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", clientIP, body, xmlBodyAsString, Encoding.UTF8); var request = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp, body, xmlBodyAsString, Encoding.UTF8);
// then // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -454,7 +454,7 @@ namespace WireMock.Net.Tests
// when // when
string bodyAsString = "{ \"things\": [ { \"name\": \"RequiredThing\" }, { \"name\": \"Wiremock\" } ] }"; string bodyAsString = "{ \"things\": [ { \"name\": \"RequiredThing\" }, { \"name\": \"Wiremock\" } ] }";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); 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 // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -470,7 +470,7 @@ namespace WireMock.Net.Tests
// when // when
string bodyAsString = "{ \"things\": { \"name\": \"Wiremock\" } }"; string bodyAsString = "{ \"things\": { \"name\": \"Wiremock\" } }";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); 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 // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -486,7 +486,7 @@ namespace WireMock.Net.Tests
// when // when
string bodyAsString = "xxx"; string bodyAsString = "xxx";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); 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 // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -500,7 +500,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithParam("bar", "1", "2"); var spec = Request.Create().WithParam("bar", "1", "2");
// when // 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 // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -514,7 +514,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithParam("bar"); var spec = Request.Create().WithParam("bar");
// when // 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 // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -528,7 +528,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().UsingAnyVerb().WithParam(p => p.ContainsKey("bar")); var spec = Request.Create().UsingAnyVerb().WithParam(p => p.ContainsKey("bar"));
// when // 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 // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
@@ -542,7 +542,7 @@ namespace WireMock.Net.Tests
var spec = Request.Create().WithParam("bar", "1"); var spec = Request.Create().WithParam("bar", "1");
// when // 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 // then
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();

View File

@@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using NFluent;
using WireMock.ResponseBuilders;
using Xunit;
namespace WireMock.Net.Tests
{
public partial class ResponseTests
{
[Fact]
public async Task Response_ProvideResponse_Handlebars_UrlPathVerb()
{
// given
string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
var response = Response.Create()
.WithBody("test {{request.url}} {{request.path}} {{request.method}}")
.WithTransformer();
// act
var responseMessage = await response.ProvideResponseAsync(request);
// then
Check.That(responseMessage.Body).Equals("test http://localhost/foo /foo post");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_Query()
{
// given
string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo?a=1&a=2&b=5"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
var response = Response.Create()
.WithBody("test keya={{request.query.a}} idx={{request.query.a.[0]}} idx={{request.query.a.[1]}} keyb={{request.query.b}}")
.WithTransformer();
// act
var responseMessage = await response.ProvideResponseAsync(request);
// then
Check.That(responseMessage.Body).Equals("test keya=1 idx=1 idx=2 keyb=5");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_Header()
{
// given
string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8, new Dictionary<string, string[]> { { "Content-Type", new[] { "text/plain" } } });
var response = Response.Create().WithHeader("x", "{{request.headers.Content-Type}}").WithBody("test").WithTransformer();
// act
var responseMessage = await response.ProvideResponseAsync(request);
// then
Check.That(responseMessage.Body).Equals("test");
Check.That(responseMessage.Headers).ContainsKey("x");
Check.That(responseMessage.Headers["x"]).ContainsExactly("text/plain");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_Headers()
{
// given
string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8, new Dictionary<string, string[]> { { "Content-Type", new[] { "text/plain" } } });
var response = Response.Create().WithHeader("x", "{{request.headers.Content-Type}}", "{{request.url}}").WithBody("test").WithTransformer();
// act
var responseMessage = await response.ProvideResponseAsync(request);
// then
Check.That(responseMessage.Body).Equals("test");
Check.That(responseMessage.Headers).ContainsKey("x");
Check.That(responseMessage.Headers["x"]).Contains("text/plain");
Check.That(responseMessage.Headers["x"]).Contains("http://localhost/foo");
}
[Fact]
public async Task Response_ProvideResponse_Handlebars_Origin_Port_Protocol_Host()
{
// given
string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost:1234"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
var response = Response.Create()
.WithBody("test {{request.origin}} {{request.port}} {{request.protocol}} {{request.host}}")
.WithTransformer();
// act
var responseMessage = await response.ProvideResponseAsync(request);
// then
Check.That(responseMessage.Body).Equals("test http://localhost:1234 1234 http localhost");
}
}
}

View File

@@ -1,83 +1,63 @@
using System; using System;
using System.Collections.Generic;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using NFluent; using NFluent;
using Xunit;
using WireMock.ResponseBuilders; using WireMock.ResponseBuilders;
using Xunit;
namespace WireMock.Net.Tests namespace WireMock.Net.Tests
{ {
//[TestFixture] public partial class ResponseTests
public class ResponseTests
{ {
private const string clientIP = "::1"; private const string ClientIp = "::1";
[Fact] [Fact]
public async Task Response_ProvideResponse_Handlebars_UrlPathVerb() public async Task Response_ProvideResponse_WithBody_Bytes_Encoding_Destination_String()
{ {
// given // given
string bodyAsString = "abc"; string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP, body, bodyAsString, Encoding.UTF8); var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
var response = Response.Create() var response = Response.Create().WithBody(new byte[] { 48, 49 }, BodyDestinationFormat.String, Encoding.ASCII);
.WithBody("test {{request.url}} {{request.path}} {{request.method}}")
.WithTransformer();
// act // act
var responseMessage = await response.ProvideResponseAsync(request); var responseMessage = await response.ProvideResponseAsync(request);
// then // then
Check.That(responseMessage.Body).Equals("test http://localhost/foo /foo post"); Check.That(responseMessage.Body).Equals("01");
Check.That(responseMessage.BodyAsBytes).IsNull();
Check.That(responseMessage.BodyEncoding).Equals(Encoding.ASCII);
} }
[Fact] [Fact]
public async Task Response_ProvideResponse_Handlebars_Query() public async Task Response_ProvideResponse_WithBody_Bytes_Encoding_Destination_Bytes()
{ {
// given // given
string bodyAsString = "abc"; string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo?a=1&a=2&b=5"), "POST", clientIP, body, bodyAsString, Encoding.UTF8); var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
var response = Response.Create() var response = Response.Create().WithBody(new byte[] { 48, 49 }, BodyDestinationFormat.SameAsSource, Encoding.ASCII);
.WithBody("test keya={{request.query.a}} idx={{request.query.a.[0]}} idx={{request.query.a.[1]}} keyb={{request.query.b}}")
.WithTransformer();
// act // act
var responseMessage = await response.ProvideResponseAsync(request); var responseMessage = await response.ProvideResponseAsync(request);
// then // then
Check.That(responseMessage.Body).Equals("test keya=1 idx=1 idx=2 keyb=5"); Check.That(responseMessage.BodyAsBytes).ContainsExactly(new byte[] { 48, 49 });
Check.That(responseMessage.Body).IsNull();
Check.That(responseMessage.BodyEncoding).IsNull();
} }
[Fact] [Fact]
public async Task Response_ProvideResponse_Handlebars_Headers() public async Task Response_ProvideResponse_WithBody_String_Encoding()
{ {
// given // given
string bodyAsString = "abc"; string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP, body, bodyAsString, Encoding.UTF8, new Dictionary<string, string> { { "Content-Type", "text/plain" } }); var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
var response = Response.Create().WithHeader("x", "{{request.headers.Content-Type}}").WithBody("test").WithTransformer(); var response = Response.Create().WithBody("test", null, Encoding.ASCII);
// act
var responseMessage = await response.ProvideResponseAsync(request);
// then
Check.That(responseMessage.Body).Equals("test");
Check.That(responseMessage.Headers).Contains(new KeyValuePair<string, string>("x", "text/plain"));
}
[Fact]
public async Task Response_ProvideResponse_Encoding_Body()
{
// given
string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP, body, bodyAsString, Encoding.UTF8);
var response = Response.Create().WithBody("test", Encoding.ASCII);
// act // act
var responseMessage = await response.ProvideResponseAsync(request); var responseMessage = await response.ProvideResponseAsync(request);
@@ -88,12 +68,12 @@ namespace WireMock.Net.Tests
} }
[Fact] [Fact]
public async Task Response_ProvideResponse_Encoding_JsonBody() public async Task Response_ProvideResponse_WithBody_Object_Encoding()
{ {
// given // given
string bodyAsString = "abc"; string bodyAsString = "abc";
byte[] body = Encoding.UTF8.GetBytes(bodyAsString); byte[] body = Encoding.UTF8.GetBytes(bodyAsString);
var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", clientIP, body, bodyAsString, Encoding.UTF8); var request = new RequestMessage(new Uri("http://localhost/foo"), "POST", ClientIp, body, bodyAsString, Encoding.UTF8);
var response = Response.Create().WithBodyAsJson(new { value = "test" }, Encoding.ASCII); var response = Response.Create().WithBodyAsJson(new { value = "test" }, Encoding.ASCII);

View File

@@ -7,7 +7,6 @@ using WireMock.RequestBuilders;
using WireMock.ResponseBuilders; using WireMock.ResponseBuilders;
using WireMock.Server; using WireMock.Server;
using Xunit; using Xunit;
using Xunit.Abstractions;
namespace WireMock.Net.Tests namespace WireMock.Net.Tests
{ {