diff --git a/.github/workflows/build_publish_master.yml b/.github/workflows/build_publish_master.yml
new file mode 100644
index 0000000..1a7fe10
--- /dev/null
+++ b/.github/workflows/build_publish_master.yml
@@ -0,0 +1,33 @@
+name: Build Master
+on:
+ push:
+ paths-ignore:
+ - "**/*.md"
+ branches: [ master ]
+
+jobs:
+ build:
+
+ strategy:
+ matrix:
+ os: ['ubuntu-latest']
+ dotnet-version: ['3.1.201']
+ project : ['Json-Rpc']
+
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup .NET Core
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: ${{matrix.dotnet-version}}
+ - name: Install dependencies
+ run: dotnet restore
+ - name: Build
+ run: dotnet build ${{matrix.project}} --configuration Release
+ - name: Test
+ run: dotnet test AustinHarris.JsonRpcTestN
+ # Publish
+ - name: publish nuget version change
+ run: dotnet nuget push Json-Rpc/bin/Release/*.nupkg --skip-duplicate --source "https://www.nuget.org" --api-key ${{secrets.NugetKey}} # API key for the NuGet feed
diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml
new file mode 100644
index 0000000..f3e06e9
--- /dev/null
+++ b/.github/workflows/build_pull_request.yml
@@ -0,0 +1,35 @@
+name: Pull Reqest
+on:
+ pull_request:
+ paths-ignore:
+ - "**/*.md"
+ branches: [ master ]
+
+jobs:
+ build:
+
+ strategy:
+ matrix:
+ os: ['windows-latest','ubuntu-latest']
+ dotnet-version: ['3.1.201']
+ project : ['Json-Rpc']
+
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup .NET Core
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: ${{matrix.dotnet-version}}
+ - name: Install dependencies
+ run: dotnet restore
+ - name: Build
+ run: dotnet build ${{matrix.project}} --configuration Release --version-suffix ci-${{ github.run_id }}-${{ github.run_number }}
+ - name: Test
+ run: dotnet test AustinHarris.JsonRpcTestN
+ # Publish
+ - name: publish nuget version change
+ if: ${{matrix.os == 'ubuntu-latest'}}
+ run: dotnet nuget push Json-Rpc/bin/Release/*.nupkg --skip-duplicate --source "https://www.nuget.org" --api-key ${{secrets.NugetKey}} # API key for the NuGet feed
+
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 11908c9..6ea46be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -107,3 +107,4 @@ _UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
.nuget/NuGet.exe
+.vs/
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index f6ab2fa..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-language: csharp
-solution: AustinHarris.JsonRpc.sln
-mono:
- - latest
- - 3.12.0
- - 3.10.0
- - 3.8.0
-install:
- - nuget restore AustinHarris.JsonRpc.sln
- - nuget install NUnit.Runners -Version 2.6.4 -OutputDirectory testrunner
-script:
- - xbuild /p:Configuration=Release AustinHarris.JsonRpc.sln
- - mono ./testrunner/NUnit.Runners.2.6.4/tools/nunit-console.exe ./AustinHarris.JsonRpcTestN/bin/Release/AustinHarris.JsonRpcTestN.dll
\ No newline at end of file
diff --git a/AustinHarris.JsonRpc.AspNet/AustinHarris.JsonRpc.AspNet.csproj b/AustinHarris.JsonRpc.AspNet/AustinHarris.JsonRpc.AspNet.csproj
index bf84656..3470e2c 100644
--- a/AustinHarris.JsonRpc.AspNet/AustinHarris.JsonRpc.AspNet.csproj
+++ b/AustinHarris.JsonRpc.AspNet/AustinHarris.JsonRpc.AspNet.csproj
@@ -36,8 +36,8 @@
4
-
- ..\packages\Newtonsoft.Json.8.0.2\lib\net40\Newtonsoft.Json.dll
+
+ ..\packages\Newtonsoft.Json.12.0.3\lib\net40\Newtonsoft.Json.dll
True
@@ -47,6 +47,7 @@
+
diff --git a/AustinHarris.JsonRpc.AspNet/JsonRpcHandler.cs b/AustinHarris.JsonRpc.AspNet/JsonRpcHandler.cs
index 7f2cf04..6a38c39 100644
--- a/AustinHarris.JsonRpc.AspNet/JsonRpcHandler.cs
+++ b/AustinHarris.JsonRpc.AspNet/JsonRpcHandler.cs
@@ -1,135 +1,16 @@
-using System;
-using System.IO;
-using System.IO.Compression;
-using System.Text;
-using System.Web;
+using AustinHarris.JsonRpc.AspNet;
namespace AustinHarris.JsonRpc.Handlers.AspNet
{
- public class JsonRpcHandler : IHttpAsyncHandler
+ ///
+ /// Used default SessionId
+ /// For routing use JsonRpcHandlerBase
+ ///
+ public class JsonRpcHandler : JsonRpcHandlerBase
{
- #region Fields
- ///
- /// UTF8 Encoding without BOM.
- ///
- static Encoding UTF8Encoding = new UTF8Encoding(false);
- #endregion
-
- #region IHttpHandler Members
-
- public bool IsReusable
- {
- get { return true; }
- }
-
- public void ProcessRequest(HttpContext context)
- {
- // not used
- }
-
- #endregion
-
- #region IHttpAsyncHandler Members
-
- ///
- /// Initiates an asynchronous call to the HTTP handler.
- ///
- /// An object that provides references to intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.
- /// The to call when the asynchronous method call is complete. If is null, the delegate is not called.
- /// Any extra data needed to process the request.
- ///
- /// An that contains information about the status of the process.
- ///
- public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
- {
- var async = new JsonRpcStateAsync(cb, context);
- async.JsonRpc = GetJsonRpcString(context.Request);
- JsonRpcProcessor.Process(async,context.Request);
- return async;
- }
-
- private static string GetJsonRpcString(System.Web.HttpRequest request)
+ protected override string GetSessionId()
{
- string json = string.Empty;
- if (request.RequestType == "GET")
- {
- json = request.Params["jsonrpc"] ?? string.Empty;
- }
- else if (request.RequestType == "POST")
- {
- if (request.ContentType == "application/x-www-form-urlencoded")
- {
- json = request.Params["jsonrpc"] ?? string.Empty;
- }
- else
- {
- json = new StreamReader(request.InputStream).ReadToEnd();
- }
- }
- return json;
+ return Handler.DefaultSessionId();
}
-
- ///
- /// Provides an asynchronous process End method when the process ends.
- ///
- /// An that contains information about the status of the process.
- public void EndProcessRequest(IAsyncResult result)
- {
- var state = result as JsonRpcStateAsync;
- if (state != null)
- {
- var r = state.Result;
- var callback = ((HttpContext)state.AsyncState).Request.Params["callback"];
- if (!string.IsNullOrWhiteSpace(callback))
- {
- r = string.Format("{0}({1})", callback, r);
- }
-
- // try to compress the response data.
- // fix me: compression filters in IHttpModule always failed for IHttpAsyncHandler
- CompressResponseIfPossible(((HttpContext)state.AsyncState).Request, ((HttpContext)state.AsyncState).Response, r, UTF8Encoding);
- }
- }
-
- #endregion
-
- #region Utility methods
-
- ///
- /// Transfer the result data compressed when the client accepts gzip.
- ///
- /// A HttpRequest object that represents the HTTP request.
- /// A HttpResponse object that represents the HTTP response to be sent to the client.
- /// The string data to be sent to the client.
- /// The Encoding to be used to encode as the result.
- static void CompressResponseIfPossible(HttpRequest request, HttpResponse response, String result, Encoding encoding)
- {
- string AcceptEncoding = request.Headers["Accept-Encoding"];
- if (AcceptEncoding != null && AcceptEncoding.Contains("gzip"))
- {
- //response.Headers.Remove("Content-Encoding");
- response.AddHeader("Content-Encoding", "gzip");
-
- using (var gstream = new GZipStream(response.OutputStream, CompressionMode.Compress))
- using (var writer = new StreamWriter(gstream, encoding))
- {
- writer.Write(result);
- writer.Flush();
- }
- }
- else
- {
- using (StreamWriter writer = new StreamWriter(response.OutputStream, encoding))
- {
- writer.Write(result);
- writer.Flush();
- }
- }
-
- response.End();
- }
-
-
- #endregion
}
-}
+}
\ No newline at end of file
diff --git a/AustinHarris.JsonRpc.AspNet/JsonRpcHandlerBase.cs b/AustinHarris.JsonRpc.AspNet/JsonRpcHandlerBase.cs
new file mode 100644
index 0000000..8d61777
--- /dev/null
+++ b/AustinHarris.JsonRpc.AspNet/JsonRpcHandlerBase.cs
@@ -0,0 +1,138 @@
+using System;
+using System.IO;
+using System.IO.Compression;
+using System.Text;
+using System.Web;
+
+namespace AustinHarris.JsonRpc.AspNet
+{
+ public abstract class JsonRpcHandlerBase : IHttpAsyncHandler
+ {
+ #region Fields
+ ///
+ /// UTF8 Encoding without BOM.
+ ///
+ private static readonly Encoding Utf8Encoding = new UTF8Encoding(false);
+
+ protected abstract string GetSessionId();
+ #endregion
+
+ #region IHttpHandler Members
+
+ public bool IsReusable
+ {
+ get { return true; }
+ }
+
+ public void ProcessRequest(HttpContext context)
+ {
+ // not used
+ }
+
+ #endregion
+
+ #region IHttpAsyncHandler Members
+
+ ///
+ /// Initiates an asynchronous call to the HTTP handler.
+ ///
+ /// An object that provides references to intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.
+ /// The to call when the asynchronous method call is complete. If is null, the delegate is not called.
+ /// Any extra data needed to process the request.
+ ///
+ /// An that contains information about the status of the process.
+ ///
+ public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
+ {
+ var async = new JsonRpcStateAsync(cb, context);
+ async.JsonRpc = GetJsonRpcString(context.Request);
+ JsonRpcProcessor.Process(GetSessionId(),async, context.Request);
+ return async;
+ }
+
+ private static string GetJsonRpcString(System.Web.HttpRequest request)
+ {
+ string json = string.Empty;
+ if (request.RequestType == "GET")
+ {
+ json = request.Params["jsonrpc"] ?? string.Empty;
+ }
+ else if (request.RequestType == "POST")
+ {
+ if (request.ContentType == "application/x-www-form-urlencoded")
+ {
+ json = request.Params["jsonrpc"] ?? string.Empty;
+ }
+ else
+ {
+ json = new StreamReader(request.InputStream).ReadToEnd();
+ }
+ }
+ return json;
+ }
+
+ ///
+ /// Provides an asynchronous process End method when the process ends.
+ ///
+ /// An that contains information about the status of the process.
+ public void EndProcessRequest(IAsyncResult result)
+ {
+ var state = result as JsonRpcStateAsync;
+ if (state == null) return;
+
+ var stateResult = state.Result;
+ var callback = ((HttpContext)state.AsyncState).Request.Params["callback"];
+ if (!string.IsNullOrWhiteSpace(callback))
+ {
+ stateResult = string.Format("{0}({1})", callback, stateResult);
+ }
+
+ // try to compress the response data.
+ // fix me: compression filters in IHttpModule always failed for IHttpAsyncHandler
+ CompressResponseIfPossible(((HttpContext)state.AsyncState).Request, ((HttpContext)state.AsyncState).Response, stateResult, Utf8Encoding);
+ }
+
+ #endregion
+
+ #region Utility methods
+
+ ///
+ /// Transfer the result data compressed when the client accepts gzip.
+ ///
+ /// A HttpRequest object that represents the HTTP request.
+ /// A HttpResponse object that represents the HTTP response to be sent to the client.
+ /// The string data to be sent to the client.
+ /// The Encoding to be used to encode as the result.
+ static void CompressResponseIfPossible(HttpRequest request, HttpResponse response, String result, Encoding encoding)
+ {
+ response.ContentType = "application/json-rpc";
+ string acceptEncoding = request.Headers["Accept-Encoding"];
+ if (acceptEncoding != null && acceptEncoding.Contains("gzip"))
+ {
+ //response.Headers.Remove("Content-Encoding");
+ response.AddHeader("Content-Encoding", "gzip");
+
+ using (var gstream = new GZipStream(response.OutputStream, CompressionMode.Compress))
+ using (var writer = new StreamWriter(gstream, encoding))
+ {
+ writer.Write(result);
+ writer.Flush();
+ }
+ }
+ else
+ {
+ using (StreamWriter writer = new StreamWriter(response.OutputStream, encoding))
+ {
+ writer.Write(result);
+ writer.Flush();
+ }
+ }
+
+ response.End();
+ }
+
+
+ #endregion
+
+ }
+}
diff --git a/AustinHarris.JsonRpc.AspNet/packages.config b/AustinHarris.JsonRpc.AspNet/packages.config
index 28e5e44..0fa4e01 100644
--- a/AustinHarris.JsonRpc.AspNet/packages.config
+++ b/AustinHarris.JsonRpc.AspNet/packages.config
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/AustinHarris.JsonRpc.sln b/AustinHarris.JsonRpc.sln
index 3fa6f16..6cf1c28 100644
--- a/AustinHarris.JsonRpc.sln
+++ b/AustinHarris.JsonRpc.sln
@@ -1,19 +1,12 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2012
-VisualStudioVersion = 12.0.30723.0
+# Visual Studio 2013
+VisualStudioVersion = 12.0.40629.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BBFBBA8A-2F75-422C-ACCD-D05A6EF7244C}"
- ProjectSection(SolutionItems) = preProject
- AustinHarris.JsonRpc.vsmdi = AustinHarris.JsonRpc.vsmdi
- Local.testsettings = Local.testsettings
- TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings
- EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AustinHarris.JsonRpc", "Json-Rpc\AustinHarris.JsonRpc.csproj", "{24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AustinHarris.JsonRpc.AspNet", "AustinHarris.JsonRpc.AspNet\AustinHarris.JsonRpc.AspNet.csproj", "{FFFDEBBC-93F5-4A22-9EC5-D86A4A792DBB}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AustinHarris.JsonRpcTestN", "AustinHarris.JsonRpcTestN\AustinHarris.JsonRpcTestN.csproj", "{8569B076-5A8B-4D6A-B75D-EF75A390AA5F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestServer_Console", "TestServer_Console\TestServer_Console.csproj", "{31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}"
@@ -44,22 +37,6 @@ Global
{24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}.Release|Mixed Platforms.Build.0 = Release|x86
{24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}.Release|x86.ActiveCfg = Release|x86
{24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}.Release|x86.Build.0 = Release|x86
- {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Debug|ARM.Build.0 = Debug|Any CPU
- {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Debug|x86.ActiveCfg = Debug|x86
- {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Debug|x86.Build.0 = Debug|x86
- {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Release|Any CPU.Build.0 = Release|Any CPU
- {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Release|ARM.ActiveCfg = Release|Any CPU
- {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Release|ARM.Build.0 = Release|Any CPU
- {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Release|x86.ActiveCfg = Release|x86
- {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Release|x86.Build.0 = Release|x86
{8569B076-5A8B-4D6A-B75D-EF75A390AA5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8569B076-5A8B-4D6A-B75D-EF75A390AA5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8569B076-5A8B-4D6A-B75D-EF75A390AA5F}.Debug|ARM.ActiveCfg = Debug|Any CPU
@@ -76,20 +53,22 @@ Global
{8569B076-5A8B-4D6A-B75D-EF75A390AA5F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{8569B076-5A8B-4D6A-B75D-EF75A390AA5F}.Release|x86.ActiveCfg = Release|Any CPU
{8569B076-5A8B-4D6A-B75D-EF75A390AA5F}.Release|x86.Build.0 = Release|Any CPU
- {FFFDEBBC-93F5-4A22-9EC5-D86A4A792DBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {FFFDEBBC-93F5-4A22-9EC5-D86A4A792DBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FFFDEBBC-93F5-4A22-9EC5-D86A4A792DBB}.Debug|ARM.ActiveCfg = Debug|Any CPU
- {FFFDEBBC-93F5-4A22-9EC5-D86A4A792DBB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {FFFDEBBC-93F5-4A22-9EC5-D86A4A792DBB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {FFFDEBBC-93F5-4A22-9EC5-D86A4A792DBB}.Debug|x86.ActiveCfg = Debug|Any CPU
- {FFFDEBBC-93F5-4A22-9EC5-D86A4A792DBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {FFFDEBBC-93F5-4A22-9EC5-D86A4A792DBB}.Release|Any CPU.Build.0 = Release|Any CPU
- {FFFDEBBC-93F5-4A22-9EC5-D86A4A792DBB}.Release|ARM.ActiveCfg = Release|Any CPU
- {FFFDEBBC-93F5-4A22-9EC5-D86A4A792DBB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {FFFDEBBC-93F5-4A22-9EC5-D86A4A792DBB}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {FFFDEBBC-93F5-4A22-9EC5-D86A4A792DBB}.Release|x86.ActiveCfg = Release|Any CPU
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
+ {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Debug|ARM.Build.0 = Debug|Any CPU
+ {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Debug|x86.ActiveCfg = Debug|x86
+ {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Debug|x86.Build.0 = Debug|x86
+ {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Release|ARM.ActiveCfg = Release|Any CPU
+ {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Release|ARM.Build.0 = Release|Any CPU
+ {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Release|x86.ActiveCfg = Release|x86
+ {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/AustinHarris.JsonRpcTestN/AustinHarris.JsonRpcTestN.csproj b/AustinHarris.JsonRpcTestN/AustinHarris.JsonRpcTestN.csproj
index 0c4bd29..c5db038 100644
--- a/AustinHarris.JsonRpcTestN/AustinHarris.JsonRpcTestN.csproj
+++ b/AustinHarris.JsonRpcTestN/AustinHarris.JsonRpcTestN.csproj
@@ -1,69 +1,21 @@
-
-
+
+
- Debug
- AnyCPU
- {8569B076-5A8B-4D6A-B75D-EF75A390AA5F}
- Library
- AustinHarris.JsonRpcTestN
- AustinHarris.JsonRpcTestN
- v4.5
- ..\
- true
+ Austin Harris
+ netcoreapp3.0;netcoreapp3.1
-
- true
- full
- false
- bin\Debug
- DEBUG;
- prompt
- 4
- false
-
-
- full
- true
- bin\Release
- prompt
- 4
- false
-
-
-
- ..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll
- True
-
-
- ..\packages\NUnit.2.6.4\lib\nunit.framework.dll
- True
-
-
-
-
-
-
-
-
-
-
- Designer
-
-
+
+
-
- {24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}
- AustinHarris.JsonRpc
-
+
+
+
+
+
+
-
+
-
-
-
- This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
+
\ No newline at end of file
diff --git a/AustinHarris.JsonRpcTestN/Test.cs b/AustinHarris.JsonRpcTestN/Test.cs
index 2a53969..ed46543 100644
--- a/AustinHarris.JsonRpcTestN/Test.cs
+++ b/AustinHarris.JsonRpcTestN/Test.cs
@@ -47,7 +47,7 @@ public void TestCanCreateMultipleServicesOfSameTypeInTheirOwnSessions()
for (int i = 0; i < 100; i++)
{
- ServiceBinder.bindService(i.ToString(), () => Poco.WithOffset(i));
+ ServiceBinder.BindService(i.ToString(), Poco.WithOffset(i));
}
for (int i = 0; i < 100; i++)
@@ -64,14 +64,11 @@ public void TestCanCreateMultipleServicesOfSameTypeInTheirOwnSessions()
public void TestCanCreateAndRemoveSession()
{
var h = JsonRpc.Handler.GetSessionHandler("this one");
-
- h.Register("workie", new Func(x => "workie ... " + x));
-
var metadata = new System.Collections.Generic.List> {
Tuple.Create ("sooper", typeof(string)),
Tuple.Create ("returns", typeof(string))
}.ToDictionary(x => x.Item1, x => x.Item2);
- h.MetaData.AddService("workie", metadata, new System.Collections.Generic.Dictionary());
+ h.RegisterFuction("workie", metadata, new System.Collections.Generic.Dictionary(),new Func(x => "workie ... " + x));
string request = @"{method:'workie',params:{'sooper':'good'},id:1}";
string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"workie ... good\",\"id\":1}";
@@ -82,14 +79,14 @@ public void TestCanCreateAndRemoveSession()
var actual1 = JObject.Parse(result.Result);
var expected1 = JObject.Parse(expectedResult);
- Assert.AreEqual(expected1, actual1);
+ Assert.IsTrue(JToken.DeepEquals(expected1, actual1));
h.Destroy();
var result2 = JsonRpcProcessor.Process("this one", request);
result2.Wait();
- Assert.AreEqual(JObject.Parse(expectedResultAfterDestroy), JObject.Parse(result2.Result));
+ Assert.IsTrue(JToken.DeepEquals(JObject.Parse(expectedResultAfterDestroy), JObject.Parse(result2.Result)));
}
[Test()]
@@ -116,40 +113,17 @@ public void NullableDateTimeToNullableDateTime()
Assert.AreEqual(expectedDate, acutalDate);
}
- [Test()]
- public void NullableFloatToNullableFloat()
+ [TestCase(@"{method:'NullableFloatToNullableFloat',params:[1.2345],id:1}", ExpectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1.2345,\"id\":1}")]
+ [TestCase(@"{method:'NullableFloatToNullableFloat',params:[3.14159],id:1}", ExpectedResult = "{\"jsonrpc\":\"2.0\",\"result\":3.14159,\"id\":1}")]
+ [TestCase(@"{method:'NullableFloatToNullableFloat',params:[null],id:1}", ExpectedResult = "{\"jsonrpc\":\"2.0\",\"result\":null,\"id\":1}")]
+ public string NullableFloatToNullableFloat(string request)
{
- string request = @"{method:'NullableFloatToNullableFloat',params:[1.2345],id:1}";
- string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1.2345,\"id\":1}";
var result = JsonRpcProcessor.Process(request);
result.Wait();
- Assert.AreEqual(result.Result, expectedResult);
- Assert.AreEqual(expectedResult, result.Result);
+ return result.Result;
}
-
- [Test()]
- public void NullableFloatToNullableFloat3()
- {
- string request = @"{method:'NullableFloatToNullableFloat',params:[3.14159],id:1}";
- string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":3.14159,\"id\":1}";
- var result = JsonRpcProcessor.Process(request);
- result.Wait();
- Assert.AreEqual(result.Result, expectedResult);
- Assert.AreEqual(expectedResult, result.Result);
- }
-
-
- [Test()]
- public void NullableFloatToNullableFloat2()
- {
- string request = @"{method:'NullableFloatToNullableFloat',params:[null],id:1}";
- string expectedResult = "{\"jsonrpc\":\"2.0\",\"id\":1}";
- var result = JsonRpcProcessor.Process(request);
- result.Wait();
- Assert.AreEqual(result.Result, expectedResult);
- Assert.AreEqual(expectedResult, result.Result);
- }
-
+
+
[Test()]
public void DecimalToNullableDecimal()
{
@@ -199,7 +173,7 @@ public void StringToRefException()
string expectedResult = "{\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"refException worked\",\"code\":-1,\"data\":null},\"id\":1}";
var result = JsonRpcProcessor.Process(request);
result.Wait();
- Assert.AreEqual(JObject.Parse(expectedResult), JObject.Parse(result.Result));
+ Assert.IsTrue(JToken.DeepEquals(JObject.Parse(expectedResult), JObject.Parse(result.Result)));
}
[Test()]
@@ -1405,6 +1379,18 @@ public void TestOptionalParametersBoolsAndStrings()
Assert.IsFalse(result.Result.Contains("error"));
Assert.AreEqual(expectedResult, result.Result);
}
+
+ [TestCase("{method:\"TestDifferentOptionalParameters\",params:{location:\"loc1\", uid:\"abc123\", wavelengths: [0.0], traces: [0.0]},id:1}", ExpectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"this is the requested measurement\",\"id\":1}")]
+ [TestCase("{method:\"TestDifferentOptionalParameters\",params:{uid:\"abc123\", wavelengths: [0.0], traces: [0.0]},id:1}", ExpectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"this is the requested measurement\",\"id\":1}")]
+ [TestCase("{method:\"TestDifferentOptionalParameters\",params:{location:\"loc1\", uid:\"abc123\", traces: [0.0]},id:1}", ExpectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"this is the requested measurement\",\"id\":1}")]
+ [TestCase("{method:\"TestDifferentOptionalParameters\",params:{location:\"loc1\", uid:\"abc123\", wavelengths: [0.0]},id:1}", ExpectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"this is the requested measurement\",\"id\":1}")]
+ [TestCase("{method:\"TestDifferentOptionalParameters\",params:{uid:\"abc123\", wavelengths: [0.0]},id:1}", ExpectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"this is the requested measurement\",\"id\":1}")]
+ public string TestDifferentOptionalParametersNamedWorking(string request)
+ {
+ var result = JsonRpcProcessor.Process(request);
+ result.Wait();
+ return result.Result;
+ }
[Test()]
public void TestBatchResultWrongRequests()
@@ -1440,6 +1426,17 @@ public void TestEmptyBatchResult()
Assert.IsTrue(string.IsNullOrEmpty(result.Result));
}
+
+ [Test()]
+ public void TestNotificationVoidResult()
+ {
+ var secondRequest = @"{""jsonrpc"":""2.0"",""method"":""Notify"",""params"":[""Hello World!""], ""id"":73}";
+ var result = JsonRpcProcessor.Process(secondRequest);
+ result.Wait();
+ Console.WriteLine(result.Result);
+ Assert.IsTrue(result.Result.Contains("result"), "Json Rpc 2.0 Spec - 'result' - This member is REQUIRED on success. A function that returns void should have the result property included even though the value may be null.");
+ }
+
[Test()]
public void TestLeftOutParams()
{
@@ -1582,13 +1579,11 @@ public void TestPreProcessOnSession()
PreProcessHandlerLocal preHandler = new PreProcessHandlerLocal();
h.SetPreProcessHandler(new PreProcessHandler(preHandler.PreProcess));
- h.Register("workie", new Func(x => "workie ... " + x));
-
var metadata = new System.Collections.Generic.List> {
Tuple.Create ("sooper", typeof(string)),
Tuple.Create ("returns", typeof(string))
}.ToDictionary(x => x.Item1, x => x.Item2);
- h.MetaData.AddService("workie", metadata, new System.Collections.Generic.Dictionary());
+ h.RegisterFuction("workie", metadata, new System.Collections.Generic.Dictionary(),new Func(x => "workie ... " + x));
string request = @"{method:'workie',params:{'sooper':'good'},id:1}";
string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"workie ... good\",\"id\":1}";
@@ -1598,7 +1593,7 @@ public void TestPreProcessOnSession()
var actual1 = JObject.Parse(result.Result);
var expected1 = JObject.Parse(expectedResult);
- Assert.AreEqual(expected1, actual1);
+ Assert.IsTrue(JToken.DeepEquals(expected1, actual1));
Assert.AreEqual(1, preHandler.run);
h.Destroy();
@@ -1607,7 +1602,7 @@ public void TestPreProcessOnSession()
result2.Wait();
Assert.AreEqual(1, preHandler.run);
- Assert.AreEqual(JObject.Parse(expectedResultAfterDestroy), JObject.Parse(result2.Result));
+ Assert.IsTrue(JToken.DeepEquals(JObject.Parse(expectedResultAfterDestroy), JObject.Parse(result2.Result)));
}
class PostProcessHandlerLocal
@@ -1824,13 +1819,11 @@ public void TestPostProcessOnSession()
PostProcessHandlerLocal postHandler = new PostProcessHandlerLocal(false);
h.SetPostProcessHandler(new PostProcessHandler(postHandler.PostProcess));
- h.Register("workie", new Func(x => "workie ... " + x));
-
var metadata = new System.Collections.Generic.List> {
Tuple.Create ("sooper", typeof(string)),
Tuple.Create ("returns", typeof(string))
}.ToDictionary(x => x.Item1, x => x.Item2);
- h.MetaData.AddService("workie", metadata, new System.Collections.Generic.Dictionary());
+ h.RegisterFuction("workie", metadata, new System.Collections.Generic.Dictionary(), new Func(x => "workie ... " + x));
string request = @"{method:'workie',params:{'sooper':'good'},id:1}";
string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"workie ... good\",\"id\":1}";
@@ -1840,7 +1833,7 @@ public void TestPostProcessOnSession()
var actual1 = JObject.Parse(result.Result);
var expected1 = JObject.Parse(expectedResult);
- Assert.AreEqual(expected1, actual1);
+ Assert.IsTrue(JToken.DeepEquals(expected1, actual1));
Assert.AreEqual(1, postHandler.run);
h.Destroy();
@@ -1849,7 +1842,7 @@ public void TestPostProcessOnSession()
result2.Wait();
Assert.AreEqual(1, postHandler.run);
- Assert.AreEqual(JObject.Parse(expectedResultAfterDestroy), JObject.Parse(result2.Result));
+ Assert.IsTrue(JToken.DeepEquals(JObject.Parse(expectedResultAfterDestroy), JObject.Parse(result2.Result)));
}
[Test()]
@@ -1862,6 +1855,42 @@ public void TestExtraParameters()
Assert.IsTrue(result.Result.Contains("\"code\":-32602"));
}
+ [Test()]
+ public void TestExtraPositionalParameters()
+ {
+ string request = @"{method:'ReturnsDateTime',params:[1,2,'mytext'],id:1}";
+ var result = JsonRpcProcessor.Process(request);
+ result.Wait();
+ Assert.IsTrue(result.Result.Contains("error"));
+ Assert.IsTrue(result.Result.Contains("\"code\":-32602"));
+ }
+
+ [Test()]
+ public void TestCustomParameterName()
+ {
+ Func request = (string paramName) => String.Format("{{method:'TestCustomParameterName',params:{{ {0}:'some string'}},id:1}}", paramName);
+ string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":1}";
+ // Check custom param name specified in attribute works
+ var result = JsonRpcProcessor.Process(request("myCustomParameter"));
+ result.Wait();
+ Assert.AreEqual(JObject.Parse(expectedResult), JObject.Parse(result.Result));
+ // Check method can't be used with its actual parameter name
+ result = JsonRpcProcessor.Process(request("arg"));
+ result.Wait();
+ StringAssert.Contains("-32602", result.Result); // check for 'invalid params' error code
+ }
+
+ [Test()]
+ public void TestCustomParameterWithNoSpecificName()
+ {
+ Func request = (string paramName) => String.Format("{{method:'TestCustomParameterWithNoSpecificName',params:{{ {0}:'some string'}},id:1}}", paramName);
+ string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":1}";
+ // Check method can be used with its parameter name
+ var result = JsonRpcProcessor.Process(request("arg"));
+ result.Wait();
+ Assert.AreEqual(JObject.Parse(expectedResult), JObject.Parse(result.Result));
+ }
+
[Test]
public void TestNestedReturnType()
{
@@ -1872,6 +1901,26 @@ public void TestNestedReturnType()
Assert.AreEqual(expected, result.Result);
}
+ [Test()]
+ public void TestWrongParamType()
+ {
+ string request = @"{method:'TestOptionalParamdouble',params:{input:'mytext'},id:1}";
+ var result = JsonRpcProcessor.Process(request);
+ result.Wait();
+ Assert.IsTrue(result.Result.Contains("error"));
+ Assert.IsTrue(result.Result.Contains("\"code\":-32603"));
+ }
+
+ [Test()]
+ public void TestWrongIdType()
+ {
+ string request = @"{method:'TestOptionalParamdouble',params:{input:5},id:{what:4,that:3}}";
+ var result = JsonRpcProcessor.Process(request);
+ result.Wait();
+ Assert.IsTrue(result.Result.Contains("error"));
+ Assert.IsTrue(result.Result.Contains("\"code\":-32600"));
+ }
+
private static void AssertJsonAreEqual(string expectedJson, string actualJson)
{
Newtonsoft.Json.Linq.JObject expected = (Newtonsoft.Json.Linq.JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(expectedJson);
@@ -1899,6 +1948,8 @@ private static void AssertJsonAreEqual(JToken expectedJson, JToken actualJson, s
private static void AssertJsonAreEqual(JObject expectedJson, JObject actualJson, string path)
{
+ Console.WriteLine("expected: {0}", expectedJson);
+ Console.WriteLine("actual : {0}", actualJson);
Assert.AreEqual(expectedJson.Count, actualJson.Count, "Count of json object at " + path);
for (var expectedElementsEnumerator = expectedJson.GetEnumerator(); expectedElementsEnumerator.MoveNext(); )
{
diff --git a/AustinHarris.JsonRpcTestN/packages.config b/AustinHarris.JsonRpcTestN/packages.config
deleted file mode 100644
index d274cb4..0000000
--- a/AustinHarris.JsonRpcTestN/packages.config
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/AustinHarris.JsonRpcTestN/service.cs b/AustinHarris.JsonRpcTestN/service.cs
index 5da391f..b34d957 100644
--- a/AustinHarris.JsonRpcTestN/service.cs
+++ b/AustinHarris.JsonRpcTestN/service.cs
@@ -79,6 +79,19 @@ private string devideByZero(string s)
return s + j / i;
}
+
+ [JsonRpcMethod]
+ private bool TestCustomParameterName([JsonRpcParam("myCustomParameter")] string arg)
+ {
+ return true;
+ }
+
+ [JsonRpcMethod]
+ private bool TestCustomParameterWithNoSpecificName([JsonRpcParam] string arg)
+ {
+ return true;
+ }
+
[JsonRpcMethod]
private string StringToRefException(string s, ref JsonRpcException refException)
{
@@ -376,6 +389,12 @@ public TreeNode TestNestedReturnType()
}
};
}
+
+ [JsonRpcMethod]
+ private string TestDifferentOptionalParameters(string uid, string location = null, List traces = null, List wavelengths = null)
+ {
+ return "this is the requested measurement";
+ }
}
}
diff --git a/Json-Rpc/Attributes.cs b/Json-Rpc/Attributes.cs
index dd78282..1592511 100644
--- a/Json-Rpc/Attributes.cs
+++ b/Json-Rpc/Attributes.cs
@@ -24,4 +24,27 @@ public string JsonMethodName
get { return jsonMethodName; }
}
}
+
+ ///
+ /// Used to assign JsonRpc parameter name to method argument.
+ ///
+ [AttributeUsage(AttributeTargets.Parameter, Inherited = false, AllowMultiple = false)]
+ public sealed class JsonRpcParamAttribute : Attribute
+ {
+ readonly string jsonParamName;
+
+ ///
+ /// Used to assign JsonRpc parameter name to method argument.
+ ///
+ /// Lets you specify the parameter name as it will be referred to by JsonRpc.
+ public JsonRpcParamAttribute(string jsonParamName = "")
+ {
+ this.jsonParamName = jsonParamName;
+ }
+
+ public string JsonParamName
+ {
+ get { return jsonParamName; }
+ }
+ }
}
diff --git a/Json-Rpc/AustinHarris.JsonRpc.csproj b/Json-Rpc/AustinHarris.JsonRpc.csproj
index 9d67f72..f6a680d 100644
--- a/Json-Rpc/AustinHarris.JsonRpc.csproj
+++ b/Json-Rpc/AustinHarris.JsonRpc.csproj
@@ -1,98 +1,27 @@
-
-
-
- Debug
- x86
- {24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}
- Library
- Properties
- AustinHarris.JsonRpc
- AustinHarris.JsonRpc
-
-
- 512
- SAK
- SAK
- SAK
- SAK
- ..\..\CoiniumServ\build\
- true
- v4.0
-
-
- AnyCPU
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
- false
-
-
- AnyCPU
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
- false
-
-
- AnyCPU
- bin\Debug\
- TRACE;DEBUG
- 4
- false
-
-
- AnyCPU
- bin\Release\
- 4
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..\packages\Newtonsoft.Json.8.0.2\lib\net40\Newtonsoft.Json.dll
- True
-
-
-
-
-
-
+
+
- This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+ Austin Harris
+ Json-Rpc.Net Core
+ Core functionality for JsonRpc.Net
+ 1.2.3
+ $(VersionSuffix)
+ Austin Harris
+ https://github.com/Astn/JSON-RPC.NET
+ https://raw.githubusercontent.com/Astn/JSON-RPC.NET/master/LICENSE
+
+ Improves support for optional parameters - @HoMS1987 https://github.com/HoMS1987
+ Fixes protocol validation of the ID property - @pedrolcl https://github.com/pedrolcl
+ DotNet Core support - @astn https://github.com/astn
+
+ netstandard2.0;netstandard2.1;netcoreapp3.1
+ true
+
-
-
-
-
-
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Json-Rpc/AustinHarris.JsonRpc.nuspec b/Json-Rpc/AustinHarris.JsonRpc.nuspec
index 5a3c555..ad36f62 100644
--- a/Json-Rpc/AustinHarris.JsonRpc.nuspec
+++ b/Json-Rpc/AustinHarris.JsonRpc.nuspec
@@ -12,12 +12,12 @@
false
The fastest .Net JSON RPC Server
JSON-RPC.Net is a high performance Json-Rpc 2.0 server, leveraging the popular JSON.NET library. Easily create a JSON RPC server for your Angular javascript apps, also supports sockets and pipes, oh my!
- Performance improvements -Astn, Bug Fixes: Optional Parameter Ordering -artnim, Batches with superflous commans -artnim, ParamCount Exception -artnim.
+ Optional JsonSerializer Settings - @hovi. ProcessSync is now public - @astn
en-US
fast json rpc server socket javascript json-rpc.net json-rpc jsonrpc json.net web services webapi service angular server angularjs
-
+
diff --git a/Json-Rpc/Handler.cs b/Json-Rpc/Handler.cs
index 9922f95..095bacb 100644
--- a/Json-Rpc/Handler.cs
+++ b/Json-Rpc/Handler.cs
@@ -14,10 +14,15 @@
public class Handler
{
#region Members
-
- //private static Handler current;
- private static ConcurrentDictionary _sessionHandlers;
- private static string _defaultSessionId;
+ private const string Name_of_JSONRPCEXCEPTION = "JsonRpcException&";
+ private static int _sessionHandlerMasterVersion = 1;
+ [ThreadStatic]
+ private static Dictionary _sessionHandlersLocal;
+ [ThreadStatic]
+ private static int _sessionHandlerLocalVersion = 0;
+ private static ConcurrentDictionary _sessionHandlersMaster;
+
+ private static volatile string _defaultSessionId;
#endregion
#region Constructors
@@ -26,15 +31,14 @@ static Handler()
{
//current = new Handler(Guid.NewGuid().ToString());
_defaultSessionId = Guid.NewGuid().ToString();
- _sessionHandlers = new ConcurrentDictionary();
- _sessionHandlers[_defaultSessionId] = new Handler(_defaultSessionId);
+ _sessionHandlersMaster = new ConcurrentDictionary();
+ _sessionHandlersMaster[_defaultSessionId] = new Handler(_defaultSessionId);
}
private Handler(string sessionId)
{
SessionId = sessionId;
this.MetaData = new SMD();
- this.Handlers = new Dictionary();
}
#endregion
@@ -54,7 +58,17 @@ private Handler(string sessionId)
///
public static Handler GetSessionHandler(string sessionId)
{
- return _sessionHandlers.GetOrAdd(sessionId, new Handler(sessionId));
+ if (_sessionHandlerMasterVersion != _sessionHandlerLocalVersion)
+ {
+ _sessionHandlersLocal = new Dictionary(_sessionHandlersMaster);
+ _sessionHandlerLocalVersion = _sessionHandlerMasterVersion;
+ }
+ if (_sessionHandlersLocal.ContainsKey(sessionId))
+ {
+ return _sessionHandlersLocal[sessionId];
+ }
+ Interlocked.Increment(ref _sessionHandlerMasterVersion);
+ return _sessionHandlersMaster.GetOrAdd(sessionId, new Handler(sessionId));
}
///
@@ -73,8 +87,8 @@ public static Handler GetSessionHandler()
public static void DestroySession(string sessionId)
{
Handler h;
- _sessionHandlers.TryRemove(sessionId, out h);
- h.Handlers.Clear();
+ _sessionHandlersMaster.TryRemove(sessionId, out h);
+ Interlocked.Increment(ref _sessionHandlerMasterVersion);
h.MetaData.Services.Clear();
}
///
@@ -95,9 +109,6 @@ public void Destroy()
///
public string SessionId { get; private set; }
- private static ConcurrentDictionary RpcContexts = new ConcurrentDictionary();
- private static ConcurrentDictionary RpcExceptions = new ConcurrentDictionary();
-
///
/// Provides access to a context specific to each JsonRpc method invocation.
/// Warning: Must be called from within the execution context of the jsonRpc Method to return the context
@@ -105,44 +116,31 @@ public void Destroy()
///
public static object RpcContext()
{
- if (Task.CurrentId == null)
- return null;
-
- if (RpcContexts.ContainsKey(Task.CurrentId.Value) == false)
- return null;
-
- return RpcContexts[Task.CurrentId.Value];
+ return __currentRpcContext;
}
+ [ThreadStatic]
+ static JsonRpcException __currentRpcException;
///
/// Allows you to set the exception used in in the JsonRpc response.
- /// Warning: Must be called from within the execution context of the jsonRpc method.
+ /// Warning: Must be called from the same thread as the jsonRpc method.
///
///
public static void RpcSetException(JsonRpcException exception)
{
- if (Task.CurrentId != null)
- RpcExceptions[Task.CurrentId.Value] = exception;
- else
- throw new InvalidOperationException("This method is only valid when used within the context of a method marked as a JsonRpcMethod, and that method must of been invoked by the JsonRpc Handler.");
+ __currentRpcException = exception;
}
-
- private void RemoveRpcException()
+ public static JsonRpcException RpcGetAndRemoveRpcException()
{
- if (Task.CurrentId != null)
- {
- var id = Task.CurrentId.Value;
- RpcExceptions[id] = null;
- JsonRpcException va;
- RpcExceptions.TryRemove(id, out va);
- }
+ var ex = __currentRpcException;
+ __currentRpcException = null ;
+ return ex;
}
private AustinHarris.JsonRpc.PreProcessHandler externalPreProcessingHandler;
private AustinHarris.JsonRpc.PostProcessHandler externalPostProcessingHandler;
private Func externalErrorHandler;
private Func parseErrorHandler;
- private Dictionary Handlers { get; set; }
#endregion
///
@@ -150,32 +148,44 @@ private void RemoveRpcException()
///
public SMD MetaData { get; set; }
- private const string THREAD_CALLBACK_SLOT_NAME ="Callback";
-
#region Public Methods
///
- /// Registers a jsonRpc method name (key) to be mapped to a specific function
+ /// Allows you to register all the functions on a Pojo Type that have been attributed as [JsonRpcMethod] to the specified sessionId
///
- /// The Method as it will be called from JsonRpc
- /// The method that will be invoked
- ///
- public bool Register(string key, Delegate handle)
+ /// The session to register against
+ /// The instance containing JsonRpcMethods to register
+ public static void RegisterInstance(string sessionID, object instance)
{
- var result = false;
+ ServiceBinder.BindService(sessionID, instance);
+ }
- if (!this.Handlers.ContainsKey(key))
- {
- this.Handlers.Add(key, handle);
- }
+ ///
+ /// Allows you to register any function, lambda, etc even when not attributed with JsonRpcMethod.
+ /// Requires you to specify all types and defaults
+ ///
+ /// The method name that will map to the registered function
+ /// The parameter names and types that will be positionally bound to the function
+ /// Optional default values for parameters
+ /// A reference to the Function
+ public void RegisterFuction(string methodName, Dictionary parameterNameTypeMapping, Dictionary parameterNameDefaultValueMapping, Delegate implementation)
+ {
+ MetaData.AddService(methodName, parameterNameTypeMapping, parameterNameDefaultValueMapping, implementation);
+ }
+
+ public void UnRegisterFunction(string methodName)
+ {
+ MetaData.Services.Remove(methodName);
+ }
- return result;
+ public void SetPreProcessHandler(AustinHarris.JsonRpc.PreProcessHandler handler)
+ {
+ externalPreProcessingHandler = handler;
}
- public void UnRegister(string key)
+ public void SetPostProcessHandler(AustinHarris.JsonRpc.PostProcessHandler handler)
{
- this.Handlers.Remove(key);
- MetaData.Services.Remove(key);
+ externalPostProcessingHandler = handler;
}
///
@@ -184,7 +194,7 @@ public void UnRegister(string key)
/// JsonRpc Request to be processed
/// Optional context that will be available from within the jsonRpcMethod.
///
- public JsonResponse Handle(JsonRequest Rpc, Object RpcContext = null, Action callback = null)
+ public JsonResponse Handle(JsonRequest Rpc, Object RpcContext = null)
{
AddRpcContext(RpcContext);
@@ -198,15 +208,15 @@ public JsonResponse Handle(JsonRequest Rpc, Object RpcContext = null, Action x != null);
- var getCount = Rpc.Params as ICollection;
+
var loopCt = 0;
-
+ var getCount = Rpc.Params as ICollection;
if (getCount != null)
{
loopCt = getCount.Count;
}
var paramCount = loopCt;
- if (paramCount == metaDataParamCount - 1 && metadata.parameters[metaDataParamCount - 1].ObjectType.Name.Contains(typeof(JsonRpcException).Name))
+ if (paramCount == metaDataParamCount - 1 && metadata.parameters[metaDataParamCount - 1].ObjectType.Name.Equals(Name_of_JSONRPCEXCEPTION))
{
paramCount++;
expectsRefException = true;
}
parameters = new object[paramCount];
- if (isJArray)
+ if (Rpc.Params is Newtonsoft.Json.Linq.JArray)
{
var jarr = ((Newtonsoft.Json.Linq.JArray)Rpc.Params);
- //var loopCt = jarr.Count;
- //var pCount = loopCt;
- //if (pCount == metaDataParamCount - 1 && metadata.parameters[metaDataParamCount].GetType() == typeof(JsonRpcException))
- // pCount++;
- //parameters = new object[pCount];
- for (int i = 0; i < loopCt; i++)
+ for (int i = 0; i < loopCt && i < metadata.parameters.Length; i++)
{
parameters[i] = CleanUpParameter(jarr[i], metadata.parameters[i]);
}
}
- else if (isJObject)
+ else if (Rpc.Params is Newtonsoft.Json.Linq.JObject)
{
- var jo = Rpc.Params as Newtonsoft.Json.Linq.JObject;
- //var loopCt = jo.Count;
- //var pCount = loopCt;
- //if (pCount == metaDataParamCount - 1 && metadata.parameters[metaDataParamCount].GetType() == typeof(JsonRpcException))
- // pCount++;
- //parameters = new object[pCount];
- var asDict = jo as IDictionary;
+ var asDict = Rpc.Params as IDictionary;
for (int i = 0; i < loopCt && i < metadata.parameters.Length; i++)
{
- if (asDict.ContainsKey(metadata.parameters[i].Name) == false)
+ if (asDict.ContainsKey(metadata.parameters[i].Name) == true)
+ {
+ parameters[i] = CleanUpParameter(asDict[metadata.parameters[i].Name], metadata.parameters[i]);
+ continue;
+ }
+ else
{
+ var foundDefault = metadata.defaultValues
+ .FirstOrDefault(defaul => defaul.Name == metadata.parameters[i].Name);
+ if (foundDefault != null)
+ {
+ parameters[i] = foundDefault.Value;
+ continue;
+ }
+
JsonResponse response = new JsonResponse()
{
Error = ProcessException(Rpc,
@@ -275,9 +285,8 @@ public JsonResponse Handle(JsonRequest Rpc, Object RpcContext = null, Action
- /// Method returns the actual callback set to this thread in Handle() method.
- /// If callback is not set, then empty callback is returned.
- ///
- ///
- internal Action GetAsyncCallback()
- {
- object o = Thread.GetData(Thread.GetNamedDataSlot(THREAD_CALLBACK_SLOT_NAME));
- Action callback;
- if(o is Action)
- {
- callback = o as Action;
- }
- else
- {
- callback = delegate(JsonResponse a) { };
- }
- return callback;
- }
-
+ [ThreadStatic]
+ static object __currentRpcContext;
private void AddRpcContext(object RpcContext)
{
- if (Task.CurrentId != null)
- RpcContexts[Task.CurrentId.Value] = RpcContext;
+ __currentRpcContext = RpcContext;
}
private void RemoveRpcContext()
{
- if (Task.CurrentId != null)
- {
- var id = Task.CurrentId.Value;
- RpcContexts[id] = null;
- object va;
- RpcContexts.TryRemove(id, out va);
- }
+ __currentRpcContext = null;
}
private JsonRpcException ProcessException(JsonRequest req, JsonRpcException ex)
@@ -446,47 +427,46 @@ internal void SetParseErrorHandler(Func callback, JsonRequest request, JsonResponse response, object context)
+ private JsonResponse PostProcess(JsonRequest request, JsonResponse response, object context)
{
if (externalPostProcessingHandler != null)
{
@@ -534,22 +512,9 @@ private JsonResponse PostProcess(Action callback, JsonRequest requ
response = new JsonResponse() { Error = ProcessException(request, new JsonRpcException(-32603, "Internal Error", ex)) };
}
}
-
- if (callback != null)
- callback.Invoke(response);
-
return response;
}
- public void SetPreProcessHandler(AustinHarris.JsonRpc.PreProcessHandler handler)
- {
- externalPreProcessingHandler = handler;
- }
-
- public void SetPostProcessHandler(AustinHarris.JsonRpc.PostProcessHandler handler)
- {
- externalPostProcessingHandler = handler;
- }
}
}
diff --git a/Json-Rpc/JsonRequest.cs b/Json-Rpc/JsonRequest.cs
index 033ea1f..e68c1ff 100644
--- a/Json-Rpc/JsonRequest.cs
+++ b/Json-Rpc/JsonRequest.cs
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Newtonsoft.Json;
+using Newtonsoft.Json;
namespace AustinHarris.JsonRpc
{
@@ -16,6 +12,19 @@ public JsonRequest()
{
}
+ public JsonRequest(string method, object pars, object id)
+ {
+ Method = method;
+ Params = pars;
+ Id = id;
+ }
+
+ [JsonProperty("jsonrpc")]
+ public string JsonRpc
+ {
+ get { return "2.0"; }
+ }
+
[JsonProperty("method")]
public string Method { get; set; }
diff --git a/Json-Rpc/JsonResponse.cs b/Json-Rpc/JsonResponse.cs
index d16a64b..941de82 100644
--- a/Json-Rpc/JsonResponse.cs
+++ b/Json-Rpc/JsonResponse.cs
@@ -13,7 +13,7 @@ namespace AustinHarris.JsonRpc
public class JsonResponse
{
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "jsonrpc")]
- public string JsonRpc { get { return "2.0"; } }
+ public string JsonRpc { get; set; } = "2.0";
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "result")]
public object Result { get; set; }
@@ -32,7 +32,7 @@ public class JsonResponse
public class JsonResponse
{
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "jsonrpc")]
- public string JsonRpc { get { return "2.0"; } }
+ public string JsonRpc { get; set; } = "2.0";
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "result")]
public T Result { get; set; }
diff --git a/Json-Rpc/JsonRpcProcessor.cs b/Json-Rpc/JsonRpcProcessor.cs
index 31bc872..5020e88 100644
--- a/Json-Rpc/JsonRpcProcessor.cs
+++ b/Json-Rpc/JsonRpcProcessor.cs
@@ -13,227 +13,168 @@ namespace AustinHarris.JsonRpc
{
public static class JsonRpcProcessor
{
- public static void AsyncProcess(string jsonRpc, Action callback, object context = null)
+ public static void Process(JsonRpcStateAsync async, object context = null,
+ JsonSerializerSettings settings = null)
{
- Task.Factory.StartNew(() => AsyncProcessInternal(Handler.DefaultSessionId(), jsonRpc, context, callback));
+ Process(Handler.DefaultSessionId(), async, context, settings);
}
- public static void AsyncProcess(string sessionId,string jsonRpc, Action callback, object context = null)
+ public static void Process(string sessionId, JsonRpcStateAsync async, object context = null,
+ JsonSerializerSettings settings = null)
{
- Task.Factory.StartNew(() => AsyncProcessInternal(sessionId, jsonRpc, context, callback));
+ Process(sessionId, async.JsonRpc, context, settings)
+ .ContinueWith(t =>
+ {
+ async.Result = t.Result;
+ async.SetCompleted();
+ });
}
- ///
- /// The callback will be returned to the user, who needs to invoke it in concrete
- /// service implementation. Call should be made directly from same thread as
- /// service method is executed.
- ///
- /// Handler session id
- ///
- public static Action GetAsyncProcessCallback(string sessionId = "")
+ public static Task Process(string jsonRpc, object context = null,
+ JsonSerializerSettings settings = null)
{
- Handler handler;
- if ("" == sessionId)
- {
- handler = Handler.GetSessionHandler(Handler.DefaultSessionId());
- }
- else
- {
- handler = Handler.GetSessionHandler(sessionId);
- }
+ return Process(Handler.DefaultSessionId(), jsonRpc, context, settings);
+ }
- return handler.GetAsyncCallback();
+ public static Task Process(string sessionId, string jsonRpc, object context = null,
+ JsonSerializerSettings settings = null)
+ {
+ return Task.Factory.StartNew((_) =>
+ {
+ var tuple = (Tuple)_;
+ return ProcessSync(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4);
+ }, new Tuple(sessionId, jsonRpc, context, settings));
}
- private static void AsyncProcessInternal(string sessionId, string jsonRpc, object jsonRpcContext, Action callback)
+ public static string ProcessSync(string sessionId, string jsonRpc, object jsonRpcContext,
+ JsonSerializerSettings settings = null)
{
- Handler handler = Handler.GetSessionHandler(sessionId);
+ var handler = Handler.GetSessionHandler(sessionId);
+
+ JsonRequest[] batch = null;
try
{
- Tuple[] batch = null;
if (isSingleRpc(jsonRpc))
{
- batch = new[] { Tuple.Create(JsonConvert.DeserializeObject(jsonRpc)) };
+ var foo = JsonConvert.DeserializeObject(jsonRpc, settings);
+ batch = new[] { foo };
}
else
{
- batch = JsonConvert.DeserializeObject(jsonRpc)
- .Select(request => new Tuple(request))
- .ToArray();
- }
-
- if (batch.Length == 0)
- {
- callback.Invoke(Newtonsoft.Json.JsonConvert.SerializeObject(new JsonResponse
- {
- Error = handler.ProcessParseException(jsonRpc,
- new JsonRpcException(3200, "Invalid Request", "Batch of calls was empty."))
- }));
- }
-
- foreach (var tuple in batch)
- {
- JsonRequest jsonRequest = tuple.Item1;
- JsonResponse jsonResponse = new JsonResponse();
-
- if (jsonRequest == null)
- {
- jsonResponse.Error = handler.ProcessParseException(jsonRpc,
- new JsonRpcException(-32700, "Parse error",
- "Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text."));
- }
- else
- {
- jsonResponse.Id = jsonRequest.Id;
-
- if (jsonRequest.Method == null)
- {
- jsonResponse.Error = handler.ProcessParseException(jsonRpc,
- new JsonRpcException(-32600, "Invalid Request", "Missing property 'method'"));
- }
- else
- {
- handler.Handle(jsonRequest, jsonRpcContext,
- delegate(JsonResponse a)
- {
- a.Id = jsonRequest.Id;
- if (a.Id != null || a.Error != null)
- {
- callback.Invoke(JsonConvert.SerializeObject(a));
- }
- }
- );
- }
- }
+ batch = JsonConvert.DeserializeObject(jsonRpc, settings);
}
}
catch (Exception ex)
{
- callback.Invoke(Newtonsoft.Json.JsonConvert.SerializeObject(new JsonResponse
+ return Newtonsoft.Json.JsonConvert.SerializeObject(new JsonResponse
{
Error = handler.ProcessParseException(jsonRpc, new JsonRpcException(-32700, "Parse error", ex))
- }));
+ }, settings);
}
- }
- public static void Process(JsonRpcStateAsync async, object context = null)
- {
- Task.Factory.StartNew((_async) =>
+ if (batch.Length == 0)
{
- var tuple = (Tuple)_async;
- ProcessJsonRpcState(tuple.Item1, tuple.Item2);
- }, new Tuple(async, context));
-
- }
+ return Newtonsoft.Json.JsonConvert.SerializeObject(new JsonResponse
+ {
+ Error = handler.ProcessParseException(jsonRpc,
+ new JsonRpcException(3200, "Invalid Request", "Batch of calls was empty."))
+ }, settings);
+ }
- public static void Process(string sessionId, JsonRpcStateAsync async, object context = null)
- {
- var t = Task.Factory.StartNew((_async) =>
+ var singleBatch = batch.Length == 1;
+ StringBuilder sbResult = null;
+ for (var i = 0; i < batch.Length; i++)
{
- var i = (Tuple)_async;
- ProcessJsonRpcState(i.Item1, i.Item2, i.Item3);
- }, new Tuple(sessionId, async, context));
+ var jsonRequest = batch[i];
+ var jsonResponse = new JsonResponse();
- }
- internal static void ProcessJsonRpcState(JsonRpcStateAsync async, object jsonRpcContext = null)
- {
- ProcessJsonRpcState(Handler.DefaultSessionId(), async, jsonRpcContext);
- }
- internal static void ProcessJsonRpcState(string sessionId, JsonRpcStateAsync async, object jsonRpcContext = null)
- {
- async.Result = ProcessInternal(sessionId, async.JsonRpc, jsonRpcContext);
- async.SetCompleted();
- }
-
- public static Task Process(string jsonRpc, object context = null)
- {
- return Process(Handler.DefaultSessionId(), jsonRpc, context);
- }
- public static Task Process(string sessionId, string jsonRpc, object context = null)
- {
- return Task.Factory.StartNew((_) =>
- {
- var tuple = (Tuple)_;
- return ProcessInternal(tuple.Item1, tuple.Item2, tuple.Item3);
- }, new Tuple(sessionId, jsonRpc, context));
- }
-
- private static string ProcessInternal(string sessionId, string jsonRpc, object jsonRpcContext)
- {
- var handler = Handler.GetSessionHandler(sessionId);
-
- try
- {
- Tuple[] batch = null;
- if (isSingleRpc(jsonRpc))
+ if (jsonRequest == null)
{
- batch = new[] { Tuple.Create(JsonConvert.DeserializeObject(jsonRpc), new JsonResponse()) };
+ jsonResponse.Error = handler.ProcessParseException(jsonRpc,
+ new JsonRpcException(-32700, "Parse error",
+ "Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text."));
}
- else
+ else if (jsonRequest.Method == null)
{
- batch = JsonConvert.DeserializeObject(jsonRpc)
- .Select(request => new Tuple(request, new JsonResponse()))
- .ToArray();
+ jsonResponse.Error = handler.ProcessParseException(jsonRpc,
+ new JsonRpcException(-32600, "Invalid Request", "Missing property 'method'"));
}
-
- if (batch.Length == 0)
+ else if (!isSimpleValueType(jsonRequest.Id))
{
- return Newtonsoft.Json.JsonConvert.SerializeObject(new JsonResponse
- {
- Error = handler.ProcessParseException(jsonRpc,
- new JsonRpcException(3200, "Invalid Request", "Batch of calls was empty."))
- });
+ jsonResponse.Error = handler.ProcessParseException(jsonRpc,
+ new JsonRpcException(-32600, "Invalid Request", "Id property must be either null or string or integer."));
}
-
- foreach (var tuple in batch)
+ else
{
- var jsonRequest = tuple.Item1;
- var jsonResponse = tuple.Item2;
+ jsonResponse.Id = jsonRequest.Id;
+
+ var data = handler.Handle(jsonRequest, jsonRpcContext);
- if (jsonRequest == null)
+ if (data == null) continue;
+
+ jsonResponse.JsonRpc = data.JsonRpc;
+ jsonResponse.Error = data.Error;
+ jsonResponse.Result = data.Result;
+
+ }
+ if (jsonResponse.Result == null && jsonResponse.Error == null)
+ {
+ // Per json rpc 2.0 spec
+ // result : This member is REQUIRED on success.
+ // This member MUST NOT exist if there was an error invoking the method.
+ // Either the result member or error member MUST be included, but both members MUST NOT be included.
+ jsonResponse.Result = new Newtonsoft.Json.Linq.JValue((Object)null);
+ }
+ // special case optimization for single Item batch
+ if (singleBatch && (jsonResponse.Id != null || jsonResponse.Error != null))
+ {
+ StringWriter sw = new StringWriter();
+ JsonTextWriter writer = new JsonTextWriter(sw);
+ writer.WriteStartObject();
+ if (!string.IsNullOrEmpty(jsonResponse.JsonRpc))
+ {
+ writer.WritePropertyName("jsonrpc"); writer.WriteValue(jsonResponse.JsonRpc);
+ }
+ if (jsonResponse.Error != null)
{
- jsonResponse.Error = handler.ProcessParseException(jsonRpc,
- new JsonRpcException(-32700, "Parse error",
- "Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text."));
+ writer.WritePropertyName("error"); writer.WriteRawValue(JsonConvert.SerializeObject(jsonResponse.Error, settings));
}
else
{
- jsonResponse.Id = jsonRequest.Id;
-
- if (jsonRequest.Method == null)
- {
- jsonResponse.Error = handler.ProcessParseException(jsonRpc,
- new JsonRpcException(-32600, "Invalid Request", "Missing property 'method'"));
- }
- else
- {
- var data = handler.Handle(jsonRequest, jsonRpcContext);
-
- if (data == null) continue;
-
- jsonResponse.Error = data.Error;
- jsonResponse.Result = data.Result;
- }
+ writer.WritePropertyName("result"); writer.WriteRawValue(JsonConvert.SerializeObject(jsonResponse.Result, settings));
}
- }
+ writer.WritePropertyName("id"); writer.WriteValue(jsonResponse.Id);
+ writer.WriteEndObject();
+ return sw.ToString();
- var responses = new string[batch.Count(x => x.Item2.Id != null || x.Item2.Error != null)];
- var idx = 0;
- foreach (var resp in batch.Where(x => x.Item2.Id != null || x.Item2.Error != null))
+ //return JsonConvert.SerializeObject(jsonResponse);
+ }
+ else if (jsonResponse.Id == null && jsonResponse.Error == null)
{
- responses[idx++] = JsonConvert.SerializeObject(resp.Item2);
+ // do nothing
+ sbResult = new StringBuilder(0);
}
-
- return responses.Length == 0 ? string.Empty : responses.Length == 1 ? responses[0] : string.Format("[{0}]", string.Join(",", responses));
- }
- catch (Exception ex)
- {
- return Newtonsoft.Json.JsonConvert.SerializeObject(new JsonResponse
+ else
{
- Error = handler.ProcessParseException(jsonRpc, new JsonRpcException(-32700, "Parse error", ex))
- });
+ // write out the response
+ if (i == 0)
+ {
+ sbResult = new StringBuilder("[");
+ }
+
+ sbResult.Append(JsonConvert.SerializeObject(jsonResponse, settings));
+ if (i < batch.Length - 1)
+ {
+ sbResult.Append(',');
+ }
+ else if (i == batch.Length - 1)
+ {
+ sbResult.Append(']');
+ }
+ }
}
+ return sbResult.ToString();
}
private static bool isSingleRpc(string json)
@@ -245,5 +186,15 @@ private static bool isSingleRpc(string json)
}
return true;
}
+
+ private static bool isSimpleValueType(object property)
+ {
+ if (property == null)
+ return true;
+ return property.GetType() == typeof(System.String) ||
+ property.GetType() == typeof(System.Int64) ||
+ property.GetType() == typeof(System.Int32) ||
+ property.GetType() == typeof(System.Int16);
+ }
}
}
diff --git a/Json-Rpc/JsonRpcService.cs b/Json-Rpc/JsonRpcService.cs
index 46bfb63..d8ec961 100644
--- a/Json-Rpc/JsonRpcService.cs
+++ b/Json-Rpc/JsonRpcService.cs
@@ -1,21 +1,22 @@
namespace AustinHarris.JsonRpc
{
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using AustinHarris.JsonRpc;
-
+ ///
+ /// For routing use SessionId
+ ///
public abstract class JsonRpcService
{
protected JsonRpcService()
{
- ServiceBinder.bindService(Handler.DefaultSessionId(), () => this);
+ ServiceBinder.BindService(Handler.DefaultSessionId(), this);
}
+ ///
+ /// Routing by SessionId
+ ///
+ ///
protected JsonRpcService(string sessionID)
{
- ServiceBinder.bindService(sessionID, () => this);
+ ServiceBinder.BindService(sessionID, this);
}
}
-}
+}
\ No newline at end of file
diff --git a/Json-Rpc/Properties/AssemblyInfo.cs b/Json-Rpc/Properties/AssemblyInfo.cs
deleted file mode 100644
index 802d0fd..0000000
--- a/Json-Rpc/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-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("Json-Rpc.Net Core")]
-[assembly: AssemblyDescription("Core functionality for JsonRpc.Net")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("Austin Harris")]
-[assembly: AssemblyProduct("Json-Rpc.Net Core")]
-[assembly: AssemblyCopyright("Austin Harris")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("2f8036b2-223d-4b90-b6a9-fadddeb3ac0d")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-[assembly: AssemblyVersion("1.0.5.0")]
-[assembly: AssemblyFileVersion("1.0.5.0")]
diff --git a/Json-Rpc/SMDService.cs b/Json-Rpc/SMDService.cs
index 2c3fa8b..fe84bb5 100644
--- a/Json-Rpc/SMDService.cs
+++ b/Json-Rpc/SMDService.cs
@@ -34,9 +34,9 @@ public SMD ()
TypeHashes = new List();
}
- public void AddService(string method, Dictionary parameters, Dictionary defaultValues)
+ internal void AddService(string method, Dictionary parameters, Dictionary defaultValues, Delegate dele)
{
- var newService = new SMDService(transport,"JSON-RPC-2.0",parameters, defaultValues);
+ var newService = new SMDService(transport,"JSON-RPC-2.0",parameters, defaultValues, dele);
Services.Add(method,newService);
}
@@ -64,6 +64,7 @@ public static bool ContainsType(JObject jo)
public class SMDService
{
+ public Delegate dele;
///
/// Defines a service method http://dojotoolkit.org/reference-guide/1.8/dojox/rpc/smd.html
///
@@ -71,9 +72,10 @@ public class SMDService
/// URL, PATH, JSON, JSON-RPC-1.0, JSON-RPC-1.1, JSON-RPC-2.0
///
///
- public SMDService(string transport, string envelope, Dictionary parameters, Dictionary defaultValues )
+ public SMDService(string transport, string envelope, Dictionary parameters, Dictionary defaultValues, Delegate dele)
{
// TODO: Complete member initialization
+ this.dele = dele;
this.transport = transport;
this.envelope = envelope;
this.parameters = new SMDAdditionalParameters[parameters.Count-1]; // last param is return type similar to Func<,>
diff --git a/Json-Rpc/ServiceBinder.cs b/Json-Rpc/ServiceBinder.cs
index d6b8451..20423a6 100644
--- a/Json-Rpc/ServiceBinder.cs
+++ b/Json-Rpc/ServiceBinder.cs
@@ -8,11 +8,18 @@
public static class ServiceBinder
{
- public static void bindService(string sessionID, Func serviceFactory)
+ public static void BindService() where T : new()
+ {
+ BindService(Handler.DefaultSessionId());
+ }
+ public static void BindService(string sessionID) where T : new()
+ {
+ BindService(sessionID, new T());
+ }
+
+ public static void BindService(string sessionID, Object instance)
{
- var instance = serviceFactory();
var item = instance.GetType(); // var item = typeof(T);
- var regMethod = typeof(Handler).GetMethod("Register");
var methods = item.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(m => m.GetCustomAttributes(typeof(JsonRpcMethodAttribute), false).Length > 0);
foreach (var meth in methods)
@@ -25,12 +32,26 @@ public static void bindService(string sessionID, Func serviceFactory)
List parameterTypeArray = new List();
for (int i = 0; i < paramzs.Length; i++)
{
+ string paramName;
+ var paramAttrs = paramzs[i].GetCustomAttributes(typeof(JsonRpcParamAttribute), false);
+ if (paramAttrs.Length > 0)
+ {
+ paramName = ((JsonRpcParamAttribute)paramAttrs[0]).JsonParamName;
+ if (string.IsNullOrEmpty(paramName))
+ {
+ paramName = paramzs[i].Name;
+ }
+ }
+ else
+ {
+ paramName = paramzs[i].Name;
+ }
// reflection attribute information for optional parameters
//http://stackoverflow.com/questions/2421994/invoking-methods-with-optional-parameters-through-reflection
- paras.Add(paramzs[i].Name, paramzs[i].ParameterType);
+ paras.Add(paramName, paramzs[i].ParameterType);
if (paramzs[i].IsOptional) // if the parameter is an optional, add the default value to our default values dictionary.
- defaultValues.Add(paramzs[i].Name, paramzs[i].DefaultValue);
+ defaultValues.Add(paramName, paramzs[i].DefaultValue);
}
var resType = meth.ReturnType;
@@ -42,18 +63,9 @@ public static void bindService(string sessionID, Func serviceFactory)
var methodName = handlerAttribute.JsonMethodName == string.Empty ? meth.Name : handlerAttribute.JsonMethodName;
var newDel = Delegate.CreateDelegate(System.Linq.Expressions.Expression.GetDelegateType(paras.Values.ToArray()), instance /*Need to add support for other methods outside of this instance*/, meth);
var handlerSession = Handler.GetSessionHandler(sessionID);
- regMethod.Invoke(handlerSession, new object[] { methodName, newDel });
- handlerSession.MetaData.AddService(methodName, paras, defaultValues);
+ handlerSession.MetaData.AddService(methodName, paras, defaultValues, newDel);
}
}
}
- public static void bindService(string sessionID) where T : new()
- {
- bindService(sessionID, () => new T());
- }
- public static void bindService() where T : new()
- {
- bindService(Handler.DefaultSessionId());
- }
}
}
\ No newline at end of file
diff --git a/Json-Rpc/packages.config b/Json-Rpc/packages.config
deleted file mode 100644
index 79f852f..0000000
--- a/Json-Rpc/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/README.md b/README.md
index 5ba55be..7847e23 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,37 @@
+

