diff --git a/WireMock.Net Solution.sln b/WireMock.Net Solution.sln
index 40211eb8..49772701 100644
--- a/WireMock.Net Solution.sln
+++ b/WireMock.Net Solution.sln
@@ -106,6 +106,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.xUnit", "src\W
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.NET6.WithCertificate", "examples\WireMock.Net.Console.NET6.WithCertificate\WireMock.Net.Console.NET6.WithCertificate.csproj", "{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueExample", "examples\WireMockAzureQueueExample\WireMockAzureQueueExample.csproj", "{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueProxy", "examples\WireMockAzureQueueProxy\WireMockAzureQueueProxy.csproj", "{ADB557D8-D66B-4387-912B-3F73E290B478}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -252,6 +256,14 @@ Global
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {ADB557D8-D66B-4387-912B-3F73E290B478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {ADB557D8-D66B-4387-912B-3F73E290B478}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {ADB557D8-D66B-4387-912B-3F73E290B478}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {ADB557D8-D66B-4387-912B-3F73E290B478}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -294,6 +306,8 @@ Global
{670C7562-C154-442E-A249-7D26849BCD13} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{0DE0954F-8C00-4E8D-B94A-4361FC1CBE44} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
+ {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
+ {ADB557D8-D66B-4387-912B-3F73E290B478} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
diff --git a/examples/WireMockAzureQueueExample/.gitignore b/examples/WireMockAzureQueueExample/.gitignore
new file mode 100644
index 00000000..ff5b00c5
--- /dev/null
+++ b/examples/WireMockAzureQueueExample/.gitignore
@@ -0,0 +1,264 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# Azure Functions localsettings file
+local.settings.json
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# DNX
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+#*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignoreable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+node_modules/
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
\ No newline at end of file
diff --git a/examples/WireMockAzureQueueExample/Function1.cs b/examples/WireMockAzureQueueExample/Function1.cs
new file mode 100644
index 00000000..630b4866
--- /dev/null
+++ b/examples/WireMockAzureQueueExample/Function1.cs
@@ -0,0 +1,13 @@
+using Microsoft.Azure.WebJobs;
+using Microsoft.Extensions.Logging;
+
+namespace WireMockAzureQueueExample;
+
+public class Function1
+{
+ [FunctionName("Function1")]
+ public void Run([QueueTrigger("myqueue-items", Connection = "ConnectionStringToWireMock")]string myQueueItem, ILogger log)
+ {
+ log.LogWarning($"C# Queue trigger function processed: {myQueueItem}");
+ }
+}
\ No newline at end of file
diff --git a/examples/WireMockAzureQueueExample/Properties/launchSettings.json b/examples/WireMockAzureQueueExample/Properties/launchSettings.json
new file mode 100644
index 00000000..c8287fab
--- /dev/null
+++ b/examples/WireMockAzureQueueExample/Properties/launchSettings.json
@@ -0,0 +1,9 @@
+{
+ "profiles": {
+ "WireMockAzureQueueExample": {
+ "commandName": "Project",
+ "commandLineArgs": "--port 7290",
+ "launchBrowser": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/WireMockAzureQueueExample/Properties/serviceDependencies.json b/examples/WireMockAzureQueueExample/Properties/serviceDependencies.json
new file mode 100644
index 00000000..4003a114
--- /dev/null
+++ b/examples/WireMockAzureQueueExample/Properties/serviceDependencies.json
@@ -0,0 +1,14 @@
+{
+ "dependencies": {
+ "appInsights1": {
+ "type": "appInsights"
+ },
+ "secrets1": {
+ "type": "secrets"
+ },
+ "storage1": {
+ "type": "storage",
+ "connectionId": "AzureWebJobsStorage"
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/WireMockAzureQueueExample/Properties/serviceDependencies.local.json b/examples/WireMockAzureQueueExample/Properties/serviceDependencies.local.json
new file mode 100644
index 00000000..c02a4e56
--- /dev/null
+++ b/examples/WireMockAzureQueueExample/Properties/serviceDependencies.local.json
@@ -0,0 +1,16 @@
+{
+ "dependencies": {
+ "appInsights1": {
+ "type": "appInsights.sdk"
+ },
+ "secrets1": {
+ "type": "secrets.user"
+ },
+ "storage1": {
+ "secretStore": null,
+ "resourceId": null,
+ "type": "storage.emulator",
+ "connectionId": "AzureWebJobsStorage"
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/WireMockAzureQueueExample/WireMockAzureQueueExample.csproj b/examples/WireMockAzureQueueExample/WireMockAzureQueueExample.csproj
new file mode 100644
index 00000000..e992e558
--- /dev/null
+++ b/examples/WireMockAzureQueueExample/WireMockAzureQueueExample.csproj
@@ -0,0 +1,25 @@
+
+
+ net6.0
+ v4
+ bb7d8355-68c4-4f81-8c2d-6cdd80cd7602
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+ Never
+
+
+
diff --git a/examples/WireMockAzureQueueExample/host.json b/examples/WireMockAzureQueueExample/host.json
new file mode 100644
index 00000000..beb2e402
--- /dev/null
+++ b/examples/WireMockAzureQueueExample/host.json
@@ -0,0 +1,11 @@
+{
+ "version": "2.0",
+ "logging": {
+ "applicationInsights": {
+ "samplingSettings": {
+ "isEnabled": true,
+ "excludedTypes": "Request"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/WireMockAzureQueueProxy/Program.cs b/examples/WireMockAzureQueueProxy/Program.cs
new file mode 100644
index 00000000..a91efe53
--- /dev/null
+++ b/examples/WireMockAzureQueueProxy/Program.cs
@@ -0,0 +1,40 @@
+using Newtonsoft.Json;
+using WireMock.Logging;
+using WireMock.Server;
+using WireMock.Settings;
+
+namespace WireMockAzureQueueProxy;
+
+static class Program
+{
+ static void Main(params string[] args)
+ {
+ var server = WireMockServer.Start(new WireMockServerSettings
+ {
+ Logger = new WireMockConsoleLogger(),
+ Urls = new[] { "http://localhost:20001/" },
+ StartAdminInterface = false,
+ ReadStaticMappings = true,
+ WatchStaticMappings = true,
+ WatchStaticMappingsInSubdirectories = true,
+ //ProxyAndRecordSettings = new ProxyAndRecordSettings
+ //{
+ // Url = "http://127.0.0.1:10001",
+ // SaveMapping = true,
+ // SaveMappingToFile = true,
+ // AppendGuidToSavedMappingFile = true
+ //}
+ });
+
+ System.Console.WriteLine("Press any key to stop the server");
+ System.Console.ReadKey();
+ server.Stop();
+
+ System.Console.WriteLine("Displaying all requests");
+ var allRequests = server.LogEntries;
+ System.Console.WriteLine(JsonConvert.SerializeObject(allRequests, Formatting.Indented));
+
+ System.Console.WriteLine("Press any key to quit");
+ System.Console.ReadKey();
+ }
+}
\ No newline at end of file
diff --git a/examples/WireMockAzureQueueProxy/WireMockAzureQueueProxy.csproj b/examples/WireMockAzureQueueProxy/WireMockAzureQueueProxy.csproj
new file mode 100644
index 00000000..f44be19a
--- /dev/null
+++ b/examples/WireMockAzureQueueProxy/WireMockAzureQueueProxy.csproj
@@ -0,0 +1,23 @@
+
+
+
+ Exe
+ net6.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
\ No newline at end of file
diff --git a/examples/WireMockAzureQueueProxy/__admin/mappings/DELETE _devstoreaccount1_myqueue-items_messages_41b2aadc-d6ea-4c3c-ae20-2ae72eb08d88.json b/examples/WireMockAzureQueueProxy/__admin/mappings/DELETE _devstoreaccount1_myqueue-items_messages_41b2aadc-d6ea-4c3c-ae20-2ae72eb08d88.json
new file mode 100644
index 00000000..2bd09ebe
--- /dev/null
+++ b/examples/WireMockAzureQueueProxy/__admin/mappings/DELETE _devstoreaccount1_myqueue-items_messages_41b2aadc-d6ea-4c3c-ae20-2ae72eb08d88.json
@@ -0,0 +1,44 @@
+{
+ "Guid": "b4a2ff02-fb7f-4496-8c04-9aafc4f5f8f7",
+ "Title": "Proxy Mapping for DELETE /devstoreaccount1/myqueue-items/messages/41b2aadc-d6ea-4c3c-ae20-2ae72eb08d88",
+ "Description": "Proxy Mapping for DELETE /devstoreaccount1/myqueue-items/messages/41b2aadc-d6ea-4c3c-ae20-2ae72eb08d88",
+ "Request": {
+ "Path": {
+ "Matchers": [
+ {
+ "Name": "WildcardMatcher",
+ "Pattern": "/devstoreaccount1/myqueue-items/messages/41b2aadc-d6ea-4c3c-ae20-2ae72eb08d88",
+ "IgnoreCase": false
+ }
+ ]
+ },
+ "Methods": [
+ "DELETE"
+ ],
+ "Params": [
+ {
+ "Name": "popreceipt",
+ "Matchers": [
+ {
+ "Name": "WildcardMatcher",
+ "Pattern": "*",
+ "IgnoreCase": false
+ }
+ ]
+ }
+ ]
+ },
+ "Response": {
+ "StatusCode": 204,
+ "Headers": {
+ "Server": "Azurite-Queue/3.19.0",
+ "x-ms-client-request-id": "{{request.headers.x-ms-client-request-id}}",
+ "x-ms-request-id": "{{Random Type=\"Guid\"}}",
+ "x-ms-version": "2021-10-04",
+ "Date": "{{DateTime.Now \"ddd, dd MMM yyy HH’:’mm’:’ss ‘GMT’\"}}",
+ "Connection": "keep-alive",
+ "Keep-Alive": "timeout=5"
+ },
+ "UseTransformer": true
+ }
+}
\ No newline at end of file
diff --git a/examples/WireMockAzureQueueProxy/__admin/mappings/GET No Messages _devstoreaccount1_myqueue-items_messages.json b/examples/WireMockAzureQueueProxy/__admin/mappings/GET No Messages _devstoreaccount1_myqueue-items_messages.json
new file mode 100644
index 00000000..e42076cd
--- /dev/null
+++ b/examples/WireMockAzureQueueProxy/__admin/mappings/GET No Messages _devstoreaccount1_myqueue-items_messages.json
@@ -0,0 +1,60 @@
+{
+ "Scenario": "AzureQueue Get Messages",
+ "WhenStateIs": "No more messages",
+ "SetStateTo": "No more messages",
+ "Guid": "4c871968-29ee-472b-a548-170444d4cc3e",
+ "Title": "Proxy Mapping for GET NO MSG /devstoreaccount1/myqueue-items/messages",
+ "Description": "Proxy Mapping for GET NO MSG /devstoreaccount1/myqueue-items/messages",
+ "Request": {
+ "Path": {
+ "Matchers": [
+ {
+ "Name": "WildcardMatcher",
+ "Pattern": "/devstoreaccount1/myqueue-items/messages",
+ "IgnoreCase": false
+ }
+ ]
+ },
+ "Methods": [
+ "GET"
+ ],
+ "Params": [
+ {
+ "Name": "numofmessages",
+ "Matchers": [
+ {
+ "Name": "ExactMatcher",
+ "Pattern": "16",
+ "IgnoreCase": false
+ }
+ ]
+ },
+ {
+ "Name": "visibilitytimeout",
+ "Matchers": [
+ {
+ "Name": "ExactMatcher",
+ "Pattern": "600",
+ "IgnoreCase": false
+ }
+ ]
+ }
+ ]
+ },
+ "Response": {
+ "StatusCode": 200,
+ "Body": "",
+ "Headers": {
+ "Content-Type": "application/xml",
+ "Server": "Azurite-Queue/3.19.0",
+ "x-ms-client-request-id": "{{request.headers.x-ms-client-request-id}}",
+ "x-ms-request-id": "{{Random Type=\"Guid\"}}",
+ "x-ms-version": "2021-10-04",
+ "Date": "{{DateTime.Now \"ddd, dd MMM yyy HH’:’mm’:’ss ‘GMT’\"}}",
+ "Connection": "keep-alive",
+ "Keep-Alive": "timeout=5",
+ "Transfer-Encoding": "chunked"
+ },
+ "UseTransformer": true
+ }
+}
\ No newline at end of file
diff --git a/examples/WireMockAzureQueueProxy/__admin/mappings/GET With Messages _devstoreaccount1_myqueue-items_messages.json b/examples/WireMockAzureQueueProxy/__admin/mappings/GET With Messages _devstoreaccount1_myqueue-items_messages.json
new file mode 100644
index 00000000..c985d9fc
--- /dev/null
+++ b/examples/WireMockAzureQueueProxy/__admin/mappings/GET With Messages _devstoreaccount1_myqueue-items_messages.json
@@ -0,0 +1,59 @@
+{
+ "Scenario": "AzureQueue Get Messages",
+ "SetStateTo": "No more messages",
+ "Guid": "da9c6799-fbf8-41b6-8933-0df50f821ebb",
+ "Title": "Proxy Mapping for GET /devstoreaccount1/myqueue-items/messages",
+ "Description": "Proxy Mapping for GET /devstoreaccount1/myqueue-items/messages",
+ "Request": {
+ "Path": {
+ "Matchers": [
+ {
+ "Name": "WildcardMatcher",
+ "Pattern": "/devstoreaccount1/myqueue-items/messages",
+ "IgnoreCase": false
+ }
+ ]
+ },
+ "Methods": [
+ "GET"
+ ],
+ "Params": [
+ {
+ "Name": "numofmessages",
+ "Matchers": [
+ {
+ "Name": "WildcardMatcher",
+ "Pattern": "*",
+ "IgnoreCase": true
+ }
+ ]
+ },
+ {
+ "Name": "visibilitytimeout",
+ "Matchers": [
+ {
+ "Name": "WildcardMatcher",
+ "Pattern": "*",
+ "IgnoreCase": false
+ }
+ ]
+ }
+ ]
+ },
+ "Response": {
+ "StatusCode": 200,
+ "Body": "41b2aadc-d6ea-4c3c-ae20-2ae72eb08d88Sat, 29 Oct 2022 07:11:40 GMTSat, 31 Dec 2022 07:11:40 GMTMjlPY3QyMDIyMDc6MTE6NDAyMjU2Sat, 29 Oct 2022 07:21:40 GMT1c3RlZg==",
+ "Headers": {
+ "Content-Type": "application/xml",
+ "Server": "Azurite-Queue/3.19.0",
+ "x-ms-client-request-id": "{{request.headers.x-ms-client-request-id}}",
+ "x-ms-request-id": "{{Random Type=\"Guid\"}}",
+ "x-ms-version": "2021-10-04",
+ "Date": "{{DateTime.Now \"ddd, dd MMM yyy HH’:’mm’:’ss ‘GMT’\"}}",
+ "Connection": "keep-alive",
+ "Keep-Alive": "timeout=5",
+ "Transfer-Encoding": "chunked"
+ },
+ "UseTransformer": true
+ }
+}
\ No newline at end of file
diff --git a/examples/WireMockAzureQueueProxy/__admin/mappings/HEAD _devstoreaccount1_myqueue-items.json b/examples/WireMockAzureQueueProxy/__admin/mappings/HEAD _devstoreaccount1_myqueue-items.json
new file mode 100644
index 00000000..9a2cc355
--- /dev/null
+++ b/examples/WireMockAzureQueueProxy/__admin/mappings/HEAD _devstoreaccount1_myqueue-items.json
@@ -0,0 +1,46 @@
+{
+ "Guid": "17c7a389-98e1-4383-975d-54c82d1e3860",
+ "Title": "Proxy Mapping for HEAD /devstoreaccount1/myqueue-items",
+ "Description": "Proxy Mapping for HEAD /devstoreaccount1/myqueue-items",
+ "Request": {
+ "Path": {
+ "Matchers": [
+ {
+ "Name": "WildcardMatcher",
+ "Pattern": "/devstoreaccount1/myqueue-items",
+ "IgnoreCase": false
+ }
+ ]
+ },
+ "Methods": [
+ "HEAD"
+ ],
+ "Params": [
+ {
+ "Name": "comp",
+ "Matchers": [
+ {
+ "Name": "ExactMatcher",
+ "Pattern": "metadata",
+ "IgnoreCase": false
+ }
+ ]
+ }
+ ]
+ },
+ "Response": {
+ "StatusCode": 200,
+ "Body": "",
+ "Headers": {
+ "Server": "Azurite-Queue/3.19.0",
+ "x-ms-client-request-id": "{{request.headers.x-ms-client-request-id}}",
+ "x-ms-approximate-messages-count": "0",
+ "x-ms-request-id": "{{Random Type=\"Guid\"}}",
+ "x-ms-version": "2021-10-04",
+ "Date": "{{DateTime.Now \"ddd, dd MMM yyy HH’:’mm’:’ss ‘GMT’\"}}",
+ "Connection": "keep-alive",
+ "Keep-Alive": "timeout=5"
+ },
+ "UseTransformer": true
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.Abstractions/Admin/Settings/ProxyAndRecordSettingsModel.cs b/src/WireMock.Net.Abstractions/Admin/Settings/ProxyAndRecordSettingsModel.cs
index 36d4b03e..1867dd9d 100644
--- a/src/WireMock.Net.Abstractions/Admin/Settings/ProxyAndRecordSettingsModel.cs
+++ b/src/WireMock.Net.Abstractions/Admin/Settings/ProxyAndRecordSettingsModel.cs
@@ -1,67 +1,71 @@
-namespace WireMock.Admin.Settings
+namespace WireMock.Admin.Settings;
+
+[FluentBuilder.AutoGenerateBuilder]
+public class ProxyAndRecordSettingsModel
{
- [FluentBuilder.AutoGenerateBuilder]
- public class ProxyAndRecordSettingsModel
- {
- ///
- /// The clientCertificate thumbprint or subject name fragment to use.
- /// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
- ///
- public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
+ ///
+ /// The clientCertificate thumbprint or subject name fragment to use.
+ /// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
+ ///
+ public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
- ///
- /// Defines the WebProxySettings.
- ///
- public WebProxySettingsModel WebProxySettings { get; set; }
+ ///
+ /// Defines the WebProxySettings.
+ ///
+ public WebProxySettingsModel WebProxySettings { get; set; }
- ///
- /// Proxy requests should follow redirection (30x).
- ///
- public bool? AllowAutoRedirect { get; set; }
+ ///
+ /// Proxy requests should follow redirection (30x).
+ ///
+ public bool? AllowAutoRedirect { get; set; }
- ///
- /// The URL to proxy.
- ///
- public string Url { get; set; }
+ ///
+ /// The URL to proxy.
+ ///
+ public string Url { get; set; }
- ///
- /// Save the mapping for each request/response to the internal Mappings.
- ///
- public bool SaveMapping { get; set; }
+ ///
+ /// Save the mapping for each request/response to the internal Mappings.
+ ///
+ public bool SaveMapping { get; set; }
- ///
- /// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
- ///
- public bool SaveMappingToFile { get; set; }
+ ///
+ /// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
+ ///
+ public bool SaveMappingToFile { get; set; }
- ///
- /// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
- /// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
- ///
- public string SaveMappingForStatusCodePattern { get; set; } = "*";
+ ///
+ /// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
+ /// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
+ ///
+ public string SaveMappingForStatusCodePattern { get; set; } = "*";
- ///
- /// Defines a list from headers which will be excluded from the saved mappings.
- ///
- public string[] ExcludedHeaders { get; set; }
+ ///
+ /// Defines a list from headers which will be excluded from the saved mappings.
+ ///
+ public string[] ExcludedHeaders { get; set; }
- ///
- /// Defines a list of cookies which will be excluded from the saved mappings.
- ///
- public string[] ExcludedCookies { get; set; }
+ ///
+ /// Defines a list of cookies which will be excluded from the saved mappings.
+ ///
+ public string[] ExcludedCookies { get; set; }
- ///
- /// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to true).
- ///
- // public bool PreferProxyMapping { get; set; }
+ ///
+ /// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to true).
+ ///
+ // public bool PreferProxyMapping { get; set; }
- ///
- /// When SaveMapping is set to true, this setting can be used to control the behavior of the generated request matchers for the new mapping.
- /// - false, the default matchers will be used.
- /// - true, the defined mappings in the request wil be used for the new mapping.
- ///
- /// Default value is false.
- ///
- public bool UseDefinedRequestMatchers { get; set; }
- }
+ ///
+ /// When SaveMapping is set to true, this setting can be used to control the behavior of the generated request matchers for the new mapping.
+ /// - false, the default matchers will be used.
+ /// - true, the defined mappings in the request wil be used for the new mapping.
+ ///
+ /// Default value is false.
+ ///
+ public bool UseDefinedRequestMatchers { get; set; }
+
+ ///
+ /// Append an unique GUID to the filename from the saved mapping file.
+ ///
+ public bool AppendGuidToSavedMappingFile { get; set; }
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Http/HttpResponseMessageHelper.cs b/src/WireMock.Net/Http/HttpResponseMessageHelper.cs
index 54934c63..75cf9642 100644
--- a/src/WireMock.Net/Http/HttpResponseMessageHelper.cs
+++ b/src/WireMock.Net/Http/HttpResponseMessageHelper.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using WireMock.Util;
@@ -35,15 +36,18 @@ internal static class HttpResponseMessageHelper
contentEncodingHeader = headers.First(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentEncoding, StringComparison.OrdinalIgnoreCase)).Value;
}
- var bodyParserSettings = new BodyParserSettings
+ if (httpResponseMessage.StatusCode != HttpStatusCode.NoContent) // A body is not allowed for 204.
{
- Stream = stream,
- ContentType = contentTypeHeader?.FirstOrDefault(),
- DeserializeJson = deserializeJson,
- ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
- DecompressGZipAndDeflate = decompressGzipAndDeflate
- };
- responseMessage.BodyData = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
+ var bodyParserSettings = new BodyParserSettings
+ {
+ Stream = stream,
+ ContentType = contentTypeHeader?.FirstOrDefault(),
+ DeserializeJson = deserializeJson,
+ ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
+ DecompressGZipAndDeflate = decompressGzipAndDeflate
+ };
+ responseMessage.BodyData = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
+ }
}
foreach (var header in headers)
diff --git a/src/WireMock.Net/Owin/Mappers/OwinResponseMapper.cs b/src/WireMock.Net/Owin/Mappers/OwinResponseMapper.cs
index cbfd92d7..1b6c920f 100644
--- a/src/WireMock.Net/Owin/Mappers/OwinResponseMapper.cs
+++ b/src/WireMock.Net/Owin/Mappers/OwinResponseMapper.cs
@@ -99,7 +99,14 @@ namespace WireMock.Owin.Mappers
if (bytes != null)
{
- await response.Body.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
+ try
+ {
+ await response.Body.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _options.Logger.Warn("Error writing response body. Exception : {0}", ex);
+ }
}
}
diff --git a/src/WireMock.Net/Serialization/MappingToFileSaver.cs b/src/WireMock.Net/Serialization/MappingToFileSaver.cs
index 5c4d98f9..ce09a909 100644
--- a/src/WireMock.Net/Serialization/MappingToFileSaver.cs
+++ b/src/WireMock.Net/Serialization/MappingToFileSaver.cs
@@ -13,11 +13,8 @@ internal class MappingToFileSaver
public MappingToFileSaver(WireMockServerSettings settings, MappingConverter mappingConverter)
{
- Guard.NotNull(settings);
- Guard.NotNull(mappingConverter);
-
- _settings = settings;
- _mappingConverter = mappingConverter;
+ _settings = Guard.NotNull(settings);
+ _mappingConverter = Guard.NotNull(mappingConverter);
}
public void SaveMappingToFile(IMapping mapping, string? folder = null)
@@ -30,17 +27,31 @@ internal class MappingToFileSaver
}
var model = _mappingConverter.ToMappingModel(mapping);
- string filename = (!string.IsNullOrEmpty(mapping.Title) ? SanitizeFileName(mapping.Title) : mapping.Guid.ToString()) + ".json";
- string path = Path.Combine(folder, filename);
+ var filename = BuildSanitizedFileName(mapping);
+ var path = Path.Combine(folder, filename);
- _settings.Logger.Info("Saving Mapping file {0}", filename);
+ _settings.Logger.Info("Saving Mapping file {0}", path);
_settings.FileSystemHandler.WriteMappingFile(path, JsonConvert.SerializeObject(model, JsonSerializationConstants.JsonSerializerSettingsDefault));
}
- private static string SanitizeFileName(string name, char replaceChar = '_')
+ private string BuildSanitizedFileName(IMapping mapping, char replaceChar = '_')
{
- return Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, replaceChar));
+ string name;
+ if (!string.IsNullOrEmpty(mapping.Title))
+ {
+ name = mapping.Title!;
+ if (_settings.ProxyAndRecordSettings?.AppendGuidToSavedMappingFile == true)
+ {
+ name += $"{replaceChar}{mapping.Guid}";
+ }
+ }
+ else
+ {
+ name = mapping.Guid.ToString();
+ }
+
+ return $"{Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, replaceChar))}.json";
}
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Server/WireMockServer.Admin.cs b/src/WireMock.Net/Server/WireMockServer.Admin.cs
index 9e0e0424..8fd41124 100644
--- a/src/WireMock.Net/Server/WireMockServer.Admin.cs
+++ b/src/WireMock.Net/Server/WireMockServer.Admin.cs
@@ -3,9 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
-using System.Net.Http;
using System.Text;
-using System.Threading.Tasks;
using JetBrains.Annotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -18,11 +16,9 @@ using WireMock.Http;
using WireMock.Logging;
using WireMock.Matchers;
using WireMock.Matchers.Request;
-using WireMock.Proxy;
using WireMock.RequestBuilders;
using WireMock.ResponseProviders;
using WireMock.Serialization;
-using WireMock.Settings;
using WireMock.Types;
using WireMock.Util;
@@ -208,62 +204,6 @@ public partial class WireMockServer
}
#endregion
- #region Proxy and Record
- private HttpClient? _httpClientForProxy;
-
- private void InitProxyAndRecord(WireMockServerSettings settings)
- {
- if (settings.ProxyAndRecordSettings == null)
- {
- _httpClientForProxy = null;
- DeleteMapping(ProxyMappingGuid);
- return;
- }
-
- _httpClientForProxy = HttpClientBuilder.Build(settings.ProxyAndRecordSettings);
-
- var proxyRespondProvider = Given(Request.Create().WithPath("/*").UsingAnyMethod()).WithGuid(ProxyMappingGuid).WithTitle("Default Proxy Mapping on /*");
- if (settings.StartAdminInterface == true)
- {
- proxyRespondProvider.AtPriority(WireMockConstants.ProxyPriority);
- }
-
- proxyRespondProvider.RespondWith(new ProxyAsyncResponseProvider(ProxyAndRecordAsync, settings));
- }
-
- private async Task ProxyAndRecordAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
- {
- var requestUri = new Uri(requestMessage.Url);
- var proxyUri = new Uri(settings.ProxyAndRecordSettings!.Url);
- var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery);
-
- var proxyHelper = new ProxyHelper(settings);
-
- var (responseMessage, mapping) = await proxyHelper.SendAsync(
- null,
- _settings.ProxyAndRecordSettings!,
- _httpClientForProxy!,
- requestMessage,
- proxyUriWithRequestPathAndQuery.AbsoluteUri
- ).ConfigureAwait(false);
-
- if (mapping != null)
- {
- if (settings.ProxyAndRecordSettings.SaveMapping)
- {
- _options.Mappings.TryAdd(mapping.Guid, mapping);
- }
-
- if (settings.ProxyAndRecordSettings.SaveMappingToFile)
- {
- _mappingToFileSaver.SaveMappingToFile(mapping);
- }
- }
-
- return responseMessage;
- }
- #endregion
-
#region Settings
private IResponseMessage SettingsGet(IRequestMessage requestMessage)
{
diff --git a/src/WireMock.Net/Server/WireMockServer.Proxy.cs b/src/WireMock.Net/Server/WireMockServer.Proxy.cs
new file mode 100644
index 00000000..b3d05599
--- /dev/null
+++ b/src/WireMock.Net/Server/WireMockServer.Proxy.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Net.Http;
+using System.Threading.Tasks;
+using WireMock.Constants;
+using WireMock.Http;
+using WireMock.Proxy;
+using WireMock.RequestBuilders;
+using WireMock.ResponseProviders;
+using WireMock.Settings;
+
+namespace WireMock.Server;
+
+public partial class WireMockServer
+{
+ private HttpClient? _httpClientForProxy;
+
+ private void InitProxyAndRecord(WireMockServerSettings settings)
+ {
+ if (settings.ProxyAndRecordSettings == null)
+ {
+ _httpClientForProxy = null;
+ DeleteMapping(ProxyMappingGuid);
+ return;
+ }
+
+ _httpClientForProxy = HttpClientBuilder.Build(settings.ProxyAndRecordSettings);
+
+ var proxyRespondProvider = Given(Request.Create().WithPath("/*").UsingAnyMethod()).WithGuid(ProxyMappingGuid).WithTitle("Default Proxy Mapping on /*");
+ if (settings.StartAdminInterface == true)
+ {
+ proxyRespondProvider.AtPriority(WireMockConstants.ProxyPriority);
+ }
+
+ proxyRespondProvider.RespondWith(new ProxyAsyncResponseProvider(ProxyAndRecordAsync, settings));
+ }
+
+ private async Task ProxyAndRecordAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
+ {
+ var requestUri = new Uri(requestMessage.Url);
+ var proxyUri = new Uri(settings.ProxyAndRecordSettings!.Url);
+ var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery);
+
+ var proxyHelper = new ProxyHelper(settings);
+
+ var (responseMessage, mapping) = await proxyHelper.SendAsync(
+ null,
+ _settings.ProxyAndRecordSettings!,
+ _httpClientForProxy!,
+ requestMessage,
+ proxyUriWithRequestPathAndQuery.AbsoluteUri
+ ).ConfigureAwait(false);
+
+ if (mapping != null)
+ {
+ if (settings.ProxyAndRecordSettings.SaveMapping)
+ {
+ _options.Mappings.TryAdd(mapping.Guid, mapping);
+ }
+
+ if (settings.ProxyAndRecordSettings.SaveMappingToFile)
+ {
+ _mappingToFileSaver.SaveMappingToFile(mapping);
+ }
+ }
+
+ return responseMessage;
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs
index 3ae30763..c9af7fbd 100644
--- a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs
+++ b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs
@@ -58,4 +58,9 @@ public class ProxyAndRecordSettings : HttpClientSettings
/// Default value is false.
///
public bool UseDefinedRequestMatchers { get; set; }
+
+ ///
+ /// Append an unique GUID to the filename from the saved mapping file.
+ ///
+ public bool AppendGuidToSavedMappingFile { get; set; }
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs
index 3f875998..1e1b6625 100644
--- a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs
+++ b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs
@@ -94,6 +94,7 @@ public static class WireMockServerSettingsParser
SaveMappingForStatusCodePattern = parser.GetStringValue("SaveMappingForStatusCodePattern", "*"),
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
UseDefinedRequestMatchers = parser.GetBoolValue(nameof(ProxyAndRecordSettings.UseDefinedRequestMatchers)),
+ AppendGuidToSavedMappingFile = parser.GetBoolValue(nameof(ProxyAndRecordSettings.AppendGuidToSavedMappingFile)),
Url = proxyUrl!
};