diff --git a/thirdparty/SimpleRestServices/README.md b/thirdparty/SimpleRestServices/README.md
new file mode 100644
index 0000000000..6d210364f5
--- /dev/null
+++ b/thirdparty/SimpleRestServices/README.md
@@ -0,0 +1,6 @@
+SimpleRestServices
+==================
+
+A simple set of REST server and client helpers
+
+Click here for License Info
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/appveyor.yml b/thirdparty/SimpleRestServices/appveyor.yml
new file mode 100644
index 0000000000..58320dc0ab
--- /dev/null
+++ b/thirdparty/SimpleRestServices/appveyor.yml
@@ -0,0 +1,11 @@
+version: 1.0.{build}
+init:
+- git config --global core.autocrlf true
+build_script:
+- cd build
+- powershell -Command .\build.ps1 -VisualStudioVersion "12.0" -InstallSHFB -Verbosity minimal -Logger "${env:ProgramFiles}\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
+- cd ..
+after_build:
+- cd build
+- powershell -Command .\appveyor-deploy-docs.ps1
+- cd ..
diff --git a/thirdparty/SimpleRestServices/build/appveyor-deploy-docs.ps1 b/thirdparty/SimpleRestServices/build/appveyor-deploy-docs.ps1
new file mode 100644
index 0000000000..16bf82e643
--- /dev/null
+++ b/thirdparty/SimpleRestServices/build/appveyor-deploy-docs.ps1
@@ -0,0 +1,17 @@
+$DocsPath = "..\src\Documentation\Help"
+
+# make sure the script was run from the expected path
+If (!(Test-Path $DocsPath)) {
+ $host.ui.WriteErrorLine('The script was run from an invalid working directory.')
+ Exit 1
+}
+
+function Upload-Folder() {
+ param([string]$LocalPath, [string]$ArtifactName)
+
+ [Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem')
+ [System.IO.Compression.ZipFile]::CreateFromDirectory((Join-Path (pwd) $LocalPath), (Join-Path (pwd) $ArtifactName))
+ Push-AppveyorArtifact (Join-Path (pwd) $ArtifactName)
+}
+
+Upload-Folder -LocalPath "$DocsPath\v4.0" -ArtifactName "docs.zip"
diff --git a/thirdparty/SimpleRestServices/build/build.ps1 b/thirdparty/SimpleRestServices/build/build.ps1
new file mode 100644
index 0000000000..f61d20c437
--- /dev/null
+++ b/thirdparty/SimpleRestServices/build/build.ps1
@@ -0,0 +1,133 @@
+param (
+ [switch]$Debug,
+ [string]$VisualStudioVersion = "12.0",
+ [switch]$NoDocs,
+ [string]$Verbosity = "normal",
+ [string]$Logger,
+ [switch]$InstallSHFB
+)
+
+# build the solution
+$SolutionPath = "..\src\SimpleRestServices.sln"
+
+# make sure the script was run from the expected path
+if (!(Test-Path $SolutionPath)) {
+ $host.ui.WriteErrorLine('The script was run from an invalid working directory.')
+ exit 1
+}
+
+. .\version.ps1
+
+If ($Debug) {
+ $BuildConfig = 'Debug'
+} Else {
+ $BuildConfig = 'Release'
+}
+
+If ($Version.Contains('-')) {
+ $KeyConfiguration = 'Dev'
+} Else {
+ $KeyConfiguration = 'Final'
+}
+
+If ($NoDocs -and -not $Debug) {
+ $SolutionBuildConfig = $BuildConfig + 'NoDocs'
+} Else {
+ $SolutionBuildConfig = $BuildConfig
+}
+
+# build the main project
+$nuget = '..\src\.nuget\NuGet.exe'
+
+if ($VisualStudioVersion -eq '4.0') {
+ $msbuild = "$env:windir\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe"
+} Else {
+ $msbuild = "${env:ProgramFiles(x86)}\MSBuild\$VisualStudioVersion\Bin\MSBuild.exe"
+}
+
+# Attempt to restore packages up to 3 times, to improve resiliency to connection timeouts and access denied errors.
+$maxAttempts = 3
+For ($attempt = 0; $attempt -lt $maxAttempts; $attempt++) {
+ &$nuget 'restore' $SolutionPath
+ If ($?) {
+ Break
+ } ElseIf (($attempt + 1) -eq $maxAttempts) {
+ $host.ui.WriteErrorLine('Failed to restore required NuGet packages, aborting!')
+ exit $LASTEXITCODE
+ }
+}
+
+If ($InstallSHFB) {
+ # This is the NuGet package name for the SHFB package
+ $SHFBPackageName = 'EWSoftware.SHFB'
+ # This is the version according to the NuGet package itself
+ $SHFBVersion = '2014.11.22.0'
+
+ $SHFBPackagePath = "..\.shfb\$SHFBPackageName.$SHFBVersion.nupkg"
+ If (-not (Test-Path $SHFBPackagePath)) {
+ If (-not (Test-Path '..\.shfb')) {
+ mkdir '..\.shfb'
+ }
+
+ # This is the release name on GitHub where the NuGet package is attached
+ $SHFBRelease = 'v2014.11.22.0-beta'
+
+ $SHFBInstallerSource = "https://github.com/tunnelvisionlabs/SHFB/releases/download/$SHFBRelease/$SHFBPackageName.$SHFBVersion.nupkg"
+ Invoke-WebRequest $SHFBInstallerSource -OutFile $SHFBPackagePath
+ If (-not $?) {
+ $host.ui.WriteErrorLine('Failed to download the SHFB NuGet package')
+ Exit $LASTEXITCODE
+ }
+ }
+
+ $SHFBPackages = [System.IO.Path]::GetFullPath((Join-Path (pwd) '..\.shfb'))
+ $SHFBPackagesUri = [System.Uri]$SHFBPackages
+ Echo "$nuget 'install' 'EWSoftware.SHFB' -Version $SHFBVersion -OutputDirectory '..\src\packages' -Source $SHFBPackagesUri"
+ &$nuget 'install' $SHFBPackageName -Version $SHFBVersion -OutputDirectory '..\src\packages' -Source $SHFBPackagesUri
+ If (-not $?) {
+ $host.ui.WriteErrorLine('Failed to install the SHFB NuGet package')
+ Exit $LASTEXITCODE
+ }
+
+ $env:SHFBROOT = [System.IO.Path]::GetFullPath((Join-Path (pwd) "..\src\packages\$SHFBPackageName.$SHFBVersion\tools"))
+}
+
+If (-not $NoDocs) {
+ If ((-not $env:SHFBROOT) -or (-not (Test-Path $env:SHFBROOT))) {
+ $host.ui.WriteErrorLine('Could not locate Sandcastle Help File Builder')
+ Exit 1
+ }
+}
+
+If ($Logger) {
+ $LoggerArgument = "/logger:$Logger"
+}
+
+&$msbuild '/nologo' '/m' '/nr:false' '/t:rebuild' $LoggerArgument "/verbosity:$Verbosity" "/p:Configuration=$SolutionBuildConfig" "/p:Platform=Any CPU" "/p:VisualStudioVersion=$VisualStudioVersion" "/p:KeyConfiguration=$KeyConfiguration" $SolutionPath
+if (-not $?) {
+ $host.ui.WriteErrorLine('Build failed, aborting!')
+ exit $LASTEXITCODE
+}
+
+# By default, do not create a NuGet package unless the expected strong name key files were used
+. .\keys.ps1
+
+foreach ($pair in $Keys.GetEnumerator()) {
+ $assembly = Resolve-FullPath -Path "..\src\SimpleRestServices\bin\$($pair.Key)\$BuildConfig\SimpleRESTServices.dll"
+ # Run the actual check in a separate process or the current process will keep the assembly file locked
+ powershell -Command ".\check-key.ps1 -Assembly '$assembly' -ExpectedKey '$($pair.Value)' -Build '$($pair.Key)'"
+ if (-not $?) {
+ $host.ui.WriteErrorLine("Failed to verify strong name key for build $($pair.Key).")
+ Exit $LASTEXITCODE
+ }
+}
+
+if (-not (Test-Path 'nuget')) {
+ mkdir "nuget"
+}
+
+# The NuGet packages reference XML documentation which is post-processed by SHFB. If the -NoDocs flag is specified,
+# these files are not created so packaging will fail.
+If (-not $NoDocs) {
+ &$nuget 'pack' '..\src\SimpleRestServices\SimpleRestServices.nuspec' '-OutputDirectory' 'nuget' '-Prop' "Configuration=$BuildConfig" '-Version' "$Version" '-Symbols'
+}
diff --git a/thirdparty/SimpleRestServices/build/check-key.ps1 b/thirdparty/SimpleRestServices/build/check-key.ps1
new file mode 100644
index 0000000000..b92a9cdccf
--- /dev/null
+++ b/thirdparty/SimpleRestServices/build/check-key.ps1
@@ -0,0 +1,31 @@
+param(
+ [string]$Assembly,
+ [string]$ExpectedKey,
+ [string]$Build = $null
+)
+
+function Get-PublicKeyToken() {
+ param([string]$assembly = $null)
+ if ($assembly) {
+ $bytes = $null
+ $bytes = [System.Reflection.Assembly]::ReflectionOnlyLoadFrom($assembly).GetName().GetPublicKeyToken()
+ if ($bytes) {
+ $key = ""
+ for ($i=0; $i -lt $bytes.Length; $i++) {
+ $key += "{0:x2}" -f $bytes[$i]
+ }
+
+ $key
+ }
+ }
+}
+
+if (-not $Build) {
+ $Build = $Assembly
+}
+
+$actual = Get-PublicKeyToken -assembly $Assembly
+if ($actual -ne $ExpectedKey) {
+ $host.ui.WriteErrorLine("Invalid publicKeyToken for '$Build'; expected '$ExpectedKey' but found '$actual'")
+ exit 1
+}
diff --git a/thirdparty/SimpleRestServices/build/keys.ps1 b/thirdparty/SimpleRestServices/build/keys.ps1
new file mode 100644
index 0000000000..32bc0c536f
--- /dev/null
+++ b/thirdparty/SimpleRestServices/build/keys.ps1
@@ -0,0 +1,25 @@
+# Note: these values may only change during major release
+# Also, if the values change, assembly binding redirection will not work between the affected releases.
+
+If ($Version.Contains('-')) {
+
+ # Use the development keys
+ $Keys = @{
+ 'v3.5' = '579ef5a5d8b3751c'
+ 'v4.0' = '579ef5a5d8b3751c'
+ }
+
+} Else {
+
+ # Use the final release keys
+ $Keys = @{
+ 'v3.5' = '541c51dcbcf0ec3c'
+ 'v4.0' = '541c51dcbcf0ec3c'
+ }
+
+}
+
+function Resolve-FullPath() {
+ param([string]$Path)
+ [System.IO.Path]::GetFullPath((Join-Path (pwd) $Path))
+}
diff --git a/thirdparty/SimpleRestServices/build/keys/TestingKey.snk b/thirdparty/SimpleRestServices/build/keys/TestingKey.snk
new file mode 100644
index 0000000000..64f666a8f6
Binary files /dev/null and b/thirdparty/SimpleRestServices/build/keys/TestingKey.snk differ
diff --git a/thirdparty/SimpleRestServices/build/keys/simplerestservices.dev.snk b/thirdparty/SimpleRestServices/build/keys/simplerestservices.dev.snk
new file mode 100644
index 0000000000..a302d290ec
Binary files /dev/null and b/thirdparty/SimpleRestServices/build/keys/simplerestservices.dev.snk differ
diff --git a/thirdparty/SimpleRestServices/build/keys/simplerestservices.snk b/thirdparty/SimpleRestServices/build/keys/simplerestservices.snk
new file mode 100644
index 0000000000..ac5b7c1164
Binary files /dev/null and b/thirdparty/SimpleRestServices/build/keys/simplerestservices.snk differ
diff --git a/thirdparty/SimpleRestServices/build/push.ps1 b/thirdparty/SimpleRestServices/build/push.ps1
new file mode 100644
index 0000000000..a1c82fbdd7
--- /dev/null
+++ b/thirdparty/SimpleRestServices/build/push.ps1
@@ -0,0 +1,8 @@
+. .\version.ps1
+
+If ($Version.EndsWith('-dev')) {
+ $host.ui.WriteErrorLine("Cannot push development version '$Version' to NuGet.")
+ Exit 1
+}
+
+..\src\.nuget\NuGet.exe 'push' ".\nuget\SimpleRestServices.$Version.nupkg"
diff --git a/thirdparty/SimpleRestServices/build/version.ps1 b/thirdparty/SimpleRestServices/build/version.ps1
new file mode 100644
index 0000000000..301eb5a36a
--- /dev/null
+++ b/thirdparty/SimpleRestServices/build/version.ps1
@@ -0,0 +1 @@
+$Version = "1.3.0.3-dev"
diff --git a/thirdparty/SimpleRestServices/src/.nuget/NuGet.Config b/thirdparty/SimpleRestServices/src/.nuget/NuGet.Config
new file mode 100644
index 0000000000..67f8ea046e
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/.nuget/NuGet.Config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/.nuget/NuGet.exe b/thirdparty/SimpleRestServices/src/.nuget/NuGet.exe
new file mode 100644
index 0000000000..9cba6edbfc
Binary files /dev/null and b/thirdparty/SimpleRestServices/src/.nuget/NuGet.exe differ
diff --git a/thirdparty/SimpleRestServices/src/Documentation/Content/MSHelpViewerRoot.aml b/thirdparty/SimpleRestServices/src/Documentation/Content/MSHelpViewerRoot.aml
new file mode 100644
index 0000000000..fcc870ff67
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/Documentation/Content/MSHelpViewerRoot.aml
@@ -0,0 +1,18 @@
+
+
+
+
+ Welcome to the SimpleRESTServices Library Reference
+
+
+
+
+ Select a topic from the table of contents.
+
+
+
+
+
+
+
+
diff --git a/thirdparty/SimpleRestServices/src/Documentation/Content/Welcome.aml b/thirdparty/SimpleRestServices/src/Documentation/Content/Welcome.aml
new file mode 100644
index 0000000000..47f825a439
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/Documentation/Content/Welcome.aml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ Welcome to the SimpleRESTServices libary. If you have any information, tips, updates, or corrections that
+ you would like to see added to the guide, feel free to submit them to the author using the
+ Send Feedback link at the bottom of the page or the e-mail link in the page footer.
+
+
+
+
+
+
diff --git a/thirdparty/SimpleRestServices/src/Documentation/Documentation.v3.5.shfbproj b/thirdparty/SimpleRestServices/src/Documentation/Documentation.v3.5.shfbproj
new file mode 100644
index 0000000000..9d22b85523
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/Documentation/Documentation.v3.5.shfbproj
@@ -0,0 +1,101 @@
+
+
+
+
+ Debug
+ AnyCPU
+ 2.0
+ 93356b99-d84c-4291-b739-ef8ec6a9b382
+ 1.9.9.0
+
+ Documentation
+ Documentation
+ Documentation
+
+ .NET Framework 3.5
+ .\Help\v3.5\
+ SimpleRESTServices
+ en-US
+
+
+
+ OnlyWarningsAndErrors
+ Website
+ False
+ True
+ False
+ False
+ True
+ Standard
+ Blank
+ API Reference
+ True
+ VS2013
+ False
+ MemberName
+ SimpleRESTServices API Reference Documentation
+ openstack.net%40lists.rackspace.com
+ AboveNamespaces
+ Msdn
+ Msdn
+ True
+ True
+ Attributes, ExplicitInterfaceImplementations, InheritedMembers, InheritedFrameworkMembers, Protected, ProtectedInternalAsProtected, SealedProtected
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/Documentation/Documentation.v4.0.shfbproj b/thirdparty/SimpleRestServices/src/Documentation/Documentation.v4.0.shfbproj
new file mode 100644
index 0000000000..8ca6cf4bba
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/Documentation/Documentation.v4.0.shfbproj
@@ -0,0 +1,97 @@
+
+
+
+
+ Debug
+ AnyCPU
+ 2.0
+ 7ac93313-d825-4668-a188-b7c9f21ddb37
+ 1.9.9.0
+
+ Documentation
+ Documentation
+ Documentation.v4.0
+
+ .NET Framework 4.0
+ .\Help\v4.0\
+ SimpleRESTServices
+ en-US
+
+
+
+ OnlyWarningsAndErrors
+ Website
+ False
+ True
+ False
+ False
+ True
+ Standard
+ Blank
+ API Reference
+ True
+ VS2013
+ False
+ MemberName
+ SimpleRESTServices API Reference Documentation
+ openstack.net%40lists.rackspace.com
+ AboveNamespaces
+ Msdn
+ Msdn
+ True
+ True
+ Attributes, ExplicitInterfaceImplementations, InheritedMembers, InheritedFrameworkMembers, Protected, ProtectedInternalAsProtected, SealedProtected
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/Documentation/SimpleRestServices.content b/thirdparty/SimpleRestServices/src/Documentation/SimpleRestServices.content
new file mode 100644
index 0000000000..3b4403fac5
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/Documentation/SimpleRestServices.content
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/Local.testsettings b/thirdparty/SimpleRestServices/src/Local.testsettings
new file mode 100644
index 0000000000..f869995adf
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/Local.testsettings
@@ -0,0 +1,10 @@
+
+
+ These are default test settings for a local test run.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices.sln b/thirdparty/SimpleRestServices/src/SimpleRestServices.sln
new file mode 100644
index 0000000000..b1fcc62beb
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices.sln
@@ -0,0 +1,89 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleRestServices.v4.0", "SimpleRestServices\SimpleRestServices.v4.0.csproj", "{4A44EB7C-06D5-43F0-B660-F684CBAAC99F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "Testing\UnitTests\UnitTests.csproj", "{BB206928-0F2E-46A1-B438-D3ED9AF72BA1}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D7C8177E-0A12-419B-B1BD-AEFD365BA657}"
+ ProjectSection(SolutionItems) = preProject
+ ..\appveyor.yml = ..\appveyor.yml
+ Local.testsettings = Local.testsettings
+ SimpleRestServices.vsmdi = SimpleRestServices.vsmdi
+ TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Testing", "Testing", "{F5B9B422-570A-4797-8F59-7A8C13A8934D}"
+EndProject
+Project("{7CF6DF6D-3B04-46F8-A40B-537D21BCA0B4}") = "Documentation.v4.0", "Documentation\Documentation.v4.0.shfbproj", "{7AC93313-D825-4668-A188-B7C9F21DDB37}"
+ ProjectSection(ProjectDependencies) = postProject
+ {4A44EB7C-06D5-43F0-B660-F684CBAAC99F} = {4A44EB7C-06D5-43F0-B660-F684CBAAC99F}
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleRestServices.v3.5", "SimpleRestServices\SimpleRestServices.v3.5.csproj", "{1960D862-8AD9-48BE-9290-B7E23EA03D5C}"
+EndProject
+Project("{7CF6DF6D-3B04-46F8-A40B-537D21BCA0B4}") = "Documentation.v3.5", "Documentation\Documentation.v3.5.shfbproj", "{93356B99-D84C-4291-B739-EF8EC6A9B382}"
+ ProjectSection(ProjectDependencies) = postProject
+ {1960D862-8AD9-48BE-9290-B7E23EA03D5C} = {1960D862-8AD9-48BE-9290-B7E23EA03D5C}
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{A57581E8-CE20-4E4A-81AA-B6BB5611D10F}"
+ ProjectSection(SolutionItems) = preProject
+ .nuget\NuGet.Config = .nuget\NuGet.Config
+ .nuget\NuGet.exe = .nuget\NuGet.exe
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{377DB120-46E3-4D5C-956B-A05F9987BF6F}"
+ ProjectSection(SolutionItems) = preProject
+ ..\build\appveyor-deploy-docs.ps1 = ..\build\appveyor-deploy-docs.ps1
+ ..\build\build.ps1 = ..\build\build.ps1
+ ..\build\check-key.ps1 = ..\build\check-key.ps1
+ ..\build\keys.ps1 = ..\build\keys.ps1
+ ..\build\push.ps1 = ..\build\push.ps1
+ ..\build\version.ps1 = ..\build\version.ps1
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(TestCaseManagementSettings) = postSolution
+ CategoryFile = SimpleRestServices.vsmdi
+ EndGlobalSection
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ ReleaseNoDocs|Any CPU = ReleaseNoDocs|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {4A44EB7C-06D5-43F0-B660-F684CBAAC99F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4A44EB7C-06D5-43F0-B660-F684CBAAC99F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4A44EB7C-06D5-43F0-B660-F684CBAAC99F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4A44EB7C-06D5-43F0-B660-F684CBAAC99F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4A44EB7C-06D5-43F0-B660-F684CBAAC99F}.ReleaseNoDocs|Any CPU.ActiveCfg = Release|Any CPU
+ {4A44EB7C-06D5-43F0-B660-F684CBAAC99F}.ReleaseNoDocs|Any CPU.Build.0 = Release|Any CPU
+ {BB206928-0F2E-46A1-B438-D3ED9AF72BA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BB206928-0F2E-46A1-B438-D3ED9AF72BA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BB206928-0F2E-46A1-B438-D3ED9AF72BA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BB206928-0F2E-46A1-B438-D3ED9AF72BA1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BB206928-0F2E-46A1-B438-D3ED9AF72BA1}.ReleaseNoDocs|Any CPU.ActiveCfg = Release|Any CPU
+ {BB206928-0F2E-46A1-B438-D3ED9AF72BA1}.ReleaseNoDocs|Any CPU.Build.0 = Release|Any CPU
+ {7AC93313-D825-4668-A188-B7C9F21DDB37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7AC93313-D825-4668-A188-B7C9F21DDB37}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7AC93313-D825-4668-A188-B7C9F21DDB37}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7AC93313-D825-4668-A188-B7C9F21DDB37}.ReleaseNoDocs|Any CPU.ActiveCfg = Release|Any CPU
+ {1960D862-8AD9-48BE-9290-B7E23EA03D5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1960D862-8AD9-48BE-9290-B7E23EA03D5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1960D862-8AD9-48BE-9290-B7E23EA03D5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1960D862-8AD9-48BE-9290-B7E23EA03D5C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1960D862-8AD9-48BE-9290-B7E23EA03D5C}.ReleaseNoDocs|Any CPU.ActiveCfg = Release|Any CPU
+ {1960D862-8AD9-48BE-9290-B7E23EA03D5C}.ReleaseNoDocs|Any CPU.Build.0 = Release|Any CPU
+ {93356B99-D84C-4291-B739-EF8EC6A9B382}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {93356B99-D84C-4291-B739-EF8EC6A9B382}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {93356B99-D84C-4291-B739-EF8EC6A9B382}.Release|Any CPU.Build.0 = Release|Any CPU
+ {93356B99-D84C-4291-B739-EF8EC6A9B382}.ReleaseNoDocs|Any CPU.ActiveCfg = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {BB206928-0F2E-46A1-B438-D3ED9AF72BA1} = {F5B9B422-570A-4797-8F59-7A8C13A8934D}
+ EndGlobalSection
+EndGlobal
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices.vsmdi b/thirdparty/SimpleRestServices/src/SimpleRestServices.vsmdi
new file mode 100644
index 0000000000..573d7a9231
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices.vsmdi
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/HttpHeader.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/HttpHeader.cs
new file mode 100644
index 0000000000..9a65a8c2b3
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/HttpHeader.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Diagnostics;
+
+namespace JSIStudios.SimpleRESTServices.Client
+{
+ ///
+ /// Represents a single header included with an HTTP response.
+ ///
+ [Serializable]
+ [DebuggerDisplay("{Key,nq} = {Value,nq}")]
+ public class HttpHeader
+ {
+ ///
+ /// The HTTP header key.
+ ///
+ private readonly string _key;
+
+ ///
+ /// The HTTP header value.
+ ///
+ private readonly string _value;
+
+ ///
+ /// Gets the HTTP header key.
+ ///
+ public string Key
+ {
+ get
+ {
+ return _key;
+ }
+ }
+
+ ///
+ /// Gets the HTTP header value.
+ ///
+ public string Value
+ {
+ get
+ {
+ return _value;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class using the specified
+ /// key and value.
+ ///
+ /// The HTTP header key.
+ /// The HTTP header value.
+ ///
+ /// If is null.
+ /// -or-
+ /// If is null.
+ ///
+ /// If is empty.
+ public HttpHeader(string key, string value)
+ {
+ if (key == null)
+ throw new ArgumentNullException("key");
+ if (value == null)
+ throw new ArgumentNullException("value");
+ if (string.IsNullOrEmpty(key))
+ throw new ArgumentException("key cannot be empty");
+
+ _key = key;
+ _value = value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/HttpMethod.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/HttpMethod.cs
new file mode 100644
index 0000000000..036c658930
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/HttpMethod.cs
@@ -0,0 +1,68 @@
+namespace JSIStudios.SimpleRESTServices.Client
+{
+ ///
+ /// Represents the types of HTTP protocol methods that can be used with a REST request.
+ ///
+ public enum HttpMethod
+ {
+ ///
+ /// Represents an HTTP GET protocol method.
+ ///
+ GET,
+
+ ///
+ /// Represents an HTTP POST protocol method that is used to post a new entity
+ /// as an addition to a URI.
+ ///
+ POST,
+
+ ///
+ /// Represents an HTTP PUT protocol method that is used to replace an entity
+ /// identified by a URI.
+ ///
+ PUT,
+
+ ///
+ /// Represents an HTTP DELETE protocol method.
+ ///
+ DELETE,
+
+ ///
+ /// Represents an HTTP HEAD protocol method. The method is identical to
+ /// except that the server only returns message-headers in the response,
+ /// without a message-body.
+ ///
+ HEAD,
+
+ ///
+ /// Represents an HTTP PATCH protocol method.
+ ///
+ PATCH,
+
+ ///
+ /// Represents an HTTP OPTIONS protocol method.
+ ///
+ OPTIONS,
+
+ ///
+ /// Represents an HTTP TRACE protocol method.
+ ///
+ TRACE,
+
+ ///
+ /// Represents the HTTP CONNECT protocol method that is used with a proxy that
+ /// can dynamically switch to tunneling, as in the case of SSL tunneling.
+ ///
+ CONNECT,
+
+ ///
+ /// Represents an HTTP COPY protocol method.
+ ///
+ COPY,
+
+ ///
+ /// Represents an HTTP MOVE protocol method.
+ ///
+ MOVE,
+ }
+}
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/IStringSerializer.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/IStringSerializer.cs
new file mode 100644
index 0000000000..16e68bde51
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/IStringSerializer.cs
@@ -0,0 +1,31 @@
+using JSIStudios.SimpleRESTServices.Client.Json;
+
+namespace JSIStudios.SimpleRESTServices.Client
+{
+ ///
+ /// This interface provides simple support for serializing and deserializing generic objects to and from strings.
+ ///
+ public interface IStringSerializer
+ {
+ ///
+ /// Deserializes an object from a string.
+ ///
+ /// The type of the object to deserialize.
+ /// The serialized representation of the object.
+ /// An instance of which describes.
+ /// If could not be deserialized to an object of type .
+ T Deserialize(string content);
+
+ ///
+ /// Serializes an object to a string.
+ ///
+ ///
+ /// The value returned by this method is suitable for deserialization using .
+ ///
+ /// The type of the object to serialize.
+ /// The object to serialize
+ /// A serialized string representation of .
+ /// If could not be serialized to a string.
+ string Serialize(T obj);
+ }
+}
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/Json/JsonRequestSettings.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/Json/JsonRequestSettings.cs
new file mode 100644
index 0000000000..fe6953552d
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/Json/JsonRequestSettings.cs
@@ -0,0 +1,27 @@
+namespace JSIStudios.SimpleRESTServices.Client.Json
+{
+ ///
+ /// Extends by setting the default
+ /// and values to application/json.
+ ///
+ public class JsonRequestSettings : RequestSettings
+ {
+ ///
+ /// The content type (MIME type) for a JSON request or response.
+ ///
+ ///
+ /// This value is equal to application/json.
+ ///
+ public static readonly string JsonContentType = "application/json";
+
+ ///
+ /// Initializes a new instance of the class with the default value
+ /// application/json for and .
+ ///
+ public JsonRequestSettings()
+ {
+ ContentType = JsonContentType;
+ Accept = JsonContentType;
+ }
+ }
+}
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/Json/JsonRestService.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/Json/JsonRestService.cs
new file mode 100644
index 0000000000..4281e671af
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/Json/JsonRestService.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Net;
+
+using JSIStudios.SimpleRESTServices.Core;
+
+namespace JSIStudios.SimpleRESTServices.Client.Json
+{
+ ///
+ /// An implementation of that uses JSON notation for
+ /// the serialized representation of objects.
+ ///
+ public class JsonRestServices : RestServiceBase, IRestService
+ {
+ ///
+ /// Initializes a new instance of the class with the default
+ /// JSON string serializer, retry logic, and URL builder.
+ ///
+ public JsonRestServices():this(null) {}
+
+ ///
+ /// Initializes a new instance of the class with the specified
+ /// request logger and the default JSON string serializer and URL builder.
+ ///
+ /// The logger to use for requests. Specify null if requests do not need to be logged.
+ public JsonRestServices(IRequestLogger requestLogger) : this(requestLogger, new RequestRetryLogic(), new UrlBuilder(), new JsonStringSerializer()) {}
+
+ ///
+ /// Initializes a new instance of the class with the specified
+ /// logger, retry logic, URI builder, and string serializer.
+ ///
+ /// The logger to use for requests. Specify null if requests do not need to be logged.
+ /// The retry logic to use for REST operations.
+ /// The URL builder to use for constructing URLs with query parameters.
+ /// The string serializer to use for requests from this service.
+ ///
+ /// If is null.
+ /// -or-
+ /// If is null.
+ /// -or-
+ /// If is null.
+ ///
+ public JsonRestServices(IRequestLogger logger, IRetryLogic retryLogic, IUrlBuilder urlBuilder, IStringSerializer stringSerializer) : base(stringSerializer, logger, retryLogic, urlBuilder) { }
+
+ ///
+ /// Gets the default to use for requests sent from this service.
+ ///
+ ///
+ /// This implementation uses a new instance of as the
+ /// default request settings.
+ ///
+ protected override RequestSettings DefaultRequestSettings
+ {
+ get
+ {
+ return new JsonRequestSettings();
+ }
+ }
+ }
+}
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/Json/JsonStringSerializer.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/Json/JsonStringSerializer.cs
new file mode 100644
index 0000000000..748cf27f13
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/Json/JsonStringSerializer.cs
@@ -0,0 +1,86 @@
+using System;
+using Newtonsoft.Json;
+
+namespace JSIStudios.SimpleRESTServices.Client.Json
+{
+ ///
+ /// Provides a default implementation of using JSON for
+ /// the underlying serialized notation.
+ ///
+ public class JsonStringSerializer : IStringSerializer
+ {
+ ///
+ public T Deserialize(string content)
+ {
+ if (string.IsNullOrEmpty(content))
+ return default(T);
+
+ try
+ {
+ return JsonConvert.DeserializeObject(content,
+ new JsonSerializerSettings
+ {
+ NullValueHandling = NullValueHandling.Ignore
+ });
+ }
+ catch (JsonReaderException ex)
+ {
+ throw new StringSerializationException(ex);
+ }
+ catch (JsonSerializationException ex)
+ {
+ throw new StringSerializationException(ex);
+ }
+ }
+
+ ///
+ public string Serialize(T obj)
+ {
+ if (Equals(obj, default(T)))
+ return null;
+
+ try
+ {
+ return JsonConvert.SerializeObject(obj,
+ new JsonSerializerSettings
+ {
+ NullValueHandling = NullValueHandling.Ignore
+ });
+ }
+ catch (JsonReaderException ex)
+ {
+ throw new StringSerializationException(ex);
+ }
+ catch (JsonSerializationException ex)
+ {
+ throw new StringSerializationException(ex);
+ }
+ }
+ }
+
+ ///
+ /// The exception thrown when string serialization or deserialization fails.
+ ///
+ public class StringSerializationException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class
+ /// with a reference to the inner exception that is the cause of this exception.
+ ///
+ /// The exception that is the cause of the current exception.
+ /// If the parameter is not a null reference, the current
+ /// exception is raised in a catch block that handles the inner exception.
+ public StringSerializationException(Exception innerException)
+ : base(GetMessageFromInnerException(innerException, "An error occurred during string serialization."), innerException)
+ {
+ }
+
+ private static string GetMessageFromInnerException(Exception innerException, string defaultMessage)
+ {
+ if (innerException == null)
+ return defaultMessage;
+
+ return innerException.Message ?? defaultMessage;
+ }
+ }
+}
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/RequestSettings.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/RequestSettings.cs
new file mode 100644
index 0000000000..5e21c74d7b
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/RequestSettings.cs
@@ -0,0 +1,186 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+using JSIStudios.SimpleRESTServices.Client.Json;
+using JSIStudios.SimpleRESTServices.Core;
+
+namespace JSIStudios.SimpleRESTServices.Client
+{
+ ///
+ /// Specifies the settings for an HTTP REST request.
+ ///
+ ///
+ /// The base implementation does not specify values for the
+ /// or properties. In most cases, these should be properly
+ /// set for the particular application either manually or in the constructor of a derived class
+ /// (e.g. ).
+ ///
+ public class RequestSettings
+ {
+ ///
+ /// Gets or sets the value of the Content-Type HTTP header. If this value is
+ /// null, the Content-Type header is omitted from the request.
+ ///
+ ///
+ /// In the base implementation, the default value is null.
+ ///
+ public virtual string ContentType { get; set; }
+
+ ///
+ /// Gets or sets the number of times this request should be retried if it fails.
+ ///
+ ///
+ /// In the base implementation, the default value is 0.
+ ///
+ public int RetryCount { get; set; }
+
+ ///
+ /// Gets or sets the delay before retrying a failed request.
+ ///
+ ///
+ /// In the base implementation, the default value is .
+ ///
+ public TimeSpan RetryDelay { get; set; }
+
+ ///
+ /// Gets or sets the set of HTTP status codes greater than or equal to 300 which
+ /// should be considered a successful result for this request. A value of
+ /// null is allowed, and should be treated as an empty collection.
+ ///
+ ///
+ /// In the base implementation, the default value is null.
+ ///
+ public IEnumerable Non200SuccessCodes { get; set; }
+
+ ///
+ /// Gets or sets the HTTP Accept header, which specifies the MIME types that are
+ /// acceptable for the response. If this value is null, the Accept header
+ /// is omitted from the request.
+ ///
+ ///
+ /// In the base implementation, the default value is null.
+ ///
+ public virtual string Accept { get; set; }
+
+ ///
+ /// Gets or sets a map of user-defined actions to execute in response to specific
+ /// HTTP status codes. A value of null is allowed, and should be treated as
+ /// an empty collection.
+ ///
+ ///
+ /// In the base implementation, the default value is null.
+ ///
+ /// If this value is non-null, and the request results in a status code not
+ /// present in this collection, no custom action is executed for the response.
+ ///
+ public Dictionary> ResponseActions { get; set; }
+
+ ///
+ /// Gets or sets the value of the User-Agent HTTP header. If this value is null,
+ /// the User-Agent header is omitted from the request.
+ ///
+ ///
+ /// In the base implementation, the default value is null.
+ ///
+ public string UserAgent { get; set; }
+
+ ///
+ /// Gets or sets the credentials to use for this request. This value can be
+ /// null if credentials are not specified.
+ ///
+ ///
+ /// In the base implementation, the default value is null.
+ ///
+ public ICredentials Credentials { get; set; }
+
+ ///
+ /// Gets or sets the request timeout.
+ ///
+ ///
+ /// The time to wait before the request times out. In the base
+ /// implementation, the default value is 100,000 milliseconds (100 seconds).
+ ///
+ public TimeSpan Timeout { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to send data in segments to the
+ /// Internet resource.
+ ///
+ ///
+ /// true to send data to the Internet resource in segments; otherwise,
+ /// false. In the base implementation, the default value is false.
+ ///
+ public bool ChunkRequest { get; set; }
+
+ ///
+ /// Gets or sets a user-defined collection to pass as the final argument to the
+ /// callback method.
+ ///
+ ///
+ /// This value not used directly within SimpleRESTServices.
+ ///
+ public Dictionary ExtendedLoggingData { get; set; }
+
+ ///
+ /// Gets or sets the value of the Content-Length HTTP header.
+ ///
+ ///
+ /// When this value is 0, the property controls
+ /// whether or not the Content-Length header is included with the request.
+ ///
+ /// In the base implementation, the default value is 0.
+ ///
+ public long ContentLength { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether or not 0 is a valid value for the Content-Length HTTP header
+ /// for this request.
+ ///
+ ///
+ /// When is non-zero, this value is ignored.
+ /// When is zero and this is true, the
+ /// Content-Length HTTP header is added to the request with an explicit value of 0.
+ /// Otherwise, when is 0 the Content-Length HTTP header
+ /// is omitted from the request.
+ ///
+ /// In the base implementation, the default value is false.
+ ///
+ public bool AllowZeroContentLength { get; set; }
+
+ ///
+ /// Gets or sets the maximum number of connections allowed on the object
+ /// used for the request. If the value is null, the connection limit value for the
+ /// object is not altered.
+ ///
+ public int? ConnectionLimit
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the request should follow redirection responses.
+ ///
+ ///
+ /// The default value is .
+ ///
+ ///
+ /// if the request should follow redirection responses; otherwise,
+ /// .
+ ///
+ ///
+ public bool AllowAutoRedirect { get; set; }
+
+ ///
+ /// Initializes a new instance of the class with the default values.
+ ///
+ public RequestSettings()
+ {
+ RetryCount = 0;
+ RetryDelay = TimeSpan.Zero;
+ Non200SuccessCodes = null;
+ Timeout = TimeSpan.FromMilliseconds(100000);
+ AllowAutoRedirect = true;
+ }
+ }
+}
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/Response.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/Response.cs
new file mode 100644
index 0000000000..2dea80f118
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/Response.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+
+namespace JSIStudios.SimpleRESTServices.Client
+{
+ ///
+ /// Represents the basic response of an HTTP REST request, where the body of the response
+ /// is stored as a text string.
+ ///
+ [Serializable]
+ public class Response
+ {
+ ///
+ /// Gets the HTTP status code for this response.
+ ///
+ public HttpStatusCode StatusCode { get; private set; }
+
+ ///
+ /// Gets a string representation of the HTTP status code for this response.
+ ///
+ public string Status { get; private set; }
+
+ ///
+ /// Gets a collection of all HTTP headers included with this response.
+ ///
+ public IList Headers { get; private set; }
+
+ ///
+ /// Gets the raw body of this HTTP response as a text string.
+ ///
+ public string RawBody { get; private set; }
+
+ ///
+ /// Initializes a new instance of the class with the given HTTP status code,
+ /// status, headers, and raw body.
+ ///
+ /// The HTTP status code.
+ /// A string representation of the HTTP status code.
+ /// A collection of all HTTP headers included with this response.
+ ///
+ /// The raw body of this HTTP response as a text string. When included in the response, this
+ /// value should be loaded with the encoding specified in the Content-Encoding and/or
+ /// Content-Type HTTP headers.
+ ///
+ public Response(HttpStatusCode responseCode, string status, IList headers, string rawBody)
+ {
+ StatusCode = responseCode;
+ Status = status;
+ Headers = headers;
+ RawBody = rawBody;
+ }
+
+ ///
+ /// Initializes a new instance of the class with the given HTTP status code,
+ /// headers, and raw body.
+ ///
+ /// The HTTP status code.
+ /// A collection of all HTTP headers included with this response.
+ ///
+ /// The raw body of this HTTP response as a text string. When included in the response, this
+ /// value should be loaded with the encoding specified in the Content-Encoding and/or
+ /// Content-Type HTTP headers.
+ ///
+ public Response(HttpStatusCode statusCode, IList headers, string rawBody)
+ : this(statusCode, statusCode.ToString(), headers, rawBody)
+ {
+ }
+ }
+}
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/ResponseOfT.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/ResponseOfT.cs
new file mode 100644
index 0000000000..4ce9611352
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/ResponseOfT.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+
+namespace JSIStudios.SimpleRESTServices.Client
+{
+ ///
+ /// Extends to include a strongly-typed return value
+ /// from the response.
+ ///
+ /// The type of the data included with the response.
+ [Serializable]
+ public class Response : Response
+ {
+ ///
+ /// Gets the strongly-typed representation of the value included with this response.
+ ///
+ public T Data { get; private set; }
+
+ ///
+ /// Initializes a new instance of the class with the given HTTP status code,
+ /// status, strongly-type data, headers, and raw body.
+ ///
+ /// The HTTP status code.
+ /// A string representation of the HTTP status code.
+ /// The strongly-typed data representation of the value returned with this response.
+ /// A collection of all HTTP headers included with this response.
+ ///
+ /// The raw body of this HTTP response as a text string. When included in the response, this
+ /// value should be loaded with the encoding specified in the Content-Encoding and/or
+ /// Content-Type HTTP headers.
+ ///
+ public Response(HttpStatusCode responseCode, string status, T data, IList headers, string rawBody)
+ : base(responseCode, status, headers, rawBody)
+ {
+ Data = data;
+ }
+
+ ///
+ /// Initializes a new instance of the class with the given HTTP status code,
+ /// strongly-type data, headers, and raw body.
+ ///
+ /// The HTTP status code.
+ /// The strongly-typed data representation of the value returned with this response.
+ /// A collection of all HTTP headers included with this response.
+ ///
+ /// The raw body of this HTTP response as a text string. When included in the response, this
+ /// value should be loaded with the encoding specified in the Content-Encoding and/or
+ /// Content-Type HTTP headers.
+ ///
+ public Response(HttpStatusCode statusCode, T data, IList headers, string rawBody)
+ : this(statusCode, statusCode.ToString(), data, headers, rawBody)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class by adding a strongly-typed
+ /// data value to a base response.
+ ///
+ /// The base response.
+ /// The strongly-typed data representation of the value returned with this response.
+ public Response(Response baseResponse, T data)
+ : this((baseResponse == null) ? default(int) : baseResponse.StatusCode,
+ (baseResponse == null) ? null : baseResponse.Status, data,
+ (baseResponse == null) ? null : baseResponse.Headers,
+ (baseResponse == null) ? null : baseResponse.RawBody) { }
+ }
+}
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/RestServiceBase.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/RestServiceBase.cs
new file mode 100644
index 0000000000..407cc235fe
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/RestServiceBase.cs
@@ -0,0 +1,583 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Text;
+using JSIStudios.SimpleRESTServices.Client.Json;
+using JSIStudios.SimpleRESTServices.Core;
+#if !NET35
+using System.Diagnostics.Contracts;
+#endif
+
+namespace JSIStudios.SimpleRESTServices.Client
+{
+ ///
+ /// Implements basic support for in terms of an implementation
+ /// of , ,
+ /// , and .
+ ///
+ public abstract class RestServiceBase : IRestService
+ {
+ private readonly IRetryLogic _retryLogic;
+ private readonly IRequestLogger _logger;
+ private readonly IUrlBuilder _urlBuilder;
+ private readonly IStringSerializer _stringSerializer;
+
+ ///
+ /// Initializes a new instance of the class with the specified string serializer
+ /// and the default retry logic and URL builder.
+ ///
+ /// The string serializer to use for requests from this service.
+ /// If is null.
+ protected RestServiceBase(IStringSerializer stringSerializer) : this(stringSerializer, null) { }
+
+ ///
+ /// Initializes a new instance of the class with the specified string serializer
+ /// and logger, and the default retry logic and URL builder.
+ ///
+ /// The string serializer to use for requests from this service.
+ /// The logger to use for requests. Specify null if requests do not need to be logged.
+ /// If is null.
+ protected RestServiceBase(IStringSerializer stringSerializer, IRequestLogger requestLogger) : this(stringSerializer, requestLogger, new RequestRetryLogic(), new UrlBuilder()) { }
+
+ ///
+ /// Initializes a new instance of the class with the specified string serializer,
+ /// logger, retry logic, and URI builder.
+ ///
+ /// The string serializer to use for requests from this service.
+ /// The logger to use for requests. Specify null if requests do not need to be logged.
+ /// The retry logic to use for REST operations.
+ /// The URL builder to use for constructing URLs with query parameters.
+ ///
+ /// If is null.
+ /// -or-
+ /// If is null.
+ /// -or-
+ /// If is null.
+ ///
+ protected RestServiceBase(IStringSerializer stringSerializer, IRequestLogger logger, IRetryLogic retryLogic, IUrlBuilder urlBuilder)
+ {
+ if (stringSerializer == null)
+ throw new ArgumentNullException("stringSerializer");
+ if (retryLogic == null)
+ throw new ArgumentNullException("retryLogic");
+ if (urlBuilder == null)
+ throw new ArgumentNullException("urlBuilder");
+
+ _retryLogic = retryLogic;
+ _logger = logger;
+ _urlBuilder = urlBuilder;
+ _stringSerializer = stringSerializer;
+ }
+
+ ///
+ /// Gets the default to use for requests sent from this service.
+ ///
+ protected virtual RequestSettings DefaultRequestSettings
+ {
+ get
+ {
+ return new RequestSettings();
+ }
+ }
+
+ ///
+ public virtual Response Execute(string url, HttpMethod method, TBody body, Dictionary headers, Dictionary queryStringParameters, RequestSettings settings)
+ {
+ if (url == null)
+ throw new ArgumentNullException("url");
+ if (string.IsNullOrEmpty(url))
+ throw new ArgumentException("url cannot be empty");
+
+ return Execute(new Uri(url), method, body, headers, queryStringParameters, settings);
+ }
+
+ ///
+ public virtual Response Execute(Uri url, HttpMethod method, TBody body, Dictionary headers, Dictionary queryStringParameters, RequestSettings settings)
+ {
+ if (url == null)
+ throw new ArgumentNullException("url");
+
+ var rawBody = _stringSerializer.Serialize(body);
+ return Execute(url, method, rawBody, headers, queryStringParameters, settings);
+ }
+
+ ///
+ public virtual Response Execute(string url, HttpMethod method, string body, Dictionary headers, Dictionary queryStringParameters, RequestSettings settings)
+ {
+ if (url == null)
+ throw new ArgumentNullException("url");
+ if (string.IsNullOrEmpty(url))
+ throw new ArgumentException("url cannot be empty");
+
+ return Execute(new Uri(url), method, body, headers, queryStringParameters, settings);
+ }
+
+ ///
+ public virtual Response Execute(Uri url, HttpMethod method, string body, Dictionary headers, Dictionary queryStringParameters, RequestSettings settings)
+ {
+ if (url == null)
+ throw new ArgumentNullException("url");
+
+ return Execute(url, method, BuildWebResponse, body, headers, queryStringParameters, settings) as Response;
+ }
+
+ ///
+ public virtual Response Execute(string url, HttpMethod method, TBody body, Dictionary headers, Dictionary queryStringParameters, RequestSettings settings)
+ {
+ if (url == null)
+ throw new ArgumentNullException("url");
+ if (string.IsNullOrEmpty(url))
+ throw new ArgumentException("url cannot be empty");
+
+ return Execute(new Uri(url), method, body, headers, queryStringParameters, settings);
+ }
+
+ ///
+ public virtual Response Execute(Uri url, HttpMethod method, TBody body, Dictionary headers, Dictionary queryStringParameters, RequestSettings settings)
+ {
+ if (url == null)
+ throw new ArgumentNullException("url");
+
+ var rawBody = _stringSerializer.Serialize(body);
+ return Execute(url, method, rawBody, headers, queryStringParameters, settings);
+ }
+
+ ///
+ public virtual Response Execute(string url, HttpMethod method, string body, Dictionary headers, Dictionary queryStringParameters, RequestSettings settings)
+ {
+ if (url == null)
+ throw new ArgumentNullException("url");
+ if (string.IsNullOrEmpty(url))
+ throw new ArgumentException("url cannot be empty");
+
+ return Execute(new Uri(url), method, body, headers, queryStringParameters, settings);
+ }
+
+ ///
+ public virtual Response Execute(Uri url, HttpMethod method, string body, Dictionary headers, Dictionary queryStringParameters, RequestSettings settings)
+ {
+ if (url == null)
+ throw new ArgumentNullException("url");
+
+ return Execute(url, method, null, body, headers, queryStringParameters, settings);
+ }
+
+ ///
+ public virtual Response Execute(Uri url, HttpMethod method, Func responseBuilderCallback, string body, Dictionary headers, Dictionary queryStringParameters, RequestSettings settings)
+ {
+ if (url == null)
+ throw new ArgumentNullException("url");
+
+ return ExecuteRequest(url, method, responseBuilderCallback, headers, queryStringParameters, settings, (req) =>
+ {
+ // Encode the parameters as form data:
+ if (!string.IsNullOrEmpty(body))
+ {
+ byte[] formData = UTF8Encoding.UTF8.GetBytes(body);
+ req.ContentLength = formData.Length;
+
+ // Send the request:
+ using (Stream post = req.GetRequestStream())
+ {
+ post.Write(formData, 0, formData.Length);
+ }
+ }
+
+ return body;
+ });
+ }
+
+ ///
+ public virtual Response Stream(string url, HttpMethod method, Stream content, int bufferSize, long maxReadLength, Dictionary headers, Dictionary queryStringParameters, RequestSettings settings, Action progressUpdated)
+ {
+ if (url == null)
+ throw new ArgumentNullException("url");
+ if (content == null)
+ throw new ArgumentNullException("content");
+ if (string.IsNullOrEmpty(url))
+ throw new ArgumentException("url cannot be empty");
+ if (bufferSize <= 0)
+ throw new ArgumentOutOfRangeException("bufferSize");
+ if (maxReadLength < 0)
+ throw new ArgumentOutOfRangeException("maxReadLength");
+
+ return Stream(new Uri(url), method, content, bufferSize, maxReadLength, headers, queryStringParameters, settings, progressUpdated) as Response;
+ }
+
+ ///
+ public virtual Response Stream(string url, HttpMethod method, Stream content, int bufferSize, long maxReadLength, Dictionary headers, Dictionary queryStringParameters, RequestSettings settings, Action progressUpdated)
+ {
+ if (url == null)
+ throw new ArgumentNullException("url");
+ if (content == null)
+ throw new ArgumentNullException("content");
+ if (string.IsNullOrEmpty(url))
+ throw new ArgumentException("url cannot be empty");
+ if (bufferSize <= 0)
+ throw new ArgumentOutOfRangeException("bufferSize");
+ if (maxReadLength < 0)
+ throw new ArgumentOutOfRangeException("maxReadLength");
+
+ return Stream(new Uri(url), method, content, bufferSize, maxReadLength, headers, queryStringParameters, settings, progressUpdated);
+ }
+
+ ///
+ public virtual Response Stream(Uri url, HttpMethod method, Stream content, int bufferSize, long maxReadLength, Dictionary headers, Dictionary queryStringParameters, RequestSettings settings, Action progressUpdated)
+ {
+ if (url == null)
+ throw new ArgumentNullException("url");
+ if (content == null)
+ throw new ArgumentNullException("content");
+ if (bufferSize <= 0)
+ throw new ArgumentOutOfRangeException("bufferSize");
+ if (maxReadLength < 0)
+ throw new ArgumentOutOfRangeException("maxReadLength");
+
+ return Stream(url, method, BuildWebResponse, content, bufferSize, maxReadLength, headers, queryStringParameters, settings, progressUpdated) as Response;
+ }
+
+ ///
+ public virtual Response Stream(Uri url, HttpMethod method, Stream content, int bufferSize, long maxReadLength, Dictionary headers, Dictionary queryStringParameters, RequestSettings settings, Action progressUpdated)
+ {
+ if (url == null)
+ throw new ArgumentNullException("url");
+ if (content == null)
+ throw new ArgumentNullException("content");
+ if (bufferSize <= 0)
+ throw new ArgumentOutOfRangeException("bufferSize");
+ if (maxReadLength < 0)
+ throw new ArgumentOutOfRangeException("maxReadLength");
+
+ return Stream(url, method, null, content, bufferSize, maxReadLength, headers, queryStringParameters, settings, progressUpdated);
+ }
+
+ ///
+ /// Executes a REST request with a
+ /// and user-defined callback function for constructing the resulting
+ /// object.
+ ///
+ /// The base URI.
+ /// The HTTP method to use for the request.
+ /// A user-specified function used to construct the resulting
+ /// object from the and a Boolean value specifying whether or not a
+ /// was thrown during the request. If this value is null, this method is equivalent to calling
+ /// .
+ /// A stream providing the body of the request.
+ ///
+ /// The size of the buffer used for copying data from to the
+ /// HTTP request stream.
+ ///
+ ///
+ /// The maximum number of bytes to send with the request. This parameter is optional.
+ /// If the value is 0, the request will include all data from .
+ ///
+ ///
+ /// A collection of custom HTTP headers to include with the request. This parameter is
+ /// optional. If the value is null, no custom headers are added to the HTTP request.
+ ///
+ ///
+ /// A collection of parameters to add to the query string portion of the request URI.
+ /// This parameter is optional. If the value is null, no parameters are added
+ /// to the query string.
+ ///
+ ///
+ /// The settings to use for the request. This parameters is optional. If the value is
+ /// null, an implementation-specific set of default settings will be used for the request.
+ ///
+ ///
+ /// A user-defined callback function for reporting progress of the send operation.
+ /// This parameter is optional. If the value is null, the method does not report
+ /// progress updates to the caller.
+ ///
+ /// Returns a object containing the HTTP status code, headers,
+ /// and body from the REST response.
+ ///
+ /// If is null.
+ /// -or-
+ /// If is null.
+ ///
+ ///
+ /// If is less than or equal to zero.
+ /// -or-
+ /// If is less than zero.
+ ///
+ /// If is not supported by the service.
+ public virtual Response Stream(Uri url, HttpMethod method, Func responseBuilderCallback, Stream content, int bufferSize, long maxReadLength, Dictionary headers, Dictionary queryStringParameters, RequestSettings settings, Action progressUpdated)
+ {
+ if (url == null)
+ throw new ArgumentNullException("url");
+ if (content == null)
+ throw new ArgumentNullException("content");
+ if (bufferSize <= 0)
+ throw new ArgumentOutOfRangeException("bufferSize");
+ if (maxReadLength < 0)
+ throw new ArgumentOutOfRangeException("maxReadLength");
+
+ return ExecuteRequest(url, method, responseBuilderCallback, headers, queryStringParameters, settings, (req) =>
+ {
+ long bytesWritten = 0;
+
+ if (settings.ChunkRequest || maxReadLength > 0 )
+ {
+ req.SendChunked = settings.ChunkRequest;
+ req.AllowWriteStreamBuffering = false;
+
+ req.ContentLength = content.Length > maxReadLength ? maxReadLength : content.Length;
+ }
+
+ using (Stream stream = req.GetRequestStream())
+ {
+ var buffer = new byte[bufferSize];
+ int count;
+ while ((count = content.Read(buffer, 0, maxReadLength > 0 ? (int)Math.Min(bufferSize, maxReadLength - bytesWritten) : bufferSize)) > 0)
+ {
+ bytesWritten += count;
+ stream.Write(buffer, 0, count);
+
+ if (progressUpdated != null)
+ progressUpdated(bytesWritten);
+
+ if (maxReadLength > 0 && bytesWritten >= maxReadLength)
+ break;
+ }
+ }
+
+ return "[STREAM CONTENT]";
+ });
+ }
+
+ ///
+ /// Executes a REST request indirectly via a callback function ,
+ /// and using a user-defined callback function for
+ /// constructing the resulting object.
+ ///
+ ///
+ /// The callback method is responsible for setting the body
+ /// of the request, if any, before executing the request. The callback method returns a string
+ /// representation of the body of the final request when available, otherwise returns a string
+ /// indicating the body is no longer available (e.g. was sent as a stream, or is binary). The
+ /// result is only required for passing as an argument to .
+ ///
+ /// The Boolean argument to indicates whether
+ /// or not an exception was thrown while executing the request. The value is true
+ /// if an exception occurred, otherwise false.
+ ///
+ /// The base URI.
+ /// The HTTP method to use for the request.
+ /// A user-specified function used to construct the resulting
+ /// object from the and a Boolean value specifying whether or not a
+ /// was thrown during the request. If this value is null, a default method is used to construct
+ /// the resulting object.
+ ///
+ /// A collection of custom HTTP headers to include with the request. If the value is
+ /// null, no custom headers are added to the HTTP request.
+ ///
+ ///
+ /// A collection of parameters to add to the query string portion of the request URI.
+ /// If the value is null, no parameters are added to the query string.
+ ///
+ ///
+ /// The settings to use for the request. If the value is null, the default settings returned
+ /// by will be used for the request.
+ ///
+ ///
+ /// Returns a object containing the HTTP status code, headers,
+ /// and body from the REST response.
+ ///
+ /// If is null.
+ /// -or-
+ /// If is null.
+ ///
+ /// If is not supported by the service.
+ public virtual Response ExecuteRequest(Uri url, HttpMethod method, Func responseBuilderCallback, Dictionary headers, Dictionary queryStringParameters, RequestSettings settings, Func executeCallback)
+ {
+ if (url == null)
+ throw new ArgumentNullException("url");
+ if (executeCallback == null)
+ throw new ArgumentNullException("executeCallback");
+
+ url = _urlBuilder.Build(url, queryStringParameters);
+
+ if (settings == null)
+ settings = DefaultRequestSettings;
+
+ return _retryLogic.Execute(() =>
+ {
+ Response response;
+
+ var startTime = DateTimeOffset.UtcNow;
+
+ string requestBodyText = null;
+ try
+ {
+ var req = WebRequest.Create(url) as HttpWebRequest;
+ req.Method = method.ToString();
+ req.ContentType = settings.ContentType;
+ req.Accept = settings.Accept;
+ req.AllowAutoRedirect = settings.AllowAutoRedirect;
+ if(settings.ContentLength > 0 || settings.AllowZeroContentLength)
+ req.ContentLength = settings.ContentLength;
+
+ if (settings.ConnectionLimit != null)
+ req.ServicePoint.ConnectionLimit = settings.ConnectionLimit.Value;
+
+ req.Timeout = (int)settings.Timeout.TotalMilliseconds;
+
+ if (!string.IsNullOrEmpty(settings.UserAgent))
+ req.UserAgent = settings.UserAgent;
+
+ if (settings.Credentials != null)
+ req.Credentials = settings.Credentials;
+
+ if (headers != null)
+ {
+ foreach (var header in headers)
+ {
+ req.Headers.Add(header.Key, header.Value);
+ }
+ }
+
+ requestBodyText = executeCallback(req);
+
+ using (var resp = req.GetResponse() as HttpWebResponse)
+ {
+ if (responseBuilderCallback != null)
+ response = responseBuilderCallback(resp, false);
+ else
+ response = BuildWebResponse(resp);
+ }
+ }
+ catch (WebException ex)
+ {
+ if (ex.Response == null)
+ throw;
+
+ using (var resp = ex.Response as HttpWebResponse)
+ {
+ if (responseBuilderCallback != null)
+ response = responseBuilderCallback(resp, true);
+ else
+ response = BuildWebResponse(resp);
+ }
+ }
+ var endTime = DateTimeOffset.UtcNow;
+
+ // Log the request
+ if (_logger != null)
+ _logger.Log(method, url.OriginalString, headers, requestBodyText, response, startTime, endTime, settings.ExtendedLoggingData);
+
+ if (response != null && settings.ResponseActions != null && settings.ResponseActions.ContainsKey(response.StatusCode))
+ {
+ var action = settings.ResponseActions[response.StatusCode];
+ if (action != null)
+ action(response);
+ }
+
+ return response;
+ }, settings.Non200SuccessCodes, settings.RetryCount, settings.RetryDelay);
+ }
+
+ ///
+ /// Build a for a given .
+ ///
+ /// The response from the REST request.
+ /// A object representing the result of the REST API call.
+ /// If is null.
+ private Response BuildWebResponse(HttpWebResponse resp)
+ {
+ if (resp == null)
+ throw new ArgumentNullException("resp");
+
+ string respBody;
+ using (var reader = new StreamReader(resp.GetResponseStream(), GetEncoding(resp)))
+ {
+ respBody = reader.ReadToEnd();
+ }
+
+ var respHeaders =
+ resp.Headers.AllKeys.Select(key => new HttpHeader(key, resp.GetResponseHeader(key)))
+ .ToList();
+ return new Response(resp.StatusCode, respHeaders, respBody);
+ }
+
+ ///
+ /// Determines the to use for reading an
+ /// body as text based on the response headers.
+ ///
+ ///
+ /// If the response provides the Content-Encoding header, then it is used.
+ /// Otherwise, if the optional charset parameter to the Content-Type header
+ /// is provided, then it is used. If no encoding is specified in the headers, or if the
+ /// encoding specified in the headers is not valid, is
+ /// used.
+ ///
+ /// The response to examine
+ /// The to use when reading the response stream as text.
+ /// is null.
+ private Encoding GetEncoding(HttpWebResponse response)
+ {
+ if (response == null)
+ throw new ArgumentNullException("response");
+#if !NET35
+ Contract.Ensures(Contract.Result() != null);
+ Contract.EndContractBlock();
+#endif
+
+ string contentEncoding = response.ContentEncoding;
+ if (!string.IsNullOrEmpty(contentEncoding))
+ {
+ try
+ {
+ return Encoding.GetEncoding(contentEncoding);
+ }
+ catch (ArgumentException)
+ {
+ // continue below
+ }
+ }
+
+ string characterSet = response.CharacterSet;
+ if (string.IsNullOrEmpty(characterSet))
+ return Encoding.Default;
+
+ try
+ {
+ return Encoding.GetEncoding(characterSet) ?? Encoding.Default;
+ }
+ catch (ArgumentException)
+ {
+ return Encoding.Default;
+ }
+ }
+
+ ///
+ /// Builds a for a given
+ /// containing a serialized representation of strongly-typed data in the body of
+ /// the response.
+ ///
+ /// The object model type for the data contained in the body of .
+ /// The response from the REST request.
+ /// Indicates whether the response is an error response. If the value is true the response
+ /// will not be deserialized to
+ /// A instance representing the response from the REST API call.
+ /// If is null.
+ ///
+ /// If the body of could not be deserialized to an object of type .
+ ///
+ private Response BuildWebResponse(HttpWebResponse resp, bool isError = false)
+ {
+ var baseReponse = BuildWebResponse(resp);
+ T data = default(T);
+
+ if (!isError)
+ {
+ if (baseReponse != null && !string.IsNullOrEmpty(baseReponse.RawBody))
+ data = _stringSerializer.Deserialize(baseReponse.RawBody);
+ }
+
+ return new Response(baseReponse, data);
+ }
+ }
+}
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/UrlBuilder.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/UrlBuilder.cs
new file mode 100644
index 0000000000..cb8345c69d
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/UrlBuilder.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using JSIStudios.SimpleRESTServices.Core;
+
+namespace JSIStudios.SimpleRESTServices.Client
+{
+ ///
+ /// A simple, default implementation of a URI builder.
+ ///
+ public class UrlBuilder : IUrlBuilder
+ {
+ ///
+ public Uri Build(Uri baseUrl, Dictionary queryStringParameters)
+ {
+ if (baseUrl == null)
+ throw new ArgumentNullException("baseUrl");
+
+ return new Uri(Build(baseUrl.AbsoluteUri, queryStringParameters));
+ }
+
+ ///
+ /// Constructs a complete URI for an HTTP request using a base URI and a
+ /// collection of query string parameters.
+ ///
+ ///
+ /// If already contains a query string, the specified
+ /// are appended to the existing query string.
+ /// This method does not perform substitution for any template parameters
+ /// which may exist in . If
+ /// is null or empty, is returned unchanged.
+ ///
+ /// The base URI.
+ /// A collection of parameters to place in the URI query string,
+ /// or null if there are no parameters.
+ /// A constructed from and the specified
+ /// .
+ /// If is null.
+ public string Build(string baseAbsoluteUrl, Dictionary queryStringParameters)
+ {
+ if (baseAbsoluteUrl == null)
+ throw new ArgumentNullException("baseAbsoluteUrl");
+
+ if (queryStringParameters != null && queryStringParameters.Count > 0)
+ {
+ var paramsCombinedList =
+ queryStringParameters.Select(
+ param =>
+ string.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(param.Key),
+ System.Web.HttpUtility.UrlEncode(param.Value)));
+ var paramsCombined = string.Join("&", paramsCombinedList.ToArray());
+
+ var separator = baseAbsoluteUrl.Contains("?") ? "&" : "?";
+ return baseAbsoluteUrl + separator + paramsCombined;
+ }
+
+ return baseAbsoluteUrl;
+ }
+ }
+}
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/WebResponseRetryLogic.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/WebResponseRetryLogic.cs
new file mode 100644
index 0000000000..d924c6bbbe
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Client/WebResponseRetryLogic.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Threading;
+using JSIStudios.SimpleRESTServices.Core;
+
+namespace JSIStudios.SimpleRESTServices.Client
+{
+ ///
+ /// Provides a simple default implementation of
+ /// for HTTP REST requests.
+ ///
+ ///
+ /// This implementation of invokes the callback
+ /// method until one of the following conditions is met.
+ ///
+ ///
+ /// - The operation has been attempted retryCount times.
+ /// - The status code is less than 300.
+ /// - The status code is contained in the
+ /// non200SuccessCodes collection.
+ ///
+ ///
+ /// If the retry delay is greater than zero, is called
+ /// between retry attempts.
+ ///
+ public class RequestRetryLogic : IRetryLogic
+ {
+ ///
+ public Response Execute(Func callback, int retryCount = 0, TimeSpan? retryDelay = null)
+ {
+ if (callback == null)
+ throw new ArgumentNullException("callback");
+ if (retryCount < 0)
+ throw new ArgumentOutOfRangeException("retryCount");
+ if (retryDelay.HasValue && retryDelay < TimeSpan.Zero)
+ throw new ArgumentOutOfRangeException("retryDelay");
+
+ return Execute(callback, Enumerable.Empty(), retryCount, retryDelay);
+ }
+
+ ///
+ public Response Execute(Func callback, IEnumerable non200SuccessCodes, int retryCount = 0, TimeSpan? retryDelay = null)
+ {
+ if (callback == null)
+ throw new ArgumentNullException("callback");
+ if (retryCount < 0)
+ throw new ArgumentOutOfRangeException("retryCount");
+ if (retryDelay.HasValue && retryDelay < TimeSpan.Zero)
+ throw new ArgumentOutOfRangeException("retryDelay");
+
+ Response response;
+ do
+ {
+ response = callback();
+ if (IsRequestSuccessful(response, non200SuccessCodes))
+ return response;
+
+ retryCount--;
+ if (retryCount >= 0)
+ Thread.Sleep(retryDelay ?? TimeSpan.Zero);
+ }
+ while (retryCount >= 0);
+
+ return response;
+ }
+
+ ///
+ /// Checks if is considered a successful HTTP response.
+ ///
+ /// The response.
+ /// The HTTP status codes to consider successful, in addition to codes
+ /// below 300.
+ /// true if is considered a successful HTTP
+ /// response, otherwise false.
+ private static bool IsRequestSuccessful(Response response, IEnumerable non200SuccessCodes)
+ {
+ if (response != null && response.StatusCode < (HttpStatusCode)300)
+ return true;
+
+ if (non200SuccessCodes == null)
+ return false;
+
+ return non200SuccessCodes.Contains(response.StatusCode);
+ }
+ }
+}
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/Exceptions/BadWebRequestException.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/Exceptions/BadWebRequestException.cs
new file mode 100644
index 0000000000..d1b47aff4b
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/Exceptions/BadWebRequestException.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace JSIStudios.SimpleRESTServices.Core.Exceptions
+{
+ public class BadWebRequestException : Exception
+ {
+ public BadWebRequestException(string message)
+ : base(message)
+ {
+ }
+
+ public BadWebRequestException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/Exceptions/HttpHeaderNotFoundException.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/Exceptions/HttpHeaderNotFoundException.cs
new file mode 100644
index 0000000000..c317d6f322
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/Exceptions/HttpHeaderNotFoundException.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace JSIStudios.SimpleRESTServices.Core.Exceptions
+{
+ public class HttpHeaderNotFoundException : Exception
+ {
+ public string Name { get; set; }
+ public HttpHeaderNotFoundException(string name, string message)
+ : base(message)
+ {
+ Name = name;
+ }
+
+ public HttpHeaderNotFoundException(string name)
+ {
+ Name = name;
+ }
+
+ public HttpHeaderNotFoundException()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/Exceptions/HttpResourceNotFoundException.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/Exceptions/HttpResourceNotFoundException.cs
new file mode 100644
index 0000000000..d181f6a3bb
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/Exceptions/HttpResourceNotFoundException.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace JSIStudios.SimpleRESTServices.Core.Exceptions
+{
+ public class HttpResourceNotFoundException : Exception
+ {
+ public HttpResourceNotFoundException(string message)
+ : base(message)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/Exceptions/HttpResourceNotModifiedException.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/Exceptions/HttpResourceNotModifiedException.cs
new file mode 100644
index 0000000000..e5f7691128
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/Exceptions/HttpResourceNotModifiedException.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace JSIStudios.SimpleRESTServices.Core.Exceptions
+{
+ public class HttpResourceNotModifiedException : Exception
+ {
+ public HttpResourceNotModifiedException()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/IRequestLogger.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/IRequestLogger.cs
new file mode 100644
index 0000000000..78036e4c59
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/IRequestLogger.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using JSIStudios.SimpleRESTServices.Client;
+
+namespace JSIStudios.SimpleRESTServices.Core
+{
+ ///
+ /// Represents custom logging behavior for a REST request.
+ ///
+ public interface IRequestLogger
+ {
+ ///
+ /// Logs a REST request along with its response.
+ ///
+ /// The used for the request.
+ /// The complete URI, including the query string (if any).
+ /// The set of custom headers sent with the request. This may be null if no custom headers were specified.
+ /// The body of the request. This is null or empty if the request did not include a body.
+ /// The response.
+ /// The request start time.
+ /// The request end time.
+ /// The user-defined extended data specified in .
+ void Log(HttpMethod httpMethod, string uri, Dictionary requestHeaders, string requestBody, Response response, DateTimeOffset requestStartTime, DateTimeOffset requestEndTime, Dictionary extendedData);
+ }
+}
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/IRequestProcessor.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/IRequestProcessor.cs
new file mode 100644
index 0000000000..0da6f3147b
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/IRequestProcessor.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Specialized;
+using System.Net;
+using JSIStudios.SimpleRESTServices.Server.EventArgs;
+
+namespace JSIStudios.SimpleRESTServices.Core
+{
+ public interface IRequestProcessor
+ {
+ event EventHandler RequestStarted;
+ event EventHandler RequestCompleted;
+ event EventHandler OnError;
+
+ void Execute(Action callBack, HttpStatusCode successStatus = HttpStatusCode.OK);
+ void Execute(Action callBack, NameValueCollection responseHeaders, HttpStatusCode successStatus = HttpStatusCode.OK);
+
+ TResult Execute(Func callBack, HttpStatusCode successStatus = HttpStatusCode.OK);
+ TResult Execute(Func callBack, NameValueCollection responseHeaders, HttpStatusCode successStatus = HttpStatusCode.OK);
+ }
+}
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/IRestService.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/IRestService.cs
new file mode 100644
index 0000000000..8f3e863f01
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/IRestService.cs
@@ -0,0 +1,585 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using JSIStudios.SimpleRESTServices.Client.Json;
+
+namespace JSIStudios.SimpleRESTServices.Client
+{
+ ///
+ /// Represents a service for executing generic REST requests.
+ ///
+ public interface IRestService
+ {
+ ///
+ /// Executes a REST request with a strongly-typed and result.
+ ///
+ /// The type of the data returned in the REST response.
+ /// The type of the data included in the body of the REST request.
+ /// The base URI.
+ /// The HTTP method to use for the request.
+ ///
+ /// The strongly-typed data to include in the body of the request. If the value is null,
+ /// the behavior is implementation-defined.
+ ///
+ ///
+ /// A collection of custom HTTP headers to include with the request. This parameter is
+ /// optional. If the value is null, no custom headers are added to the HTTP request.
+ ///
+ ///
+ /// A collection of parameters to add to the query string portion of the request URI.
+ /// This parameter is optional. If the value is null, no parameters are added
+ /// to the query string.
+ ///
+ ///
+ /// The settings to use for the request. This parameters is optional. If the value is
+ /// null, an implementation-specific set of default settings will be used for the request.
+ ///
+ /// Returns a object containing the HTTP status code, headers, body,
+ /// and strongly-typed data from the REST response.
+ /// If is null.
+ /// If is not a valid base URI.
+ /// If is not supported by the service.
+ ///
+ /// If the body of the response could not be deserialized to an object of type .
+ ///
+ Response Execute(
+ String url,
+ HttpMethod method,
+ TBody body,
+ Dictionary headers = null,
+ Dictionary queryStringParameters = null,
+ RequestSettings settings = null);
+
+ ///
+ /// Executes a REST request with a strongly-typed and result.
+ ///
+ /// The type of the data returned in the REST response.
+ /// The type of the data included in the body of the REST request.
+ /// The base URI.
+ /// The HTTP method to use for the request.
+ ///
+ /// The strongly-typed data to include in the body of the request. If the value is null,
+ /// the behavior is implementation-defined.
+ ///
+ ///
+ /// A collection of custom HTTP headers to include with the request. This parameter is
+ /// optional. If the value is null, no custom headers are added to the HTTP request.
+ ///
+ ///
+ /// A collection of parameters to add to the query string portion of the request URI.
+ /// This parameter is optional. If the value is null, no parameters are added
+ /// to the query string.
+ ///
+ ///
+ /// The settings to use for the request. This parameters is optional. If the value is
+ /// null, an implementation-specific set of default settings will be used for the request.
+ ///
+ /// Returns a object containing the HTTP status code, headers, body,
+ /// and strongly-typed data from the REST response.
+ /// If is null.
+ /// If is not supported by the service.
+ ///
+ /// If the body of the response could not be deserialized to an object of type .
+ ///
+ Response Execute(
+ Uri url,
+ HttpMethod method,
+ TBody body,
+ Dictionary headers = null,
+ Dictionary queryStringParameters = null,
+ RequestSettings settings = null);
+
+ ///
+ /// Executes a REST request with a string and strongly-typed result.
+ ///
+ /// The type of the data returned in the REST response.
+ /// The base URI.
+ /// The HTTP method to use for the request.
+ ///
+ /// The body of the request. This parameter is optional. If the value is null,
+ /// the request is sent without a body.
+ ///
+ ///
+ /// A collection of custom HTTP headers to include with the request. This parameter is
+ /// optional. If the value is null, no custom headers are added to the HTTP request.
+ ///
+ ///
+ /// A collection of parameters to add to the query string portion of the request URI.
+ /// This parameter is optional. If the value is null, no parameters are added
+ /// to the query string.
+ ///
+ ///
+ /// The settings to use for the request. This parameters is optional. If the value is
+ /// null, an implementation-specific set of default settings will be used for the request.
+ ///
+ /// Returns a object containing the HTTP status code, headers, body,
+ /// and strongly-typed data from the REST response.
+ /// If is null.
+ /// If is not a valid base URI.
+ /// If is not supported by the service.
+ ///
+ /// If the body of the response could not be deserialized to an object of type .
+ ///
+ Response Execute(
+ String url,
+ HttpMethod method,
+ string body = null,
+ Dictionary headers = null,
+ Dictionary queryStringParameters = null,
+ RequestSettings settings = null);
+
+ ///
+ /// Executes a REST request with a string and strongly-typed result.
+ ///
+ /// The type of the data returned in the REST response.
+ /// The base URI.
+ /// The HTTP method to use for the request.
+ ///
+ /// The body of the request. This parameter is optional. If the value is null,
+ /// the request is sent without a body.
+ ///
+ ///
+ /// A collection of custom HTTP headers to include with the request. This parameter is
+ /// optional. If the value is null, no custom headers are added to the HTTP request.
+ ///
+ ///
+ /// A collection of parameters to add to the query string portion of the request URI.
+ /// This parameter is optional. If the value is null, no parameters are added
+ /// to the query string.
+ ///
+ ///
+ /// The settings to use for the request. This parameters is optional. If the value is
+ /// null, an implementation-specific set of default settings will be used for the request.
+ ///
+ /// Returns a object containing the HTTP status code, headers, body,
+ /// and strongly-typed data from the REST response.
+ /// If is null.
+ /// If is not supported by the service.
+ ///
+ /// If the body of the response could not be deserialized to an object of type .
+ ///
+ Response Execute(
+ Uri url,
+ HttpMethod method,
+ string body = null,
+ Dictionary headers = null,
+ Dictionary queryStringParameters = null,
+ RequestSettings settings = null);
+
+ ///
+ /// Executes a REST request with a strongly-typed and basic result (text or no content).
+ ///
+ /// The type of the data included in the body of the REST request.
+ /// The base URI.
+ /// The HTTP method to use for the request.
+ ///
+ /// The strongly-typed data to include in the body of the request. If the value is null,
+ /// the behavior is implementation-defined.
+ ///
+ ///
+ /// A collection of custom HTTP headers to include with the request. This parameter is
+ /// optional. If the value is null, no custom headers are added to the HTTP request.
+ ///
+ ///
+ /// A collection of parameters to add to the query string portion of the request URI.
+ /// This parameter is optional. If the value is null, no parameters are added
+ /// to the query string.
+ ///
+ ///
+ /// The settings to use for the request. This parameters is optional. If the value is
+ /// null, an implementation-specific set of default settings will be used for the request.
+ ///
+ /// Returns a object containing the HTTP status code, headers,
+ /// and body from the REST response.
+ /// If is null.
+ /// If is not a valid base URI.
+ /// If is not supported by the service.
+ Response Execute(
+ String url,
+ HttpMethod method,
+ TBody body,
+ Dictionary headers = null,
+ Dictionary queryStringParameters = null,
+ RequestSettings settings = null);
+
+ ///
+ /// Executes a REST request with a strongly-typed and basic result (text or no content).
+ ///
+ /// The type of the data included in the body of the REST request.
+ /// The base URI.
+ /// The HTTP method to use for the request.
+ ///
+ /// The strongly-typed data to include in the body of the request. If the value is null,
+ /// the behavior is implementation-defined.
+ ///
+ ///
+ /// A collection of custom HTTP headers to include with the request. This parameter is
+ /// optional. If the value is null, no custom headers are added to the HTTP request.
+ ///
+ ///
+ /// A collection of parameters to add to the query string portion of the request URI.
+ /// This parameter is optional. If the value is null, no parameters are added
+ /// to the query string.
+ ///
+ ///
+ /// The settings to use for the request. This parameters is optional. If the value is
+ /// null, an implementation-specific set of default settings will be used for the request.
+ ///
+ /// Returns a object containing the HTTP status code, headers,
+ /// and body from the REST response.
+ /// If is null.
+ /// If is not supported by the service.
+ Response Execute(
+ Uri url,
+ HttpMethod method,
+ TBody body,
+ Dictionary headers = null,
+ Dictionary queryStringParameters = null,
+ RequestSettings settings = null);
+
+ ///
+ /// Executes a REST request with a string and basic result (text or no content).
+ ///
+ /// The base URI.
+ /// The HTTP method to use for the request.
+ ///
+ /// The body of the request. This parameter is optional. If the value is null,
+ /// the request is sent without a body.
+ ///
+ ///
+ /// A collection of custom HTTP headers to include with the request. This parameter is
+ /// optional. If the value is null, no custom headers are added to the HTTP request.
+ ///
+ ///
+ /// A collection of parameters to add to the query string portion of the request URI.
+ /// This parameter is optional. If the value is null, no parameters are added
+ /// to the query string.
+ ///
+ ///
+ /// The settings to use for the request. This parameters is optional. If the value is
+ /// null, an implementation-specific set of default settings will be used for the request.
+ ///
+ /// Returns a object containing the HTTP status code, headers,
+ /// and body from the REST response.
+ /// If is null.
+ /// If is not a valid base URI.
+ /// If is not supported by the service.
+ Response Execute(
+ String url,
+ HttpMethod method,
+ string body = null,
+ Dictionary headers = null,
+ Dictionary queryStringParameters = null,
+ RequestSettings settings = null);
+
+ ///
+ /// Executes a REST request with a string and basic result (text or no content).
+ ///
+ /// The base URI.
+ /// The HTTP method to use for the request.
+ ///
+ /// The body of the request. This parameter is optional. If the value is null,
+ /// the request is sent without a body.
+ ///
+ ///
+ /// A collection of custom HTTP headers to include with the request. This parameter is
+ /// optional. If the value is null, no custom headers are added to the HTTP request.
+ ///
+ ///
+ /// A collection of parameters to add to the query string portion of the request URI.
+ /// This parameter is optional. If the value is null, no parameters are added
+ /// to the query string.
+ ///
+ ///
+ /// The settings to use for the request. This parameters is optional. If the value is
+ /// null, an implementation-specific set of default settings will be used for the request.
+ ///
+ /// Returns a object containing the HTTP status code, headers,
+ /// and body from the REST response.
+ /// If is null.
+ /// If is not supported by the service.
+ Response Execute(
+ Uri url,
+ HttpMethod method,
+ string body = null,
+ Dictionary headers = null,
+ Dictionary queryStringParameters = null,
+ RequestSettings settings = null);
+
+ ///
+ /// Executes a REST request with a and strongly-typed result.
+ ///
+ /// The type of the data returned in the REST response.
+ /// The base URI.
+ /// The HTTP method to use for the request.
+ /// A stream providing the body of the request.
+ ///
+ /// The size of the buffer used for copying data from to the
+ /// HTTP request stream.
+ ///
+ ///
+ /// The maximum number of bytes to send with the request. This parameter is optional.
+ /// If the value is 0, the request will include all data from .
+ ///
+ ///
+ /// A collection of custom HTTP headers to include with the request. This parameter is
+ /// optional. If the value is null, no custom headers are added to the HTTP request.
+ ///
+ ///
+ /// A collection of parameters to add to the query string portion of the request URI.
+ /// This parameter is optional. If the value is null, no parameters are added
+ /// to the query string.
+ ///
+ ///
+ /// The settings to use for the request. This parameters is optional. If the value is
+ /// null, an implementation-specific set of default settings will be used for the request.
+ ///
+ ///
+ /// A user-defined callback function for reporting progress of the send operation.
+ /// This parameter is optional. If the value is null, the method does not report
+ /// progress updates to the caller.
+ ///
+ /// Returns a object containing the HTTP status code, headers, body,
+ /// and strongly-typed data from the REST response.
+ ///
+ /// If is null.
+ /// -or-
+ /// If is null.
+ ///
+ ///
+ /// If is less than or equal to zero.
+ /// -or-
+ /// If is less than zero.
+ ///
+ /// If is not supported by the service.
+ ///
+ /// If the body of the response could not be deserialized to an object of type .
+ ///
+ Response Stream(
+ Uri url,
+ HttpMethod method,
+ Stream content,
+ int bufferSize,
+ long maxReadLength = 0,
+ Dictionary headers = null,
+ Dictionary queryStringParameters = null,
+ RequestSettings settings = null,
+ Action progressUpdated = null);
+
+ ///
+ /// Executes a REST request with a and basic result (text or no content).
+ ///
+ /// The base URI.
+ /// The HTTP method to use for the request.
+ /// A stream providing the body of the request.
+ ///
+ /// The size of the buffer used for copying data from to the
+ /// HTTP request stream.
+ ///
+ ///
+ /// The maximum number of bytes to send with the request. This parameter is optional.
+ /// If the value is 0, the request will include all data from .
+ ///
+ ///
+ /// A collection of custom HTTP headers to include with the request. This parameter is
+ /// optional. If the value is null, no custom headers are added to the HTTP request.
+ ///
+ ///
+ /// A collection of parameters to add to the query string portion of the request URI.
+ /// This parameter is optional. If the value is null, no parameters are added
+ /// to the query string.
+ ///
+ ///
+ /// The settings to use for the request. This parameters is optional. If the value is
+ /// null, an implementation-specific set of default settings will be used for the request.
+ ///
+ ///
+ /// A user-defined callback function for reporting progress of the send operation.
+ /// This parameter is optional. If the value is null, the method does not report
+ /// progress updates to the caller.
+ ///
+ /// Returns a object containing the HTTP status code, headers,
+ /// and body from the REST response.
+ ///
+ /// If is null.
+ /// -or-
+ /// If is null.
+ ///
+ ///
+ /// If is less than or equal to zero.
+ /// -or-
+ /// If is less than zero.
+ ///
+ /// If is not supported by the service.
+ Response Stream(
+ Uri url,
+ HttpMethod method,
+ Stream content,
+ int bufferSize,
+ long maxReadLength = 0,
+ Dictionary headers = null,
+ Dictionary queryStringParameters = null,
+ RequestSettings settings = null,
+ Action progressUpdated = null);
+
+ ///
+ /// Executes a REST request with a and strongly-typed result.
+ ///
+ /// The type of the data returned in the REST response.
+ /// The base URI.
+ /// The HTTP method to use for the request.
+ /// A stream providing the body of the request.
+ ///
+ /// The size of the buffer used for copying data from to the
+ /// HTTP request stream.
+ ///
+ ///
+ /// The maximum number of bytes to send with the request. This parameter is optional.
+ /// If the value is 0, the request will include all data from .
+ ///
+ ///
+ /// A collection of custom HTTP headers to include with the request. This parameter is
+ /// optional. If the value is null, no custom headers are added to the HTTP request.
+ ///
+ ///
+ /// A collection of parameters to add to the query string portion of the request URI.
+ /// This parameter is optional. If the value is null, no parameters are added
+ /// to the query string.
+ ///
+ ///
+ /// The settings to use for the request. This parameters is optional. If the value is
+ /// null, an implementation-specific set of default settings will be used for the request.
+ ///
+ ///
+ /// A user-defined callback function for reporting progress of the send operation.
+ /// This parameter is optional. If the value is null, the method does not report
+ /// progress updates to the caller.
+ ///
+ /// Returns a object containing the HTTP status code, headers, body,
+ /// and strongly-typed data from the REST response.
+ ///
+ /// If is null.
+ /// -or-
+ /// If is null.
+ ///
+ ///
+ /// If is less than or equal to zero.
+ /// -or-
+ /// If is less than zero.
+ ///
+ /// If is not a valid base URI.
+ /// If is not supported by the service.
+ ///
+ /// If the body of the response could not be deserialized to an object of type .
+ ///
+ Response Stream(
+ string url,
+ HttpMethod method,
+ Stream content,
+ int bufferSize,
+ long maxReadLength = 0,
+ Dictionary headers = null,
+ Dictionary queryStringParameters = null,
+ RequestSettings settings = null,
+ Action progressUpdated = null);
+
+ ///
+ /// Executes a REST request with a and basic result (text or no content).
+ ///
+ /// The base URI.
+ /// The HTTP method to use for the request.
+ /// A stream providing the body of the request.
+ ///
+ /// The size of the buffer used for copying data from to the
+ /// HTTP request stream.
+ ///
+ ///
+ /// The maximum number of bytes to send with the request. This parameter is optional.
+ /// If the value is 0, the request will include all data from .
+ ///
+ ///
+ /// A collection of custom HTTP headers to include with the request. This parameter is
+ /// optional. If the value is null, no custom headers are added to the HTTP request.
+ ///
+ ///
+ /// A collection of parameters to add to the query string portion of the request URI.
+ /// This parameter is optional. If the value is null, no parameters are added
+ /// to the query string.
+ ///
+ ///
+ /// The settings to use for the request. This parameters is optional. If the value is
+ /// null, an implementation-specific set of default settings will be used for the request.
+ ///
+ ///
+ /// A user-defined callback function for reporting progress of the send operation.
+ /// This parameter is optional. If the value is null, the method does not report
+ /// progress updates to the caller.
+ ///
+ /// Returns a object containing the HTTP status code, headers,
+ /// and body from the REST response.
+ ///
+ /// If is null.
+ /// -or-
+ /// If is null.
+ ///
+ ///
+ /// If is less than or equal to zero.
+ /// -or-
+ /// If is less than zero.
+ ///
+ /// If is not a valid base URI.
+ /// If is not supported by the service.
+ Response Stream(
+ string url,
+ HttpMethod method,
+ Stream content,
+ int bufferSize,
+ long maxReadLength = 0,
+ Dictionary headers = null,
+ Dictionary queryStringParameters = null,
+ RequestSettings settings = null,
+ Action progressUpdated = null);
+
+ ///
+ /// Executes a REST request with a string and user-defined
+ /// callback function for constructing the resulting object.
+ ///
+ ///
+ /// The Boolean argument to indicates whether
+ /// or not an exception was thrown while executing the request. The value is true
+ /// if an exception occurred, otherwise false.
+ ///
+ /// The base URI.
+ /// The HTTP method to use for the request.
+ /// A user-specified function used to construct the resulting
+ /// object from the and a Boolean value specifying whether or not a
+ /// was thrown during the request. If this value is null, this method is equivalent to calling
+ /// .
+ /// The body of the request. If the value is null, the request is sent without a body.
+ ///
+ /// A collection of custom HTTP headers to include with the request. If the value is
+ /// null, no custom headers are added to the HTTP request.
+ ///
+ ///
+ /// A collection of parameters to add to the query string portion of the request URI.
+ /// If the value is null, no parameters are added to the query string.
+ ///
+ ///
+ /// The settings to use for the request. If the value is null, an implementation-specific
+ /// set of default settings will be used for the request.
+ ///
+ /// Returns a object containing the HTTP status code, headers,
+ /// and body from the REST response.
+ /// If is null.
+ /// If is not supported by the service.
+ Response Execute(
+ Uri url,
+ HttpMethod method,
+ Func responseBuilderCallback,
+ string body,
+ Dictionary headers,
+ Dictionary queryStringParameters,
+ RequestSettings settings);
+ }
+}
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/IRetryLogic.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/IRetryLogic.cs
new file mode 100644
index 0000000000..68a4178df6
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/IRetryLogic.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+
+namespace JSIStudios.SimpleRESTServices.Core
+{
+ ///
+ /// Provides the behavior for executing a callback method with configurable success
+ /// values, number of retries, and the retry delay.
+ ///
+ /// The operation return type
+ /// The type of the value used to represent the operation's success or failure
+#if NET35
+ public interface IRetryLogic
+#else
+ public interface IRetryLogic
+#endif
+ {
+ ///
+ /// Executes a user-defined operation with the specified number of retry attempts
+ /// if a failure occurs and delay time between retry attempts.
+ ///
+ /// The user-defined operation to execute.
+ /// The number of times to retry a failed operation. This parameter is optional. The default value is 1.
+ /// The delay between retry operations. This parameter is optional. If the value is null, the default is (no delay).
+ /// Returns the result of a successful execution of . If
+ /// failed and the maximum number of retries has been reached,
+ /// the method returns the last (unsuccessful) result returned by .
+ /// If is null.
+ ///
+ /// If is less than zero.
+ /// -or-
+ /// If is less than .
+ ///
+ T Execute(Func logic, int retryCount = 0, TimeSpan? retryDelay = null);
+
+ ///
+ /// Executes a user-defined operation with the specified "success" values, number of
+ /// retry attempts if a failure occurs, and delay time between retry attempts.
+ ///
+ /// The user-defined operation to execute.
+ /// A collection of values which are generally considered failures, but should be treated as success values for this call.
+ /// The number of times to retry a failed operation. This parameter is optional. The default value is 1.
+ /// The delay between retry operations. This parameter is optional. If the value is null, the default is (no delay).
+ /// Returns the result of a successful execution of . If
+ /// failed and the maximum number of retries has been reached,
+ /// the method returns the last (unsuccessful) result returned by .
+ /// If is null.
+ ///
+ /// If is less than zero.
+ /// -or-
+ /// If is less than .
+ ///
+ T Execute(Func logic, IEnumerable successValues, int retryCount = 0, TimeSpan? retryDelay = null);
+ }
+}
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/ITextCleaner.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/ITextCleaner.cs
new file mode 100644
index 0000000000..d6e9a7b893
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/ITextCleaner.cs
@@ -0,0 +1,7 @@
+namespace JSIStudios.SimpleRESTServices.Core
+{
+ public interface ITextCleaner
+ {
+ string Clean(string text);
+ }
+}
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/IUrlBuilder.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/IUrlBuilder.cs
new file mode 100644
index 0000000000..2441ff4c86
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Core/IUrlBuilder.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+
+namespace JSIStudios.SimpleRESTServices.Core
+{
+ ///
+ /// Represents a builder which can construct a complete URI for a GET or HEAD request
+ /// from a base URI and a collection of query parameters.
+ ///
+ public interface IUrlBuilder
+ {
+ ///
+ /// Constructs a complete URI for an HTTP request using a base URI and a
+ /// collection of query string parameters.
+ ///
+ ///
+ /// If already contains a query string, the specified
+ /// are appended to the existing query string.
+ /// This method does not perform substitution for any template parameters
+ /// which may exist in . If
+ /// is null or empty, is returned unchanged.
+ ///
+ /// The base URI.
+ /// A collection of parameters to place in the URI query string,
+ /// or null if there are no parameters.
+ /// A constructed from and the specified
+ /// .
+ /// If is null.
+ Uri Build(Uri baseUrl, Dictionary queryStringParameters);
+ }
+}
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Properties/AssemblyInfo.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..ed4abb41fc
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Properties/AssemblyInfo.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("SimpleRESTServices")]
+[assembly: AssemblyDescription("A simple set of client side and server side REST helpers")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("JSI Studios")]
+[assembly: AssemblyProduct("SimpleRESTServices")]
+[assembly: AssemblyCopyright("Copyright © JSI Studios 2013")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: CLSCompliant(true)]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("5a741815-e26d-4cb0-a42b-78f7c1611190")]
+
+// Refer to the following issue before changing these version numbers:
+// https://github.com/JSIStudios/SimpleRestServices/issues/53
+[assembly: AssemblyVersion("1.3.0.0")]
+[assembly: AssemblyFileVersion("1.3.0.3")]
+[assembly: AssemblyInformationalVersion("1.3.0.3-dev")]
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Server/EventArgs/RESTRequestCompletedEventArgs.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Server/EventArgs/RESTRequestCompletedEventArgs.cs
new file mode 100644
index 0000000000..4ea560c0f7
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Server/EventArgs/RESTRequestCompletedEventArgs.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace JSIStudios.SimpleRESTServices.Server.EventArgs
+{
+ public class RESTRequestCompletedEventArgs : System.EventArgs
+ {
+ public Guid RequestId { get; private set; }
+
+ public object Response { get; private set; }
+
+ public TimeSpan ExecutionTime { get; private set; }
+
+ public RESTRequestCompletedEventArgs(Guid requestId, object response, TimeSpan executionTime)
+ : base()
+ {
+ RequestId = requestId;
+ Response = response;
+ ExecutionTime = executionTime;
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Server/EventArgs/RESTRequestErrorEventArgs.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Server/EventArgs/RESTRequestErrorEventArgs.cs
new file mode 100644
index 0000000000..63fd3e718d
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Server/EventArgs/RESTRequestErrorEventArgs.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace JSIStudios.SimpleRESTServices.Server.EventArgs
+{
+ public class RESTRequestErrorEventArgs : System.EventArgs
+ {
+ public Guid RequestId { get; private set; }
+
+ public Exception Error { get; private set; }
+
+ public RESTRequestErrorEventArgs(Guid requestId, Exception error)
+ : base()
+ {
+ RequestId = requestId;
+ Error = error;
+ }
+ }
+}
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Server/EventArgs/RESTRequestStartedEventArgs.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Server/EventArgs/RESTRequestStartedEventArgs.cs
new file mode 100644
index 0000000000..6ad9eb21ea
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Server/EventArgs/RESTRequestStartedEventArgs.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace JSIStudios.SimpleRESTServices.Server.EventArgs
+{
+ public class RESTRequestStartedEventArgs : System.EventArgs
+ {
+ public Guid RequestId { get; private set; }
+
+ public string Request { get; private set; }
+
+ public RESTRequestStartedEventArgs(Guid requestId, string request)
+ : base()
+ {
+ RequestId = requestId;
+ Request = request;
+ }
+ }
+}
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Server/FullHtmlTagCleaner.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Server/FullHtmlTagCleaner.cs
new file mode 100644
index 0000000000..276ead2099
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Server/FullHtmlTagCleaner.cs
@@ -0,0 +1,219 @@
+using System;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Web;
+using JSIStudios.SimpleRESTServices.Core;
+
+namespace JSIStudios.SimpleRESTServices.Server
+{
+ public class FullHtmlTagCleaner : ITextCleaner
+ {
+ public static string Name = "FullHtmlTagCleaner";
+
+ private string[] _allowedTags = new string[] { };
+ private string[] _illegalTags = new string[] { "style", "script", "embed", "object" };
+ private static string[] _illegalCharacters = new[] { "", "", "%0", "", "", "%1", "", "", "%2", "", "", "%3", "", "", "%4", "", "", "%5", "", "", "%6", "", "", "%7", "", "", "%8", " ", " ", "%9", "
", "
", "%a", "", "", "%b", "", "", "%c", "
", "
", "%d", "", "", "%e", "", "", "%f", "", "", "%10", "", "", "%11", "", "", "%12", "", "", "%13", "", "", "%14", "", "", "%15", "", "", "%16", "", "", "%17", "", "", "%18", "", "", "%19", "", "", "%1a", "", "", "%1b", "", "", "%1c", "", "", "%1d", "", "", "%1e", "", "", "%1f", "", "", "%7f", "", "", "%80", "", "", "%81", "", "", "%82", "", "", "%83", "", "", "%84", "
", "
", "%85", "", "", "%86", "", "", "%87", "", "", "%88", "", "", "%89", "", "", "%8a", "", "", "%8b", "", "", "%8c", "", "", "%8d", "", "", "%8e", "", "", "%8f", "", "", "%90", "", "", "%91", "", "", "%92", "", "", "%93", "", "", "%94", "", "", "%95", "", "", "%96", "", "", "%97", "", "", "%98", "", "", "%99", "", "", "%9a", "", "", "%9b", "", "", "%9c", "", "", "%9d", "", "", "%9e", "", "", "%9f" };
+
+ public virtual string[] IllegalTags
+ {
+ get { return _illegalTags; }
+ set { _illegalTags = value; }
+ }
+
+ public virtual string[] AllowedTags
+ {
+ get { return _allowedTags; }
+ set { _allowedTags = value; }
+ }
+
+ public static string[] IllegalCharacters
+ {
+ get { return _illegalCharacters; }
+ set { _illegalCharacters = value; }
+ }
+
+ public string Clean(string source)
+ {
+ if (string.IsNullOrEmpty(source))
+ {
+ return null;
+ }
+
+ var allowedTags = AllowedTags;
+ var illegalTags = IllegalTags;
+
+ source = RemoveIllegalCharacters(source);
+ source = illegalTags.Where(illegalTag => illegalTag == "style" || illegalTag == "script").Aggregate(source, (current, illegalTag) => RemoveTagsAndTextBetweenTags("<" + illegalTag + ">", "" + illegalTag + ">", current));
+
+ var array = new char[source.Length];
+ var arrayIndex = 0;
+
+ source = Regex.Replace(source, @"(\r\n)|(\r)|(\n)", " ").Replace("\t", "");
+ var sourceLen = source.Length;
+
+ for (int idx = 0; idx < sourceLen; idx++)
+ {
+ char let = source[idx];
+ if (let == '<')
+ {
+ var len = 0;
+ var idx2 = idx;
+ char let2;
+ string tag = string.Empty;
+
+ do
+ {
+ idx2++;
+ if (idx2 >= sourceLen)
+ {
+ tag = string.Empty;
+ break;
+ }
+
+ len++;
+ let2 = source[idx2];
+
+
+ if (string.IsNullOrEmpty(tag) && (let2 == ' ' || (let2 == '/' && len > 1)))
+ {
+ tag = source.Substring(idx + 1, len - 1);
+ }
+
+ } while (let2 != '>');
+
+ if (string.IsNullOrEmpty(tag))
+ {
+ int start = idx + 1;
+ if (source[start] == '/')
+ {
+ start++;
+ len--;
+ }
+
+ tag = source.Substring(start, len - 1);
+ }
+
+ if (allowedTags.Contains(tag.ToLower()))
+ {
+ for (int i = idx; i <= idx2; i++)
+ {
+ if (i >= sourceLen)
+ break;
+ array[arrayIndex] = source[i];
+ arrayIndex++;
+ }
+
+ }
+
+ idx = idx2;
+
+ if (illegalTags.Contains(tag.ToLower()))
+ {
+ do
+ {
+ idx++;
+ if (idx < (sourceLen - 1))
+ {
+ let = source[idx];
+
+ if (let == '<' && source[++idx] == '/')
+ {
+ len = 0;
+ idx2 = idx;
+ var endTag = string.Empty;
+ do
+ {
+ idx2++;
+ len++;
+ let2 = source[idx2];
+ } while (let2 != '>' && idx2 < sourceLen);
+
+ idx = idx2;
+ let = let2;
+ }
+ }
+ } while (let != '>' && idx < (sourceLen - 1));
+ }
+ }
+ else
+ {
+ array[arrayIndex] = let;
+ arrayIndex++;
+ }
+ }
+ //return new string(array, 0, arrayIndex);
+ var s = new string(array, 0, arrayIndex);
+ return HttpUtility.HtmlDecode(s.Trim());
+
+ }
+
+ private static string RemoveIllegalCharacters(string source)
+ {
+ var sortedArray = from s in IllegalCharacters
+ orderby s.Length descending
+ select s;
+ return sortedArray.Aggregate(source, (current, illegalCharacter) => current.Replace(illegalCharacter, ""));
+ }
+
+ private static string RemoveTagsAndTextBetweenTags(string startTag, string endTag, string strSource)
+ {
+ var temp = strSource;
+
+ // remove the text in start tag if any. eg -
+ // replaces the tag with simple tags like
+ while (temp.IndexOf(startTag.Substring(0, startTag.Length - 1), StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ var result = GetTextAlongWithTag(startTag.Substring(0, startTag.Length - 1), ">", temp, true, true);
+ temp = !string.IsNullOrWhiteSpace(result[0]) ? strSource.Replace(result[0], string.Empty) : temp;
+ if (result[0] != startTag)
+ {
+ // to handle the tags which doesn't have closing tags. eg -
+ if (result[0].IndexOf("/>") != -1)
+ strSource = !string.IsNullOrWhiteSpace(result[0]) ? strSource.Replace(result[0], startTag + endTag) : strSource;
+ else
+ strSource = !string.IsNullOrWhiteSpace(result[0]) ? strSource.Replace(result[0], startTag) : strSource;
+ }
+ }
+
+ //declare safety int variable to prevent infinite loop if any use case is not covered.
+ var iSafetyCheck = 10000;
+
+ //remove all the tags along with the text in between the tags
+ while (strSource.IndexOf(startTag, StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ if (--iSafetyCheck == 0) return strSource;
+ var result = GetTextAlongWithTag(startTag, endTag, strSource, true, true);
+ strSource = !string.IsNullOrWhiteSpace(result[0]) ? strSource.Replace(result[0], string.Empty) : strSource;
+ }
+ return strSource;
+ }
+
+ private static string[] GetTextAlongWithTag(string startTag, string endTag, string strSource, bool removeBegin, bool removeEnd)
+ {
+ string[] result = { string.Empty, string.Empty };
+ var iIndexOfBegin = strSource.IndexOf(startTag, StringComparison.OrdinalIgnoreCase);
+ if (iIndexOfBegin != -1)
+ {
+ if (removeBegin)
+ iIndexOfBegin -= startTag.Length;
+ strSource = strSource.Substring(iIndexOfBegin
+ + startTag.Length);
+ var iEnd = strSource.IndexOf(endTag, StringComparison.OrdinalIgnoreCase);
+ if (iEnd != -1)
+ {
+ if (removeEnd)
+ iEnd += endTag.Length;
+ result[0] = strSource.Substring(0, iEnd);
+ if (iEnd + endTag.Length < strSource.Length)
+ result[1] = strSource.Substring(iEnd
+ + endTag.Length);
+ }
+ else
+ result[0] = strSource;
+ }
+ else
+ result[1] = strSource;
+ return result;
+ }
+ }
+}
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/Server/HtmlTagReplacerAndCleaner.cs b/thirdparty/SimpleRestServices/src/SimpleRestServices/Server/HtmlTagReplacerAndCleaner.cs
new file mode 100644
index 0000000000..58c062c56b
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/Server/HtmlTagReplacerAndCleaner.cs
@@ -0,0 +1,82 @@
+using System.Web;
+using JSIStudios.SimpleRESTServices.Core;
+
+namespace JSIStudios.SimpleRESTServices.Server
+{
+ public class HtmlTagReplacerAndCleaner : ITextCleaner
+ {
+ public static string Name = "ReplaceCleanHtml";
+
+ public string Clean(string source)
+ {
+ if (string.IsNullOrEmpty(source))
+ {
+ return null;
+ }
+
+ source = source.Replace("", "\n\n");
+ source = source.Replace("
", "\n\n");
+ source = source.Replace("
", "\n\n");
+ source = source.Replace("
", "\n\n");
+ source = source.Replace("
", "\n\n");
+ source = source.Replace("
", "\n\n");
+ source = source.Replace("
", "\n\n");
+
+ char[] array = new char[source.Length];
+ int arrayIndex = 0;
+ bool inside = false;
+ string[,] special = new string[,] { { "’", "'" }
+ , { " ", " " }
+ , { """, "\"" }
+ , { "'", "'" }
+ , { "<", "<" }
+ , { "»", ">>" }
+ , { " ", " "}
+ , { "'", "'"}
+ , { ">", ">" }
+ };
+
+ // , { "—", System.Windows.Browser.HttpUtility.HtmlDecode("—")}
+ var sourceLen = source.Length;
+ for (int idx = 0; idx < sourceLen; idx++)
+ {
+ char let = source[idx];
+ if (let == '<')
+ {
+ inside = true;
+ continue;
+ }
+ if (let == '>')
+ {
+ inside = false;
+ continue;
+ }
+ if (!inside)
+ {
+ if (let == '&')
+ {
+ for (int cnt = 0; cnt < special.GetLength(0); cnt++)
+ {
+ var specialLen = special[cnt, 0].Length;
+ if ((idx + specialLen) > sourceLen)
+ {
+ continue;
+ }
+ if (source.Substring(idx, specialLen).Equals(special[cnt, 0]))
+ {
+ let = (char)special[cnt, 1][0];
+ idx += specialLen - 1;
+ break;
+ }
+ }
+ }
+ array[arrayIndex] = let;
+ arrayIndex++;
+ }
+ }
+ //return new string(array, 0, arrayIndex);
+ var s = new string(array, 0, arrayIndex);
+ return HttpUtility.HtmlDecode(s.Trim());
+ }
+ }
+}
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/SimpleRestServices.nuspec b/thirdparty/SimpleRestServices/src/SimpleRestServices/SimpleRestServices.nuspec
new file mode 100644
index 0000000000..a7bb817c67
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/SimpleRestServices.nuspec
@@ -0,0 +1,35 @@
+
+
+
+ SimpleRESTServices
+ 0.0.0
+ SimpleRESTServices
+ Alan Quillin, Sam Harwell
+ Alan Quillin
+ https://github.com/JSIStudios/SimpleRestServices/wiki/License
+ https://github.com/JSIStudios/SimpleRestServices
+ true
+ A simple set of client side and server side REST helpers
+ https://github.com/JSIStudios/SimpleRestServices/releases/tag/v$version$
+ Copyright © JSI Studios 2013
+ REST REST_client
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/SimpleRestServices.v3.5.csproj b/thirdparty/SimpleRestServices/src/SimpleRestServices/SimpleRestServices.v3.5.csproj
new file mode 100644
index 0000000000..75c34981ee
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/SimpleRestServices.v3.5.csproj
@@ -0,0 +1,98 @@
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {1960D862-8AD9-48BE-9290-B7E23EA03D5C}
+ Library
+ Properties
+ JSIStudios.SimpleRESTServices
+ SimpleRESTServices
+ v3.5
+ 512
+ obj\v3.5\
+ ..\
+
+
+ true
+ full
+ false
+ bin\v3.5\Debug\
+ DEBUG;TRACE;NET35
+ prompt
+ 4
+ bin\v3.5\Debug\SimpleRESTServices.xml
+
+
+ pdbonly
+ true
+ bin\v3.5\Release\
+ TRACE;NET35
+ prompt
+ 4
+ bin\v3.5\Release\SimpleRESTServices.xml
+
+
+ ..\..\build\keys\simplerestservices.snk
+ ..\..\build\keys\simplerestservices.dev.snk
+ true
+
+
+
+ False
+ ..\packages\Newtonsoft.Json.5.0.6\lib\net35\Newtonsoft.Json.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ simplerestservices.dev.snk
+
+
+ simplerestservices.snk
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/SimpleRestServices.v4.0.csproj b/thirdparty/SimpleRestServices/src/SimpleRestServices/SimpleRestServices.v4.0.csproj
new file mode 100644
index 0000000000..6a09f5be36
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/SimpleRestServices.v4.0.csproj
@@ -0,0 +1,19 @@
+
+
+ A simple set of client side and server side REST helpers
+ SimpleRESTServices
+ Alan Quillin, Sam Harwell
+ net5.0
+ false
+ SimpleRESTServices
+ SimpleRESTServicesNET50
+ REST, REST_client
+ false
+ true
+ false
+ 1.3.0.2
+
+
+
+
+
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/packages.SimpleRestServices.v3.5.config b/thirdparty/SimpleRestServices/src/SimpleRestServices/packages.SimpleRestServices.v3.5.config
new file mode 100644
index 0000000000..f1148b1484
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/packages.SimpleRestServices.v3.5.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/SimpleRestServices/packages.SimpleRestServices.v4.0.config b/thirdparty/SimpleRestServices/src/SimpleRestServices/packages.SimpleRestServices.v4.0.config
new file mode 100644
index 0000000000..a7ac88acdb
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/SimpleRestServices/packages.SimpleRestServices.v4.0.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/thirdparty/SimpleRestServices/src/Testing/UnitTests/Client/Json/JsonStringSerializerTests.cs b/thirdparty/SimpleRestServices/src/Testing/UnitTests/Client/Json/JsonStringSerializerTests.cs
new file mode 100644
index 0000000000..ccf43f20a6
--- /dev/null
+++ b/thirdparty/SimpleRestServices/src/Testing/UnitTests/Client/Json/JsonStringSerializerTests.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Text;
+using System.Collections.Generic;
+using System.Linq;
+using JSIStudios.SimpleRESTServices.Client;
+using JSIStudios.SimpleRESTServices.Client.Json;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace JSIStudios.SimpleRestServices.Testing.UnitTests.Client.Json
+{
+ [TestClass]
+ public class JsonStringSerializerTests
+ {
+ [TestMethod]
+ public void DeserializeNullStringToNull()
+ {
+ IStringSerializer serializer = new JsonStringSerializer();
+ Assert.IsNull(serializer.Deserialize