json-rpc.net
============
-.Net [](https://www.myget.org/feed/Activity/astn-jsonrpc) Mono [](https://travis-ci.org/Astn/JSON-RPC.NET)
+ 
JSON-RPC.Net is a high performance Json-Rpc 2.0 server, leveraging the popular JSON.NET library. Host in ASP.NET, also supports sockets and pipes, oh my!
+## Performance
+
+These are results from running the TestServer_Console project.
+
+##### Xeon E-2176M @ 2.70GHz 64.0 GB (Date: Thu Apr 30 17:34:22 2020 -0600)
+
+```
+Starting benchmark
+processed 50 rpc in 137ms for 364.96 rpc/sec
+processed 100 rpc in 0ms for ∞ rpc/sec
+processed 300 rpc in 1ms for 300,000.00 rpc/sec
+processed 1,200 rpc in 7ms for 171,428.57 rpc/sec
+processed 6,000 rpc in 26ms for 230,769.23 rpc/sec
+processed 36,000 rpc in 166ms for 216,867.47 rpc/sec
+processed 252,000 rpc in 1,121ms for 224,799.29 rpc/sec
+Finished benchmark...
+```
+
+## Do you like this?
+
+[](https://www.buymeacoffee.com/Ekati)
+
+
##### Requirements
-* dotnet 4.0 or mono
+* dotnet-standard (dotnet core | mono | .net framework)
##### License
JSON-RPC.net is licensed under The MIT License (MIT), check the [LICENSE](https://github.com/CoiniumServ/JSON-RPC.NET/blob/master/LICENSE) file for details.
@@ -28,9 +52,10 @@ To install JSON-RPC.NET AspNet, run the following command in the Package Manager
PM> Install-Package AustinHarris.JsonRpc.AspNet
```
-##### Performance
-Under ideal conditions > 120k rpc/sec (cpu i7-2600,console server, no IO bottleneck)
+
+
+
##### Getting Started & Documentation
diff --git a/TestServer_Console/Program.cs b/TestServer_Console/Program.cs
index 34f76ae..df3152f 100644
--- a/TestServer_Console/Program.cs
+++ b/TestServer_Console/Program.cs
@@ -5,6 +5,7 @@
using AustinHarris.JsonRpc;
using System.Threading;
using System.Diagnostics;
+using System.Threading.Tasks;
namespace TestServer_Console
{
@@ -16,36 +17,29 @@ class Program
static void Main(string[] args)
{
- string input = "";
- do
+ PrintOptions();
+ for (string line = Console.ReadLine(); line != "q"; line = Console.ReadLine())
{
- input = PrintOptions();
- if (string.IsNullOrWhiteSpace(input))
+ if (string.IsNullOrWhiteSpace(line))
Benchmark();
- else if (input.StartsWith("C", StringComparison.CurrentCultureIgnoreCase))
+ else if (line.StartsWith("c", StringComparison.CurrentCultureIgnoreCase))
ConsoleInput();
- else
- PrintOptions();
- } while (input != "x");
+ PrintOptions();
+ }
}
- private static string PrintOptions()
+ private static void PrintOptions()
{
Console.WriteLine("Hit Enter to run benchmark");
Console.WriteLine("'c' to start reading console input");
- Console.WriteLine("'x' to exit");
- return Console.ReadLine();
+ Console.WriteLine("'q' to quit");
}
private static void ConsoleInput()
{
- var rpcResultHandler = new AsyncCallback(_ => Console.WriteLine(((JsonRpcStateAsync)_).Result));
-
for (string line = Console.ReadLine(); !string.IsNullOrEmpty(line); line = Console.ReadLine())
{
- var async = new JsonRpcStateAsync(rpcResultHandler, null);
- async.JsonRpc = line;
- JsonRpcProcessor.Process(async);
+ JsonRpcProcessor.Process(line).ContinueWith(response => Console.WriteLine( response.Result ));
}
}
@@ -60,41 +54,21 @@ private static void Benchmark()
{
cnt *= iteration;
ctr = 0;
+ Task[] tasks = new Task[cnt];
var sw = Stopwatch.StartNew();
- AutoResetEvent are = new AutoResetEvent(false);
- var rpcResultHandler = new AsyncCallback(_ =>
- {
- if(Interlocked.Increment(ref ctr) == cnt)
- {
- sw.Stop();
- Console.WriteLine("processed {0} rpc in {1}ms for {2} rpc/sec",cnt,sw.ElapsedMilliseconds, (double)cnt * 1000d / sw.ElapsedMilliseconds);
- are.Set();
- }
- });
+ var sessionid = Handler.DefaultSessionId();
for (int i = 0; i < cnt; i+=5)
{
- var async = new JsonRpcStateAsync(rpcResultHandler, null);
- async.JsonRpc = "{'method':'add','params':[1,2],'id':1}";
- JsonRpcProcessor.Process(async);
-
- async = new JsonRpcStateAsync(rpcResultHandler, null);
- async.JsonRpc = "{'method':'addInt','params':[1,7],'id':2}";
- JsonRpcProcessor.Process(async);
-
- async = new JsonRpcStateAsync(rpcResultHandler, null);
- async.JsonRpc = "{'method':'NullableFloatToNullableFloat','params':[1.23],'id':3}";
- JsonRpcProcessor.Process(async);
-
- async = new JsonRpcStateAsync(rpcResultHandler, null);
- async.JsonRpc = "{'method':'Test2','params':[3.456],'id':4}";
- JsonRpcProcessor.Process(async);
-
- async = new JsonRpcStateAsync(rpcResultHandler, null);
- async.JsonRpc = "{'method':'StringMe','params':['Foo'],'id':5}";
- JsonRpcProcessor.Process(async);
+ tasks[i] = JsonRpcProcessor.Process(sessionid, "{'method':'add','params':[1,2],'id':1}");
+ tasks[i+1] = JsonRpcProcessor.Process(sessionid, "{'method':'addInt','params':[1,7],'id':2}");
+ tasks[i+2] = JsonRpcProcessor.Process(sessionid, "{'method':'NullableFloatToNullableFloat','params':[1.23],'id':3}");
+ tasks[i+3] = JsonRpcProcessor.Process(sessionid, "{'method':'Test2','params':[3.456],'id':4}");
+ tasks[i+4] = JsonRpcProcessor.Process(sessionid, "{'method':'StringMe','params':['Foo'],'id':5}");
}
- are.WaitOne();
+ Task.WaitAll(tasks);
+ sw.Stop();
+ Console.WriteLine("processed {0:N0} rpc in \t {1:N0}ms for \t {2:N} rpc/sec", cnt, sw.ElapsedMilliseconds, (double)cnt * 1000d / sw.ElapsedMilliseconds);
}
diff --git a/TestServer_Console/Properties/AssemblyInfo.cs b/TestServer_Console/Properties/AssemblyInfo.cs
deleted file mode 100644
index fdd1edf..0000000
--- a/TestServer_Console/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-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("TestServer_Console")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("TestServer_Console")]
-[assembly: AssemblyCopyright("Copyright © 2012")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("5e7696bc-52cc-48f8-a575-40b17165ee20")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/TestServer_Console/TestServer_Console.csproj b/TestServer_Console/TestServer_Console.csproj
index 5e276d2..ba5f427 100644
--- a/TestServer_Console/TestServer_Console.csproj
+++ b/TestServer_Console/TestServer_Console.csproj
@@ -1,94 +1,24 @@
-
-
+
+
- Debug
- x86
- {31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}
+ Austin Harris
Exe
- Properties
- TestServer_Console
- TestServer_Console
- v4.0
- Client
- 512
- SAK
- SAK
- SAK
- SAK
- ..\
- true
-
-
- AnyCPU
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- x86
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
- AnyCPU
- bin\Debug\
- 4
- false
-
-
- AnyCPU
- bin\Release\
- 4
- false
+ netcoreapp3.1
+
+
-
- ..\packages\Newtonsoft.Json.8.0.2\lib\net40\Newtonsoft.Json.dll
- True
-
-
-
-
-
-
-
-
-
- ..\packages\Newtonsoft.Json.7.0.1\lib\net40\Newtonsoft.Json.dll
-
+
+
+
-
-
-
+
+
+
-
- {24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}
- AustinHarris.JsonRpc
-
+
-
-
-
-
-
- This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
+
+
\ No newline at end of file
diff --git a/TestServer_Console/packages.config b/TestServer_Console/packages.config
deleted file mode 100644
index bfe088a..0000000
--- a/TestServer_Console/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/TestServer_Console/service.cs b/TestServer_Console/service.cs
index 5802453..6fc2602 100644
--- a/TestServer_Console/service.cs
+++ b/TestServer_Console/service.cs
@@ -37,5 +37,95 @@ public string StringMe(string x)
{
return x;
}
+
+ [JsonRpcMethod]
+ private double add_1(double l, double r)
+ {
+ return l + r;
+ }
+
+ [JsonRpcMethod]
+ private int addInt_1(int l, int r)
+ {
+ return l + r;
+ }
+
+ [JsonRpcMethod]
+ public float? NullableFloatToNullableFloat_1(float? a)
+ {
+ return a;
+ }
+
+ [JsonRpcMethod]
+ public decimal? Test2_1(decimal x)
+ {
+ return x;
+ }
+
+ [JsonRpcMethod]
+ public string StringMe_1(string x)
+ {
+ return x;
+ }
+
+ [JsonRpcMethod]
+ private double add_2(double l, double r)
+ {
+ return l + r;
+ }
+
+ [JsonRpcMethod]
+ private int addInt_2(int l, int r)
+ {
+ return l + r;
+ }
+
+ [JsonRpcMethod]
+ public float? NullableFloatToNullableFloat_2(float? a)
+ {
+ return a;
+ }
+
+ [JsonRpcMethod]
+ public decimal? Test2_2(decimal x)
+ {
+ return x;
+ }
+
+ [JsonRpcMethod]
+ public string StringMe_2(string x)
+ {
+ return x;
+ }
+
+ [JsonRpcMethod]
+ private double add_3(double l, double r)
+ {
+ return l + r;
+ }
+
+ [JsonRpcMethod]
+ private int addInt_3(int l, int r)
+ {
+ return l + r;
+ }
+
+ [JsonRpcMethod]
+ public float? NullableFloatToNullableFloat_3(float? a)
+ {
+ return a;
+ }
+
+ [JsonRpcMethod]
+ public decimal? Test2_3(decimal x)
+ {
+ return x;
+ }
+
+ [JsonRpcMethod]
+ public string StringMe_3(string x)
+ {
+ return x;
+ }
}
}