mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-02-13 22:07:41 +01:00
Compare commits
24 Commits
version-2.
...
ws3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa9be33620 | ||
|
|
0f4f52cedd | ||
|
|
d678048aae | ||
|
|
721bf14539 | ||
|
|
37201bd65c | ||
|
|
e4ebdfba76 | ||
|
|
3a266c3e18 | ||
|
|
f9741af021 | ||
|
|
d6c5ede20b | ||
|
|
deb9777f88 | ||
|
|
1bac18aeb1 | ||
|
|
547b5673c0 | ||
|
|
ecb3e249cc | ||
|
|
cbd73eed0b | ||
|
|
842a4e0aeb | ||
|
|
c1cf61862e | ||
|
|
ceab19514f | ||
|
|
fc24eaf376 | ||
|
|
4f6d4a01b8 | ||
|
|
1d2b22545b | ||
|
|
f53afec823 | ||
|
|
2c1ab7e1d6 | ||
|
|
7cffd98cb6 | ||
|
|
39b09ccb44 |
4
.github/copilot-instructions.md
vendored
Normal file
4
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# Copilot Instructions
|
||||
|
||||
## Project Guidelines
|
||||
- All new byte[xx] calls should use using var data = ArrayPool<byte>.Shared.Lease(xx); instead of directly allocating byte arrays
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -254,6 +254,6 @@ paket-files/
|
||||
./report/coverlet/
|
||||
/test/WireMock.Net.Tests/coverage.opencover.xml
|
||||
/test/WireMock.Net.Tests/coverage.netcoreapp3.1.opencover.xml
|
||||
/test/WireMock.Net.Tests/coverage.net5.0.opencover.xml
|
||||
/test/WireMock.Net.Tests/coverage.net8.0.opencover.xml
|
||||
|
||||
*.received.*
|
||||
14
CHANGELOG.md
14
CHANGELOG.md
@@ -475,7 +475,7 @@
|
||||
# 1.5.30 (28 June 2023)
|
||||
- [#959](https://github.com/wiremock/WireMock.Net/pull/959) - Fixed logic for FluentAssertions WithHeader [bug] contributed by [StefH](https://github.com/StefH)
|
||||
- [#962](https://github.com/wiremock/WireMock.Net/pull/962) - Bump System.Linq.Dynamic.Core from 1.2.23 to 1.3.0 in /examples/WireMock.Net.Console.Net472.Classic [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#963](https://github.com/wiremock/WireMock.Net/pull/963) - Bump System.Linq.Dynamic.Core from 1.2.23 to 1.3.0 in /examples/WireMock.Net.StandAlone.Net461 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#963](https://github.com/wiremock/WireMock.Net/pull/963) - Bump System.Linq.Dynamic.Core from 1.2.23 to 1.3.0 in /examples/WireMock.Net.StandAlone.net462 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#958](https://github.com/wiremock/WireMock.Net/issues/958) - [FluentAssertions] Should().HaveReceivedACall().WithHeader() only checks the first header with the matching key. [bug]
|
||||
|
||||
# 1.5.29 (22 June 2023)
|
||||
@@ -587,7 +587,7 @@
|
||||
- [#828](https://github.com/wiremock/WireMock.Net/pull/828) - Add setting to skip saving the string-response in the logging when using WithBody(Func...) [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#832](https://github.com/wiremock/WireMock.Net/pull/832) - Fixes for WireMock.Net.FluentAssertions (callcount behaviour) [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#834](https://github.com/wiremock/WireMock.Net/pull/834) - Support deleting / resetting a single scenario [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#837](https://github.com/wiremock/WireMock.Net/pull/837) - Bump Microsoft.AspNetCore.Server.Kestrel.Core from 2.1.7 to 2.1.25 in /examples/WireMock.Net.StandAlone.Net461 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#837](https://github.com/wiremock/WireMock.Net/pull/837) - Bump Microsoft.AspNetCore.Server.Kestrel.Core from 2.1.7 to 2.1.25 in /examples/WireMock.Net.StandAlone.net462 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#838](https://github.com/wiremock/WireMock.Net/pull/838) - Add option to ProxySettings to append guid to mapping file contributed by [StefH](https://github.com/StefH)
|
||||
- [#826](https://github.com/wiremock/WireMock.Net/issues/826) - Dynamic Body not to be cached when a Func is used to created the body [feature]
|
||||
|
||||
@@ -635,14 +635,14 @@
|
||||
- [#776](https://github.com/wiremock/WireMock.Net/issues/776) - Update Scriban.Signed to support more functions, e.g math.random [feature]
|
||||
|
||||
# 1.5.2 (24 July 2022)
|
||||
- [#769](https://github.com/wiremock/WireMock.Net/pull/769) - Bump Microsoft.AspNetCore.Server.Kestrel.Core from 2.1.3 to 2.1.7 in /examples/WireMock.Net.StandAlone.Net461 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#769](https://github.com/wiremock/WireMock.Net/pull/769) - Bump Microsoft.AspNetCore.Server.Kestrel.Core from 2.1.3 to 2.1.7 in /examples/WireMock.Net.StandAlone.net462 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#771](https://github.com/wiremock/WireMock.Net/pull/771) - JsonPartialMatcher - support Regex [feature] contributed by [StefH](https://github.com/StefH)
|
||||
|
||||
# 1.5.1 (08 July 2022)
|
||||
- [#762](https://github.com/wiremock/WireMock.Net/pull/762) - Bump Newtonsoft.Json from 11.0.2 to 13.0.1 in /examples/WireMock.Net.WebApplication.NETCore2 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#763](https://github.com/wiremock/WireMock.Net/pull/763) - Bump Newtonsoft.Json from 6.0.1 to 13.0.1 in /examples/WireMock.Net.Client.Net472 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#765](https://github.com/wiremock/WireMock.Net/pull/765) - Update WireMock.Org.Abstractions and WireMock.Org.RestClient [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#766](https://github.com/wiremock/WireMock.Net/pull/766) - Bump Microsoft.AspNetCore.Http from 2.1.1 to 2.1.22 in /examples/WireMock.Net.StandAlone.Net461 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#766](https://github.com/wiremock/WireMock.Net/pull/766) - Bump Microsoft.AspNetCore.Http from 2.1.1 to 2.1.22 in /examples/WireMock.Net.StandAlone.net462 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#767](https://github.com/wiremock/WireMock.Net/pull/767) - Rename (WireMock.Pact.Models.V2)-Request to PactRequest and -Response to PactResponse [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#764](https://github.com/wiremock/WireMock.Net/issues/764) - Wrong mapping of method GetAdminMappingsAsync from IWireMockOrgApi [bug]
|
||||
|
||||
@@ -794,7 +794,7 @@
|
||||
- [#612](https://github.com/wiremock/WireMock.Net/pull/612) - Don't run SonarCloud tasks for PullRequests [feature] contributed by [StefH](https://github.com/StefH)
|
||||
|
||||
# 1.4.13 (26 April 2021)
|
||||
- [#607](https://github.com/wiremock/WireMock.Net/pull/607) - Bump System.Text.Encodings.Web from 4.5.0 to 4.5.1 in /examples/WireMock.Net.StandAlone.Net461 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#607](https://github.com/wiremock/WireMock.Net/pull/607) - Bump System.Text.Encodings.Web from 4.5.0 to 4.5.1 in /examples/WireMock.Net.StandAlone.net462 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#609](https://github.com/wiremock/WireMock.Net/pull/609) - Add possibility to use settings to generate MappingModel models with wildcard path parameters. [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#608](https://github.com/wiremock/WireMock.Net/issues/608) - Import from OpenApi generates model with path parameter narrowed in range (example value=42 instead of '*') [feature]
|
||||
|
||||
@@ -838,7 +838,7 @@
|
||||
- [#570](https://github.com/wiremock/WireMock.Net/pull/570) - Bump log4net from 2.0.8 to 2.0.10 in /examples/WireMock.Net.StandAlone.NETCoreApp [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#571](https://github.com/wiremock/WireMock.Net/pull/571) - Bump log4net from 2.0.8 to 2.0.10 in /examples/WireMock.Net.Console.NETCoreApp2 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#572](https://github.com/wiremock/WireMock.Net/pull/572) - Bump log4net from 2.0.8 to 2.0.10 in /examples/WireMock.Net.Console.NETCoreApp [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#573](https://github.com/wiremock/WireMock.Net/pull/573) - Bump log4net from 2.0.8 to 2.0.10 in /examples/WireMock.Net.Console.Net461.Classic [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#573](https://github.com/wiremock/WireMock.Net/pull/573) - Bump log4net from 2.0.8 to 2.0.10 in /examples/WireMock.Net.Console.net462.Classic [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#574](https://github.com/wiremock/WireMock.Net/pull/574) - Bump log4net from 2.0.8 to 2.0.10 in /examples/WireMock.Net.Console.Net452.Classic [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#575](https://github.com/wiremock/WireMock.Net/pull/575) - Bump log4net from 2.0.8 to 2.0.10 in /examples/WireMock.Net.StandAlone.Net452 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#576](https://github.com/wiremock/WireMock.Net/pull/576) - Bump log4net from 2.0.8 to 2.0.10 in /examples/WireMock.Net.Service [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
@@ -1434,7 +1434,7 @@
|
||||
- [#72](https://github.com/wiremock/WireMock.Net/issues/72) - Matching WithParam on OData End Points
|
||||
|
||||
# 1.0.2.10 (12 December 2017)
|
||||
- [#70](https://github.com/wiremock/WireMock.Net/issues/70) - Proxy/Intercept pattern is throwing a keep alive header error with net461
|
||||
- [#70](https://github.com/wiremock/WireMock.Net/issues/70) - Proxy/Intercept pattern is throwing a keep alive header error with net462
|
||||
|
||||
# 1.0.2.9 (07 December 2017)
|
||||
- [#71](https://github.com/wiremock/WireMock.Net/pull/71) - Fixed restricted headers on response contributed by [StefH](https://github.com/StefH)
|
||||
|
||||
@@ -13,8 +13,9 @@
|
||||
<RepositoryUrl>https://github.com/wiremock/WireMock.Net</RepositoryUrl>
|
||||
<ApplicationIcon>../../resources/WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
<PackageReadmeFile>PackageReadme.md</PackageReadmeFile>
|
||||
<LangVersion>12</LangVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TF_BUILD)' == 'true'">
|
||||
@@ -45,14 +46,11 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<NuGetAudit>true</NuGetAudit>
|
||||
<!--<NuGetAuditLevel>low</NuGetAuditLevel>-->
|
||||
<NuGetAuditLevel>low</NuGetAuditLevel>
|
||||
<NuGetAuditMode>all</NuGetAuditMode>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- CVE-2019-0820 -->
|
||||
<!--<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />-->
|
||||
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2024.3.0" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -154,6 +154,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.NET8.W
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.WebApplication.IIS", "examples\WireMock.Net.WebApplication.IIS\WireMock.Net.WebApplication.IIS.csproj", "{5E6E9FA7-9135-7B82-2CCD-8CA87AC8043C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.WebSocketExamples", "examples\WireMock.Net.WebSocketExamples\WireMock.Net.WebSocketExamples.csproj", "{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -788,6 +790,18 @@ Global
|
||||
{9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9957038D-F9C3-CA5D-E8AE-BE188E512635}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|x86.Build.0 = Release|Any CPU
|
||||
{2D86546D-8A24-0A55-C962-2071BD299E05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2D86546D-8A24-0A55-C962-2071BD299E05}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2D86546D-8A24-0A55-C962-2071BD299E05}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
@@ -812,18 +826,18 @@ Global
|
||||
{5E6E9FA7-9135-7B82-2CCD-8CA87AC8043C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{5E6E9FA7-9135-7B82-2CCD-8CA87AC8043C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{5E6E9FA7-9135-7B82-2CCD-8CA87AC8043C}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F}.Release|x86.Build.0 = Release|Any CPU
|
||||
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -883,9 +897,10 @@ Global
|
||||
{2DBBD70D-8051-441F-92BB-FF9B8B4B4982} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||
{C8F4E6D2-9A3B-4F1C-8D5E-7A2B3C4D5E6F} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||
{9957038D-F9C3-CA5D-E8AE-BE188E512635} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{2D86546D-8A24-0A55-C962-2071BD299E05} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{5E6E9FA7-9135-7B82-2CCD-8CA87AC8043C} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{4005E20C-D42B-138A-79BE-B3F5420C563F} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
|
||||
|
||||
678
examples/WireMock.Net.WebSocketExamples/Program.cs
Normal file
678
examples/WireMock.Net.WebSocketExamples/Program.cs
Normal file
@@ -0,0 +1,678 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using WireMock.Logging;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Server;
|
||||
using WireMock.Settings;
|
||||
|
||||
namespace WireMock.Net.WebSocketExamples;
|
||||
|
||||
public static class Program
|
||||
{
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("WireMock.Net WebSocket Examples");
|
||||
Console.WriteLine("================================\n");
|
||||
|
||||
Console.WriteLine("Choose an example to run:");
|
||||
Console.WriteLine("1. Echo Server");
|
||||
Console.WriteLine("2. Custom Message Handler");
|
||||
Console.WriteLine("3. ...");
|
||||
Console.WriteLine("4. Scenario/State Machine");
|
||||
Console.WriteLine("5. WebSocket Proxy");
|
||||
Console.WriteLine("6. Multiple WebSocket Endpoints");
|
||||
Console.WriteLine("7. All Examples (runs all endpoints)");
|
||||
Console.WriteLine("0. Exit\n");
|
||||
|
||||
Console.Write("Enter choice: ");
|
||||
var choice = Console.ReadLine();
|
||||
|
||||
switch (choice)
|
||||
{
|
||||
case "1":
|
||||
await RunEchoServerExample();
|
||||
break;
|
||||
case "2":
|
||||
await RunCustomMessageHandlerExample();
|
||||
break;
|
||||
case "3":
|
||||
await RunBroadcastExample();
|
||||
break;
|
||||
case "4":
|
||||
await RunScenarioExample();
|
||||
break;
|
||||
case "5":
|
||||
await RunProxyExample();
|
||||
break;
|
||||
case "6":
|
||||
await RunMultipleEndpointsExample();
|
||||
break;
|
||||
case "7":
|
||||
await RunAllExamples();
|
||||
break;
|
||||
case "0":
|
||||
return;
|
||||
default:
|
||||
Console.WriteLine("Invalid choice");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example 1: Simple Echo Server
|
||||
/// Echoes back all messages received from the client
|
||||
/// </summary>
|
||||
private static async Task RunEchoServerExample()
|
||||
{
|
||||
Console.WriteLine("\n=== Echo Server Example ===");
|
||||
Console.WriteLine("Starting WebSocket echo server...\n");
|
||||
|
||||
var server = WireMockServer.Start(new WireMockServerSettings
|
||||
{
|
||||
Port = 9091,
|
||||
Logger = new WireMockConsoleLogger()
|
||||
});
|
||||
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/ws/echo")
|
||||
.WithWebSocketUpgrade()
|
||||
)
|
||||
.RespondWith(Response.Create()
|
||||
.WithWebSocket(ws => ws
|
||||
.WithEcho()
|
||||
)
|
||||
);
|
||||
|
||||
Console.WriteLine($"Echo server listening at: {server.Urls[0]}/ws/echo");
|
||||
Console.WriteLine("\nTest with a WebSocket client:");
|
||||
Console.WriteLine(" wscat -c ws://localhost:9091/ws/echo");
|
||||
Console.WriteLine("\nPress any key to test or CTRL+C to exit...");
|
||||
Console.ReadKey();
|
||||
|
||||
// Test the echo server
|
||||
await TestWebSocketEcho(server.Urls[0]);
|
||||
|
||||
Console.WriteLine("\nPress any key to stop server...");
|
||||
Console.ReadKey();
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example 2: Custom Message Handler
|
||||
/// Processes messages and sends custom responses
|
||||
/// </summary>
|
||||
private static async Task RunCustomMessageHandlerExample()
|
||||
{
|
||||
Console.WriteLine("\n=== Custom Message Handler Example ===");
|
||||
Console.WriteLine("Starting WebSocket server with custom message handler...\n");
|
||||
|
||||
var server = WireMockServer.Start(new WireMockServerSettings
|
||||
{
|
||||
Port = 9091,
|
||||
Logger = new WireMockConsoleLogger()
|
||||
});
|
||||
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/ws/chat")
|
||||
.WithWebSocketUpgrade()
|
||||
)
|
||||
.RespondWith(Response.Create()
|
||||
.WithWebSocket(ws => ws
|
||||
.WithMessageHandler(async (message, context) =>
|
||||
{
|
||||
if (message.MessageType == WebSocketMessageType.Text)
|
||||
{
|
||||
var text = message.Text ?? string.Empty;
|
||||
|
||||
// Handle different commands
|
||||
if (text.StartsWith("/help"))
|
||||
{
|
||||
await context.SendAsync("Available commands: /help, /time, /echo <text>, /upper <text>, /reverse <text>");
|
||||
}
|
||||
else if (text.StartsWith("/time"))
|
||||
{
|
||||
await context.SendAsync($"Server time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC");
|
||||
}
|
||||
else if (text.StartsWith("/echo "))
|
||||
{
|
||||
await context.SendAsync(text.Substring(6));
|
||||
}
|
||||
else if (text.StartsWith("/upper "))
|
||||
{
|
||||
await context.SendAsync(text.Substring(7).ToUpper());
|
||||
}
|
||||
else if (text.StartsWith("/reverse "))
|
||||
{
|
||||
var toReverse = text.Substring(9);
|
||||
var reversed = new string(toReverse.Reverse().ToArray());
|
||||
await context.SendAsync(reversed);
|
||||
}
|
||||
else if (text == "/quit")
|
||||
{
|
||||
await context.SendAsync("Goodbye!");
|
||||
await context.CloseAsync(WebSocketCloseStatus.NormalClosure, "Client requested disconnect");
|
||||
}
|
||||
else
|
||||
{
|
||||
await context.SendAsync($"Unknown command: {text}. Type /help for available commands.");
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
Console.WriteLine($"Chat server listening at: {server.Urls[0]}/ws/chat");
|
||||
Console.WriteLine("\nTest with:");
|
||||
Console.WriteLine(" wscat -c ws://localhost:9091/ws/chat");
|
||||
Console.WriteLine("\nThen try commands: /help, /time, /echo hello, /upper hello, /reverse hello");
|
||||
Console.WriteLine("\nPress any key to test or CTRL+C to exit...");
|
||||
Console.ReadKey();
|
||||
|
||||
await TestWebSocketChat(server.Urls[0]);
|
||||
|
||||
Console.WriteLine("\nPress any key to stop server...");
|
||||
Console.ReadKey();
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example 3: Broadcast Server
|
||||
/// Broadcasts messages to all connected clients
|
||||
/// </summary>
|
||||
private static async Task RunBroadcastExample()
|
||||
{
|
||||
Console.WriteLine("\n=== Broadcast Server Example ===");
|
||||
Console.WriteLine("Starting WebSocket broadcast server...\n");
|
||||
|
||||
var server = WireMockServer.Start(new WireMockServerSettings
|
||||
{
|
||||
Port = 9091,
|
||||
Logger = new WireMockConsoleLogger()
|
||||
});
|
||||
|
||||
var broadcastMappingGuid = Guid.NewGuid();
|
||||
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/ws/broadcast")
|
||||
.WithWebSocketUpgrade()
|
||||
)
|
||||
.WithGuid(broadcastMappingGuid)
|
||||
.RespondWith(Response.Create()
|
||||
.WithWebSocket(ws => ws
|
||||
.WithBroadcast()
|
||||
.WithMessageHandler(async (message, context) =>
|
||||
{
|
||||
if (message.MessageType == WebSocketMessageType.Text)
|
||||
{
|
||||
var text = message.Text ?? string.Empty;
|
||||
var timestamp = DateTime.UtcNow.ToString("HH:mm:ss");
|
||||
var broadcastMessage = $"[{timestamp}] Broadcast: {text}";
|
||||
|
||||
// Broadcast to all connected clients
|
||||
await context.BroadcastTextAsync(broadcastMessage);
|
||||
|
||||
Console.WriteLine($"Broadcasted to {server.GetWebSocketConnections(broadcastMappingGuid).Count} clients: {text}");
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
Console.WriteLine($"Broadcast server listening at: {server.Urls[0]}/ws/broadcast");
|
||||
Console.WriteLine("\nConnect multiple clients:");
|
||||
Console.WriteLine(" wscat -c ws://localhost:9091/ws/broadcast");
|
||||
Console.WriteLine("\nMessages sent from any client will be broadcast to all clients");
|
||||
Console.WriteLine("\nPress any key to stop server...");
|
||||
Console.ReadKey();
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example 4: Scenario/State Machine
|
||||
/// Demonstrates state transitions during WebSocket session
|
||||
/// </summary>
|
||||
private static async Task RunScenarioExample()
|
||||
{
|
||||
Console.WriteLine("\n=== Scenario/State Machine Example ===");
|
||||
Console.WriteLine("Starting WebSocket server with scenario support...\n");
|
||||
|
||||
var server = WireMockServer.Start(new WireMockServerSettings
|
||||
{
|
||||
Port = 9091,
|
||||
Logger = new WireMockConsoleLogger()
|
||||
});
|
||||
|
||||
// Initial state: Waiting for players
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/ws/game")
|
||||
.WithWebSocketUpgrade()
|
||||
)
|
||||
.InScenario("GameSession")
|
||||
.WillSetStateTo("Lobby")
|
||||
.RespondWith(Response.Create()
|
||||
.WithWebSocket(ws => ws
|
||||
.WithMessageHandler(async (msg, ctx) =>
|
||||
{
|
||||
await ctx.SendAsync("Welcome to the game lobby! Type 'ready' to start or 'quit' to leave.");
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// Lobby state: Waiting for ready
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/ws/game")
|
||||
.WithWebSocketUpgrade()
|
||||
)
|
||||
.InScenario("GameSession")
|
||||
.WhenStateIs("Lobby")
|
||||
.RespondWith(Response.Create()
|
||||
.WithWebSocket(ws => ws
|
||||
.WithMessageHandler(async (msg, ctx) =>
|
||||
{
|
||||
var text = msg.Text?.ToLower() ?? string.Empty;
|
||||
|
||||
if (text == "ready")
|
||||
{
|
||||
ctx.SetScenarioState("Playing");
|
||||
await ctx.SendAsync("Game started! Type 'attack' to attack, 'defend' to defend, or 'quit' to exit.");
|
||||
}
|
||||
else if (text == "quit")
|
||||
{
|
||||
await ctx.SendAsync("You left the lobby. Goodbye!");
|
||||
await ctx.CloseAsync(WebSocketCloseStatus.NormalClosure, "Player quit");
|
||||
}
|
||||
else
|
||||
{
|
||||
await ctx.SendAsync("In lobby. Type 'ready' to start or 'quit' to leave.");
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// Playing state: Game is active
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/ws/game")
|
||||
.WithWebSocketUpgrade()
|
||||
)
|
||||
.InScenario("GameSession")
|
||||
.WhenStateIs("Playing")
|
||||
.RespondWith(Response.Create()
|
||||
.WithWebSocket(ws => ws
|
||||
.WithMessageHandler(async (msg, ctx) =>
|
||||
{
|
||||
var text = msg.Text?.ToLower() ?? string.Empty;
|
||||
|
||||
if (text == "attack")
|
||||
{
|
||||
await ctx.SendAsync("You attacked! Critical hit! 💥");
|
||||
}
|
||||
else if (text == "defend")
|
||||
{
|
||||
await ctx.SendAsync("You defended! Shield up! 🛡️");
|
||||
}
|
||||
else if (text == "quit")
|
||||
{
|
||||
ctx.SetScenarioState("GameOver");
|
||||
await ctx.SendAsync("Game over! Thanks for playing.");
|
||||
await ctx.CloseAsync(WebSocketCloseStatus.NormalClosure, "Game ended");
|
||||
}
|
||||
else
|
||||
{
|
||||
await ctx.SendAsync("Unknown action. Type 'attack', 'defend', or 'quit'.");
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
Console.WriteLine($"Game server listening at: {server.Urls[0]}/ws/game");
|
||||
Console.WriteLine("\nConnect and follow the game flow:");
|
||||
Console.WriteLine(" wscat -c ws://localhost:9091/ws/game");
|
||||
Console.WriteLine("\nGame flow: Lobby -> Type 'ready' -> Playing -> Type 'attack'/'defend' -> Type 'quit'");
|
||||
Console.WriteLine("\nPress any key to stop server...");
|
||||
Console.ReadKey();
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example 5: WebSocket Proxy
|
||||
/// Proxies WebSocket connections to another server
|
||||
/// </summary>
|
||||
private static async Task RunProxyExample()
|
||||
{
|
||||
Console.WriteLine("\n=== WebSocket Proxy Example ===");
|
||||
Console.WriteLine("Starting WebSocket proxy server...\n");
|
||||
|
||||
var server = WireMockServer.Start(new WireMockServerSettings
|
||||
{
|
||||
Port = 9091,
|
||||
Logger = new WireMockConsoleLogger()
|
||||
});
|
||||
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/ws/proxy")
|
||||
.WithWebSocketUpgrade()
|
||||
)
|
||||
.RespondWith(Response.Create()
|
||||
.WithWebSocketProxy("ws://echo.websocket.org")
|
||||
);
|
||||
|
||||
Console.WriteLine($"Proxy server listening at: {server.Urls[0]}/ws/proxy");
|
||||
Console.WriteLine("Proxying to: ws://echo.websocket.org");
|
||||
Console.WriteLine("\nTest with:");
|
||||
Console.WriteLine(" wscat -c ws://localhost:9091/ws/proxy");
|
||||
Console.WriteLine("\nPress any key to stop server...");
|
||||
Console.ReadKey();
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example 6: Multiple WebSocket Endpoints
|
||||
/// Demonstrates running multiple WebSocket endpoints simultaneously
|
||||
/// </summary>
|
||||
private static async Task RunMultipleEndpointsExample()
|
||||
{
|
||||
Console.WriteLine("\n=== Multiple WebSocket Endpoints Example ===");
|
||||
Console.WriteLine("Starting server with multiple WebSocket endpoints...\n");
|
||||
|
||||
var server = WireMockServer.Start(new WireMockServerSettings
|
||||
{
|
||||
Port = 9091,
|
||||
Logger = new WireMockConsoleLogger(),
|
||||
WebSocketSettings = new WebSocketSettings
|
||||
{
|
||||
MaxConnections = 100,
|
||||
KeepAliveIntervalSeconds = 30
|
||||
}
|
||||
});
|
||||
|
||||
// Endpoint 1: Echo
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/ws/echo")
|
||||
.WithWebSocketUpgrade()
|
||||
)
|
||||
.RespondWith(Response.Create()
|
||||
.WithWebSocket(ws => ws.WithEcho())
|
||||
);
|
||||
|
||||
// Endpoint 2: Time service
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/ws/time")
|
||||
.WithWebSocketUpgrade()
|
||||
)
|
||||
.RespondWith(Response.Create()
|
||||
.WithWebSocket(ws => ws
|
||||
.WithMessageHandler(async (msg, ctx) =>
|
||||
{
|
||||
await ctx.SendAsync($"Server time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC");
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// Endpoint 4: Protocol-specific
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/ws/protocol")
|
||||
.WithWebSocketUpgrade("chat", "superchat")
|
||||
)
|
||||
.RespondWith(Response.Create()
|
||||
.WithWebSocket(ws => ws
|
||||
.WithAcceptProtocol("chat")
|
||||
.WithMessageHandler(async (msg, ctx) =>
|
||||
{
|
||||
await ctx.SendAsync($"Using protocol: chat. Message: {msg.Text}");
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
Console.WriteLine("Available WebSocket endpoints:");
|
||||
Console.WriteLine($" 1. Echo: {server.Urls[0]}/ws/echo");
|
||||
Console.WriteLine($" 2. Time: {server.Urls[0]}/ws/time");
|
||||
Console.WriteLine($" 3. JSON: {server.Urls[0]}/ws/json");
|
||||
Console.WriteLine($" 4. Protocol: {server.Urls[0]}/ws/protocol");
|
||||
Console.WriteLine("\nTest with wscat:");
|
||||
Console.WriteLine(" wscat -c ws://localhost:9091/ws/echo");
|
||||
Console.WriteLine(" wscat -c ws://localhost:9091/ws/time");
|
||||
Console.WriteLine(" wscat -c ws://localhost:9091/ws/json");
|
||||
Console.WriteLine(" wscat -c ws://localhost:9091/ws/protocol -s chat");
|
||||
Console.WriteLine("\nPress any key to stop server...");
|
||||
Console.ReadKey();
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example 7: Run All Examples
|
||||
/// Starts a server with all example endpoints
|
||||
/// </summary>
|
||||
private static async Task RunAllExamples()
|
||||
{
|
||||
Console.WriteLine("\n=== All Examples Running ===");
|
||||
Console.WriteLine("Starting server with all WebSocket endpoints...\n");
|
||||
|
||||
var server = WireMockServer.Start(new WireMockServerSettings
|
||||
{
|
||||
Port = 9091,
|
||||
Logger = new WireMockConsoleLogger(),
|
||||
WebSocketSettings = new WebSocketSettings
|
||||
{
|
||||
MaxConnections = 200
|
||||
}
|
||||
});
|
||||
|
||||
SetupAllEndpoints(server);
|
||||
|
||||
Console.WriteLine("All WebSocket endpoints are running:");
|
||||
Console.WriteLine($" Echo: {server.Urls[0]}/ws/echo");
|
||||
Console.WriteLine($" Chat: {server.Urls[0]}/ws/chat");
|
||||
Console.WriteLine($" Broadcast: {server.Urls[0]}/ws/broadcast");
|
||||
Console.WriteLine($" Game: {server.Urls[0]}/ws/game");
|
||||
Console.WriteLine($" Time: {server.Urls[0]}/ws/time");
|
||||
Console.WriteLine($" JSON: {server.Urls[0]}/ws/json");
|
||||
Console.WriteLine("\nServer statistics:");
|
||||
Console.WriteLine($" Total mappings: {server.Mappings.Count}");
|
||||
|
||||
Console.WriteLine("\nPress any key to view connection stats or CTRL+C to exit...");
|
||||
|
||||
while (true)
|
||||
{
|
||||
Console.ReadKey(true);
|
||||
var connections = server.GetWebSocketConnections();
|
||||
Console.WriteLine($"\nActive WebSocket connections: {connections.Count}");
|
||||
foreach (var conn in connections)
|
||||
{
|
||||
Console.WriteLine($" - {conn.ConnectionId}: {conn.RequestMessage.Path} (State: {conn.WebSocket.State})");
|
||||
}
|
||||
Console.WriteLine("\nPress any key to refresh or CTRL+C to exit...");
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetupAllEndpoints(WireMockServer server)
|
||||
{
|
||||
// Echo endpoint
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/ws/echo")
|
||||
.WithWebSocketUpgrade()
|
||||
)
|
||||
.RespondWith(Response.Create()
|
||||
.WithWebSocket(ws => ws.WithEcho())
|
||||
);
|
||||
|
||||
// Chat endpoint
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/ws/chat")
|
||||
.WithWebSocketUpgrade()
|
||||
)
|
||||
.RespondWith(Response.Create()
|
||||
.WithWebSocket(ws => ws
|
||||
.WithMessageHandler(async (message, context) =>
|
||||
{
|
||||
if (message.MessageType == WebSocketMessageType.Text)
|
||||
{
|
||||
await context.SendAsync($"Echo: {message.Text}");
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// Broadcast endpoint
|
||||
var broadcastGuid = Guid.NewGuid();
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/ws/broadcast")
|
||||
.WithWebSocketUpgrade()
|
||||
)
|
||||
.WithGuid(broadcastGuid)
|
||||
.RespondWith(Response.Create()
|
||||
.WithWebSocket(ws => ws
|
||||
.WithBroadcast()
|
||||
.WithMessageHandler(async (message, context) =>
|
||||
{
|
||||
if (message.MessageType == WebSocketMessageType.Text)
|
||||
{
|
||||
await context.BroadcastTextAsync($"[Broadcast] {message.Text}");
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// Game scenario endpoint
|
||||
SetupGameScenario(server);
|
||||
|
||||
// Time endpoint
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/ws/time")
|
||||
.WithWebSocketUpgrade()
|
||||
)
|
||||
.RespondWith(Response.Create()
|
||||
.WithWebSocket(ws => ws
|
||||
.WithMessageHandler(async (msg, ctx) =>
|
||||
{
|
||||
await ctx.SendAsync($"Server time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC");
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static void SetupGameScenario(WireMockServer server)
|
||||
{
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/ws/game")
|
||||
.WithWebSocketUpgrade()
|
||||
)
|
||||
.InScenario("GameSession")
|
||||
.WillSetStateTo("Lobby")
|
||||
.RespondWith(Response.Create()
|
||||
.WithWebSocket(ws => ws
|
||||
.WithMessageHandler(async (msg, ctx) =>
|
||||
{
|
||||
await ctx.SendAsync("Welcome! Type 'ready' to start.");
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/ws/game")
|
||||
.WithWebSocketUpgrade()
|
||||
)
|
||||
.InScenario("GameSession")
|
||||
.WhenStateIs("Lobby")
|
||||
.RespondWith(Response.Create()
|
||||
.WithWebSocket(ws => ws
|
||||
.WithMessageHandler(async (msg, ctx) =>
|
||||
{
|
||||
if (msg.Text?.ToLower() == "ready")
|
||||
{
|
||||
ctx.SetScenarioState("Playing");
|
||||
await ctx.SendAsync("Game started!");
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Helper methods for testing
|
||||
private static async Task TestWebSocketEcho(string baseUrl)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var client = new ClientWebSocket();
|
||||
var uri = new Uri($"{baseUrl.Replace("http://", "ws://")}/ws/echo");
|
||||
|
||||
Console.WriteLine($"\nConnecting to {uri}...");
|
||||
await client.ConnectAsync(uri, CancellationToken.None);
|
||||
Console.WriteLine("Connected!");
|
||||
|
||||
var testMessages = new[] { "Hello", "World", "WebSocket", "Test" };
|
||||
|
||||
foreach (var testMessage in testMessages)
|
||||
{
|
||||
Console.WriteLine($"\nSending: {testMessage}");
|
||||
var bytes = Encoding.UTF8.GetBytes(testMessage);
|
||||
await client.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, CancellationToken.None);
|
||||
|
||||
var buffer = new byte[1024];
|
||||
var result = await client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
||||
var received = Encoding.UTF8.GetString(buffer, 0, result.Count);
|
||||
Console.WriteLine($"Received: {received}");
|
||||
}
|
||||
|
||||
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None);
|
||||
Console.WriteLine("\nTest completed successfully!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"\nTest failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task TestWebSocketChat(string baseUrl)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var client = new ClientWebSocket();
|
||||
var uri = new Uri($"{baseUrl.Replace("http://", "ws://")}/ws/chat");
|
||||
|
||||
Console.WriteLine($"\nConnecting to {uri}...");
|
||||
await client.ConnectAsync(uri, CancellationToken.None);
|
||||
Console.WriteLine("Connected!");
|
||||
|
||||
var commands = new[] { "/help", "/time", "/echo Hello", "/upper test", "/reverse hello" };
|
||||
|
||||
foreach (var command in commands)
|
||||
{
|
||||
Console.WriteLine($"\nSending: {command}");
|
||||
var bytes = Encoding.UTF8.GetBytes(command);
|
||||
await client.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, CancellationToken.None);
|
||||
|
||||
var buffer = new byte[1024];
|
||||
var result = await client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
||||
var received = Encoding.UTF8.GetString(buffer, 0, result.Count);
|
||||
Console.WriteLine($"Received: {received}");
|
||||
|
||||
await Task.Delay(500);
|
||||
}
|
||||
|
||||
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None);
|
||||
Console.WriteLine("\nTest completed successfully!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"\nTest failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
156
examples/WireMock.Net.WebSocketExamples/README.md
Normal file
156
examples/WireMock.Net.WebSocketExamples/README.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# WireMock.Net WebSocket Examples
|
||||
|
||||
This project demonstrates all the WebSocket capabilities of WireMock.Net.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET 8.0 SDK
|
||||
- Optional: `wscat` for manual testing (`npm install -g wscat`)
|
||||
|
||||
## Running the Examples
|
||||
|
||||
```bash
|
||||
cd examples/WireMock.Net.WebSocketExamples
|
||||
dotnet run
|
||||
```
|
||||
|
||||
## Available Examples
|
||||
|
||||
### 1. Echo Server
|
||||
Simple WebSocket echo server that returns all messages back to the client.
|
||||
|
||||
**Test with:**
|
||||
```bash
|
||||
wscat -c ws://localhost:9091/ws/echo
|
||||
```
|
||||
|
||||
### 2. Custom Message Handler
|
||||
Chat server with commands: `/help`, `/time`, `/echo`, `/upper`, `/reverse`, `/quit`
|
||||
|
||||
**Test with:**
|
||||
```bash
|
||||
wscat -c ws://localhost:9091/ws/chat
|
||||
> /help
|
||||
> /time
|
||||
> /echo Hello World
|
||||
> /upper test
|
||||
> /reverse hello
|
||||
```
|
||||
|
||||
### 3. Broadcast Server
|
||||
Messages sent by any client are broadcast to all connected clients.
|
||||
|
||||
**Test with multiple terminals:**
|
||||
```bash
|
||||
# Terminal 1
|
||||
wscat -c ws://localhost:9091/ws/broadcast
|
||||
|
||||
# Terminal 2
|
||||
wscat -c ws://localhost:9091/ws/broadcast
|
||||
|
||||
# Terminal 3
|
||||
wscat -c ws://localhost:9091/ws/broadcast
|
||||
```
|
||||
|
||||
Type messages in any terminal and see them appear in all terminals.
|
||||
|
||||
### 4. Scenario/State Machine
|
||||
Game server with state transitions: Lobby -> Playing -> GameOver
|
||||
|
||||
**Test with:**
|
||||
```bash
|
||||
wscat -c ws://localhost:9091/ws/game
|
||||
> ready
|
||||
> attack
|
||||
> defend
|
||||
> quit
|
||||
```
|
||||
|
||||
### 5. WebSocket Proxy
|
||||
Proxies WebSocket connections to echo.websocket.org
|
||||
|
||||
**Test with:**
|
||||
```bash
|
||||
wscat -c ws://localhost:9091/ws/proxy
|
||||
```
|
||||
|
||||
### 6. Multiple Endpoints
|
||||
Runs multiple WebSocket endpoints simultaneously:
|
||||
- `/ws/echo` - Echo server
|
||||
- `/ws/time` - Returns server time
|
||||
- `/ws/json` - Returns JSON responses
|
||||
- `/ws/protocol` - Protocol-specific endpoint
|
||||
|
||||
### 7. All Examples
|
||||
Runs all endpoints at once with connection statistics.
|
||||
|
||||
## Features Demonstrated
|
||||
|
||||
- ✅ **Echo Server** - Simple message echo
|
||||
- ✅ **Custom Handlers** - Complex message processing
|
||||
- ✅ **Broadcast** - Multi-client communication
|
||||
- ✅ **Scenarios** - State machine patterns
|
||||
- ✅ **Proxy** - Forwarding to real WebSocket servers
|
||||
- ✅ **Protocol Negotiation** - Sec-WebSocket-Protocol support
|
||||
- ✅ **JSON Messaging** - Structured data exchange
|
||||
- ✅ **Connection Management** - Track and manage connections
|
||||
- ✅ **Configuration** - Custom WebSocket settings
|
||||
|
||||
## Testing with wscat
|
||||
|
||||
Install wscat globally:
|
||||
```bash
|
||||
npm install -g wscat
|
||||
```
|
||||
|
||||
Basic usage:
|
||||
```bash
|
||||
# Connect to endpoint
|
||||
wscat -c ws://localhost:9091/ws/echo
|
||||
|
||||
# Connect with protocol
|
||||
wscat -c ws://localhost:9091/ws/protocol -s chat
|
||||
|
||||
# Connect with headers
|
||||
wscat -c ws://localhost:9091/ws/echo -H "X-Custom-Header: value"
|
||||
```
|
||||
|
||||
## Testing with C# Client
|
||||
|
||||
The examples include built-in C# WebSocket clients for automated testing.
|
||||
Select options 1 or 2 and press any key to run the automated tests.
|
||||
|
||||
## Configuration
|
||||
|
||||
WebSocket settings can be configured:
|
||||
|
||||
```csharp
|
||||
var server = WireMockServer.Start(new WireMockServerSettings
|
||||
{
|
||||
Port = 9091,
|
||||
WebSocketSettings = new WebSocketSettings
|
||||
{
|
||||
MaxConnections = 100,
|
||||
ReceiveBufferSize = 8192,
|
||||
MaxMessageSize = 1048576,
|
||||
KeepAliveInterval = TimeSpan.FromSeconds(30),
|
||||
CloseTimeout = TimeSpan.FromMinutes(10),
|
||||
EnableCompression = true
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
When running "All Examples" (option 7), press any key to view:
|
||||
- Active connection count
|
||||
- Connection IDs
|
||||
- Request paths
|
||||
- WebSocket states
|
||||
|
||||
## Notes
|
||||
|
||||
- All examples run on port 9091 by default
|
||||
- Press CTRL+C to stop the server
|
||||
- Multiple clients can connect simultaneously
|
||||
- Connection states are tracked and can be queried
|
||||
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AssemblyName>WireMock.Net.WebSocketExamples</AssemblyName>
|
||||
<RootNamespace>WireMock.Net.WebSocketExamples</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -131,4 +131,9 @@ public class SettingsModel
|
||||
/// Whether to accept any client certificate
|
||||
/// </summary>
|
||||
public bool AcceptAnyClientCertificate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WebSocket settings.
|
||||
/// </summary>
|
||||
public WebSocketSettingsModel? WebSocketSettings { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Admin.Settings;
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket Settings Model
|
||||
/// </summary>
|
||||
[FluentBuilder.AutoGenerateBuilder]
|
||||
public class WebSocketSettingsModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Maximum number of concurrent WebSocket connections (default: 100)
|
||||
/// </summary>
|
||||
public int MaxConnections { get; set; } = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Default receive buffer size in bytes (default: 4096)
|
||||
/// </summary>
|
||||
public int ReceiveBufferSize { get; set; } = 4096;
|
||||
|
||||
/// <summary>
|
||||
/// Default keep-alive interval in seconds (default: 30)
|
||||
/// </summary>
|
||||
public int KeepAliveIntervalSeconds { get; set; } = 30;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum message size in bytes (default: 1048576 - 1 MB)
|
||||
/// </summary>
|
||||
public int MaxMessageSize { get; set; } = 1048576;
|
||||
|
||||
/// <summary>
|
||||
/// Enable WebSocket compression (default: true)
|
||||
/// </summary>
|
||||
public bool EnableCompression { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Default close timeout in minutes (default: 10)
|
||||
/// </summary>
|
||||
public int CloseTimeoutMinutes { get; set; } = 10;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Constants;
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket constants
|
||||
/// </summary>
|
||||
public static class WebSocketConstants
|
||||
{
|
||||
/// <summary>
|
||||
/// Default receive buffer size for WebSocket messages (4 KB)
|
||||
/// </summary>
|
||||
public const int DefaultReceiveBufferSize = 4096;
|
||||
|
||||
/// <summary>
|
||||
/// Default keep-alive interval in seconds
|
||||
/// </summary>
|
||||
public const int DefaultKeepAliveIntervalSeconds = 30;
|
||||
|
||||
/// <summary>
|
||||
/// Default close timeout in minutes
|
||||
/// </summary>
|
||||
public const int DefaultCloseTimeoutMinutes = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum buffer size for WebSocket operations (1 KB)
|
||||
/// </summary>
|
||||
public const int MinimumBufferSize = 1024;
|
||||
|
||||
/// <summary>
|
||||
/// Default maximum message size (1 MB)
|
||||
/// </summary>
|
||||
public const int DefaultMaxMessageSize = 1024 * 1024;
|
||||
|
||||
/// <summary>
|
||||
/// Proxy forward buffer size (4 KB)
|
||||
/// </summary>
|
||||
public const int ProxyForwardBufferSize = 4096;
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
|
||||
namespace WireMock.Types;
|
||||
|
||||
[Flags]
|
||||
@@ -13,5 +11,11 @@ public enum HostingScheme
|
||||
|
||||
Https = 0x2,
|
||||
|
||||
HttpAndHttps = Http | Https
|
||||
HttpAndHttps = Http | Https,
|
||||
|
||||
Ws = 0x4,
|
||||
|
||||
Wss = 0x8,
|
||||
|
||||
WsAndWss = Ws | Wss
|
||||
}
|
||||
@@ -4,8 +4,8 @@
|
||||
<Description>Commonly used interfaces, models, enumerations and types.</Description>
|
||||
<AssemblyTitle>WireMock.Net.Abstractions</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<!--<TargetFrameworks>net45;net451;net461;netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>-->
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<!--<TargetFrameworks>net45;net451;net462;netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>-->
|
||||
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>$(NoWarn);1591;8603</NoWarn>
|
||||
<AssemblyName>WireMock.Net.Abstractions</AssemblyName>
|
||||
@@ -34,19 +34,28 @@
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net48' ">
|
||||
<!-- CVE-2018-8292 / https://github.com/advisories/GHSA-7jgj-8wvc-jh57 -->
|
||||
<PackageReference Include="System.Net.Http " Version="4.3.4" />
|
||||
</ItemGroup>
|
||||
<!--<ItemGroup Condition=" '$(TargetFramework)' == 'net48' ">
|
||||
--><!-- CVE-2018-8292 / https://github.com/advisories/GHSA-7jgj-8wvc-jh57 --><!--
|
||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||
</ItemGroup>-->
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentBuilder" Version="0.13.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Keep at 6.14.0 -->
|
||||
<PackageReference Include="Polyfill" Version="6.14.0">
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' ">
|
||||
<PackageReference Include="Required" Version="1.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="IsExternalInit" Version="1.0.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Nullable" Version="1.3.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>AwesomeAssertions extensions for WireMock.Net</Description>
|
||||
<AssemblyTitle>WireMock.Net.AwesomeAssertions</AssemblyTitle>
|
||||
<Authors>Francesco Venturoli;Mahmoud Ali;Stef Heyenrath</Authors>
|
||||
<!--<TargetFrameworks>net47;netstandard2.0;netstandard2.1</TargetFrameworks>-->
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net.AwesomeAssertions</AssemblyName>
|
||||
<PackageId>WireMock.Net.AwesomeAssertions</PackageId>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<Description>WireMock.Net.Routing extends WireMock.Net with modern, minimal-API-style routing for .NET</Description>
|
||||
<Authors>Gennadii Saltyshchak</Authors>
|
||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>tdd;mock;http;wiremock;test;server;unittest;routing;minimalapi</PackageTags>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
<Description>FluentAssertions extensions for WireMock.Net</Description>
|
||||
<AssemblyTitle>WireMock.Net.FluentAssertions</AssemblyTitle>
|
||||
<Authors>Mahmoud Ali;Stef Heyenrath</Authors>
|
||||
<!--<TargetFrameworks>net451;net47;netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>-->
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net.FluentAssertions</AssemblyName>
|
||||
<PackageId>WireMock.Net.FluentAssertions</PackageId>
|
||||
@@ -33,7 +32,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="7.2.0" />
|
||||
<PackageReference Include="FluentAssertions" Version="7.2.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>GraphQL support for WireMock.Net</Description>
|
||||
<AssemblyTitle>WireMock.Net.Matchers.GraphQL</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<!--<TargetFrameworks>netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net8.0</TargetFrameworks>-->
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<TargetFrameworks>net462;netstandard2.1;net8.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>wiremock;matchers;matcher;graphql</PackageTags>
|
||||
<RootNamespace>WireMock</RootNamespace>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using AnyOfTypes;
|
||||
@@ -106,74 +104,6 @@ public class CSharpCodeMatcher : ICSharpCodeMatcher
|
||||
var inputValue = isMatchWithString ? input : JObject.FromObject(input);
|
||||
var source = GetSourceForIsMatchWithString(pattern, isMatchWithString);
|
||||
|
||||
object? result;
|
||||
|
||||
//#if (NET451 || NET452)
|
||||
// var compilerParams = new System.CodeDom.Compiler.CompilerParameters
|
||||
// {
|
||||
// GenerateInMemory = true,
|
||||
// GenerateExecutable = false,
|
||||
// ReferencedAssemblies =
|
||||
// {
|
||||
// "System.dll",
|
||||
// "System.Core.dll",
|
||||
// "Microsoft.CSharp.dll",
|
||||
// "Newtonsoft.Json.dll"
|
||||
// }
|
||||
// };
|
||||
|
||||
// using (var codeProvider = new Microsoft.CSharp.CSharpCodeProvider())
|
||||
// {
|
||||
// var compilerResults = codeProvider.CompileAssemblyFromSource(compilerParams, source);
|
||||
|
||||
// if (compilerResults.Errors.Count != 0)
|
||||
// {
|
||||
// var errors = from System.CodeDom.Compiler.CompilerError er in compilerResults.Errors select er.ToString();
|
||||
// throw new WireMockException(string.Join(", ", errors));
|
||||
// }
|
||||
|
||||
// var helper = compilerResults.CompiledAssembly?.CreateInstance("CodeHelper");
|
||||
// if (helper == null)
|
||||
// {
|
||||
// throw new WireMockException("CSharpCodeMatcher: Unable to create instance from WireMock.CodeHelper");
|
||||
// }
|
||||
|
||||
// var methodInfo = helper.GetType().GetMethod("IsMatch");
|
||||
// if (methodInfo == null)
|
||||
// {
|
||||
// throw new WireMockException("CSharpCodeMatcher: Unable to find method 'IsMatch' in WireMock.CodeHelper");
|
||||
// }
|
||||
|
||||
// try
|
||||
// {
|
||||
// result = methodInfo.Invoke(helper, new[] { inputValue });
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// throw new WireMockException("CSharpCodeMatcher: Unable to call method 'IsMatch' in WireMock.CodeHelper", ex);
|
||||
// }
|
||||
// }
|
||||
//#elif (NET46 || NET461)
|
||||
// dynamic script;
|
||||
// try
|
||||
// {
|
||||
// script = CSScriptLibrary.CSScript.Evaluator.CompileCode(source).CreateObject("*");
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// throw new WireMockException("CSharpCodeMatcher: Unable to create compiler for WireMock.CodeHelper", ex);
|
||||
// }
|
||||
|
||||
// try
|
||||
// {
|
||||
// result = script.IsMatch(inputValue);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// throw new WireMockException("CSharpCodeMatcher: Problem calling method 'IsMatch' in WireMock.CodeHelper", ex);
|
||||
// }
|
||||
|
||||
//#elif (NETSTANDARD2_0 || NETSTANDARD2_1 || NETCOREAPP3_1 || NET5_0_OR_GREATER || NET48)
|
||||
Assembly assembly;
|
||||
try
|
||||
{
|
||||
@@ -194,6 +124,7 @@ public class CSharpCodeMatcher : ICSharpCodeMatcher
|
||||
throw new WireMockException("CSharpCodeMatcher: Unable to create object from assembly", ex);
|
||||
}
|
||||
|
||||
object? result;
|
||||
try
|
||||
{
|
||||
result = script.IsMatch(inputValue);
|
||||
@@ -202,9 +133,7 @@ public class CSharpCodeMatcher : ICSharpCodeMatcher
|
||||
{
|
||||
throw new WireMockException("CSharpCodeMatcher: Problem calling method 'IsMatch' in WireMock.CodeHelper", ex);
|
||||
}
|
||||
//#else
|
||||
// throw new NotSupportedException("The 'CSharpCodeMatcher' cannot be used in netstandard 1.3");
|
||||
//#endif
|
||||
|
||||
try
|
||||
{
|
||||
return (bool)result;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>A CSharpCodeMatcher which can be used to match WireMock.Net Requests using C# code.</Description>
|
||||
<AssemblyTitle>WireMock.Net.Matchers.CSharpCode</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<!--<TargetFrameworks>net451;net452;net46;net461;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>-->
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>wiremock;matchers;matcher;csharp;csharpcode</PackageTags>
|
||||
<RootNamespace>WireMock</RootNamespace>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>MultiPart Mime support for WireMock.Net using MimeKitLite</Description>
|
||||
<AssemblyTitle>WireMock.Net.MimePart</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<!--<TargetFrameworks>netstandard2.0;netstandard2.1;net462;net47;net48;net6.0;net8.0</TargetFrameworks>-->
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>wiremock;matchers;matcher;mime;multipart;mimekit</PackageTags>
|
||||
<RootNamespace>WireMock</RootNamespace>
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if NET48
|
||||
using System.Text.RegularExpressions;
|
||||
using WireMock.Constants;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System;
|
||||
|
||||
internal static class StringExtensions
|
||||
{
|
||||
public static string Replace(this string text, string oldValue, string newValue, StringComparison stringComparison)
|
||||
{
|
||||
var options = stringComparison == StringComparison.OrdinalIgnoreCase ? RegexOptions.IgnoreCase : RegexOptions.None;
|
||||
return Regex.Replace(text, oldValue, newValue, options, RegexConstants.DefaultTimeout);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -56,7 +56,11 @@ internal static class HttpClientBuilder
|
||||
}
|
||||
}
|
||||
|
||||
#if NET8_0_OR_GREATER
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
|
||||
#else
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
|
||||
#endif
|
||||
ServicePointManager.ServerCertificateValidationCallback = (message, cert, chain, errors) => true;
|
||||
|
||||
return HttpClientFactory2.Create(handler);
|
||||
|
||||
@@ -28,7 +28,7 @@ internal static class HttpClientFactory2
|
||||
|
||||
var next = handler;
|
||||
|
||||
foreach (var delegatingHandler in delegatingHandlers.Reverse())
|
||||
foreach (var delegatingHandler in Enumerable.Reverse(delegatingHandlers))
|
||||
{
|
||||
delegatingHandler.InnerHandler = next;
|
||||
next = delegatingHandler;
|
||||
|
||||
86
src/WireMock.Net.Minimal/Matchers/FuncMatcher.cs
Normal file
86
src/WireMock.Net.Minimal/Matchers/FuncMatcher.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// FuncMatcher - matches using a custom function
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IFuncMatcher"/>
|
||||
public class FuncMatcher : IFuncMatcher
|
||||
{
|
||||
private readonly Func<string?, bool>? _stringFunc;
|
||||
private readonly Func<byte[]?, bool>? _bytesFunc;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FuncMatcher"/> class for string matching.
|
||||
/// </summary>
|
||||
/// <param name="func">The function to check if a string is a match.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
public FuncMatcher(Func<string?, bool> func, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
_stringFunc = Guard.NotNull(func);
|
||||
MatchBehaviour = matchBehaviour;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FuncMatcher"/> class for byte array matching.
|
||||
/// </summary>
|
||||
/// <param name="func">The function to check if a byte[] is a match.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
public FuncMatcher(Func<byte[]?, bool> func, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
_bytesFunc = Guard.NotNull(func);
|
||||
MatchBehaviour = matchBehaviour;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(object? value)
|
||||
{
|
||||
if (value is string stringValue && _stringFunc != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return MatchResult.From(Name, MatchBehaviour, _stringFunc(stringValue));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return MatchResult.From(Name, ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (value is byte[] bytesValue && _bytesFunc != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return MatchResult.From(Name, MatchBehaviour, _bytesFunc(bytesValue));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return MatchResult.From(Name, ex);
|
||||
}
|
||||
}
|
||||
|
||||
return MatchResult.From(Name, MatchScores.Mismatch);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(FuncMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetCSharpCodeArguments()
|
||||
{
|
||||
var funcType = _stringFunc != null ? "Func<string?, bool>" : "Func<byte[]?, bool>";
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"/* {funcType} function */, " +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}" +
|
||||
$")";
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Owin.Mappers;
|
||||
using WireMock.Services;
|
||||
@@ -16,7 +14,7 @@ using WireMock.Util;
|
||||
|
||||
namespace WireMock.Owin;
|
||||
|
||||
internal partial class AspNetCoreSelfHost : IOwinSelfHost
|
||||
internal partial class AspNetCoreSelfHost
|
||||
{
|
||||
private readonly CancellationTokenSource _cts = new();
|
||||
private readonly IWireMockMiddlewareOptions _wireMockMiddlewareOptions;
|
||||
@@ -27,9 +25,9 @@ internal partial class AspNetCoreSelfHost : IOwinSelfHost
|
||||
|
||||
public bool IsStarted { get; private set; }
|
||||
|
||||
public List<string> Urls { get; } = new();
|
||||
public List<string> Urls { get; } = [];
|
||||
|
||||
public List<int> Ports { get; } = new();
|
||||
public List<int> Ports { get; } = [];
|
||||
|
||||
public Exception? RunningException { get; private set; }
|
||||
|
||||
@@ -80,6 +78,14 @@ internal partial class AspNetCoreSelfHost : IOwinSelfHost
|
||||
|
||||
#if NET8_0_OR_GREATER
|
||||
UseCors(appBuilder);
|
||||
|
||||
var webSocketOptions = new WebSocketOptions();
|
||||
if (_wireMockMiddlewareOptions.WebSocketSettings?.KeepAliveIntervalSeconds != null)
|
||||
{
|
||||
webSocketOptions.KeepAliveInterval = TimeSpan.FromSeconds(_wireMockMiddlewareOptions.WebSocketSettings.KeepAliveIntervalSeconds);
|
||||
}
|
||||
|
||||
appBuilder.UseWebSockets(webSocketOptions);
|
||||
#endif
|
||||
_wireMockMiddlewareOptions.PreWireMockMiddlewareInit?.Invoke(appBuilder);
|
||||
|
||||
@@ -112,14 +118,42 @@ internal partial class AspNetCoreSelfHost : IOwinSelfHost
|
||||
{
|
||||
var addresses = _host.ServerFeatures
|
||||
.Get<Microsoft.AspNetCore.Hosting.Server.Features.IServerAddressesFeature>()!
|
||||
.Addresses;
|
||||
.Addresses
|
||||
.ToArray();
|
||||
|
||||
foreach (var address in addresses)
|
||||
if (_urlOptions.Urls == null)
|
||||
{
|
||||
Urls.Add(address.Replace("0.0.0.0", "localhost").Replace("[::]", "localhost"));
|
||||
foreach (var address in addresses)
|
||||
{
|
||||
PortUtils.TryExtract(address, out _, out _, out var scheme, out var host, out var port);
|
||||
|
||||
PortUtils.TryExtract(address, out _, out _, out _, out _, out var port);
|
||||
Ports.Add(port);
|
||||
var replacedHost = ReplaceHostWithLocalhost(host!);
|
||||
var newUrl = $"{scheme}://{replacedHost}:{port}";
|
||||
Urls.Add(newUrl);
|
||||
Ports.Add(port);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var urlOptions = _urlOptions.Urls?.ToArray() ?? [];
|
||||
|
||||
for (int i = 0; i < urlOptions.Length; i++)
|
||||
{
|
||||
PortUtils.TryExtract(urlOptions[i], out _, out _, out var originalScheme, out _, out _);
|
||||
if (originalScheme!.StartsWith("grpc", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Always replace "grpc" with "http" in the scheme because GrpcChannel needs http or https.
|
||||
originalScheme = originalScheme.Replace("grpc", "http", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
PortUtils.TryExtract(addresses[i], out _, out _, out _, out var realHost, out var realPort);
|
||||
|
||||
var replacedHost = ReplaceHostWithLocalhost(realHost!);
|
||||
var newUrl = $"{originalScheme}://{replacedHost}:{realPort}";
|
||||
|
||||
Urls.Add(newUrl);
|
||||
Ports.Add(realPort);
|
||||
}
|
||||
}
|
||||
|
||||
IsStarted = true;
|
||||
@@ -127,8 +161,8 @@ internal partial class AspNetCoreSelfHost : IOwinSelfHost
|
||||
|
||||
#if NET8_0
|
||||
_logger.Info("Server using .NET 8.0");
|
||||
#elif NET48
|
||||
_logger.Info("Server using .NET Framework 4.8");
|
||||
#else
|
||||
_logger.Info("Server using .NET Standard 2.0");
|
||||
#endif
|
||||
|
||||
return _host.RunAsync(token);
|
||||
@@ -151,4 +185,9 @@ internal partial class AspNetCoreSelfHost : IOwinSelfHost
|
||||
IsStarted = false;
|
||||
return _host.StopAsync();
|
||||
}
|
||||
|
||||
private static string ReplaceHostWithLocalhost(string host)
|
||||
{
|
||||
return host.Replace("0.0.0.0", "localhost").Replace("[::]", "localhost");
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
@@ -23,20 +22,34 @@ internal class HostUrlOptions
|
||||
var list = new List<HostUrlDetails>();
|
||||
if (Urls == null)
|
||||
{
|
||||
if (HostingScheme is HostingScheme.Http or HostingScheme.Https)
|
||||
if (HostingScheme is not HostingScheme.None)
|
||||
{
|
||||
var port = Port > 0 ? Port.Value : FindFreeTcpPort();
|
||||
var scheme = HostingScheme == HostingScheme.Https ? "https" : "http";
|
||||
list.Add(new HostUrlDetails { IsHttps = HostingScheme == HostingScheme.Https, IsHttp2 = UseHttp2 == true, Url = $"{scheme}://{Star}:{port}", Scheme = scheme, Host = Star, Port = port });
|
||||
var scheme = GetSchemeAsString(HostingScheme);
|
||||
var port = Port > 0 ? Port.Value : 0;
|
||||
var isHttps = HostingScheme == HostingScheme.Https || HostingScheme == HostingScheme.Wss;
|
||||
list.Add(new HostUrlDetails { IsHttps = isHttps, IsHttp2 = UseHttp2 == true, Url = $"{scheme}://{Star}:{port}", Scheme = scheme, Host = Star, Port = port });
|
||||
}
|
||||
|
||||
if (HostingScheme == HostingScheme.HttpAndHttps)
|
||||
{
|
||||
var httpPort = Port > 0 ? Port.Value : FindFreeTcpPort();
|
||||
list.Add(new HostUrlDetails { IsHttps = false, IsHttp2 = UseHttp2 == true, Url = $"http://{Star}:{httpPort}", Scheme = "http", Host = Star, Port = httpPort });
|
||||
var port = Port > 0 ? Port.Value : 0;
|
||||
var scheme = GetSchemeAsString(HostingScheme.Http);
|
||||
list.Add(new HostUrlDetails { IsHttps = false, IsHttp2 = UseHttp2 == true, Url = $"{scheme}://{Star}:{port}", Scheme = scheme, Host = Star, Port = port });
|
||||
|
||||
var httpsPort = FindFreeTcpPort(); // In this scenario, always get a free port for https.
|
||||
list.Add(new HostUrlDetails { IsHttps = true, IsHttp2 = UseHttp2 == true, Url = $"https://{Star}:{httpsPort}", Scheme = "https", Host = Star, Port = httpsPort });
|
||||
var securePort = 0; // In this scenario, always get a free port for https.
|
||||
var secureScheme = GetSchemeAsString(HostingScheme.Https);
|
||||
list.Add(new HostUrlDetails { IsHttps = true, IsHttp2 = UseHttp2 == true, Url = $"{secureScheme}://{Star}:{securePort}", Scheme = secureScheme, Host = Star, Port = securePort });
|
||||
}
|
||||
|
||||
if (HostingScheme == HostingScheme.WsAndWss)
|
||||
{
|
||||
var port = Port > 0 ? Port.Value : 0;
|
||||
var scheme = GetSchemeAsString(HostingScheme.Ws);
|
||||
list.Add(new HostUrlDetails { IsHttps = false, IsHttp2 = UseHttp2 == true, Url = $"{scheme}://{Star}:{port}", Scheme = scheme, Host = Star, Port = port });
|
||||
|
||||
var securePort = 0; // In this scenario, always get a free port for https.
|
||||
var secureScheme = GetSchemeAsString(HostingScheme.Wss);
|
||||
list.Add(new HostUrlDetails { IsHttps = true, IsHttp2 = UseHttp2 == true, Url = $"{secureScheme}://{Star}:{securePort}", Scheme = secureScheme, Host = Star, Port = securePort });
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -53,12 +66,19 @@ internal class HostUrlOptions
|
||||
return list;
|
||||
}
|
||||
|
||||
private static int FindFreeTcpPort()
|
||||
private string GetSchemeAsString(HostingScheme scheme)
|
||||
{
|
||||
//#if USE_ASPNETCORE || NETSTANDARD2_0 || NETSTANDARD2_1
|
||||
return 0;
|
||||
//#else
|
||||
//return PortUtils.FindFreeTcpPort();
|
||||
//#endif
|
||||
return scheme switch
|
||||
{
|
||||
HostingScheme.Http => "http",
|
||||
HostingScheme.Https => "https",
|
||||
HostingScheme.HttpAndHttps => "http", // Default to http when both are specified, since the https URL will be added separately with a free port.
|
||||
|
||||
HostingScheme.Ws => "ws",
|
||||
HostingScheme.Wss => "wss",
|
||||
HostingScheme.WsAndWss => "ws", // Default to ws when both are specified, since the wss URL will be added separately with a free port.
|
||||
|
||||
_ => throw new NotSupportedException($"Unsupported hosting scheme: {HostingScheme}")
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
|
||||
namespace WireMock.Owin;
|
||||
|
||||
interface IOwinSelfHost
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this server is started.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this server is started; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
bool IsStarted { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the urls.
|
||||
/// </summary>
|
||||
List<string> Urls { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ports.
|
||||
/// </summary>
|
||||
List<int> Ports { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The exception occurred when the host is running.
|
||||
/// </summary>
|
||||
Exception? RunningException { get; }
|
||||
|
||||
Task StartAsync();
|
||||
|
||||
Task StopAsync();
|
||||
}
|
||||
@@ -11,6 +11,7 @@ using WireMock.Matchers;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
using WireMock.WebSockets;
|
||||
using ClientCertificateMode = Microsoft.AspNetCore.Server.Kestrel.Https.ClientCertificateMode;
|
||||
|
||||
namespace WireMock.Owin;
|
||||
@@ -82,11 +83,21 @@ internal interface IWireMockMiddlewareOptions
|
||||
|
||||
QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
|
||||
|
||||
public bool ProxyAll { get; set; }
|
||||
bool ProxyAll { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the activity tracing options.
|
||||
/// When set, System.Diagnostics.Activity objects are created for request tracing.
|
||||
/// </summary>
|
||||
ActivityTracingOptions? ActivityTracingOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The WebSocket connection registries per mapping (used for broadcast).
|
||||
/// </summary>
|
||||
ConcurrentDictionary<Guid, WebSocketConnectionRegistry> WebSocketRegistries { get; }
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket settings.
|
||||
/// </summary>
|
||||
WebSocketSettings? WebSocketSettings { get; set; }
|
||||
}
|
||||
@@ -1,12 +1,6 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
//#if !USE_ASPNETCORE
|
||||
//using IRequest = Microsoft.Owin.IOwinRequest;
|
||||
//#else
|
||||
//using IRequest = Microsoft.AspNetCore.Http.HttpRequest;
|
||||
//#endif
|
||||
|
||||
namespace WireMock.Owin.Mappers;
|
||||
|
||||
@@ -18,8 +12,8 @@ internal interface IOwinRequestMapper
|
||||
/// <summary>
|
||||
/// MapAsync IRequest to RequestMessage
|
||||
/// </summary>
|
||||
/// <param name="request">The HttpRequest</param>
|
||||
/// <param name="context">The HttpContext</param>
|
||||
/// <param name="options">The WireMockMiddlewareOptions</param>
|
||||
/// <returns>RequestMessage</returns>
|
||||
Task<RequestMessage> MapAsync(HttpRequest request, IWireMockMiddlewareOptions options);
|
||||
Task<RequestMessage> MapAsync(HttpContext context, IWireMockMiddlewareOptions options);
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using WireMock.Http;
|
||||
@@ -18,8 +15,9 @@ namespace WireMock.Owin.Mappers;
|
||||
internal class OwinRequestMapper : IOwinRequestMapper
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public async Task<RequestMessage> MapAsync(HttpRequest request, IWireMockMiddlewareOptions options)
|
||||
public async Task<RequestMessage> MapAsync(HttpContext context, IWireMockMiddlewareOptions options)
|
||||
{
|
||||
var request = context.Request;
|
||||
var (urlDetails, clientIP) = ParseRequest(request);
|
||||
|
||||
var method = request.Method;
|
||||
|
||||
@@ -15,6 +15,7 @@ using RandomDataGenerator.Randomizers;
|
||||
using Stef.Validation;
|
||||
using WireMock.Http;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.ResponseProviders;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
@@ -58,7 +59,7 @@ namespace WireMock.Owin.Mappers
|
||||
/// <inheritdoc />
|
||||
public async Task MapAsync(IResponseMessage? responseMessage, HttpResponse response)
|
||||
{
|
||||
if (responseMessage == null)
|
||||
if (responseMessage == null || responseMessage is WebSocketHandledResponse)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace WireMock.Owin;
|
||||
internal class WireMockMiddleware
|
||||
{
|
||||
private readonly object _lock = new();
|
||||
private static readonly Task CompletedTask = Task.FromResult(false);
|
||||
|
||||
private readonly IWireMockMiddlewareOptions _options;
|
||||
private readonly IOwinRequestMapper _requestMapper;
|
||||
@@ -66,7 +65,10 @@ internal class WireMockMiddleware
|
||||
|
||||
private async Task InvokeInternalAsync(HttpContext ctx)
|
||||
{
|
||||
var request = await _requestMapper.MapAsync(ctx.Request, _options).ConfigureAwait(false);
|
||||
// Store options in HttpContext for providers to access (e.g., WebSocketResponseProvider)
|
||||
ctx.Items[nameof(WireMockMiddlewareOptions)] = _options;
|
||||
|
||||
var request = await _requestMapper.MapAsync(ctx, _options).ConfigureAwait(false);
|
||||
|
||||
var logRequest = false;
|
||||
IResponseMessage? response = null;
|
||||
@@ -144,9 +146,7 @@ internal class WireMockMiddleware
|
||||
var (theResponse, theOptionalNewMapping) = await targetMapping.ProvideResponseAsync(ctx, request).ConfigureAwait(false);
|
||||
response = theResponse;
|
||||
|
||||
var responseBuilder = targetMapping.Provider as Response;
|
||||
|
||||
if (!targetMapping.IsAdminInterface && theOptionalNewMapping != null)
|
||||
if (targetMapping.Provider is Response responseBuilder && !targetMapping.IsAdminInterface && theOptionalNewMapping != null)
|
||||
{
|
||||
if (responseBuilder?.ProxyAndRecordSettings?.SaveMapping == true || targetMapping.Settings.ProxyAndRecordSettings?.SaveMapping == true)
|
||||
{
|
||||
@@ -227,8 +227,6 @@ internal class WireMockMiddleware
|
||||
await _responseMapper.MapAsync(notFoundResponse, ctx.Response).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
await CompletedTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task SendToWebhooksAsync(IMapping mapping, IRequestMessage request, IResponseMessage response)
|
||||
|
||||
@@ -8,10 +8,10 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using WireMock.Handlers;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Owin.ActivityTracing;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
using WireMock.WebSockets;
|
||||
using ClientCertificateMode = Microsoft.AspNetCore.Server.Kestrel.Https.ClientCertificateMode;
|
||||
|
||||
namespace WireMock.Owin;
|
||||
@@ -40,7 +40,6 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
|
||||
|
||||
public Action<IApplicationBuilder>? PostWireMockMiddlewareInit { get; set; }
|
||||
|
||||
//#if USE_ASPNETCORE
|
||||
public Action<IServiceCollection>? AdditionalServiceRegistration { get; set; }
|
||||
|
||||
public CorsPolicyOptions? CorsPolicyOptions { get; set; }
|
||||
@@ -49,7 +48,6 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool AcceptAnyClientCertificate { get; set; }
|
||||
//#endif
|
||||
|
||||
/// <inheritdoc cref="IWireMockMiddlewareOptions.FileSystemHandler"/>
|
||||
public IFileSystemHandler? FileSystemHandler { get; set; }
|
||||
@@ -107,4 +105,9 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
|
||||
|
||||
/// <inheritdoc />
|
||||
public ActivityTracingOptions? ActivityTracingOptions { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public ConcurrentDictionary<Guid, WebSocketConnectionRegistry> WebSocketRegistries { get; } = new();
|
||||
|
||||
public WebSocketSettings? WebSocketSettings { get; set; }
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Transformers;
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
|
||||
namespace WireMock.RequestBuilders;
|
||||
|
||||
public partial class Request
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public bool IsWebSocket { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithWebSocketUpgrade(params string[] protocols)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageHeaderMatcher(
|
||||
MatchBehaviour.AcceptOnMatch,
|
||||
MatchOperator.Or,
|
||||
"Upgrade",
|
||||
true,
|
||||
new ExactMatcher(true, "websocket")
|
||||
));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageHeaderMatcher(
|
||||
MatchBehaviour.AcceptOnMatch,
|
||||
MatchOperator.Or,
|
||||
"Connection",
|
||||
true,
|
||||
new WildcardMatcher("*Upgrade*", true)
|
||||
));
|
||||
|
||||
if (protocols.Length > 0)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageHeaderMatcher(
|
||||
MatchBehaviour.AcceptOnMatch,
|
||||
MatchOperator.Or,
|
||||
"Sec-WebSocket-Protocol",
|
||||
true,
|
||||
protocols.Select(p => new ExactMatcher(true, p)).ToArray()
|
||||
));
|
||||
}
|
||||
|
||||
IsWebSocket = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
// This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License.
|
||||
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Settings;
|
||||
using WireMock.WebSockets;
|
||||
|
||||
namespace WireMock.ResponseBuilders;
|
||||
|
||||
public partial class Response
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal property to store WebSocket builder configuration
|
||||
/// </summary>
|
||||
internal WebSocketBuilder? WebSocketBuilder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configure WebSocket response behavior
|
||||
/// </summary>
|
||||
public IResponseBuilder WithWebSocket(Action<IWebSocketBuilder> configure)
|
||||
{
|
||||
var builder = new WebSocketBuilder(this);
|
||||
configure(builder);
|
||||
|
||||
WebSocketBuilder = builder;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Proxy WebSocket to another server
|
||||
/// </summary>
|
||||
public IResponseBuilder WithWebSocketProxy(string targetUrl)
|
||||
{
|
||||
return WithWebSocketProxy(new ProxyAndRecordSettings { Url = targetUrl });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Proxy WebSocket to another server with settings
|
||||
/// </summary>
|
||||
public IResponseBuilder WithWebSocketProxy(ProxyAndRecordSettings settings)
|
||||
{
|
||||
var builder = new WebSocketBuilder(this);
|
||||
builder.WithProxy(settings);
|
||||
|
||||
WebSocketBuilder = builder;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ using WireMock.ResponseBuilders;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
using Stef.Validation;
|
||||
using WireMock.WebSockets;
|
||||
|
||||
namespace WireMock;
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Net;
|
||||
|
||||
namespace WireMock.ResponseProviders;
|
||||
|
||||
/// <summary>
|
||||
/// Special response marker to indicate WebSocket has been handled
|
||||
/// </summary>
|
||||
internal class WebSocketHandledResponse : ResponseMessage
|
||||
{
|
||||
public WebSocketHandledResponse()
|
||||
{
|
||||
// 101 Switching Protocols
|
||||
StatusCode = (int)HttpStatusCode.SwitchingProtocols;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Buffers;
|
||||
using System.Net;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Stef.Validation;
|
||||
using WireMock.Constants;
|
||||
using WireMock.Owin;
|
||||
using WireMock.Settings;
|
||||
using WireMock.WebSockets;
|
||||
|
||||
namespace WireMock.ResponseProviders;
|
||||
|
||||
internal class WebSocketResponseProvider(WebSocketBuilder builder) : IResponseProvider
|
||||
{
|
||||
private readonly WebSocketBuilder _builder = Guard.NotNull(builder);
|
||||
|
||||
public async Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(
|
||||
IMapping mapping,
|
||||
HttpContext context,
|
||||
IRequestMessage requestMessage,
|
||||
WireMockServerSettings settings)
|
||||
{
|
||||
// Check if this is a WebSocket upgrade request
|
||||
if (!context.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
return (ResponseMessageBuilder.Create(HttpStatusCode.BadRequest, "Bad Request: Not a WebSocket upgrade request"), null);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Accept the WebSocket connection
|
||||
#if NET8_0_OR_GREATER
|
||||
var acceptContext = new WebSocketAcceptContext
|
||||
{
|
||||
SubProtocol = _builder.AcceptProtocol,
|
||||
KeepAliveInterval = _builder.KeepAliveIntervalSeconds ?? TimeSpan.FromSeconds(WebSocketConstants.DefaultKeepAliveIntervalSeconds)
|
||||
|
||||
};
|
||||
var webSocket = await context.WebSockets.AcceptWebSocketAsync(acceptContext).ConfigureAwait(false);
|
||||
#else
|
||||
var webSocket = await context.WebSockets.AcceptWebSocketAsync(_builder.AcceptProtocol).ConfigureAwait(false);
|
||||
#endif
|
||||
|
||||
// Get options from HttpContext.Items (set by WireMockMiddleware)
|
||||
if (!context.Items.TryGetValue(nameof(WireMockMiddlewareOptions), out var optionsObj) ||
|
||||
optionsObj is not IWireMockMiddlewareOptions options)
|
||||
{
|
||||
throw new InvalidOperationException("WireMockMiddlewareOptions not found in HttpContext.Items");
|
||||
}
|
||||
|
||||
// Get or create registry from options
|
||||
var registry = _builder.IsBroadcast
|
||||
? options.WebSocketRegistries.GetOrAdd(mapping.Guid, _ => new WebSocketConnectionRegistry())
|
||||
: null;
|
||||
|
||||
// Create WebSocket context
|
||||
var wsContext = new WireMockWebSocketContext(
|
||||
context,
|
||||
webSocket,
|
||||
requestMessage,
|
||||
mapping,
|
||||
registry,
|
||||
_builder
|
||||
);
|
||||
|
||||
// Update scenario state following the same pattern as WireMockMiddleware
|
||||
if (mapping.Scenario != null)
|
||||
{
|
||||
wsContext.UpdateScenarioState();
|
||||
}
|
||||
|
||||
// Add to registry if broadcast is enabled
|
||||
if (registry != null)
|
||||
{
|
||||
registry.AddConnection(wsContext);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Handle the WebSocket based on configuration
|
||||
if (_builder.ProxySettings != null)
|
||||
{
|
||||
await HandleProxyAsync(wsContext, _builder.ProxySettings).ConfigureAwait(false);
|
||||
}
|
||||
else if (_builder.IsEcho)
|
||||
{
|
||||
await HandleEchoAsync(wsContext).ConfigureAwait(false);
|
||||
}
|
||||
else if (_builder.MessageHandler != null)
|
||||
{
|
||||
await HandleCustomAsync(wsContext, _builder.MessageHandler).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default: keep connection open until client closes
|
||||
await WaitForCloseAsync(wsContext).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Remove from registry
|
||||
registry?.RemoveConnection(wsContext.ConnectionId);
|
||||
}
|
||||
|
||||
// Return special marker to indicate WebSocket was handled
|
||||
return (new WebSocketHandledResponse(), null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
settings.Logger?.Error($"WebSocket error for mapping '{mapping.Guid}': {ex.Message}", ex);
|
||||
|
||||
// If we haven't upgraded yet, we can return HTTP error
|
||||
if (!context.Response.HasStarted)
|
||||
{
|
||||
return (ResponseMessageBuilder.Create(HttpStatusCode.InternalServerError, $"WebSocket error: {ex.Message}"), null);
|
||||
}
|
||||
|
||||
// Already upgraded - return marker
|
||||
return (new WebSocketHandledResponse(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleEchoAsync(WireMockWebSocketContext context)
|
||||
{
|
||||
var bufferSize = context.Builder.MaxMessageSize ?? WebSocketConstants.DefaultReceiveBufferSize;
|
||||
using var buffer = ArrayPool<byte>.Shared.Lease(bufferSize);
|
||||
var timeout = context.Builder.CloseTimeout ?? TimeSpan.FromMinutes(WebSocketConstants.DefaultCloseTimeoutMinutes);
|
||||
using var cts = new CancellationTokenSource(timeout);
|
||||
|
||||
try
|
||||
{
|
||||
while (context.WebSocket.State == WebSocketState.Open && !cts.Token.IsCancellationRequested)
|
||||
{
|
||||
var result = await context.WebSocket.ReceiveAsync(
|
||||
new ArraySegment<byte>(buffer),
|
||||
cts.Token
|
||||
).ConfigureAwait(false);
|
||||
|
||||
if (result.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
await context.CloseAsync(
|
||||
WebSocketCloseStatus.NormalClosure,
|
||||
"Closed by client"
|
||||
).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// Echo back
|
||||
await context.WebSocket.SendAsync(
|
||||
new ArraySegment<byte>(buffer, 0, result.Count),
|
||||
result.MessageType,
|
||||
result.EndOfMessage,
|
||||
cts.Token
|
||||
).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
if (context.WebSocket.State == WebSocketState.Open)
|
||||
{
|
||||
await context.CloseAsync(WebSocketCloseStatus.NormalClosure, "Timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleCustomAsync(
|
||||
WireMockWebSocketContext context,
|
||||
Func<WebSocketMessage, IWebSocketContext, Task> handler)
|
||||
{
|
||||
var bufferSize = context.Builder.MaxMessageSize ?? WebSocketConstants.DefaultReceiveBufferSize;
|
||||
var buffer = new byte[bufferSize];
|
||||
var timeout = context.Builder.CloseTimeout ?? TimeSpan.FromMinutes(WebSocketConstants.DefaultCloseTimeoutMinutes);
|
||||
using var cts = new CancellationTokenSource(timeout);
|
||||
|
||||
try
|
||||
{
|
||||
while (context.WebSocket.State == WebSocketState.Open && !cts.Token.IsCancellationRequested)
|
||||
{
|
||||
var result = await context.WebSocket.ReceiveAsync(
|
||||
new ArraySegment<byte>(buffer),
|
||||
cts.Token
|
||||
).ConfigureAwait(false);
|
||||
|
||||
if (result.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
await context.CloseAsync(
|
||||
WebSocketCloseStatus.NormalClosure,
|
||||
"Closed by client"
|
||||
).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
|
||||
var message = CreateWebSocketMessage(result, buffer);
|
||||
|
||||
// Call custom handler
|
||||
await handler(message, context).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
if (context.WebSocket.State == WebSocketState.Open)
|
||||
{
|
||||
await context.CloseAsync(WebSocketCloseStatus.NormalClosure, "Timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleProxyAsync(WireMockWebSocketContext context, ProxyAndRecordSettings settings)
|
||||
{
|
||||
using var clientWebSocket = new ClientWebSocket();
|
||||
|
||||
var targetUri = new Uri(settings.Url);
|
||||
await clientWebSocket.ConnectAsync(targetUri, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
// Bidirectional proxy
|
||||
var clientToServer = ForwardMessagesAsync(context.WebSocket, clientWebSocket);
|
||||
var serverToClient = ForwardMessagesAsync(clientWebSocket, context.WebSocket);
|
||||
|
||||
await Task.WhenAny(clientToServer, serverToClient).ConfigureAwait(false);
|
||||
|
||||
// Close both
|
||||
if (context.WebSocket.State == WebSocketState.Open)
|
||||
{
|
||||
await context.CloseAsync(WebSocketCloseStatus.NormalClosure, "Proxy closed");
|
||||
}
|
||||
|
||||
if (clientWebSocket.State == WebSocketState.Open)
|
||||
{
|
||||
await clientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Proxy closed", CancellationToken.None);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ForwardMessagesAsync(WebSocket source, WebSocket destination)
|
||||
{
|
||||
var buffer = new byte[WebSocketConstants.ProxyForwardBufferSize];
|
||||
|
||||
while (source.State == WebSocketState.Open && destination.State == WebSocketState.Open)
|
||||
{
|
||||
var result = await source.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
||||
|
||||
if (result.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
await destination.CloseAsync(
|
||||
result.CloseStatus ?? WebSocketCloseStatus.NormalClosure,
|
||||
result.CloseStatusDescription,
|
||||
CancellationToken.None
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
await destination.SendAsync(
|
||||
new ArraySegment<byte>(buffer, 0, result.Count),
|
||||
result.MessageType,
|
||||
result.EndOfMessage,
|
||||
CancellationToken.None
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task WaitForCloseAsync(WireMockWebSocketContext context)
|
||||
{
|
||||
var buffer = new byte[WebSocketConstants.MinimumBufferSize];
|
||||
var timeout = context.Builder.CloseTimeout ?? TimeSpan.FromMinutes(WebSocketConstants.DefaultCloseTimeoutMinutes);
|
||||
using var cts = new CancellationTokenSource(timeout);
|
||||
|
||||
try
|
||||
{
|
||||
while (context.WebSocket.State == WebSocketState.Open && !cts.Token.IsCancellationRequested)
|
||||
{
|
||||
var result = await context.WebSocket.ReceiveAsync(
|
||||
new ArraySegment<byte>(buffer),
|
||||
cts.Token
|
||||
);
|
||||
|
||||
if (result.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
await context.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closed by client");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
if (context.WebSocket.State == WebSocketState.Open)
|
||||
{
|
||||
await context.CloseAsync(WebSocketCloseStatus.NormalClosure, "Timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static WebSocketMessage CreateWebSocketMessage(WebSocketReceiveResult result, byte[] buffer)
|
||||
{
|
||||
var message = new WebSocketMessage
|
||||
{
|
||||
MessageType = result.MessageType,
|
||||
EndOfMessage = result.EndOfMessage,
|
||||
Timestamp = DateTime.UtcNow
|
||||
};
|
||||
|
||||
if (result.MessageType == WebSocketMessageType.Text)
|
||||
{
|
||||
message.Text = Encoding.UTF8.GetString(buffer, 0, result.Count);
|
||||
}
|
||||
else
|
||||
{
|
||||
message.Bytes = new byte[result.Count];
|
||||
Array.Copy(buffer, message.Bytes, result.Count);
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public class MappingFileNameSanitizer
|
||||
if (!string.IsNullOrEmpty(mapping.Title))
|
||||
{
|
||||
// remove 'Proxy Mapping for ' and an extra space character after the HTTP request method
|
||||
name = mapping.Title.Replace(ProxyAndRecordSettings.DefaultPrefixForSavedMappingFile, "").Replace(' '.ToString(), string.Empty);
|
||||
name = mapping.Title!.Replace(ProxyAndRecordSettings.DefaultPrefixForSavedMappingFile, "").Replace(' '.ToString(), string.Empty);
|
||||
if (_settings.ProxyAndRecordSettings?.AppendGuidToSavedMappingFile == true)
|
||||
{
|
||||
name += $"{ReplaceChar}{mapping.Guid}";
|
||||
|
||||
@@ -37,7 +37,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
private int _timesInSameState = 1;
|
||||
private bool? _useWebhookFireAndForget;
|
||||
private double? _probability;
|
||||
private GraphQLSchemaDetails? _graphQLSchemaDetails;
|
||||
private GraphQLSchemaDetails? _graphQLSchemaDetails; // Future Use.
|
||||
|
||||
public Guid Guid { get; private set; }
|
||||
|
||||
@@ -79,6 +79,12 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
/// <inheritdoc />
|
||||
public void RespondWith(IResponseProvider provider)
|
||||
{
|
||||
if (provider is Response response && response.WebSocketBuilder != null)
|
||||
{
|
||||
// If the provider is a Response with a WebSocketBuilder, we need to use a WebSocketResponseProvider instead.
|
||||
provider = new WebSocketResponseProvider(response.WebSocketBuilder);
|
||||
}
|
||||
|
||||
var mapping = new Mapping
|
||||
(
|
||||
Guid,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
@@ -702,7 +700,7 @@ public partial class WireMockServer
|
||||
{
|
||||
var name = string.Equals(HttpRequestMethod.DELETE, requestMessage.Method, StringComparison.OrdinalIgnoreCase) ?
|
||||
requestMessage.Path.Substring(_adminPaths!.Scenarios.Length + 1) :
|
||||
requestMessage.Path.Split('/').Reverse().Skip(1).First();
|
||||
Enumerable.Reverse(requestMessage.Path.Split('/')).Skip(1).First();
|
||||
|
||||
return ResetScenario(name) ?
|
||||
ResponseMessageBuilder.Create(200, "Scenario reset") :
|
||||
@@ -711,7 +709,7 @@ public partial class WireMockServer
|
||||
|
||||
private IResponseMessage ScenariosSetState(HttpContext _, IRequestMessage requestMessage)
|
||||
{
|
||||
var name = requestMessage.Path.Split('/').Reverse().Skip(1).First();
|
||||
var name = Enumerable.Reverse(requestMessage.Path.Split('/')).Skip(1).First();
|
||||
if (!_options.Scenarios.ContainsKey(name))
|
||||
{
|
||||
ResponseMessageBuilder.Create(HttpStatusCode.NotFound, $"No scenario found by name '{name}'.");
|
||||
|
||||
@@ -106,7 +106,6 @@ public partial class WireMockServer
|
||||
/// Checks if file exists.
|
||||
/// Note: Response is returned with no body as a head request doesn't accept a body, only the status code.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The request message.</param>
|
||||
private IResponseMessage FileHead(HttpContext _, IRequestMessage requestMessage)
|
||||
{
|
||||
var filename = GetFileNameFromRequestMessage(requestMessage);
|
||||
|
||||
77
src/WireMock.Net.Minimal/Server/WireMockServer.WebSocket.cs
Normal file
77
src/WireMock.Net.Minimal/Server/WireMockServer.WebSocket.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.WebSockets;
|
||||
|
||||
namespace WireMock.Server;
|
||||
|
||||
public partial class WireMockServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Get all active WebSocket connections
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public IReadOnlyCollection<WireMockWebSocketContext> GetWebSocketConnections()
|
||||
{
|
||||
return _options.WebSocketRegistries.Values
|
||||
.SelectMany(r => r.GetConnections())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get WebSocket connections for a specific mapping
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public IReadOnlyCollection<WireMockWebSocketContext> GetWebSocketConnections(Guid mappingGuid)
|
||||
{
|
||||
return _options.WebSocketRegistries.TryGetValue(mappingGuid, out var registry) ? registry.GetConnections() : [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close a specific WebSocket connection
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public async Task CloseWebSocketConnectionAsync(
|
||||
Guid connectionId,
|
||||
WebSocketCloseStatus closeStatus = WebSocketCloseStatus.NormalClosure,
|
||||
string statusDescription = "Closed by server")
|
||||
{
|
||||
foreach (var registry in _options.WebSocketRegistries.Values)
|
||||
{
|
||||
if (registry.TryGetConnection(connectionId, out var connection))
|
||||
{
|
||||
await connection.CloseAsync(closeStatus, statusDescription);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Broadcast a message to all WebSocket connections in a specific mapping
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public async Task BroadcastToWebSocketsAsync(Guid mappingGuid, string text)
|
||||
{
|
||||
if (_options.WebSocketRegistries.TryGetValue(mappingGuid, out var registry))
|
||||
{
|
||||
await registry.BroadcastTextAsync(text);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Broadcast a message to all WebSocket connections
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public async Task BroadcastToAllWebSocketsAsync(string text)
|
||||
{
|
||||
foreach (var registry in _options.WebSocketRegistries.Values)
|
||||
{
|
||||
await registry.BroadcastTextAsync(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ public partial class WireMockServer : IWireMockServer
|
||||
private const int ServerStartDelayInMs = 100;
|
||||
|
||||
private readonly WireMockServerSettings _settings;
|
||||
private readonly IOwinSelfHost? _httpServer;
|
||||
private readonly AspNetCoreSelfHost? _httpServer;
|
||||
private readonly IWireMockMiddlewareOptions _options = new WireMockMiddlewareOptions();
|
||||
private readonly MappingConverter _mappingConverter;
|
||||
private readonly MatcherMapper _matcherMapper;
|
||||
@@ -537,15 +537,11 @@ public partial class WireMockServer : IWireMockServer
|
||||
Guard.NotNull(tenant);
|
||||
Guard.NotNull(audience);
|
||||
|
||||
//#if NETSTANDARD1_3
|
||||
// throw new NotSupportedException("AzureADAuthentication is not supported for NETStandard 1.3");
|
||||
//#else
|
||||
_options.AuthenticationMatcher = new AzureADAuthenticationMatcher(
|
||||
new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler(),
|
||||
new Microsoft.IdentityModel.Protocols.ConfigurationManager<Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfiguration>($"https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration", new Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfigurationRetriever()),
|
||||
tenant,
|
||||
audience);
|
||||
//#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IWireMockServer.SetBasicAuthentication(string, string)" />
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Stef.Validation;
|
||||
using WireMock.Constants;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Models;
|
||||
using WireMock.Types;
|
||||
@@ -86,6 +87,7 @@ public static class WireMockServerSettingsParser
|
||||
ParseCertificateSettings(settings, parser);
|
||||
ParseHandlebarsSettings(settings, parser);
|
||||
ParseActivityTracingSettings(settings, parser);
|
||||
ParseWebSocketSettings(settings, parser);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -242,4 +244,20 @@ public static class WireMockServerSettingsParser
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParseWebSocketSettings(WireMockServerSettings settings, SimpleSettingsParser parser)
|
||||
{
|
||||
// Check if any WebSocket setting is present
|
||||
if (parser.ContainsAny(
|
||||
nameof(WebSocketSettings) + '.' + nameof(WebSocketSettings.MaxConnections),
|
||||
nameof(WebSocketSettings) + '.' + nameof(WebSocketSettings.KeepAliveIntervalSeconds))
|
||||
)
|
||||
{
|
||||
settings.WebSocketSettings = new WebSocketSettings
|
||||
{
|
||||
MaxConnections = parser.GetIntValue(nameof(WebSocketSettings) + '.' + nameof(WebSocketSettings.MaxConnections), 100),
|
||||
KeepAliveIntervalSeconds = parser.GetIntValue(nameof(WebSocketSettings) + '.' + nameof(WebSocketSettings.KeepAliveIntervalSeconds), WebSocketConstants.DefaultKeepAliveIntervalSeconds),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -16,7 +14,7 @@ namespace WireMock.Util;
|
||||
/// </summary>
|
||||
internal static class PortUtils
|
||||
{
|
||||
private static readonly Regex UrlDetailsRegex = new(@"^((?<proto>\w+)://)(?<host>[^/]+?):(?<port>\d+)\/?$", RegexOptions.Compiled, RegexConstants.DefaultTimeout);
|
||||
private static readonly Regex UrlDetailsRegex = new(@"^((?<scheme>\w+)://)(?<host>[^/]+?):(?<port>\d+)\/?$", RegexOptions.Compiled, RegexConstants.DefaultTimeout);
|
||||
|
||||
/// <summary>
|
||||
/// Finds a random, free port to be listened on.
|
||||
@@ -37,9 +35,7 @@ internal static class PortUtils
|
||||
}
|
||||
finally
|
||||
{
|
||||
//#if !NETSTANDARD1_3
|
||||
portSocket.Close();
|
||||
//#endif
|
||||
portSocket.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -75,9 +71,7 @@ internal static class PortUtils
|
||||
{
|
||||
foreach (var socket in sockets)
|
||||
{
|
||||
//#if !NETSTANDARD1_3
|
||||
socket.Close();
|
||||
//#endif
|
||||
socket.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -97,8 +91,8 @@ internal static class PortUtils
|
||||
var match = UrlDetailsRegex.Match(url);
|
||||
if (match.Success)
|
||||
{
|
||||
scheme = match.Groups["proto"].Value;
|
||||
isHttps = scheme.StartsWith("https", StringComparison.OrdinalIgnoreCase) || scheme.StartsWith("grpcs", StringComparison.OrdinalIgnoreCase);
|
||||
scheme = match.Groups["scheme"].Value;
|
||||
isHttps = scheme.StartsWith("https", StringComparison.OrdinalIgnoreCase) || scheme.StartsWith("grpcs", StringComparison.OrdinalIgnoreCase) || scheme.StartsWith("wss", StringComparison.OrdinalIgnoreCase);
|
||||
isHttp2 = scheme.StartsWith("grpc", StringComparison.OrdinalIgnoreCase);
|
||||
host = match.Groups["host"].Value;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Nelibur.ObjectMapper;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Admin.Settings;
|
||||
@@ -7,6 +8,7 @@ using WireMock.Settings;
|
||||
|
||||
namespace WireMock.Util;
|
||||
|
||||
[SuppressMessage("Performance", "CA1822:Mark members as static")]
|
||||
internal sealed class TinyMapperUtils
|
||||
{
|
||||
public static TinyMapperUtils Instance { get; } = new();
|
||||
@@ -22,6 +24,9 @@ internal sealed class TinyMapperUtils
|
||||
TinyMapper.Bind<WebProxySettingsModel, WebProxySettings>();
|
||||
TinyMapper.Bind<WebProxyModel, WebProxySettings>();
|
||||
TinyMapper.Bind<ProxyUrlReplaceSettingsModel, ProxyUrlReplaceSettings>();
|
||||
|
||||
TinyMapper.Bind<WebSocketSettings, WebSocketSettingsModel>();
|
||||
TinyMapper.Bind<WebSocketSettingsModel, WebSocketSettings>();
|
||||
}
|
||||
|
||||
public ProxyAndRecordSettingsModel? Map(ProxyAndRecordSettings? instance)
|
||||
@@ -53,4 +58,14 @@ internal sealed class TinyMapperUtils
|
||||
{
|
||||
return model == null ? null : TinyMapper.Map<WebProxySettings>(model);
|
||||
}
|
||||
|
||||
public WebSocketSettingsModel? Map(WebSocketSettings? instance)
|
||||
{
|
||||
return instance == null ? null : TinyMapper.Map<WebSocketSettingsModel>(instance);
|
||||
}
|
||||
|
||||
public WebSocketSettings? Map(WebSocketSettingsModel? model)
|
||||
{
|
||||
return model == null ? null : TinyMapper.Map<WebSocketSettings>(model);
|
||||
}
|
||||
}
|
||||
291
src/WireMock.Net.Minimal/WebSockets/WebSocketBuilder.cs
Normal file
291
src/WireMock.Net.Minimal/WebSockets/WebSocketBuilder.cs
Normal file
@@ -0,0 +1,291 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Net.WebSockets;
|
||||
using Stef.Validation;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Transformers;
|
||||
|
||||
namespace WireMock.WebSockets;
|
||||
|
||||
internal class WebSocketBuilder(Response response) : IWebSocketBuilder
|
||||
{
|
||||
private readonly List<(IMatcher matcher, List<WebSocketMessageBuilder> messages)> _conditionalMessages = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? AcceptProtocol { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsEcho { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsBroadcast { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Func<WebSocketMessage, IWebSocketContext, Task>? MessageHandler { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public ProxyAndRecordSettings? ProxySettings { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public TimeSpan? CloseTimeout { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int? MaxMessageSize { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int? ReceiveBufferSize { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public TimeSpan? KeepAliveIntervalSeconds { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IWebSocketBuilder WithAcceptProtocol(string protocol)
|
||||
{
|
||||
AcceptProtocol = Guard.NotNull(protocol);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IWebSocketBuilder WithEcho()
|
||||
{
|
||||
IsEcho = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IWebSocketBuilder SendMessage(Action<IWebSocketMessageBuilder> configure)
|
||||
{
|
||||
Guard.NotNull(configure);
|
||||
var messageBuilder = new WebSocketMessageBuilder();
|
||||
configure(messageBuilder);
|
||||
|
||||
return WithMessageHandler(async (message, context) =>
|
||||
{
|
||||
if (messageBuilder.Delay.HasValue)
|
||||
{
|
||||
await Task.Delay(messageBuilder.Delay.Value);
|
||||
}
|
||||
|
||||
await SendMessageAsync(context, messageBuilder, message);
|
||||
});
|
||||
}
|
||||
|
||||
public IWebSocketBuilder SendMessages(Action<IWebSocketMessagesBuilder> configure)
|
||||
{
|
||||
Guard.NotNull(configure);
|
||||
var messagesBuilder = new WebSocketMessagesBuilder();
|
||||
configure(messagesBuilder);
|
||||
|
||||
return WithMessageHandler(async (message, context) =>
|
||||
{
|
||||
foreach (var messageBuilder in messagesBuilder.Messages)
|
||||
{
|
||||
if (messageBuilder.Delay.HasValue)
|
||||
{
|
||||
await Task.Delay(messageBuilder.Delay.Value);
|
||||
}
|
||||
|
||||
await SendMessageAsync(context, messageBuilder, message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public IWebSocketMessageConditionBuilder WhenMessage(string wildcardPattern)
|
||||
{
|
||||
Guard.NotNull(wildcardPattern);
|
||||
var matcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, wildcardPattern);
|
||||
return new WebSocketMessageConditionBuilder(this, matcher);
|
||||
}
|
||||
|
||||
public IWebSocketMessageConditionBuilder WhenMessage(byte[] exactPattern)
|
||||
{
|
||||
Guard.NotNull(exactPattern);
|
||||
var matcher = new ExactObjectMatcher(MatchBehaviour.AcceptOnMatch, exactPattern);
|
||||
return new WebSocketMessageConditionBuilder(this, matcher);
|
||||
}
|
||||
|
||||
public IWebSocketMessageConditionBuilder WhenMessage(IMatcher matcher)
|
||||
{
|
||||
Guard.NotNull(matcher);
|
||||
return new WebSocketMessageConditionBuilder(this, matcher);
|
||||
}
|
||||
|
||||
public IWebSocketBuilder WithMessageHandler(Func<WebSocketMessage, IWebSocketContext, Task> handler)
|
||||
{
|
||||
MessageHandler = Guard.NotNull(handler);
|
||||
IsEcho = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IWebSocketBuilder WithBroadcast()
|
||||
{
|
||||
IsBroadcast = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IWebSocketBuilder WithProxy(ProxyAndRecordSettings settings)
|
||||
{
|
||||
ProxySettings = Guard.NotNull(settings);
|
||||
IsEcho = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IWebSocketBuilder WithCloseTimeout(TimeSpan timeout)
|
||||
{
|
||||
CloseTimeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IWebSocketBuilder WithMaxMessageSize(int sizeInBytes)
|
||||
{
|
||||
MaxMessageSize = Guard.Condition(sizeInBytes, s => s > 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IWebSocketBuilder WithReceiveBufferSize(int sizeInBytes)
|
||||
{
|
||||
ReceiveBufferSize = Guard.Condition(sizeInBytes, s => s > 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IWebSocketBuilder WithKeepAliveInterval(TimeSpan interval)
|
||||
{
|
||||
KeepAliveIntervalSeconds = interval;
|
||||
return this;
|
||||
}
|
||||
|
||||
internal IWebSocketBuilder AddConditionalMessage(IMatcher matcher, WebSocketMessageBuilder messageBuilder)
|
||||
{
|
||||
_conditionalMessages.Add((matcher, new List<WebSocketMessageBuilder> { messageBuilder }));
|
||||
SetupConditionalHandler();
|
||||
return this;
|
||||
}
|
||||
|
||||
internal IWebSocketBuilder AddConditionalMessages(IMatcher matcher, List<WebSocketMessageBuilder> messages)
|
||||
{
|
||||
_conditionalMessages.Add((matcher, messages));
|
||||
SetupConditionalHandler();
|
||||
return this;
|
||||
}
|
||||
|
||||
private void SetupConditionalHandler()
|
||||
{
|
||||
if (_conditionalMessages.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WithMessageHandler(async (message, context) =>
|
||||
{
|
||||
// Check each condition in order
|
||||
foreach (var (matcher, messages) in _conditionalMessages)
|
||||
{
|
||||
// Try to match the message
|
||||
if (await MatchMessageAsync(message, matcher))
|
||||
{
|
||||
// Execute the corresponding messages
|
||||
foreach (var messageBuilder in messages)
|
||||
{
|
||||
if (messageBuilder.Delay.HasValue)
|
||||
{
|
||||
await Task.Delay(messageBuilder.Delay.Value);
|
||||
}
|
||||
|
||||
await SendMessageAsync(context, messageBuilder, message);
|
||||
|
||||
// If this message should close the connection, do it after sending
|
||||
if (messageBuilder.ShouldClose)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(100); // Small delay to ensure message is sent
|
||||
await context.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closed by handler");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors during close
|
||||
}
|
||||
}
|
||||
}
|
||||
return; // Stop after first match
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task SendMessageAsync(IWebSocketContext context, WebSocketMessageBuilder messageBuilder, WebSocketMessage incomingMessage)
|
||||
{
|
||||
switch (messageBuilder.Type)
|
||||
{
|
||||
case WebSocketMessageType.Text:
|
||||
var text = messageBuilder.MessageText!;
|
||||
if (response.UseTransformer)
|
||||
{
|
||||
text = ApplyTransformer(context, incomingMessage, text);
|
||||
}
|
||||
await context.SendAsync(text);
|
||||
break;
|
||||
|
||||
case WebSocketMessageType.Binary:
|
||||
await context.SendAsync(messageBuilder.MessageBytes!);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private string ApplyTransformer(IWebSocketContext context, WebSocketMessage incomingMessage, string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (incomingMessage == null)
|
||||
{
|
||||
// No incoming message, can't apply transformer
|
||||
return text;
|
||||
}
|
||||
|
||||
var transformer = TransformerFactory.Create(response.TransformerType, context.Mapping.Settings);
|
||||
|
||||
var model = new WebSocketTransformModel
|
||||
{
|
||||
Mapping = context.Mapping,
|
||||
Request = context.RequestMessage,
|
||||
Message = incomingMessage,
|
||||
Data = incomingMessage.MessageType == WebSocketMessageType.Text ? incomingMessage.Text : null
|
||||
};
|
||||
|
||||
return transformer.Transform(text, model);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If transformation fails, return original text
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<bool> MatchMessageAsync(WebSocketMessage message, IMatcher matcher)
|
||||
{
|
||||
if (message.MessageType == WebSocketMessageType.Text)
|
||||
{
|
||||
if (matcher is IStringMatcher stringMatcher)
|
||||
{
|
||||
var result = stringMatcher.IsMatch(message.Text);
|
||||
return result.IsPerfect();
|
||||
}
|
||||
|
||||
if (matcher is IFuncMatcher funcMatcher)
|
||||
{
|
||||
var result = funcMatcher.IsMatch(message.Text);
|
||||
return result.IsPerfect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (message.MessageType == WebSocketMessageType.Binary && matcher is IBytesMatcher bytesMatcher && message.Bytes != null)
|
||||
{
|
||||
var result = await bytesMatcher.IsMatchAsync(message.Bytes);
|
||||
return result.IsPerfect();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net.WebSockets;
|
||||
|
||||
namespace WireMock.WebSockets;
|
||||
|
||||
/// <summary>
|
||||
/// Registry for managing WebSocket connections per mapping
|
||||
/// </summary>
|
||||
internal class WebSocketConnectionRegistry
|
||||
{
|
||||
private readonly ConcurrentDictionary<Guid, WireMockWebSocketContext> _connections = new();
|
||||
|
||||
/// <summary>
|
||||
/// Add a connection to the registry
|
||||
/// </summary>
|
||||
public void AddConnection(WireMockWebSocketContext context)
|
||||
{
|
||||
_connections.TryAdd(context.ConnectionId, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a connection from the registry
|
||||
/// </summary>
|
||||
public void RemoveConnection(Guid connectionId)
|
||||
{
|
||||
_connections.TryRemove(connectionId, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all connections
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<WireMockWebSocketContext> GetConnections()
|
||||
{
|
||||
return _connections.Values.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to get a specific connection
|
||||
/// </summary>
|
||||
public bool TryGetConnection(Guid connectionId, [NotNullWhen(true)] out WireMockWebSocketContext? connection)
|
||||
{
|
||||
return _connections.TryGetValue(connectionId, out connection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Broadcast text to all connections
|
||||
/// </summary>
|
||||
public async Task BroadcastTextAsync(string text, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var tasks = _connections.Values
|
||||
.Where(c => c.WebSocket.State == WebSocketState.Open)
|
||||
.Select(c => c.SendAsync(text, cancellationToken));
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Net.WebSockets;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.WebSockets;
|
||||
|
||||
internal class WebSocketMessageBuilder : IWebSocketMessageBuilder
|
||||
{
|
||||
public string? MessageText { get; private set; }
|
||||
|
||||
public byte[]? MessageBytes { get; private set; }
|
||||
|
||||
public object? MessageData { get; private set; }
|
||||
|
||||
public TimeSpan? Delay { get; private set; }
|
||||
|
||||
public WebSocketMessageType Type { get; private set; }
|
||||
|
||||
public bool ShouldClose { get; private set; }
|
||||
|
||||
public IWebSocketMessageBuilder WithText(string text)
|
||||
{
|
||||
MessageText = Guard.NotNull(text);
|
||||
Type = WebSocketMessageType.Text;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IWebSocketMessageBuilder WithBinary(byte[] bytes)
|
||||
{
|
||||
MessageBytes = Guard.NotNull(bytes);
|
||||
Type = WebSocketMessageType.Binary;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IWebSocketMessageBuilder WithDelay(TimeSpan delay)
|
||||
{
|
||||
Delay = delay;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IWebSocketMessageBuilder WithDelay(int delayInMilliseconds)
|
||||
{
|
||||
Guard.Condition(delayInMilliseconds, d => d >= 0, nameof(delayInMilliseconds));
|
||||
Delay = TimeSpan.FromMilliseconds(delayInMilliseconds);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IWebSocketMessageBuilder Close()
|
||||
{
|
||||
ShouldClose = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IWebSocketMessageBuilder AndClose() => Close();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Matchers;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.WebSockets;
|
||||
|
||||
internal class WebSocketMessageConditionBuilder : IWebSocketMessageConditionBuilder
|
||||
{
|
||||
private readonly WebSocketBuilder _parent;
|
||||
private readonly IMatcher _matcher;
|
||||
|
||||
public WebSocketMessageConditionBuilder(WebSocketBuilder parent, IMatcher matcher)
|
||||
{
|
||||
_parent = Guard.NotNull(parent);
|
||||
_matcher = Guard.NotNull(matcher);
|
||||
}
|
||||
|
||||
public IWebSocketBuilder SendMessage(Action<IWebSocketMessageBuilder> configure)
|
||||
{
|
||||
Guard.NotNull(configure);
|
||||
var messageBuilder = new WebSocketMessageBuilder();
|
||||
configure(messageBuilder);
|
||||
|
||||
return _parent.AddConditionalMessage(_matcher, messageBuilder);
|
||||
}
|
||||
|
||||
public IWebSocketBuilder SendMessages(Action<IWebSocketMessagesBuilder> configure)
|
||||
{
|
||||
Guard.NotNull(configure);
|
||||
var messagesBuilder = new WebSocketMessagesBuilder();
|
||||
configure(messagesBuilder);
|
||||
|
||||
return _parent.AddConditionalMessages(_matcher, messagesBuilder.Messages);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.WebSockets;
|
||||
|
||||
internal class WebSocketMessagesBuilder : IWebSocketMessagesBuilder
|
||||
{
|
||||
internal List<WebSocketMessageBuilder> Messages { get; } = [];
|
||||
|
||||
public IWebSocketMessagesBuilder AddMessage(Action<IWebSocketMessageBuilder> configure)
|
||||
{
|
||||
var messageBuilder = new WebSocketMessageBuilder();
|
||||
configure(messageBuilder);
|
||||
Messages.Add(messageBuilder);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.WebSockets;
|
||||
|
||||
/// <summary>
|
||||
/// Model for WebSocket message transformation
|
||||
/// </summary>
|
||||
internal struct WebSocketTransformModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The mapping that matched this WebSocket request
|
||||
/// </summary>
|
||||
public IMapping Mapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The original request that initiated the WebSocket connection
|
||||
/// </summary>
|
||||
public IRequestMessage Request { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The incoming WebSocket message
|
||||
/// </summary>
|
||||
public WebSocketMessage Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The message data as string
|
||||
/// </summary>
|
||||
public string? Data { get; set; }
|
||||
}
|
||||
178
src/WireMock.Net.Minimal/WebSockets/WireMockWebSocketContext.cs
Normal file
178
src/WireMock.Net.Minimal/WebSockets/WireMockWebSocketContext.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Newtonsoft.Json;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Owin;
|
||||
|
||||
namespace WireMock.WebSockets;
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket context implementation
|
||||
/// </summary>
|
||||
public class WireMockWebSocketContext : IWebSocketContext
|
||||
{
|
||||
private readonly IWireMockMiddlewareOptions _options;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid ConnectionId { get; } = Guid.NewGuid();
|
||||
|
||||
/// <inheritdoc />
|
||||
public HttpContext HttpContext { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public WebSocket WebSocket { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestMessage RequestMessage { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IMapping Mapping { get; }
|
||||
|
||||
internal WebSocketConnectionRegistry? Registry { get; }
|
||||
|
||||
internal WebSocketBuilder Builder { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new WebSocketContext
|
||||
/// </summary>
|
||||
internal WireMockWebSocketContext(
|
||||
HttpContext httpContext,
|
||||
WebSocket webSocket,
|
||||
IRequestMessage requestMessage,
|
||||
IMapping mapping,
|
||||
WebSocketConnectionRegistry? registry,
|
||||
WebSocketBuilder builder)
|
||||
{
|
||||
HttpContext = Guard.NotNull(httpContext);
|
||||
WebSocket = Guard.NotNull(webSocket);
|
||||
RequestMessage = Guard.NotNull(requestMessage);
|
||||
Mapping = Guard.NotNull(mapping);
|
||||
Registry = registry;
|
||||
Builder = Guard.NotNull(builder);
|
||||
|
||||
// Get options from HttpContext
|
||||
if (httpContext.Items.TryGetValue<IWireMockMiddlewareOptions>(nameof(WireMockMiddlewareOptions), out var options))
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("WireMockMiddlewareOptions not found in HttpContext.Items");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SendAsync(string text, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(text);
|
||||
return WebSocket.SendAsync(
|
||||
new ArraySegment<byte>(bytes),
|
||||
WebSocketMessageType.Text,
|
||||
true,
|
||||
cancellationToken
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SendAsync(byte[] bytes, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return WebSocket.SendAsync(
|
||||
new ArraySegment<byte>(bytes),
|
||||
WebSocketMessageType.Binary,
|
||||
true,
|
||||
cancellationToken
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription)
|
||||
{
|
||||
return WebSocket.CloseAsync(closeStatus, statusDescription, CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetScenarioState(string nextState)
|
||||
{
|
||||
SetScenarioState(nextState, null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetScenarioState(string nextState, string? description)
|
||||
{
|
||||
if (Mapping.Scenario == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the same logic as WireMockMiddleware
|
||||
if (_options.Scenarios.TryGetValue(Mapping.Scenario, out var scenarioState))
|
||||
{
|
||||
// Directly set the next state (bypass counter logic for manual WebSocket state changes)
|
||||
scenarioState.NextState = nextState;
|
||||
scenarioState.Started = true;
|
||||
scenarioState.Finished = nextState == null;
|
||||
|
||||
// Reset counter when manually setting state
|
||||
scenarioState.Counter = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create new scenario state if it doesn't exist
|
||||
_options.Scenarios.TryAdd(Mapping.Scenario, new ScenarioState
|
||||
{
|
||||
Name = Mapping.Scenario,
|
||||
NextState = nextState,
|
||||
Started = true,
|
||||
Finished = nextState == null,
|
||||
Counter = 0
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update scenario state following the same pattern as WireMockMiddleware.UpdateScenarioState
|
||||
/// This is called automatically when the WebSocket connection is established.
|
||||
/// </summary>
|
||||
internal void UpdateScenarioState()
|
||||
{
|
||||
if (Mapping.Scenario == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure scenario exists
|
||||
if (!_options.Scenarios.TryGetValue(Mapping.Scenario, out var scenario))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Follow exact same logic as WireMockMiddleware.UpdateScenarioState
|
||||
// Increase the number of times this state has been executed
|
||||
scenario.Counter++;
|
||||
|
||||
// Only if the number of times this state is executed equals the required StateTimes,
|
||||
// proceed to next state and reset the counter to 0
|
||||
if (scenario.Counter == (Mapping.TimesInSameState ?? 1))
|
||||
{
|
||||
scenario.NextState = Mapping.NextState;
|
||||
scenario.Counter = 0;
|
||||
}
|
||||
|
||||
// Else just update Started and Finished
|
||||
scenario.Started = true;
|
||||
scenario.Finished = Mapping.NextState == null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task BroadcastTextAsync(string text, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (Registry != null)
|
||||
{
|
||||
await Registry.BroadcastTextAsync(text, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,7 @@
|
||||
<Description>Minimal version from the lightweight Http Mocking Server for .NET</Description>
|
||||
<AssemblyTitle>WireMock.Net.Minimal</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<!--<TargetFrameworks>net451;net452;net46;net461;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>-->
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net.Minimal</AssemblyName>
|
||||
<PackageId>WireMock.Net.Minimal</PackageId>
|
||||
@@ -38,10 +37,11 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!--<PackageReference Include="JmesPath.Net.SourceOnly" Version="1.0.330-20260213.1" />-->
|
||||
<PackageReference Include="JmesPath.Net" Version="1.0.330" />
|
||||
<PackageReference Include="NJsonSchema.Extensions" Version="0.1.0" />
|
||||
<PackageReference Include="NSwag.Core" Version="13.16.1" />
|
||||
<PackageReference Include="SimMetrics.Net" Version="1.0.5" />
|
||||
<PackageReference Include="JmesPath.Net" Version="1.0.330" />
|
||||
<PackageReference Include="TinyMapper.Signed" Version="4.0.0" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="6.34.0" />
|
||||
<PackageReference Include="Scriban.Signed" Version="5.5.0" />
|
||||
@@ -51,11 +51,23 @@
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net48'">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.3.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.3.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.3.9" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Suppress warnings from JmesPath source-only package -->
|
||||
<!--<NoWarn>$(NoWarn);CS8600;CS8602;CS8603;CS8604;CS8619;CS8625;CS0649</NoWarn>-->
|
||||
</PropertyGroup>
|
||||
|
||||
<!--<ItemGroup>
|
||||
--><!-- Disable all warnings for JmesPath source-only package files --><!--
|
||||
<Compile Update="**\jmespath.net.sourceonly\**\*.cs">
|
||||
<NoWarn>$(NoWarn);CS0001-CS9999</NoWarn>
|
||||
</Compile>
|
||||
</ItemGroup>-->
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Server\WireMockServer.*.cs">
|
||||
<DependentUpon>WireMockServer.cs</DependentUpon>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Description>Some extensions for NUnit</Description>
|
||||
<AssemblyTitle>WireMock.Net.NUnit</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<TargetFrameworks>net462;net8.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net.NUnit</AssemblyName>
|
||||
<RootNamespace>WireMock.Net.NUnit</RootNamespace>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if NET46 || NET47 || NETSTANDARD2_0
|
||||
#if NETSTANDARD2_0
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WireMock.Net.OpenApiParser.Extensions;
|
||||
|
||||
@@ -293,17 +293,17 @@ internal class OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
|
||||
return newPath;
|
||||
}
|
||||
|
||||
private IDictionary<string, object>? MapHeaders(string? responseContentType, IDictionary<string, IOpenApiHeader>? headers)
|
||||
private Dictionary<string, object>? MapHeaders(string? responseContentType, IDictionary<string, IOpenApiHeader>? headers)
|
||||
{
|
||||
var mappedHeaders = headers?
|
||||
.ToDictionary(item => item.Key, _ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern!) ?? new Dictionary<string, object>();
|
||||
.ToDictionary(item => item.Key, _ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern!) ?? [];
|
||||
|
||||
if (!string.IsNullOrEmpty(responseContentType))
|
||||
{
|
||||
mappedHeaders.TryAdd(HeaderContentType, responseContentType);
|
||||
}
|
||||
|
||||
return mappedHeaders.Keys.Any() ? mappedHeaders : null;
|
||||
return mappedHeaders.Count > 0 ? mappedHeaders : null;
|
||||
}
|
||||
|
||||
private IList<ParamModel>? MapQueryParameters(IEnumerable<IOpenApiParameter> queryParameters)
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>An OpenApi (swagger) parser to generate MappingModel or mapping.json file.</Description>
|
||||
<!--<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>-->
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>wiremock;openapi;OAS;raml;converter;parser;openapiparser</PackageTags>
|
||||
<ProjectGuid>{E5B03EEF-822C-4295-952B-4479AD30082B}</ProjectGuid>
|
||||
@@ -24,14 +23,23 @@
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
|
||||
<!-- Keep at 6.14.0 -->
|
||||
<PackageReference Include="Polyfill" Version="6.14.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' ">
|
||||
<PackageReference Include="Required" Version="1.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="IsExternalInit" Version="1.0.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Nullable" Version="1.3.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageReference Include="RamlToOpenApiConverter.SourceOnly" Version="0.11.0" />
|
||||
<PackageReference Include="YamlDotNet" Version="16.3.0" />
|
||||
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.19" />
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!--<Version>0.0.1-preview-01</Version>-->
|
||||
<Description>OpenTelemetry exporter support for WireMock.Net</Description>
|
||||
<AssemblyTitle>WireMock.Net.OpenTelemetry</AssemblyTitle>
|
||||
<Authors>Petr Houška</Authors>
|
||||
<TargetFrameworks>net4.8;net8.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<RootNamespace>WireMock.OpenTelemetry</RootNamespace>
|
||||
<PackageTags>wiremock;opentelemetry;otel;tracing;telemetry</PackageTags>
|
||||
|
||||
@@ -40,7 +40,7 @@ internal static class ProtoDefinitionDataHelper
|
||||
|
||||
// Build comment and get content from file.
|
||||
var comment = $"// {protoRelativePath}";
|
||||
#if NETSTANDARD2_0 || NET462 || NET48
|
||||
#if NET462
|
||||
var content = File.ReadAllText(filePath);
|
||||
#else
|
||||
var content = await File.ReadAllTextAsync(filePath, cancellationToken);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<AssemblyTitle>WireMock.Net.ProtoBuf</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<!--<TargetFrameworks>netstandard2.1;net462;net6.0;net8.0</TargetFrameworks>-->
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<TargetFrameworks>net462;netstandard2.1</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>wiremock;matchers;matcher;protobuf;grpc</PackageTags>
|
||||
<RootNamespace>WireMock</RootNamespace>
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
<Description>A RestClient using RestEase to access the admin interface.</Description>
|
||||
<AssemblyTitle>WireMock.Net.RestClient</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<!--<TargetFrameworks>net45;netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>-->
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net.RestClient</AssemblyName>
|
||||
<PackageId>WireMock.Net.RestClient</PackageId>
|
||||
|
||||
20
src/WireMock.Net.Shared/Extensions/ArrayPoolExtensions.cs
Normal file
20
src/WireMock.Net.Shared/Extensions/ArrayPoolExtensions.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace System.Buffers;
|
||||
|
||||
internal sealed class Lease<T>(ArrayPool<T> pool, int length) : IDisposable
|
||||
{
|
||||
public T[] Rented { get; } = pool.Rent(length);
|
||||
|
||||
public static implicit operator T[](Lease<T> lease) => lease.Rented;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
pool.Return(Rented, true);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class ArrayPoolExtensions
|
||||
{
|
||||
public static Lease<T> Lease<T>(this ArrayPool<T> source, int length) => new(source, length);
|
||||
}
|
||||
@@ -28,4 +28,18 @@ internal static class DictionaryExtensions
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetValue<T>(this IDictionary<object, object?> dictionary, string key, [NotNullWhen(true)] out T? value)
|
||||
{
|
||||
Guard.NotNull(dictionary);
|
||||
|
||||
if (dictionary[key] is T typedValue)
|
||||
{
|
||||
value = typedValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
21
src/WireMock.Net.Shared/Extensions/EnumerableExtensions.cs
Normal file
21
src/WireMock.Net.Shared/Extensions/EnumerableExtensions.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace System.Collections.Generic;
|
||||
|
||||
internal static class EnumerableExtensions
|
||||
{
|
||||
//internal static IEnumerable<T> ReverseEx<T>(this IEnumerable<T> source)
|
||||
//{
|
||||
// var stack = new Stack<T>();
|
||||
|
||||
// foreach (var item in source)
|
||||
// {
|
||||
// stack.Push(item);
|
||||
// }
|
||||
|
||||
// while (stack.Count > 0)
|
||||
// {
|
||||
// yield return stack.Pop();
|
||||
// }
|
||||
//}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
using System.Globalization;
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Extensions;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using WireMock.Constants;
|
||||
|
||||
namespace System;
|
||||
|
||||
internal static class StringExtensions
|
||||
{
|
||||
@@ -28,4 +32,12 @@ internal static class StringExtensions
|
||||
return result.ToString(CultureInfo.InvariantCulture).Replace('-', '_');
|
||||
}
|
||||
}
|
||||
|
||||
#if !NET8_0_OR_GREATER
|
||||
public static string Replace(this string text, string oldValue, string newValue, StringComparison stringComparison)
|
||||
{
|
||||
var options = stringComparison == StringComparison.OrdinalIgnoreCase ? RegexOptions.IgnoreCase : RegexOptions.None;
|
||||
return Regex.Replace(text, oldValue, newValue, options, RegexConstants.DefaultTimeout);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
17
src/WireMock.Net.Shared/Matchers/IFuncMatcher.cs
Normal file
17
src/WireMock.Net.Shared/Matchers/IFuncMatcher.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// IFuncMatcher
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IMatcher"/>
|
||||
public interface IFuncMatcher : IMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the specified function is match.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to check for a match.</param>
|
||||
/// <returns>MatchResult</returns>
|
||||
MatchResult IsMatch(object? value);
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
@@ -40,6 +38,18 @@ public class MatchResult
|
||||
/// </summary>
|
||||
public bool IsPerfect() => MatchScores.IsPerfect(Score);
|
||||
|
||||
/// <summary>
|
||||
/// Create a MatchResult.
|
||||
/// </summary>
|
||||
/// <param name="name">The name or description of the matcher.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="isMatch">Is this a match?</param>
|
||||
/// <param name="exception">The exception in case the matching fails. [Optional]</param>
|
||||
public static MatchResult From(string name, MatchBehaviour matchBehaviour, bool isMatch, Exception? exception = null)
|
||||
{
|
||||
return From(name, MatchBehaviourHelper.Convert(matchBehaviour, MatchScores.ToScore(isMatch)), exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a MatchResult.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
|
||||
namespace WireMock.RequestBuilders;
|
||||
|
||||
/// <summary>
|
||||
/// The HttpVersionBuilder interface.
|
||||
/// </summary>
|
||||
public interface IHttpVersionBuilder : IRequestMatcher
|
||||
public interface IHttpVersionBuilder : IWebSocketRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// WithHttpVersion
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright © WireMock.Net
|
||||
using WireMock.Matchers.Request;
|
||||
|
||||
namespace WireMock.RequestBuilders;
|
||||
|
||||
/// <summary>
|
||||
/// The BodyRequestBuilder interface.
|
||||
/// </summary>
|
||||
public interface IWebSocketRequestBuilder : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the connection uses the WebSocket protocol.
|
||||
/// </summary>
|
||||
bool IsWebSocket { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Match WebSocket upgrade with optional protocols.
|
||||
/// </summary>
|
||||
IRequestBuilder WithWebSocketUpgrade(params string[] protocols);
|
||||
}
|
||||
@@ -3,14 +3,13 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.ResponseProviders;
|
||||
|
||||
namespace WireMock.ResponseBuilders;
|
||||
|
||||
/// <summary>
|
||||
/// The CallbackResponseBuilder interface.
|
||||
/// </summary>
|
||||
public interface ICallbackResponseBuilder : IResponseProvider
|
||||
public interface ICallbackResponseBuilder : IWebSocketResponseBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The callback builder
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using WireMock.ResponseProviders;
|
||||
using WireMock.Settings;
|
||||
using WireMock.WebSockets;
|
||||
|
||||
namespace WireMock.ResponseBuilders;
|
||||
|
||||
/// <summary>
|
||||
/// The WebSocketResponseBuilder interface.
|
||||
/// </summary>
|
||||
public interface IWebSocketResponseBuilder : IResponseProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Configure WebSocket response behavior
|
||||
/// </summary>
|
||||
IResponseBuilder WithWebSocket(Action<IWebSocketBuilder> configure);
|
||||
|
||||
/// <summary>
|
||||
/// Proxy WebSocket to another server
|
||||
/// </summary>
|
||||
IResponseBuilder WithWebSocketProxy(string targetUrl);
|
||||
|
||||
/// <summary>
|
||||
/// Proxy WebSocket to another server with settings
|
||||
/// </summary>
|
||||
IResponseBuilder WithWebSocketProxy(ProxyAndRecordSettings settings);
|
||||
}
|
||||
21
src/WireMock.Net.Shared/Settings/WebSocketSettings.cs
Normal file
21
src/WireMock.Net.Shared/Settings/WebSocketSettings.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Constants;
|
||||
|
||||
namespace WireMock.Settings;
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket-specific settings
|
||||
/// </summary>
|
||||
public class WebSocketSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Maximum number of concurrent WebSocket connections (default: 100)
|
||||
/// </summary>
|
||||
public int MaxConnections { get; set; } = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Default keep-alive interval (default: 30 seconds)
|
||||
/// </summary>
|
||||
public int KeepAliveIntervalSeconds { get; set; } = WebSocketConstants.DefaultKeepAliveIntervalSeconds;
|
||||
}
|
||||
@@ -346,4 +346,10 @@ public class WireMockServerSettings
|
||||
/// </remarks>
|
||||
[PublicAPI]
|
||||
public ActivityTracingOptions? ActivityTracingOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket settings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public WebSocketSettings? WebSocketSettings { get; set; }
|
||||
}
|
||||
104
src/WireMock.Net.Shared/WebSockets/IWebSocketBuilder.cs
Normal file
104
src/WireMock.Net.Shared/WebSockets/IWebSocketBuilder.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.WebSockets;
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket Response Builder interface
|
||||
/// </summary>
|
||||
public interface IWebSocketBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Accept the WebSocket with a specific protocol
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
IWebSocketBuilder WithAcceptProtocol(string protocol);
|
||||
|
||||
/// <summary>
|
||||
/// Echo all received messages back to client
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
IWebSocketBuilder WithEcho();
|
||||
|
||||
/// <summary>
|
||||
/// Configure and send a single message in response to any received message
|
||||
/// </summary>
|
||||
/// <param name="configure">Action to configure the message</param>
|
||||
[PublicAPI]
|
||||
IWebSocketBuilder SendMessage(Action<IWebSocketMessageBuilder> configure);
|
||||
|
||||
/// <summary>
|
||||
/// Configure and send multiple messages in response to any received message
|
||||
/// </summary>
|
||||
/// <param name="configure">Action to configure the messages</param>
|
||||
[PublicAPI]
|
||||
IWebSocketBuilder SendMessages(Action<IWebSocketMessagesBuilder> configure);
|
||||
|
||||
/// <summary>
|
||||
/// Configure message sending based on message content matching
|
||||
/// </summary>
|
||||
/// <param name="condition">String to match in message text</param>
|
||||
[PublicAPI]
|
||||
IWebSocketMessageConditionBuilder WhenMessage(string condition);
|
||||
|
||||
/// <summary>
|
||||
/// Configure message sending based on message content matching
|
||||
/// </summary>
|
||||
/// <param name="condition">Bytes to match in message</param>
|
||||
[PublicAPI]
|
||||
IWebSocketMessageConditionBuilder WhenMessage(byte[] condition);
|
||||
|
||||
/// <summary>
|
||||
/// Configure message sending based on IMatcher
|
||||
/// </summary>
|
||||
/// <param name="matcher">IMatcher to match the message</param>
|
||||
[PublicAPI]
|
||||
IWebSocketMessageConditionBuilder WhenMessage(IMatcher matcher);
|
||||
|
||||
/// <summary>
|
||||
/// Handle incoming WebSocket messages
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
IWebSocketBuilder WithMessageHandler(Func<WebSocketMessage, IWebSocketContext, Task> handler);
|
||||
|
||||
/// <summary>
|
||||
/// Enable broadcast mode for this mapping
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
IWebSocketBuilder WithBroadcast();
|
||||
|
||||
/// <summary>
|
||||
/// Proxy to another WebSocket server
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
IWebSocketBuilder WithProxy(ProxyAndRecordSettings settings);
|
||||
|
||||
/// <summary>
|
||||
/// Set close timeout (default: 10 minutes)
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
IWebSocketBuilder WithCloseTimeout(TimeSpan timeout);
|
||||
|
||||
/// <summary>
|
||||
/// Set maximum message size in bytes (default: 1 MB)
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
IWebSocketBuilder WithMaxMessageSize(int sizeInBytes);
|
||||
|
||||
/// <summary>
|
||||
/// Set receive buffer size (default: 4096 bytes)
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
IWebSocketBuilder WithReceiveBufferSize(int sizeInBytes);
|
||||
|
||||
/// <summary>
|
||||
/// Set keep-alive interval (default: 30 seconds)
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
IWebSocketBuilder WithKeepAliveInterval(TimeSpan interval);
|
||||
}
|
||||
72
src/WireMock.Net.Shared/WebSockets/IWebSocketContext.cs
Normal file
72
src/WireMock.Net.Shared/WebSockets/IWebSocketContext.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Net.WebSockets;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace WireMock.WebSockets;
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket context interface for handling WebSocket connections
|
||||
/// </summary>
|
||||
public interface IWebSocketContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique connection identifier
|
||||
/// </summary>
|
||||
Guid ConnectionId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The ASP.NET Core HttpContext
|
||||
/// </summary>
|
||||
HttpContext HttpContext { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The WebSocket instance
|
||||
/// </summary>
|
||||
WebSocket WebSocket { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The original request that initiated the WebSocket connection
|
||||
/// </summary>
|
||||
IRequestMessage RequestMessage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The mapping that matched this WebSocket request
|
||||
/// </summary>
|
||||
IMapping Mapping { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Send text message to the client
|
||||
/// </summary>
|
||||
Task SendAsync(string text, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Send binary message to the client
|
||||
/// </summary>
|
||||
Task SendAsync(byte[] bytes, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Close the WebSocket connection
|
||||
/// </summary>
|
||||
Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription);
|
||||
|
||||
/// <summary>
|
||||
/// Manually set the scenario state. This bypasses the counter logic and directly sets the next state.
|
||||
/// Use this for programmatic state changes during WebSocket sessions.
|
||||
/// </summary>
|
||||
/// <param name="nextState">The next state to transition to</param>
|
||||
void SetScenarioState(string nextState);
|
||||
|
||||
/// <summary>
|
||||
/// Manually set the scenario state with description. This bypasses the counter logic and directly sets the next state.
|
||||
/// Use this for programmatic state changes during WebSocket sessions.
|
||||
/// </summary>
|
||||
/// <param name="nextState">The next state to transition to</param>
|
||||
/// <param name="description">Optional description for logging</param>
|
||||
void SetScenarioState(string nextState, string? description);
|
||||
|
||||
/// <summary>
|
||||
/// Broadcast text message to all connections in this mapping
|
||||
/// </summary>
|
||||
Task BroadcastTextAsync(string text, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace WireMock.WebSockets;
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket Message Builder interface for building individual messages with optional delays
|
||||
/// </summary>
|
||||
public interface IWebSocketMessageBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Send a specific text message
|
||||
/// </summary>
|
||||
/// <param name="text">The text message to send</param>
|
||||
[PublicAPI]
|
||||
IWebSocketMessageBuilder WithText(string text);
|
||||
|
||||
/// <summary>
|
||||
/// Send specific binary data
|
||||
/// </summary>
|
||||
/// <param name="bytes">The binary data to send</param>
|
||||
[PublicAPI]
|
||||
IWebSocketMessageBuilder WithBinary(byte[] bytes);
|
||||
|
||||
/// <summary>
|
||||
/// Set a delay before sending the message (using TimeSpan)
|
||||
/// </summary>
|
||||
/// <param name="delay">The delay before sending the message</param>
|
||||
[PublicAPI]
|
||||
IWebSocketMessageBuilder WithDelay(TimeSpan delay);
|
||||
|
||||
/// <summary>
|
||||
/// Set a delay before sending the message (using milliseconds)
|
||||
/// </summary>
|
||||
/// <param name="delayInMilliseconds">The delay in milliseconds before sending the message</param>
|
||||
[PublicAPI]
|
||||
IWebSocketMessageBuilder WithDelay(int delayInMilliseconds);
|
||||
|
||||
/// <summary>
|
||||
/// Close the WebSocket connection after this message
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
IWebSocketMessageBuilder AndClose();
|
||||
|
||||
/// <summary>
|
||||
/// Close the WebSocket connection.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
IWebSocketMessageBuilder Close();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace WireMock.WebSockets;
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket Message Condition Builder interface for building conditional message responses
|
||||
/// </summary>
|
||||
public interface IWebSocketMessageConditionBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Configure and send a message when the condition matches
|
||||
/// </summary>
|
||||
/// <param name="configure">Action to configure the message</param>
|
||||
[PublicAPI]
|
||||
IWebSocketBuilder SendMessage(Action<IWebSocketMessageBuilder> configure);
|
||||
|
||||
/// <summary>
|
||||
/// Configure and send multiple messages when the condition matches
|
||||
/// </summary>
|
||||
/// <param name="configure">Action to configure the messages</param>
|
||||
[PublicAPI]
|
||||
IWebSocketBuilder SendMessages(Action<IWebSocketMessagesBuilder> configure);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace WireMock.WebSockets;
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket Messages Builder interface for building multiple messages
|
||||
/// </summary>
|
||||
public interface IWebSocketMessagesBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Add a message to the sequence
|
||||
/// </summary>
|
||||
/// <param name="configure">Action to configure the message</param>
|
||||
[PublicAPI]
|
||||
IWebSocketMessagesBuilder AddMessage(Action<IWebSocketMessageBuilder> configure);
|
||||
}
|
||||
36
src/WireMock.Net.Shared/WebSockets/WebSocketMessage.cs
Normal file
36
src/WireMock.Net.Shared/WebSockets/WebSocketMessage.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Net.WebSockets;
|
||||
|
||||
namespace WireMock.WebSockets;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a WebSocket message
|
||||
/// </summary>
|
||||
public class WebSocketMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// The message type (Text or Binary)
|
||||
/// </summary>
|
||||
public WebSocketMessageType MessageType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Text content (when MessageType is Text)
|
||||
/// </summary>
|
||||
public string? Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Binary content (when MessageType is Binary)
|
||||
/// </summary>
|
||||
public byte[]? Bytes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this is the final message
|
||||
/// </summary>
|
||||
public bool EndOfMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp when the message was received
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; set; }
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Description>Shared interfaces, models, enumerations and types.</Description>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<!--<TargetFrameworks>net451;net452;net46;net461;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>-->
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<!--<TargetFrameworks>net451;net452;net46;net462;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>-->
|
||||
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>tdd;mock;http;wiremock;test;server;shared</PackageTags>
|
||||
<RootNamespace>WireMock</RootNamespace>
|
||||
@@ -27,15 +27,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Keep at 6.14.0 -->
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.3.9" />
|
||||
<PackageReference Include="Polyfill" Version="6.14.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Stef.Validation" Version="0.2.0" />
|
||||
<PackageReference Include="AnyOf" Version="0.5.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
|
||||
<PackageReference Include="JsonConverter.Abstractions" Version="0.7.2" />
|
||||
</ItemGroup>
|
||||
@@ -54,4 +49,19 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' ">
|
||||
<PackageReference Include="Required" Version="1.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="IsExternalInit" Version="1.0.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Nullable" Version="1.3.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,10 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Description>Lightweight StandAlone Http Mocking Server for .Net.</Description>
|
||||
<AssemblyTitle>WireMock.Net.StandAlone</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<!--<TargetFrameworks>net451;net452;net46;net461;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>-->
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<!--<TargetFrameworks>net451;net452;net46;net462;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>-->
|
||||
<TargetFrameworks>netstandard2.1;net8.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net.StandAlone</AssemblyName>
|
||||
<PackageId>WireMock.Net.StandAlone</PackageId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<Description>Some extensions for TUnit (TUnitLogger)</Description>
|
||||
<AssemblyTitle>WireMock.Net.TUnit</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net.TUnit</AssemblyName>
|
||||
<RootNamespace>WireMock.Net.Tunit</RootNamespace>
|
||||
@@ -26,7 +26,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageReference Include="Stef.Validation" Version="0.2.0" />
|
||||
<PackageReference Include="TUnit.Core" Version="0.57.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -22,17 +22,18 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\WireMock.Net.Minimal\Http\HttpClientFactory2.cs" Link="Http\HttpClientFactory2.cs" />
|
||||
<Compile Include="..\WireMock.Net.Minimal\Util\EnhancedFileSystemWatcher.cs" Link="Utils\EnhancedFileSystemWatcher.cs" />
|
||||
<Compile Include="..\WireMock.Net.Minimal\Util\PortUtils.cs" Link="Util\PortUtils.cs" />
|
||||
<Compile Include="..\WireMock.Net.Minimal\Constants\WireMockConstants.cs" Link="Constants\WireMockConstants.cs" />
|
||||
<Compile Include="..\WireMock.Net.Shared\Constants\RegexConstants.cs" Link="Constants\RegexConstants.cs" />
|
||||
<Compile Include="..\WireMock.Net.Minimal\Http\HttpClientFactory2.cs" Link="Http\HttpClientFactory2.cs" />
|
||||
<Compile Include="..\WireMock.Net.Minimal\Util\EnhancedFileSystemWatcher.cs" Link="Utils\EnhancedFileSystemWatcher.cs" />
|
||||
<Compile Include="..\WireMock.Net.Minimal\Util\PortUtils.cs" Link="Util\PortUtils.cs" />
|
||||
<Compile Include="..\WireMock.Net.Minimal\Constants\WireMockConstants.cs" Link="Constants\WireMockConstants.cs" />
|
||||
<Compile Include="..\WireMock.Net.Shared\Constants\RegexConstants.cs" Link="Constants\RegexConstants.cs" />
|
||||
<Compile Include="..\WireMock.Net.Shared\Extensions\EnumerableExtensions.cs" Link="Extensions\EnumerableExtensions.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IsExternalInit" Version="1.0.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Nullable" Version="1.3.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
@@ -43,7 +44,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WireMock.Net.RestClient\WireMock.Net.RestClient.csproj" />
|
||||
<ProjectReference Include="..\WireMock.Net.RestClient\WireMock.Net.RestClient.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Description>Some extensions for xUnit (ITestOutputHelper)</Description>
|
||||
<AssemblyTitle>WireMock.Net.xUnit.v3</AssemblyTitle>
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
<Description>Some extensions for xUnit (ITestOutputHelper)</Description>
|
||||
<AssemblyTitle>WireMock.Net.xUnit</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<!--<TargetFrameworks>net45;net451;netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>-->
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net.xUnit</AssemblyName>
|
||||
<RootNamespace>WireMock.Net.Xunit</RootNamespace>
|
||||
@@ -26,7 +25,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageReference Include="Stef.Validation" Version="0.2.0" />
|
||||
<PackageReference Include="xUnit.abstractions" Version="2.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Description>Lightweight Http Mocking Server for .NET, inspired by WireMock from the Java landscape.</Description>
|
||||
<AssemblyTitle>WireMock.Net</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<!--<TargetFrameworks>net451;net452;net46;net461;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>-->
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<!--<TargetFrameworks>net451;net452;net46;net462;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>-->
|
||||
<TargetFrameworks>net462;netstandard2.1;net8.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net</AssemblyName>
|
||||
<PackageId>WireMock.Net</PackageId>
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
<Description>Commonly used interfaces, models, enumerations and types.</Description>
|
||||
<AssemblyTitle>WireMock.Org.Abstractions</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<!--<TargetFrameworks>net45;netstandard1.0;netstandard2.0;netstandard2.1</TargetFrameworks>-->
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>$(NoWarn);1591;8603</NoWarn>
|
||||
<RootNamespace>WireMock.Org.Abstractions</RootNamespace>
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
<Description>A WireMock.org RestClient using RestEase to access the admin interface.</Description>
|
||||
<AssemblyTitle>WireMock.Org.RestClient</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<!--<TargetFrameworks>net45;netstandard1.1;netstandard2.0;netstandard2.1</TargetFrameworks>-->
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Org.RestClient</AssemblyName>
|
||||
<PackageId>WireMock.Org.RestClient</PackageId>
|
||||
|
||||
@@ -4,11 +4,10 @@ using System.Net.Http.Json;
|
||||
using AwesomeAssertions;
|
||||
using Projects;
|
||||
using WireMock.Net.Aspire.Tests.Facts;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace WireMock.Net.Aspire.Tests;
|
||||
|
||||
public class IntegrationTests(ITestOutputHelper output)
|
||||
public class IntegrationTests
|
||||
{
|
||||
private record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary);
|
||||
|
||||
|
||||
16963
test/WireMock.Net.Aspire.Tests/coverage.opencover.xml
Normal file
16963
test/WireMock.Net.Aspire.Tests/coverage.opencover.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Authors>Gennadii Saltyshchak</Authors>
|
||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<DebugType>full</DebugType>
|
||||
@@ -28,10 +28,10 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\WireMock.Net.Extensions.Routing\WireMock.Net.Extensions.Routing.csproj" />
|
||||
<ProjectReference Include="..\..\src\WireMock.Net.Extensions.Routing\WireMock.Net.Extensions.Routing.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
51302
test/WireMock.Net.Middleware.Tests/coverage.opencover.xml
Normal file
51302
test/WireMock.Net.Middleware.Tests/coverage.opencover.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -10,15 +10,15 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AwesomeAssertions" Version="9.1.0" />
|
||||
<PackageReference Include="AwesomeAssertions" Version="9.3.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="WireMock.Net" Version="1.23.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
|
||||
<PackageReference Include="WireMock.Net" Version="1.25.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -29,6 +29,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="JetBrains.Annotations" Version="2025.2.4" />
|
||||
<PackageReference Update="SonarAnalyzer.CSharp" Version="10.11.0.117924" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
429
test/WireMock.Net.Tests/Matchers/FuncMatcherTests.cs
Normal file
429
test/WireMock.Net.Tests/Matchers/FuncMatcherTests.cs
Normal file
@@ -0,0 +1,429 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using FluentAssertions;
|
||||
using WireMock.Matchers;
|
||||
|
||||
namespace WireMock.Net.Tests.Matchers;
|
||||
|
||||
public class FuncMatcherTests
|
||||
{
|
||||
[Fact]
|
||||
public void FuncMatcher_For_String_IsMatch_Should_Return_Perfect_When_Function_Returns_True()
|
||||
{
|
||||
// Arrange
|
||||
Func<string?, bool> func = s => s == "test";
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var result = matcher.IsMatch("test");
|
||||
|
||||
// Assert
|
||||
result.IsPerfect().Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_String_IsMatch_Should_Return_Mismatch_When_Function_Returns_False()
|
||||
{
|
||||
// Arrange
|
||||
Func<string?, bool> func = s => s == "test";
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var result = matcher.IsMatch("other");
|
||||
|
||||
// Assert
|
||||
result.IsPerfect().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_String_IsMatch_Should_Handle_Null_String()
|
||||
{
|
||||
// Arrange
|
||||
Func<string?, bool> func = s => s == null;
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act - passing null as object, not as string
|
||||
var result = matcher.IsMatch((object?)null);
|
||||
|
||||
// Assert - null object doesn't match, returns mismatch
|
||||
result.IsPerfect().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_String_IsMatch_With_ByteArray_Input_Should_Return_Mismatch()
|
||||
{
|
||||
// Arrange
|
||||
Func<string?, bool> func = s => s == "test";
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var result = matcher.IsMatch(new byte[] { 1, 2, 3 });
|
||||
|
||||
// Assert
|
||||
result.IsPerfect().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_String_IsMatch_With_Null_Object_Should_Return_Mismatch()
|
||||
{
|
||||
// Arrange
|
||||
Func<string?, bool> func = s => s == "test";
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var result = matcher.IsMatch((object?)null);
|
||||
|
||||
// Assert
|
||||
result.IsPerfect().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_String_IsMatch_Should_Handle_Exception()
|
||||
{
|
||||
// Arrange
|
||||
Func<string?, bool> func = s => throw new InvalidOperationException("Test exception");
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var result = matcher.IsMatch("test");
|
||||
|
||||
// Assert
|
||||
result.IsPerfect().Should().BeFalse();
|
||||
result.Exception.Should().NotBeNull();
|
||||
result.Exception.Should().BeOfType<InvalidOperationException>();
|
||||
result.Exception!.Message.Should().Be("Test exception");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_Bytes_IsMatch_Should_Return_Perfect_When_Function_Returns_True()
|
||||
{
|
||||
// Arrange
|
||||
Func<byte[]?, bool> func = b => b != null && b.Length == 3;
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var result = matcher.IsMatch(new byte[] { 1, 2, 3 });
|
||||
|
||||
// Assert
|
||||
result.IsPerfect().Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_Bytes_IsMatch_Should_Return_Mismatch_When_Function_Returns_False()
|
||||
{
|
||||
// Arrange
|
||||
Func<byte[]?, bool> func = b => b != null && b.Length == 3;
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var result = matcher.IsMatch(new byte[] { 1, 2, 3, 4, 5 });
|
||||
|
||||
// Assert
|
||||
result.IsPerfect().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_Bytes_IsMatch_Should_Handle_Null_ByteArray()
|
||||
{
|
||||
// Arrange
|
||||
Func<byte[]?, bool> func = b => b == null;
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act - passing null as object, not as byte[]
|
||||
var result = matcher.IsMatch((object?)null);
|
||||
|
||||
// Assert - null object doesn't match, returns mismatch
|
||||
result.IsPerfect().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_Bytes_IsMatch_With_String_Input_Should_Return_Mismatch()
|
||||
{
|
||||
// Arrange
|
||||
Func<byte[]?, bool> func = b => b != null && b.Length > 0;
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var result = matcher.IsMatch("test");
|
||||
|
||||
// Assert
|
||||
result.IsPerfect().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_Bytes_IsMatch_Should_Handle_Exception()
|
||||
{
|
||||
// Arrange
|
||||
Func<byte[]?, bool> func = b => throw new InvalidOperationException("Bytes exception");
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var result = matcher.IsMatch(new byte[] { 1, 2, 3 });
|
||||
|
||||
// Assert
|
||||
result.IsPerfect().Should().BeFalse();
|
||||
result.Exception.Should().NotBeNull();
|
||||
result.Exception.Should().BeOfType<InvalidOperationException>();
|
||||
result.Exception!.Message.Should().Be("Bytes exception");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_String_With_Contains_Logic_Should_Work()
|
||||
{
|
||||
// Arrange
|
||||
Func<string?, bool> func = s => s?.Contains("foo") == true;
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var result1 = matcher.IsMatch("foo");
|
||||
var result2 = matcher.IsMatch("foobar");
|
||||
var result3 = matcher.IsMatch("bar");
|
||||
|
||||
// Assert
|
||||
result1.IsPerfect().Should().BeTrue();
|
||||
result2.IsPerfect().Should().BeTrue();
|
||||
result3.IsPerfect().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_Bytes_With_Length_Logic_Should_Work()
|
||||
{
|
||||
// Arrange
|
||||
Func<byte[]?, bool> func = b => b != null && b.Length > 2;
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var result1 = matcher.IsMatch(new byte[] { 1 });
|
||||
var result2 = matcher.IsMatch(new byte[] { 1, 2 });
|
||||
var result3 = matcher.IsMatch(new byte[] { 1, 2, 3 });
|
||||
|
||||
// Assert
|
||||
result1.IsPerfect().Should().BeFalse();
|
||||
result2.IsPerfect().Should().BeFalse();
|
||||
result3.IsPerfect().Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_Name_Should_Return_FuncMatcher()
|
||||
{
|
||||
// Arrange
|
||||
Func<string?, bool> func = s => s == "test";
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act & Assert
|
||||
matcher.Name.Should().Be("FuncMatcher");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_MatchBehaviour_Should_Return_AcceptOnMatch_By_Default()
|
||||
{
|
||||
// Arrange
|
||||
Func<string?, bool> func = s => s == "test";
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act & Assert
|
||||
matcher.MatchBehaviour.Should().Be(MatchBehaviour.AcceptOnMatch);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_MatchBehaviour_Should_Return_Custom_Value_For_String()
|
||||
{
|
||||
// Arrange
|
||||
Func<string?, bool> func = s => s == "test";
|
||||
var matcher = new FuncMatcher(func, MatchBehaviour.RejectOnMatch);
|
||||
|
||||
// Act & Assert
|
||||
matcher.MatchBehaviour.Should().Be(MatchBehaviour.RejectOnMatch);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_MatchBehaviour_Should_Return_Custom_Value_For_Bytes()
|
||||
{
|
||||
// Arrange
|
||||
Func<byte[]?, bool> func = b => b != null;
|
||||
var matcher = new FuncMatcher(func, MatchBehaviour.RejectOnMatch);
|
||||
|
||||
// Act & Assert
|
||||
matcher.MatchBehaviour.Should().Be(MatchBehaviour.RejectOnMatch);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_GetCSharpCodeArguments_For_String_Should_Return_Correct_Code()
|
||||
{
|
||||
// Arrange
|
||||
Func<string?, bool> func = s => s == "test";
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var code = matcher.GetCSharpCodeArguments();
|
||||
|
||||
// Assert
|
||||
code.Should().Be("new FuncMatcher(/* Func<string?, bool> function */, WireMock.Matchers.MatchBehaviour.AcceptOnMatch)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_GetCSharpCodeArguments_For_Bytes_Should_Return_Correct_Code()
|
||||
{
|
||||
// Arrange
|
||||
Func<byte[]?, bool> func = b => b != null;
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var code = matcher.GetCSharpCodeArguments();
|
||||
|
||||
// Assert
|
||||
code.Should().Be("new FuncMatcher(/* Func<byte[]?, bool> function */, WireMock.Matchers.MatchBehaviour.AcceptOnMatch)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_With_RejectOnMatch_For_String_Should_Invert_Result_When_True()
|
||||
{
|
||||
// Arrange
|
||||
Func<string?, bool> func = s => s == "test";
|
||||
var matcher = new FuncMatcher(func, MatchBehaviour.RejectOnMatch);
|
||||
|
||||
// Act
|
||||
var result = matcher.IsMatch("test");
|
||||
|
||||
// Assert
|
||||
result.IsPerfect().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_With_RejectOnMatch_For_String_Should_Invert_Result_When_False()
|
||||
{
|
||||
// Arrange
|
||||
Func<string?, bool> func = s => s == "test";
|
||||
var matcher = new FuncMatcher(func, MatchBehaviour.RejectOnMatch);
|
||||
|
||||
// Act
|
||||
var result = matcher.IsMatch("other");
|
||||
|
||||
// Assert
|
||||
result.IsPerfect().Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_With_RejectOnMatch_For_Bytes_Should_Invert_Result_When_True()
|
||||
{
|
||||
// Arrange
|
||||
Func<byte[]?, bool> func = b => b != null && b.Length > 0;
|
||||
var matcher = new FuncMatcher(func, MatchBehaviour.RejectOnMatch);
|
||||
|
||||
// Act
|
||||
var result = matcher.IsMatch(new byte[] { 1, 2, 3 });
|
||||
|
||||
// Assert
|
||||
result.IsPerfect().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_With_RejectOnMatch_For_Bytes_Should_Invert_Result_When_False()
|
||||
{
|
||||
// Arrange
|
||||
Func<byte[]?, bool> func = b => b != null && b.Length > 0;
|
||||
var matcher = new FuncMatcher(func, MatchBehaviour.RejectOnMatch);
|
||||
|
||||
// Act
|
||||
var result = matcher.IsMatch(new byte[0]);
|
||||
|
||||
// Assert
|
||||
result.IsPerfect().Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_String_IsMatch_With_Integer_Input_Should_Return_Mismatch()
|
||||
{
|
||||
// Arrange
|
||||
Func<string?, bool> func = s => s == "test";
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var result = matcher.IsMatch(42);
|
||||
|
||||
// Assert
|
||||
result.IsPerfect().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_Bytes_IsMatch_With_Integer_Input_Should_Return_Mismatch()
|
||||
{
|
||||
// Arrange
|
||||
Func<byte[]?, bool> func = b => b != null;
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var result = matcher.IsMatch(42);
|
||||
|
||||
// Assert
|
||||
result.IsPerfect().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_String_With_Empty_String_Should_Work()
|
||||
{
|
||||
// Arrange
|
||||
Func<string?, bool> func = s => string.IsNullOrEmpty(s);
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var result1 = matcher.IsMatch("");
|
||||
var result2 = matcher.IsMatch("test");
|
||||
|
||||
// Assert
|
||||
result1.IsPerfect().Should().BeTrue();
|
||||
result2.IsPerfect().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_Bytes_With_Empty_Array_Should_Work()
|
||||
{
|
||||
// Arrange
|
||||
Func<byte[]?, bool> func = b => b != null && b.Length == 0;
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var result1 = matcher.IsMatch(new byte[0]);
|
||||
var result2 = matcher.IsMatch(new byte[] { 1 });
|
||||
|
||||
// Assert
|
||||
result1.IsPerfect().Should().BeTrue();
|
||||
result2.IsPerfect().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_String_With_Complex_Logic_Should_Work()
|
||||
{
|
||||
// Arrange
|
||||
Func<string?, bool> func = s => s != null && s.Length > 3 && s.StartsWith("t") && s.EndsWith("t");
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var result1 = matcher.IsMatch("test");
|
||||
var result2 = matcher.IsMatch("tart");
|
||||
var result3 = matcher.IsMatch("tes");
|
||||
var result4 = matcher.IsMatch("best");
|
||||
|
||||
// Assert
|
||||
result1.IsPerfect().Should().BeTrue();
|
||||
result2.IsPerfect().Should().BeTrue();
|
||||
result3.IsPerfect().Should().BeFalse();
|
||||
result4.IsPerfect().Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FuncMatcher_For_Bytes_With_Specific_Byte_Check_Should_Work()
|
||||
{
|
||||
// Arrange
|
||||
Func<byte[]?, bool> func = b => b != null && b.Length > 0 && b[0] == 0xFF;
|
||||
var matcher = new FuncMatcher(func);
|
||||
|
||||
// Act
|
||||
var result1 = matcher.IsMatch(new byte[] { 0xFF, 0x00 });
|
||||
var result2 = matcher.IsMatch(new byte[] { 0x00, 0xFF });
|
||||
|
||||
// Assert
|
||||
result1.IsPerfect().Should().BeTrue();
|
||||
result2.IsPerfect().Should().BeFalse();
|
||||
}
|
||||
}
|
||||
@@ -1,45 +1,36 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Moq;
|
||||
using NFluent;
|
||||
using WireMock.Owin;
|
||||
using WireMock.Owin.Mappers;
|
||||
using Xunit;
|
||||
#if NET452
|
||||
using IContext = Microsoft.Owin.IOwinContext;
|
||||
using IResponse = Microsoft.Owin.IOwinResponse;
|
||||
#else
|
||||
using IContext = Microsoft.AspNetCore.Http.HttpContext;
|
||||
using IResponse = Microsoft.AspNetCore.Http.HttpResponse;
|
||||
#endif
|
||||
|
||||
namespace WireMock.Net.Tests.Owin
|
||||
namespace WireMock.Net.Tests.Owin;
|
||||
|
||||
public class GlobalExceptionMiddlewareTests
|
||||
{
|
||||
public class GlobalExceptionMiddlewareTests
|
||||
private readonly Mock<IWireMockMiddlewareOptions> _optionsMock;
|
||||
private readonly Mock<IOwinResponseMapper> _responseMapperMock;
|
||||
|
||||
private readonly GlobalExceptionMiddleware _sut;
|
||||
|
||||
public GlobalExceptionMiddlewareTests()
|
||||
{
|
||||
private readonly Mock<IWireMockMiddlewareOptions> _optionsMock;
|
||||
private readonly Mock<IOwinResponseMapper> _responseMapperMock;
|
||||
_optionsMock = new Mock<IWireMockMiddlewareOptions>();
|
||||
_optionsMock.SetupAllProperties();
|
||||
|
||||
private readonly GlobalExceptionMiddleware _sut;
|
||||
_responseMapperMock = new Mock<IOwinResponseMapper>();
|
||||
_responseMapperMock.SetupAllProperties();
|
||||
_responseMapperMock.Setup(m => m.MapAsync(It.IsAny<ResponseMessage?>(), It.IsAny<HttpResponse>())).Returns(Task.FromResult(true));
|
||||
|
||||
public GlobalExceptionMiddlewareTests()
|
||||
{
|
||||
_optionsMock = new Mock<IWireMockMiddlewareOptions>();
|
||||
_optionsMock.SetupAllProperties();
|
||||
_sut = new GlobalExceptionMiddleware(null, _optionsMock.Object, _responseMapperMock.Object);
|
||||
}
|
||||
|
||||
_responseMapperMock = new Mock<IOwinResponseMapper>();
|
||||
_responseMapperMock.SetupAllProperties();
|
||||
_responseMapperMock.Setup(m => m.MapAsync(It.IsAny<ResponseMessage?>(), It.IsAny<IResponse>())).Returns(Task.FromResult(true));
|
||||
|
||||
_sut = new GlobalExceptionMiddleware(null, _optionsMock.Object, _responseMapperMock.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GlobalExceptionMiddleware_Invoke_NullAsNext_DoesNotInvokeNextAndDoesNotThrow()
|
||||
{
|
||||
// Act
|
||||
Check.ThatCode(() => _sut.Invoke(null)).DoesNotThrow();
|
||||
}
|
||||
[Fact]
|
||||
public void GlobalExceptionMiddleware_Invoke_NullAsNext_DoesNotInvokeNextAndDoesNotThrow()
|
||||
{
|
||||
// Act
|
||||
Check.ThatCode(() => _sut.Invoke(null)).DoesNotThrow();
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,14 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using WireMock.Models;
|
||||
using WireMock.Owin;
|
||||
using WireMock.Owin.Mappers;
|
||||
using WireMock.Util;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers;
|
||||
using System.Collections.Generic;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Admin.Requests;
|
||||
using WireMock.Settings;
|
||||
@@ -21,21 +17,12 @@ using WireMock.Handlers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.RequestBuilders;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
using WireMock.Owin.ActivityTracing;
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
#if NET452
|
||||
using Microsoft.Owin;
|
||||
using IContext = Microsoft.Owin.IOwinContext;
|
||||
using IRequest = Microsoft.Owin.IOwinRequest;
|
||||
using IResponse = Microsoft.Owin.IOwinResponse;
|
||||
#else
|
||||
using IContext = Microsoft.AspNetCore.Http.HttpContext;
|
||||
using IRequest = Microsoft.AspNetCore.Http.HttpRequest;
|
||||
using IResponse = Microsoft.AspNetCore.Http.HttpResponse;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
#endif
|
||||
|
||||
namespace WireMock.Net.Tests.Owin;
|
||||
|
||||
@@ -51,7 +38,7 @@ public class WireMockMiddlewareTests
|
||||
private readonly Mock<IMappingMatcher> _matcherMock;
|
||||
private readonly Mock<IMapping> _mappingMock;
|
||||
private readonly Mock<IRequestMatchResult> _requestMatchResultMock;
|
||||
private readonly Mock<IContext> _contextMock;
|
||||
private readonly Mock<HttpContext> _contextMock;
|
||||
|
||||
private readonly WireMockMiddleware _sut;
|
||||
|
||||
@@ -72,17 +59,18 @@ public class WireMockMiddlewareTests
|
||||
_requestMapperMock = new Mock<IOwinRequestMapper>();
|
||||
_requestMapperMock.SetupAllProperties();
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<IRequest>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
|
||||
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<HttpContext>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
|
||||
|
||||
_responseMapperMock = new Mock<IOwinResponseMapper>();
|
||||
_responseMapperMock.SetupAllProperties();
|
||||
_responseMapperMock.Setup(m => m.MapAsync(It.IsAny<ResponseMessage?>(), It.IsAny<IResponse>())).Returns(Task.FromResult(true));
|
||||
_responseMapperMock.Setup(m => m.MapAsync(It.IsAny<ResponseMessage?>(), It.IsAny<HttpResponse>())).Returns(Task.FromResult(true));
|
||||
|
||||
_matcherMock = new Mock<IMappingMatcher>();
|
||||
_matcherMock.SetupAllProperties();
|
||||
// _matcherMock.Setup(m => m.FindBestMatch(It.IsAny<RequestMessage>())).Returns((new MappingMatcherResult(), new MappingMatcherResult()));
|
||||
|
||||
_contextMock = new Mock<IContext>();
|
||||
_contextMock = new Mock<HttpContext>();
|
||||
_contextMock.SetupGet(c => c.Items).Returns(new Dictionary<object, object?>());
|
||||
|
||||
_mappingMock = new Mock<IMapping>();
|
||||
|
||||
@@ -110,7 +98,7 @@ public class WireMockMiddlewareTests
|
||||
_optionsMock.Verify(o => o.Logger.Warn(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once);
|
||||
|
||||
Expression<Func<ResponseMessage, bool>> match = r => (int)r.StatusCode! == 404 && ((StatusModel)r.BodyData!.BodyAsJson!).Status == "No matching mapping found";
|
||||
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<IResponse>()), Times.Once);
|
||||
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<HttpResponse>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -128,7 +116,7 @@ public class WireMockMiddlewareTests
|
||||
_optionsMock.Verify(o => o.Logger.Warn(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once);
|
||||
|
||||
Expression<Func<ResponseMessage, bool>> match = r => (int)r.StatusCode! == 404 && ((StatusModel)r.BodyData!.BodyAsJson!).Status == "No matching mapping found";
|
||||
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<IResponse>()), Times.Once);
|
||||
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<HttpResponse>()), Times.Once);
|
||||
|
||||
// Verify
|
||||
fileSystemHandlerMock.Verify(f => f.WriteUnmatchedRequest("98fae52e-76df-47d9-876f-2ee32e931d9b.LogEntry.json", It.IsAny<string>()));
|
||||
@@ -140,7 +128,7 @@ public class WireMockMiddlewareTests
|
||||
{
|
||||
// Assign
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1", null, new Dictionary<string, string[]>());
|
||||
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<IRequest>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
|
||||
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<HttpContext>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
|
||||
|
||||
_optionsMock.SetupGet(o => o.AuthenticationMatcher).Returns(new ExactMatcher());
|
||||
_mappingMock.SetupGet(m => m.IsAdminInterface).Returns(true);
|
||||
@@ -155,7 +143,7 @@ public class WireMockMiddlewareTests
|
||||
_optionsMock.Verify(o => o.Logger.Error(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once);
|
||||
|
||||
Expression<Func<ResponseMessage, bool>> match = r => (int?)r.StatusCode == 401;
|
||||
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<IResponse>()), Times.Once);
|
||||
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<HttpResponse>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -163,7 +151,7 @@ public class WireMockMiddlewareTests
|
||||
{
|
||||
// Assign
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1", null, new Dictionary<string, string[]> { { "h", new[] { "x" } } });
|
||||
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<IRequest>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
|
||||
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<HttpContext>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
|
||||
|
||||
_optionsMock.SetupGet(o => o.AuthenticationMatcher).Returns(new ExactMatcher());
|
||||
_mappingMock.SetupGet(m => m.IsAdminInterface).Returns(true);
|
||||
@@ -178,7 +166,7 @@ public class WireMockMiddlewareTests
|
||||
_optionsMock.Verify(o => o.Logger.Error(It.IsAny<string>(), It.IsAny<object[]>()), Times.Once);
|
||||
|
||||
Expression<Func<ResponseMessage, bool>> match = r => (int?)r.StatusCode == 401;
|
||||
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<IResponse>()), Times.Once);
|
||||
_responseMapperMock.Verify(m => m.MapAsync(It.Is(match), It.IsAny<HttpResponse>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -196,7 +184,7 @@ public class WireMockMiddlewareTests
|
||||
{
|
||||
// Assign
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1", null, new Dictionary<string, string[]>());
|
||||
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<IRequest>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
|
||||
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<HttpContext>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
|
||||
|
||||
_optionsMock.SetupGet(o => o.AuthenticationMatcher).Returns(new ExactMatcher());
|
||||
|
||||
@@ -245,7 +233,7 @@ public class WireMockMiddlewareTests
|
||||
{
|
||||
// Assign
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1", null, new Dictionary<string, string[]>());
|
||||
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<IRequest>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
|
||||
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<HttpContext>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
|
||||
|
||||
_optionsMock.SetupGet(o => o.AuthenticationMatcher).Returns(new ExactMatcher());
|
||||
|
||||
@@ -300,7 +288,7 @@ public class WireMockMiddlewareTests
|
||||
{
|
||||
// Arrange
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/__admin/health"), "GET", "::1");
|
||||
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<IRequest>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
|
||||
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<HttpContext>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
|
||||
|
||||
_optionsMock.SetupGet(o => o.ActivityTracingOptions).Returns(new ActivityTracingOptions
|
||||
{
|
||||
@@ -329,7 +317,7 @@ public class WireMockMiddlewareTests
|
||||
{
|
||||
// Arrange
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/api/orders"), "GET", "::1");
|
||||
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<IRequest>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
|
||||
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<HttpContext>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
|
||||
|
||||
_optionsMock.SetupGet(o => o.ActivityTracingOptions).Returns(new ActivityTracingOptions
|
||||
{
|
||||
@@ -358,7 +346,7 @@ public class WireMockMiddlewareTests
|
||||
{
|
||||
// Arrange
|
||||
var request = new RequestMessage(new UrlDetails("http://localhost/api/orders"), "GET", "::1");
|
||||
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<IRequest>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
|
||||
_requestMapperMock.Setup(m => m.MapAsync(It.IsAny<HttpContext>(), It.IsAny<IWireMockMiddlewareOptions>())).ReturnsAsync(request);
|
||||
|
||||
_optionsMock.SetupGet(o => o.ActivityTracingOptions).Returns((ActivityTracingOptions?)null);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user