From 269dc70cacd5bc9e75890923cc6e5f4d74e62a85 Mon Sep 17 00:00:00 2001 From: chaeyk Date: Fri, 25 May 2018 10:14:53 +0900 Subject: [PATCH 1/9] supports async/await on JsonRpcService methods. --- Json-Rpc/AustinHarris.JsonRpc.csproj | 4 +++- Json-Rpc/Handler.cs | 14 +++++++++++--- Json-Rpc/JsonRpcProcessor.cs | 18 +++++++----------- Json-Rpc/packages.config | 2 +- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/Json-Rpc/AustinHarris.JsonRpc.csproj b/Json-Rpc/AustinHarris.JsonRpc.csproj index 9d97aeb..78f21c7 100644 --- a/Json-Rpc/AustinHarris.JsonRpc.csproj +++ b/Json-Rpc/AustinHarris.JsonRpc.csproj @@ -17,7 +17,7 @@ SAK ..\..\CoiniumServ\build\ true - v4.0 + v4.5 AnyCPU @@ -47,6 +47,7 @@ TRACE;DEBUG 4 false + false AnyCPU @@ -54,6 +55,7 @@ 4 true false + false diff --git a/Json-Rpc/Handler.cs b/Json-Rpc/Handler.cs index 474ff91..c6f8550 100644 --- a/Json-Rpc/Handler.cs +++ b/Json-Rpc/Handler.cs @@ -194,7 +194,7 @@ public void SetPostProcessHandler(AustinHarris.JsonRpc.PostProcessHandler handle /// JsonRpc Request to be processed /// Optional context that will be available from within the jsonRpcMethod. /// - public JsonResponse Handle(JsonRequest Rpc, Object RpcContext = null) + public async Task Handle(JsonRequest Rpc, Object RpcContext = null) { AddRpcContext(RpcContext); @@ -333,8 +333,16 @@ public JsonResponse Handle(JsonRequest Rpc, Object RpcContext = null) try { - var results = handle.DynamicInvoke(parameters); - + object results = handle.DynamicInvoke(parameters); + if (results is Task) + { + var task = (Task)results; + await task; + PropertyInfo pInfo = task.GetType().GetRuntimeProperty("Result"); + if (pInfo != null) + results = pInfo.GetValue(task); + } + var last = parameters.LastOrDefault(); var contextException = RpcGetAndRemoveRpcException(); JsonResponse response = null; diff --git a/Json-Rpc/JsonRpcProcessor.cs b/Json-Rpc/JsonRpcProcessor.cs index fd2126f..dc46e7c 100644 --- a/Json-Rpc/JsonRpcProcessor.cs +++ b/Json-Rpc/JsonRpcProcessor.cs @@ -27,20 +27,16 @@ public static void Process(string sessionId, JsonRpcStateAsync async, object con async.SetCompleted(); }); } - public static Task Process(string jsonRpc, object context = null) + public static async Task Process(string jsonRpc, object context = null) { - return Process(Handler.DefaultSessionId(), jsonRpc, context); + return await 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)); + public static async Task Process(string sessionId, string jsonRpc, object context = null) + { + return await ProcessInternal(sessionId, jsonRpc, context); } - private static string ProcessInternal(string sessionId, string jsonRpc, object jsonRpcContext) + private static async Task ProcessInternal(string sessionId, string jsonRpc, object jsonRpcContext) { var handler = Handler.GetSessionHandler(sessionId); @@ -97,7 +93,7 @@ private static string ProcessInternal(string sessionId, string jsonRpc, object j { jsonResponse.Id = jsonRequest.Id; - var data = handler.Handle(jsonRequest, jsonRpcContext); + var data = await handler.Handle(jsonRequest, jsonRpcContext); if (data == null) continue; diff --git a/Json-Rpc/packages.config b/Json-Rpc/packages.config index 7c276ed..0cdd293 100644 --- a/Json-Rpc/packages.config +++ b/Json-Rpc/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file From ba6d5c988392629c0927f760ccbd5ad43eecf724 Mon Sep 17 00:00:00 2001 From: chaeyk Date: Fri, 25 May 2018 11:22:37 +0900 Subject: [PATCH 2/9] rpc response returns null result when rpc service task has VoidTaskResult. --- Json-Rpc/Handler.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Json-Rpc/Handler.cs b/Json-Rpc/Handler.cs index c6f8550..de89a0f 100644 --- a/Json-Rpc/Handler.cs +++ b/Json-Rpc/Handler.cs @@ -339,8 +339,14 @@ public async Task Handle(JsonRequest Rpc, Object RpcContext = null var task = (Task)results; await task; PropertyInfo pInfo = task.GetType().GetRuntimeProperty("Result"); - if (pInfo != null) + if (pInfo != null && !pInfo.PropertyType.Name.Equals("VoidTaskResult")) + { results = pInfo.GetValue(task); + } + else + { + results = null; + } } var last = parameters.LastOrDefault(); From 09696590e72b99cca4c738f92b0951d8c37f7b28 Mon Sep 17 00:00:00 2001 From: chaeyk Date: Mon, 28 May 2018 11:35:14 +0900 Subject: [PATCH 3/9] Return error when request id is missing. --- Json-Rpc/JsonRpcProcessor.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Json-Rpc/JsonRpcProcessor.cs b/Json-Rpc/JsonRpcProcessor.cs index dc46e7c..008e13d 100644 --- a/Json-Rpc/JsonRpcProcessor.cs +++ b/Json-Rpc/JsonRpcProcessor.cs @@ -84,6 +84,11 @@ private static async Task ProcessInternal(string sessionId, string jsonR new JsonRpcException(-32700, "Parse error", "Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.")); } + else if (jsonRequest.Id == null) + { + jsonResponse.Error = handler.ProcessParseException(jsonRpc, + new JsonRpcException(-32600, "Invalid Request", "Missing property 'id'")); + } else if (jsonRequest.Method == null) { jsonResponse.Error = handler.ProcessParseException(jsonRpc, From 5decee0dbc3fd2c5a05253ba61092a7ba3a373db Mon Sep 17 00:00:00 2001 From: chaeyk Date: Mon, 28 May 2018 11:49:28 +0900 Subject: [PATCH 4/9] Revert "Return error when request id is missing." This reverts commit 09696590e72b99cca4c738f92b0951d8c37f7b28. --- Json-Rpc/JsonRpcProcessor.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Json-Rpc/JsonRpcProcessor.cs b/Json-Rpc/JsonRpcProcessor.cs index 008e13d..dc46e7c 100644 --- a/Json-Rpc/JsonRpcProcessor.cs +++ b/Json-Rpc/JsonRpcProcessor.cs @@ -84,11 +84,6 @@ private static async Task ProcessInternal(string sessionId, string jsonR new JsonRpcException(-32700, "Parse error", "Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.")); } - else if (jsonRequest.Id == null) - { - jsonResponse.Error = handler.ProcessParseException(jsonRpc, - new JsonRpcException(-32600, "Invalid Request", "Missing property 'id'")); - } else if (jsonRequest.Method == null) { jsonResponse.Error = handler.ProcessParseException(jsonRpc, From 0d87c9769e75adfc309fdaae56c7a32c0cf021de Mon Sep 17 00:00:00 2001 From: chaeyk Date: Tue, 29 May 2018 18:19:19 +0900 Subject: [PATCH 5/9] added JsonRpcContextAttribute. - service method can receive context object by JsonRpcContextAttribute annotated parameter. removed threadlocal variables (exception, context) --- .../AustinHarris.JsonRpc.AspNet.csproj | 5 +- AustinHarris.JsonRpc.AspNet/packages.config | 2 +- AustinHarris.JsonRpcTestN/Test.cs | 6 +- AustinHarris.JsonRpcTestN/service.cs | 14 --- Json-Rpc/Attributes.cs | 9 ++ Json-Rpc/Handler.cs | 109 ++++++++---------- Json-Rpc/JsonRpcContext.cs | 20 ---- Json-Rpc/SMDService.cs | 13 ++- Json-Rpc/ServiceBinder.cs | 11 +- TestServer_Console/TestServer_Console.csproj | 10 +- TestServer_Console/packages.config | 2 +- 11 files changed, 91 insertions(+), 110 deletions(-) diff --git a/AustinHarris.JsonRpc.AspNet/AustinHarris.JsonRpc.AspNet.csproj b/AustinHarris.JsonRpc.AspNet/AustinHarris.JsonRpc.AspNet.csproj index f026b13..8baedb9 100644 --- a/AustinHarris.JsonRpc.AspNet/AustinHarris.JsonRpc.AspNet.csproj +++ b/AustinHarris.JsonRpc.AspNet/AustinHarris.JsonRpc.AspNet.csproj @@ -9,7 +9,7 @@ Properties AustinHarris.JsonRpc.AspNet AustinHarris.JsonRpc.AspNet - v4.0 + v4.5 512 SAK SAK @@ -17,6 +17,7 @@ SAK ..\ true + true @@ -26,6 +27,7 @@ DEBUG;TRACE prompt 4 + false pdbonly @@ -34,6 +36,7 @@ TRACE prompt 4 + false diff --git a/AustinHarris.JsonRpc.AspNet/packages.config b/AustinHarris.JsonRpc.AspNet/packages.config index 7c276ed..0cdd293 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.JsonRpcTestN/Test.cs b/AustinHarris.JsonRpcTestN/Test.cs index 3fd2e7f..426f4b1 100644 --- a/AustinHarris.JsonRpcTestN/Test.cs +++ b/AustinHarris.JsonRpcTestN/Test.cs @@ -68,7 +68,7 @@ public void TestCanCreateAndRemoveSession() Tuple.Create ("sooper", typeof(string)), Tuple.Create ("returns", typeof(string)) }.ToDictionary(x => x.Item1, x => x.Item2); - h.RegisterFuction("workie", metadata, new System.Collections.Generic.Dictionary(),new Func(x => "workie ... " + x)); + 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}"; @@ -1571,7 +1571,7 @@ public void TestPreProcessOnSession() Tuple.Create ("sooper", typeof(string)), Tuple.Create ("returns", typeof(string)) }.ToDictionary(x => x.Item1, x => x.Item2); - h.RegisterFuction("workie", metadata, new System.Collections.Generic.Dictionary(),new Func(x => "workie ... " + x)); + 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}"; @@ -1811,7 +1811,7 @@ public void TestPostProcessOnSession() Tuple.Create ("sooper", typeof(string)), Tuple.Create ("returns", typeof(string)) }.ToDictionary(x => x.Item1, x => x.Item2); - h.RegisterFuction("workie", metadata, new System.Collections.Generic.Dictionary(), new Func(x => "workie ... " + x)); + 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}"; diff --git a/AustinHarris.JsonRpcTestN/service.cs b/AustinHarris.JsonRpcTestN/service.cs index 623a0e8..7a823ae 100644 --- a/AustinHarris.JsonRpcTestN/service.cs +++ b/AustinHarris.JsonRpcTestN/service.cs @@ -343,13 +343,6 @@ public string TestPreProcessorThrowsException(string inputValue) throw new Exception("TestException"); } - [JsonRpcMethod] - public string TestPreProcessorSetsException(string inputValue) - { - JsonRpcContext.SetException(new JsonRpcException(-27000, "This exception was thrown using: JsonRpcContext.SetException()", null)); - return null; - } - [JsonRpcMethod] public string TestPostProcessor(string inputValue) { @@ -368,13 +361,6 @@ public string TestPostProcessorThrowsException(string inputValue) throw new Exception("TestException"); } - [JsonRpcMethod] - public string TestPostProcessorSetsException(string inputValue) - { - JsonRpcContext.SetException(new JsonRpcException(-27001, "This exception was thrown using: JsonRpcContext.SetException()", null)); - return null; - } - [JsonRpcMethod] public TreeNode TestNestedReturnType() { diff --git a/Json-Rpc/Attributes.cs b/Json-Rpc/Attributes.cs index 1592511..1131d5e 100644 --- a/Json-Rpc/Attributes.cs +++ b/Json-Rpc/Attributes.cs @@ -47,4 +47,13 @@ public string JsonParamName get { return jsonParamName; } } } + + /// + /// Used to assign context parameter. + /// + [AttributeUsage(AttributeTargets.Parameter, Inherited = false, AllowMultiple = false)] + public sealed class JsonRpcContextAttribute : Attribute + { + + } } diff --git a/Json-Rpc/Handler.cs b/Json-Rpc/Handler.cs index de89a0f..7a95045 100644 --- a/Json-Rpc/Handler.cs +++ b/Json-Rpc/Handler.cs @@ -109,34 +109,6 @@ public void Destroy() /// public string SessionId { get; private set; } - /// - /// 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 - /// - /// - public static object RpcContext() - { - return __currentRpcContext; - } - - [ThreadStatic] - static JsonRpcException __currentRpcException; - /// - /// Allows you to set the exception used in in the JsonRpc response. - /// Warning: Must be called from the same thread as the jsonRpc method. - /// - /// - public static void RpcSetException(JsonRpcException exception) - { - __currentRpcException = exception; - } - public static JsonRpcException RpcGetAndRemoveRpcException() - { - var ex = __currentRpcException; - __currentRpcException = null ; - return ex; - } - private AustinHarris.JsonRpc.PreProcessHandler externalPreProcessingHandler; private AustinHarris.JsonRpc.PostProcessHandler externalPostProcessingHandler; private Func externalErrorHandler; @@ -168,9 +140,9 @@ public static void RegisterInstance(string sessionID, object instance) /// 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) + public void RegisterFuction(string methodName, Dictionary parameterNameTypeMapping, Dictionary parameterNameDefaultValueMapping, string contextParameter, Delegate implementation) { - MetaData.AddService(methodName, parameterNameTypeMapping, parameterNameDefaultValueMapping, implementation); + MetaData.AddService(methodName, parameterNameTypeMapping, parameterNameDefaultValueMapping, contextParameter, implementation); } public void UnRegisterFunction(string methodName) @@ -196,8 +168,6 @@ public void SetPostProcessHandler(AustinHarris.JsonRpc.PostProcessHandler handle /// public async Task Handle(JsonRequest Rpc, Object RpcContext = null) { - AddRpcContext(RpcContext); - var preProcessingException = PreProcess(Rpc, RpcContext); if (preProcessingException != null) { @@ -250,9 +220,19 @@ public async Task Handle(JsonRequest Rpc, Object RpcContext = null if (Rpc.Params is Newtonsoft.Json.Linq.JArray) { var jarr = ((Newtonsoft.Json.Linq.JArray)Rpc.Params); - for (int i = 0; i < loopCt; i++) + for (int i = 0, j = 0; i < loopCt; i++, j++) { - parameters[i] = CleanUpParameter(jarr[i], metadata.parameters[i]); + if (metadata.parameters[i].Name == metadata.contextParameter) + { + Array.Resize(ref parameters, parameters.Length + 1); + loopCt++; + paramCount++; + parameters[i++] = RpcContext; + } + else + { + parameters[i] = CleanUpParameter(jarr[j], metadata.parameters[i]); + } } } else if (Rpc.Params is Newtonsoft.Json.Linq.JObject) @@ -260,7 +240,14 @@ public async Task Handle(JsonRequest Rpc, Object RpcContext = null var asDict = Rpc.Params as IDictionary; for (int i = 0; i < loopCt && i < metadata.parameters.Length; i++) { - if (asDict.ContainsKey(metadata.parameters[i].Name) == true) + if (metadata.parameters[i].Name == metadata.contextParameter) + { + Array.Resize(ref parameters, parameters.Length + 1); + loopCt++; + paramCount++; + parameters[i++] = RpcContext; + } + else if (asDict.ContainsKey(metadata.parameters[i].Name) == true) { parameters[i] = CleanUpParameter(asDict[metadata.parameters[i].Name], metadata.parameters[i]); continue; @@ -282,23 +269,18 @@ public async Task Handle(JsonRequest Rpc, Object RpcContext = null } } - // Optional Parameter support - // check if we still miss parameters compared to metadata which may include optional parameters. + // Optional Parameter & Context Parameter support + // check if we still miss parameters compared to metadata which may include optional parameters or context parameter. // if the rpc-call didn't supply a value for an optional parameter, we should be assinging the default value of it. - if (parameters.Length < metaDataParamCount && metadata.defaultValues.Length > 0) // rpc call didn't set values for all optional parameters, so we need to assign the default values for them. + if (parameters.Length < metaDataParamCount && + (metadata.defaultValues.Length > 0 || !String.IsNullOrEmpty(metadata.contextParameter))) // rpc call didn't set values for all optional parameters, so we need to assign the default values for them. { var suppliedParamsCount = parameters.Length; // the index we should start storing default values of optional parameters. var missingParamsCount = metaDataParamCount - parameters.Length; // the amount of optional parameters without a value set by rpc-call. + var contextParamsCount = String.IsNullOrEmpty(metadata.contextParameter) ? 0 : 1; Array.Resize(ref parameters, parameters.Length + missingParamsCount); // resize the array to include all optional parameters. - for (int paramIndex = parameters.Length - 1, defaultIndex = metadata.defaultValues.Length - 1; // fill missing parameters from the back - paramIndex >= suppliedParamsCount && defaultIndex >= 0; // to don't overwrite supplied ones. - paramIndex--, defaultIndex--) - { - parameters[paramIndex] = metadata.defaultValues[defaultIndex].Value; - } - - if (missingParamsCount > metadata.defaultValues.Length) + if (missingParamsCount > metadata.defaultValues.Length + contextParamsCount) { JsonResponse response = new JsonResponse { @@ -313,6 +295,22 @@ public async Task Handle(JsonRequest Rpc, Object RpcContext = null }; return PostProcess(Rpc, response, RpcContext); } + + for (int paramIndex = parameters.Length - 1, defaultIndex = metadata.defaultValues.Length - 1; // fill missing parameters from the back + paramIndex >= suppliedParamsCount && (defaultIndex >= 0 || contextParamsCount > 0); // to don't overwrite supplied ones. + paramIndex--, defaultIndex--) + { + if (metadata.parameters[paramIndex].Name == metadata.contextParameter) + { + parameters[paramIndex] = RpcContext; + contextParamsCount--; + } + else + { + parameters[paramIndex] = metadata.defaultValues[defaultIndex].Value; + } + } + } if (parameters.Length != metaDataParamCount) @@ -350,13 +348,8 @@ public async Task Handle(JsonRequest Rpc, Object RpcContext = null } var last = parameters.LastOrDefault(); - var contextException = RpcGetAndRemoveRpcException(); JsonResponse response = null; - if (contextException != null) - { - response = new JsonResponse() { Error = ProcessException(Rpc, contextException), Id = Rpc.Id }; - } - else if (expectsRefException && last != null && last is JsonRpcException) + if (expectsRefException && last != null && last is JsonRpcException) { response = new JsonResponse() { Error = ProcessException(Rpc, last as JsonRpcException), Id = Rpc.Id }; } @@ -397,22 +390,10 @@ public async Task Handle(JsonRequest Rpc, Object RpcContext = null } finally { - RemoveRpcContext(); } } #endregion - [ThreadStatic] - static object __currentRpcContext; - private void AddRpcContext(object RpcContext) - { - __currentRpcContext = RpcContext; - } - private void RemoveRpcContext() - { - __currentRpcContext = null; - } - private JsonRpcException ProcessException(JsonRequest req, JsonRpcException ex) { if (externalErrorHandler != null) diff --git a/Json-Rpc/JsonRpcContext.cs b/Json-Rpc/JsonRpcContext.cs index e9a5ee6..4e0f7b0 100644 --- a/Json-Rpc/JsonRpcContext.cs +++ b/Json-Rpc/JsonRpcContext.cs @@ -20,25 +20,5 @@ private JsonRpcContext(object value) /// The data associated with this context. /// public object Value { get; private set; } - - /// - /// 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 to function. - /// - /// - public static void SetException(JsonRpcException exception) - { - Handler.RpcSetException(exception); - } - - - /// - /// Must be called from within the execution context of the jsonRpc Method to return the context - /// - /// - public static JsonRpcContext Current() - { - return new JsonRpcContext(Handler.RpcContext()); - } } } diff --git a/Json-Rpc/SMDService.cs b/Json-Rpc/SMDService.cs index fe84bb5..fa783b9 100644 --- a/Json-Rpc/SMDService.cs +++ b/Json-Rpc/SMDService.cs @@ -34,9 +34,9 @@ public SMD () TypeHashes = new List(); } - internal void AddService(string method, Dictionary parameters, Dictionary defaultValues, Delegate dele) + internal void AddService(string method, Dictionary parameters, Dictionary defaultValues, string contextParameter, Delegate dele) { - var newService = new SMDService(transport,"JSON-RPC-2.0",parameters, defaultValues, dele); + var newService = new SMDService(transport,"JSON-RPC-2.0",parameters, defaultValues, contextParameter, dele); Services.Add(method,newService); } @@ -72,7 +72,7 @@ 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, Delegate dele) + public SMDService(string transport, string envelope, Dictionary parameters, Dictionary defaultValues, string contextParameter, Delegate dele) { // TODO: Complete member initialization this.dele = dele; @@ -98,6 +98,8 @@ public SMDService(string transport, string envelope, Dictionary pa // this is getting the return type from the end of the param list this.returns = new SMDResult(parameters.Values.LastOrDefault()); + + this.contextParameter = contextParameter; } public string transport { get; private set; } public string envelope { get; private set; } @@ -116,6 +118,11 @@ public SMDService(string transport, string envelope, Dictionary pa /// Stores default values for optional parameters. /// public ParameterDefaultValue[] defaultValues { get; private set; } + + /// + /// Stores context parameter name. + /// + public string contextParameter; } public class SMDResult diff --git a/Json-Rpc/ServiceBinder.cs b/Json-Rpc/ServiceBinder.cs index 20423a6..144d833 100644 --- a/Json-Rpc/ServiceBinder.cs +++ b/Json-Rpc/ServiceBinder.cs @@ -26,6 +26,7 @@ public static void BindService(string sessionID, Object instance) { Dictionary paras = new Dictionary(); Dictionary defaultValues = new Dictionary(); // dictionary that holds default values for optional params. + string contextParameter = null; var paramzs = meth.GetParameters(); @@ -52,6 +53,14 @@ public static void BindService(string sessionID, Object instance) if (paramzs[i].IsOptional) // if the parameter is an optional, add the default value to our default values dictionary. defaultValues.Add(paramName, paramzs[i].DefaultValue); + + var contextAttrs = paramzs[i].GetCustomAttributes(typeof(JsonRpcContextAttribute), false); + if (contextAttrs.Length > 0) + { + if (contextParameter != null) + throw new Exception("There are multiple context parameters: " + contextParameter + ", " + paramName); + contextParameter = paramName; + } } var resType = meth.ReturnType; @@ -63,7 +72,7 @@ public static void BindService(string sessionID, Object instance) 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); - handlerSession.MetaData.AddService(methodName, paras, defaultValues, newDel); + handlerSession.MetaData.AddService(methodName, paras, defaultValues, contextParameter, newDel); } } } diff --git a/TestServer_Console/TestServer_Console.csproj b/TestServer_Console/TestServer_Console.csproj index be9574b..1f9c7bd 100644 --- a/TestServer_Console/TestServer_Console.csproj +++ b/TestServer_Console/TestServer_Console.csproj @@ -8,8 +8,9 @@ Properties TestServer_Console TestServer_Console - v4.0 - Client + v4.5 + + 512 SAK SAK @@ -27,6 +28,7 @@ DEBUG;TRACE prompt 4 + false x86 @@ -36,12 +38,14 @@ TRACE prompt 4 + false AnyCPU bin\Debug\ 4 false + false AnyCPU @@ -49,6 +53,7 @@ 4 true false + false @@ -78,6 +83,7 @@ + diff --git a/TestServer_Console/packages.config b/TestServer_Console/packages.config index e307e1c..5fef524 100644 --- a/TestServer_Console/packages.config +++ b/TestServer_Console/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file From 164698362189a4e6b2f548eb9afe42aac94df123 Mon Sep 17 00:00:00 2001 From: chaeyk Date: Wed, 30 May 2018 18:11:19 +0900 Subject: [PATCH 6/9] parameter passing bug fix --- Json-Rpc/Handler.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Json-Rpc/Handler.cs b/Json-Rpc/Handler.cs index 7a95045..54356d6 100644 --- a/Json-Rpc/Handler.cs +++ b/Json-Rpc/Handler.cs @@ -226,8 +226,8 @@ public async Task Handle(JsonRequest Rpc, Object RpcContext = null { Array.Resize(ref parameters, parameters.Length + 1); loopCt++; - paramCount++; - parameters[i++] = RpcContext; + parameters[i] = RpcContext; + j--; } else { @@ -244,8 +244,7 @@ public async Task Handle(JsonRequest Rpc, Object RpcContext = null { Array.Resize(ref parameters, parameters.Length + 1); loopCt++; - paramCount++; - parameters[i++] = RpcContext; + parameters[i] = RpcContext; } else if (asDict.ContainsKey(metadata.parameters[i].Name) == true) { From ecf8f08cad83e4e28c57fcff67cb3b521a01303a Mon Sep 17 00:00:00 2001 From: chaeyk Date: Thu, 31 May 2018 15:00:05 +0900 Subject: [PATCH 7/9] newtonsoft.json version 9.0.1 -> 11.0.2 --- .gitignore | 1 + .../AustinHarris.JsonRpc.AspNet.csproj | 5 ++--- AustinHarris.JsonRpc.AspNet/packages.config | 2 +- AustinHarris.JsonRpcTestN/AustinHarris.JsonRpcTestN.csproj | 5 ++--- AustinHarris.JsonRpcTestN/packages.config | 2 +- Json-Rpc/AustinHarris.JsonRpc.csproj | 5 ++--- Json-Rpc/packages.config | 2 +- TestServer_Console/TestServer_Console.csproj | 5 ++--- TestServer_Console/packages.config | 2 +- 9 files changed, 13 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 11908c9..d5b7685 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ ipch/ *.ncb *.opensdf *.sdf +.vs # Visual Studio profiler *.psess diff --git a/AustinHarris.JsonRpc.AspNet/AustinHarris.JsonRpc.AspNet.csproj b/AustinHarris.JsonRpc.AspNet/AustinHarris.JsonRpc.AspNet.csproj index 8baedb9..a134fea 100644 --- a/AustinHarris.JsonRpc.AspNet/AustinHarris.JsonRpc.AspNet.csproj +++ b/AustinHarris.JsonRpc.AspNet/AustinHarris.JsonRpc.AspNet.csproj @@ -39,9 +39,8 @@ false - - ..\packages\Newtonsoft.Json.9.0.1\lib\net40\Newtonsoft.Json.dll - True + + ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll diff --git a/AustinHarris.JsonRpc.AspNet/packages.config b/AustinHarris.JsonRpc.AspNet/packages.config index 0cdd293..c8b3ae6 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.JsonRpcTestN/AustinHarris.JsonRpcTestN.csproj b/AustinHarris.JsonRpcTestN/AustinHarris.JsonRpcTestN.csproj index fe8c6d8..ce26feb 100644 --- a/AustinHarris.JsonRpcTestN/AustinHarris.JsonRpcTestN.csproj +++ b/AustinHarris.JsonRpcTestN/AustinHarris.JsonRpcTestN.csproj @@ -30,9 +30,8 @@ false - - ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll - True + + ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll ..\packages\NUnit.2.6.4\lib\nunit.framework.dll diff --git a/AustinHarris.JsonRpcTestN/packages.config b/AustinHarris.JsonRpcTestN/packages.config index 3632280..f19116d 100644 --- a/AustinHarris.JsonRpcTestN/packages.config +++ b/AustinHarris.JsonRpcTestN/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/Json-Rpc/AustinHarris.JsonRpc.csproj b/Json-Rpc/AustinHarris.JsonRpc.csproj index 78f21c7..d103721 100644 --- a/Json-Rpc/AustinHarris.JsonRpc.csproj +++ b/Json-Rpc/AustinHarris.JsonRpc.csproj @@ -75,9 +75,8 @@ - - ..\packages\Newtonsoft.Json.9.0.1\lib\net40\Newtonsoft.Json.dll - True + + ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll diff --git a/Json-Rpc/packages.config b/Json-Rpc/packages.config index 0cdd293..c8b3ae6 100644 --- a/Json-Rpc/packages.config +++ b/Json-Rpc/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/TestServer_Console/TestServer_Console.csproj b/TestServer_Console/TestServer_Console.csproj index 1f9c7bd..0446212 100644 --- a/TestServer_Console/TestServer_Console.csproj +++ b/TestServer_Console/TestServer_Console.csproj @@ -56,9 +56,8 @@ false - - ..\packages\Newtonsoft.Json.9.0.1\lib\net40\Newtonsoft.Json.dll - True + + ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll diff --git a/TestServer_Console/packages.config b/TestServer_Console/packages.config index 5fef524..c8b3ae6 100644 --- a/TestServer_Console/packages.config +++ b/TestServer_Console/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file From b971e6bf3f64c29b34a0a236a398c471659969ad Mon Sep 17 00:00:00 2001 From: chaeyk Date: Thu, 7 Jun 2018 18:24:03 +0900 Subject: [PATCH 8/9] Now, We can use JsonRequest/JsonResponse as JsonRpcProcessor's parameter/return type. --- Json-Rpc/JsonRpcProcessor.cs | 247 +++++++++++++++++++++++------------ 1 file changed, 160 insertions(+), 87 deletions(-) diff --git a/Json-Rpc/JsonRpcProcessor.cs b/Json-Rpc/JsonRpcProcessor.cs index dc46e7c..0bcb668 100644 --- a/Json-Rpc/JsonRpcProcessor.cs +++ b/Json-Rpc/JsonRpcProcessor.cs @@ -36,23 +36,35 @@ public static async Task Process(string sessionId, string jsonRpc, objec return await ProcessInternal(sessionId, jsonRpc, context); } + public static async Task Process(string jsonRpc, JsonRequest jsonRequest, object context = null) + { + return await Process(Handler.DefaultSessionId(), jsonRpc, jsonRequest, context); + } + + public static async Task Process(string sessionId, string jsonRpc, JsonRequest jsonRequest, object context = null) + { + return await ProcessInternal(sessionId, jsonRpc, jsonRequest, context); + } + + public static async Task Process(string jsonRpc, JsonRequest[] jsonRequests, object context = null) + { + return await Process(Handler.DefaultSessionId(), jsonRpc, jsonRequests, context); + } + + public static async Task Process(string sessionId, string jsonRpc, JsonRequest[] jsonRequests, object context = null) + { + return await ProcessInternal(sessionId, jsonRpc, jsonRequests, context); + } + private static async Task ProcessInternal(string sessionId, string jsonRpc, object jsonRpcContext) { var handler = Handler.GetSessionHandler(sessionId); - JsonRequest[] batch = null; + bool singleBatch; try { - if (isSingleRpc(jsonRpc)) - { - var foo = JsonConvert.DeserializeObject(jsonRpc); - batch = new[] { foo }; - } - else - { - batch = JsonConvert.DeserializeObject(jsonRpc); - } + batch = DeserializeRequest(jsonRpc, out singleBatch); } catch (Exception ex) { @@ -62,110 +74,171 @@ private static async Task ProcessInternal(string sessionId, string jsonR }); } - if (batch.Length == 0) + JsonResponse[] jsonResponses = await ProcessInternal(sessionId, jsonRpc, batch, jsonRpcContext); + return SerializeResponse(jsonResponses, singleBatch); + } + + private static async Task ProcessInternal(string sessionId, string jsonRpc, JsonRequest[] jsonRequests, object jsonRpcContext) + { + var handler = Handler.GetSessionHandler(sessionId); + + if (jsonRequests.Length == 0) { - return Newtonsoft.Json.JsonConvert.SerializeObject(new JsonResponse + return new JsonResponse[] { - Error = handler.ProcessParseException(jsonRpc, - new JsonRpcException(3200, "Invalid Request", "Batch of calls was empty.")) - }); + new JsonResponse + { + Error = handler.ProcessParseException(jsonRpc, + new JsonRpcException(-32600, "Invalid Request", "Batch of calls was empty.")) + } + }; } - var singleBatch = batch.Length == 1; - StringBuilder sbResult = null; - for (var i = 0; i < batch.Length; i++) + List jsonResponses = null; + for (var i = 0; i < jsonRequests.Length; i++) { - var jsonRequest = batch[i]; - var jsonResponse = new JsonResponse(); + var jsonRequest = jsonRequests[i]; + var jsonResponse = await ProcessInternal(sessionId, jsonRpc, jsonRequest, jsonRpcContext); - if (jsonRequest == null) + // single rpc optimization + if (jsonRequests.Length == 1 && (jsonResponse.Id != null || 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.")); + return new JsonResponse[] { jsonResponse }; } - else if (jsonRequest.Method == null) + + if (jsonResponses == null) { - jsonResponse.Error = handler.ProcessParseException(jsonRpc, - new JsonRpcException(-32600, "Invalid Request", "Missing property 'method'")); + jsonResponses = new List(); } - else - { - jsonResponse.Id = jsonRequest.Id; + jsonResponses.Add(jsonResponse); + } + return jsonResponses.ToArray(); + } - var data = await handler.Handle(jsonRequest, jsonRpcContext); + private static async Task ProcessInternal(string sessionId, string jsonRpc, JsonRequest jsonRequest, object jsonRpcContext) + { + var handler = Handler.GetSessionHandler(sessionId); - if (data == null) continue; + var jsonResponse = new JsonResponse(); - jsonResponse.Error = data.Error; - jsonResponse.Result = data.Result; + 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 if (jsonRequest.Method == null) + { + jsonResponse.Error = handler.ProcessParseException(jsonRpc, + new JsonRpcException(-32601, "Invalid Request", "Missing property 'method'")); + } + else + { + jsonResponse.Id = jsonRequest.Id; - } - 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(); - writer.WritePropertyName("jsonrpc"); writer.WriteValue("2.0"); + var data = await handler.Handle(jsonRequest, jsonRpcContext); - if (jsonResponse.Error != null) - { - writer.WritePropertyName("error"); writer.WriteRawValue(JsonConvert.SerializeObject(jsonResponse.Error)); - } - else - { - writer.WritePropertyName("result"); writer.WriteRawValue(JsonConvert.SerializeObject(jsonResponse.Result)); - } - writer.WritePropertyName("id"); writer.WriteValue(jsonResponse.Id); - writer.WriteEndObject(); - return sw.ToString(); + if (data == null) return null; - //return JsonConvert.SerializeObject(jsonResponse); - } - else if (jsonResponse.Id == null && jsonResponse.Error == null) + jsonResponse.Error = data.Error; + jsonResponse.Result = data.Result; + } + return jsonResponse; + } + + private static bool IsSingleRpc(string json) + { + for (int i = 0; i < json.Length; i++) + { + if (json[i] == '{') return true; + else if (json[i] == '[') return false; + } + return true; + } + + public static JsonRequest[] DeserializeRequest(string jsonRpc, out bool isSingleRpc) + { + JsonRequest[] batch = null; + try + { + isSingleRpc = IsSingleRpc(jsonRpc); + if (isSingleRpc) { - // do nothing - sbResult = new StringBuilder(0); + var foo = JsonConvert.DeserializeObject(jsonRpc); + batch = new[] { foo }; } else { - // write out the response - if (i == 0) - { - sbResult = new StringBuilder("["); - } - - sbResult.Append(JsonConvert.SerializeObject(jsonResponse)); - if (i < batch.Length - 1) - { - sbResult.Append(','); - } - else if (i == batch.Length - 1) - { - sbResult.Append(']'); - } + batch = JsonConvert.DeserializeObject(jsonRpc); } } - return sbResult.ToString(); + catch (Exception ex) + { + throw new JsonRpcException(-32700, "Parse error", ex); + } + return batch; } - private static bool isSingleRpc(string json) + public static string SerializeResponse(JsonResponse jsonResponse) { - for (int i = 0; i < json.Length; i++) + if (jsonResponse.Id == null && jsonResponse.Error == null) { - if (json[i] == '{') return true; - else if (json[i] == '[') return false; + // notification returns empty string + return ""; } - return true; + + 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); + } + + StringWriter sw = new StringWriter(); + JsonTextWriter writer = new JsonTextWriter(sw); + writer.WriteStartObject(); + writer.WritePropertyName("jsonrpc"); writer.WriteValue("2.0"); + + if (jsonResponse.Error != null) + { + writer.WritePropertyName("error"); writer.WriteRawValue(JsonConvert.SerializeObject(jsonResponse.Error)); + } + else + { + writer.WritePropertyName("result"); writer.WriteRawValue(JsonConvert.SerializeObject(jsonResponse.Result)); + } + writer.WritePropertyName("id"); writer.WriteValue(jsonResponse.Id); + writer.WriteEndObject(); + return sw.ToString(); + } + + public static string SerializeResponse(IEnumerable jsonResponses, bool isSingleRpc) + { + if (isSingleRpc) + { + return SerializeResponse(jsonResponses.First()); + } + + StringBuilder sbResult = new StringBuilder(0); + foreach (JsonResponse jsonResponse in jsonResponses) + { + string str = SerializeResponse(jsonResponse); + if (str.Length == 0) + { + // this is notification + continue; + } + + sbResult.Append(sbResult.Length == 0 ? "[" : ","); + sbResult.Append(str); + } + if (sbResult.Length > 0) + { + sbResult.Append("]"); + } + return sbResult.ToString(); } } } From 6a7b792bf267709303bdf943033d10d9e61361ad Mon Sep 17 00:00:00 2001 From: chaeyk Date: Fri, 8 Jun 2018 19:15:36 +0900 Subject: [PATCH 9/9] fixed duplicated property name bug. --- Json-Rpc/SMDService.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Json-Rpc/SMDService.cs b/Json-Rpc/SMDService.cs index fa783b9..488a827 100644 --- a/Json-Rpc/SMDService.cs +++ b/Json-Rpc/SMDService.cs @@ -215,7 +215,23 @@ internal static int GetTypeRecursive(Type t) if (item.PropertyType != t) { var jt = GetTypeRecursive(item.PropertyType); - jo.Add(item.Name, jt); + ParameterInfo[] parameterInfos = item.GetIndexParameters(); + if (parameterInfos?.Length > 0) + { + string name = item.Name + "["; + for (int i = 0; i < parameterInfos.Length; i++) + { + name += parameterInfos[i].ParameterType.Name; + if (i != parameterInfos.Length - 1) + name += ","; + } + name += "]"; + jo.Add(name, jt); + } + else + { + jo.Add(item.Name, jt); + } } else {