diff --git a/AustinHarris.JsonRpc.Jsmn/AustinHarris.JsonRpc.Jsmn.csproj b/AustinHarris.JsonRpc.Jsmn/AustinHarris.JsonRpc.Jsmn.csproj new file mode 100644 index 0000000..6d160cf --- /dev/null +++ b/AustinHarris.JsonRpc.Jsmn/AustinHarris.JsonRpc.Jsmn.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.0 + + + + true + + + true + + + diff --git a/AustinHarris.JsonRpc.Jsmn/Jsmn.cs b/AustinHarris.JsonRpc.Jsmn/Jsmn.cs new file mode 100644 index 0000000..01e2bea --- /dev/null +++ b/AustinHarris.JsonRpc.Jsmn/Jsmn.cs @@ -0,0 +1,672 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AustinHarris.JsonRpc.Jsmn +{ + + + /** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ + public enum jsmntype_t + { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 + } + + public enum jsmnerr + { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 + } + + /** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ + public struct jsmntok_t + { + public jsmntype_t type; + public int start; + public int end; + public int size; + public int parent; + }; + + /** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string + */ + public struct jsmn_parser + { + + public int pos; /* offset in the JSON string */ + public int toknext; /* next token to allocate */ + public int toksuper; /* superior token node, e.g parent object or array */ + }; + + + public static class jsmn + { + public static unsafe int parse(string json, jsmntok_t[] tokens) + { + fixed(jsmntok_t* thead = &tokens[0]) + fixed (char* pjson = json) + { + jsmn_parser parser; + jsmn_c.init(&parser); + return jsmn_c.jsmn_parse(&parser, pjson, json.Length, thead, 100); + + } + } + + public static unsafe bool parseFirstField(string json, string fieldName, out string value) + { + const int count = 20; + jsmntok_t* tokens = stackalloc jsmntok_t[count]; + var found = 0; + fixed (char* pjson = json) + { + jsmn_parser parser; + jsmn_c.init(&parser); + found = jsmn_c.jsmn_parse(&parser, pjson, json.Length, tokens, count); + } + + for (int i = 0; i < found; i++) + { + var tokSz = tokens[i].end - tokens[i].start; + if (tokSz == fieldName.Length && String.Compare(fieldName, 0, json, tokens[i].start, tokSz) == 0) + { + i++; + value = json.Substring(tokens[i].start, tokens[i].end - tokens[i].start); + return true; + } + } + + value = string.Empty; + return false; + + } + + static readonly string idField = "id"; + static readonly string paramsField = "params"; + public static unsafe void DeserializeJsonRef(string json, ref ValueTuple functionParameters, ref string rawId, KeyValuePair[] info) + { + const int count = 20; + jsmntok_t* tokens = stackalloc jsmntok_t[count]; + var found = 0; + fixed (char* pjson = json) + { + jsmn_parser parser; + jsmn_c.init(&parser); + found = jsmn_c.jsmn_parse(&parser, pjson, json.Length, tokens, count); + } + + + + for (int i = 0; i < found; i++) + { + var tokSz = tokens[i].end - tokens[i].start; + var current = tokens[i]; + var currentval = json.Substring(tokens[i].start, tokens[i].end - tokens[i].start); + if (tokSz == idField.Length && String.Compare(idField, 0, json, tokens[i].start, tokSz) == 0) + { + i++; + rawId = json.Substring(tokens[i].start, tokens[i].end - tokens[i].start); + } + + else if (tokSz == paramsField.Length && String.Compare(paramsField, 0, json, tokens[i].start, tokSz) == 0) + { + i++; + switch (tokens[i].type) + { + case jsmntype_t.JSMN_ARRAY: + var level = i; + i++; + while (tokens[i].parent == level) + { + switch (tokens[i].type) + { + case jsmntype_t.JSMN_UNDEFINED: + break; + case jsmntype_t.JSMN_OBJECT: + break; + case jsmntype_t.JSMN_ARRAY: + break; + case jsmntype_t.JSMN_STRING: + if (i - level == 1) + functionParameters.Item1 = + (T) Convert.ChangeType( + json.Substring(tokens[i].start, tokens[i].end - tokens[i].start), + typeof(T)); + break; + case jsmntype_t.JSMN_PRIMITIVE: + if (i - level == 1) + if (info[0].Value.IsGenericType && info[0].Value.GenericTypeArguments.Length==1) + { + var substr = json.Substring(tokens[i].start, + tokens[i].end - tokens[i].start); + if(substr != "null"){ + functionParameters.Item1 = (T)Convert.ChangeType( + substr, + info[0].Value.GenericTypeArguments[0]); + } + } + else { + functionParameters.Item1 = + (T) Convert.ChangeType( + json.Substring(tokens[i].start, tokens[i].end - tokens[i].start), + typeof(T)); + } + break; + default: + throw new ArgumentOutOfRangeException(); + } + i++; + } + + i--; + break; + case jsmntype_t.JSMN_OBJECT: + break; + case jsmntype_t.JSMN_UNDEFINED: + break; + case jsmntype_t.JSMN_STRING: + break; + case jsmntype_t.JSMN_PRIMITIVE: + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } + } + + public static unsafe void DeserializeJsonRef(string json, ref ValueTuple functionParameters, ref string rawId, KeyValuePair[] info) + { + const int count = 20; + jsmntok_t* tokens = stackalloc jsmntok_t[count]; + var found = 0; + fixed (char* pjson = json) + { + jsmn_parser parser; + jsmn_c.init(&parser); + found = jsmn_c.jsmn_parse(&parser, pjson, json.Length, tokens, count); + } + + const string idField = "id"; + const string paramsField = "params"; + + for (int i = 0; i < found; i++) + { + var tokSz = tokens[i].end - tokens[i].start; + var current = tokens[i]; + var currentval = json.Substring(tokens[i].start, tokens[i].end - tokens[i].start); + if (tokSz == idField.Length && String.Compare(idField, 0, json, tokens[i].start, tokSz) == 0) + { + i++; + rawId = json.Substring(tokens[i].start, tokens[i].end - tokens[i].start); + } + + else if (tokSz == paramsField.Length && String.Compare(paramsField, 0, json, tokens[i].start, tokSz) == 0) + { + i++; + switch (tokens[i].type) + { + case jsmntype_t.JSMN_ARRAY: + var level = i; + i++; + while (tokens[i].parent == level) + { + switch (tokens[i].type) + { + case jsmntype_t.JSMN_UNDEFINED: + break; + case jsmntype_t.JSMN_OBJECT: + break; + case jsmntype_t.JSMN_ARRAY: + break; + case jsmntype_t.JSMN_STRING: + if (i - level == 1) + functionParameters.Item1 = + (T1) Convert.ChangeType( + json.Substring(tokens[i].start, tokens[i].end - tokens[i].start), + typeof(T1)); + if (i - level == 2) + functionParameters.Item2 = + (T2) Convert.ChangeType( + json.Substring(tokens[i].start, tokens[i].end - tokens[i].start), + typeof(T2)); + break; + case jsmntype_t.JSMN_PRIMITIVE: + if (i - level == 1) + functionParameters.Item1 = + (T1) Convert.ChangeType( + json.Substring(tokens[i].start, tokens[i].end - tokens[i].start), + typeof(T1)); + if (i - level == 2) + functionParameters.Item2 = + (T2) Convert.ChangeType( + json.Substring(tokens[i].start, tokens[i].end - tokens[i].start), + typeof(T2)); + break; + default: + throw new ArgumentOutOfRangeException(); + } + i++; + } + + i--; + break; + case jsmntype_t.JSMN_OBJECT: + break; + case jsmntype_t.JSMN_UNDEFINED: + break; + case jsmntype_t.JSMN_STRING: + break; + case jsmntype_t.JSMN_PRIMITIVE: + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } + } + } + + internal static unsafe class jsmn_c + { + /** + * Allocates a fresh unused token from the token pull. + */ + static jsmntok_t* jsmn_alloc_token(jsmn_parser* parser, jsmntok_t* tokens, int num_tokens) + { + jsmntok_t* tok; + if (parser->toknext >= num_tokens) + { + return null; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; + + tok->parent = -1; + + return tok; + } + + /** + * Fills token type and boundaries. + */ + static void jsmn_fill_token(jsmntok_t* token, jsmntype_t type, int start, int end) + { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; + } + + /** + * Fills next available token with JSON primitive. + */ + static int jsmn_parse_primitive(jsmn_parser* parser, char* js, int len, jsmntok_t* tokens, int num_tokens) + { + jsmntok_t* token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) + { + switch (js[parser->pos]) + { + //# ifndef JSMN_STRICT + // /* In strict mode primitive must be followed by "," or "}" or "]" */ + // case ':': + //#endif + case '\t': + case '\r': + case '\n': + case ' ': + case ',': + case ']': + case '}': + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) + { + parser->pos = start; + return (int)jsmnerr.JSMN_ERROR_INVAL; + } + } + //# ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return (int)jsmnerr.JSMN_ERROR_PART; + //#endif + + found: + if (tokens == null) + { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == null) + { + parser->pos = start; + return (int)jsmnerr.JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, jsmntype_t.JSMN_PRIMITIVE, (int)start, (int)parser->pos); + //# ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; + //#endif + parser->pos--; + return 0; + } + + /** + * Fills next token with JSON string. + */ + static int jsmn_parse_string(jsmn_parser* parser, char* js, int len, jsmntok_t* tokens, int num_tokens) + { + jsmntok_t* token; + + int start = (int)parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) + { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') + { + if (tokens == null) + { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == null) + { + parser->pos = start; + return (int)jsmnerr.JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, jsmntype_t.JSMN_STRING, start + 1, parser->pos); + //# ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; + //#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) + { + int i; + parser->pos++; + switch (js[parser->pos]) + { + /* Allowed escaped symbols */ + case '\"': + case '/': + case '\\': + case 'b': + case 'f': + case 'r': + case 'n': + case 't': + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) + { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) + { /* a-f */ + parser->pos = start; + return (int)jsmnerr.JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return (int)jsmnerr.JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return (int)jsmnerr.JSMN_ERROR_PART; + } + + /** + * Parse JSON string and fill tokens. + */ + internal static int jsmn_parse(jsmn_parser* parser, char* js, int len, jsmntok_t* tokens, int num_tokens) + { + int r; + int i; + jsmntok_t* token; + int count = (int)parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) + { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) + { + case '{': + case '[': + count++; + if (tokens == null) + { + break; + } + token = jsmn_alloc_token(parser, tokens, (int)num_tokens); + if (token == null) + return (int)jsmnerr.JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) + { + tokens[parser->toksuper].size++; + //# ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; + //#endif + } + token->type = (c == '{' ? jsmntype_t.JSMN_OBJECT : jsmntype_t.JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = (int)parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == null) + break; + type = (c == '}' ? jsmntype_t.JSMN_OBJECT : jsmntype_t.JSMN_ARRAY); + //# ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) + { + return (int)jsmnerr.JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (; ; ) + { + if (token->start != -1 && token->end == -1) + { + if (token->type != type) + { + return (int)jsmnerr.JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) + { + if (token->type != type || parser->toksuper == -1) + { + return (int)jsmnerr.JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } + //#else + // for (i = parser->toknext - 1; i >= 0; i--) + // { + // token = &tokens[i]; + // if (token->start != -1 && token->end == -1) + // { + // if (token->type != type) + // { + // return jsmnerr.JSMN_ERROR_INVAL; + // } + // parser->toksuper = -1; + // token->end = parser->pos + 1; + // break; + // } + // } + // /* Error if unmatched closing bracket */ + // if (i == -1) return jsmnerr.JSMN_ERROR_INVAL; + // for (; i >= 0; i--) + // { + // token = &tokens[i]; + // if (token->start != -1 && token->end == -1) + // { + // parser->toksuper = i; + // break; + // } + // } + //#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != null) + tokens[parser->toksuper].size++; + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != null && parser->toksuper != -1 && + tokens[parser->toksuper].type != jsmntype_t.JSMN_ARRAY && + tokens[parser->toksuper].type != jsmntype_t.JSMN_OBJECT) + { + //# ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; + //#else + // for (i = parser->toknext - 1; i >= 0; i--) + // { + // if (tokens[i].type == jsmntype_t.JSMN_ARRAY || tokens[i].type == jsmntype_t.JSMN_OBJECT) + // { + // if (tokens[i].start != -1 && tokens[i].end == -1) + // { + // parser->toksuper = i; + // break; + // } + // } + // } + //#endif + } + break; + //# ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 't': + case 'f': + case 'n': + /* And they must not be keys of the object */ + if (tokens != null && parser->toksuper != -1) + { + jsmntok_t* t = &tokens[parser->toksuper]; + if (t->type == jsmntype_t.JSMN_OBJECT || + (t->type == jsmntype_t.JSMN_STRING && t->size != 0)) + { + return (int)jsmnerr.JSMN_ERROR_INVAL; + } + } + //#else + // /* In non-strict mode every unquoted value is a primitive */ + // default: + //#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != null) + tokens[parser->toksuper].size++; + break; + + //# ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return (int)jsmnerr.JSMN_ERROR_INVAL; + //#endif + } + } + + if (tokens != null) + { + for (i = parser->toknext - 1; i >= 0; i--) + { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) + { + return (int)jsmnerr.JSMN_ERROR_PART; + } + } + } + + return count; + } + + /** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ + internal static void init(jsmn_parser* parser) + { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; + } + + } +} \ No newline at end of file diff --git a/AustinHarris.JsonRpc.Newtonsoft/AustinHarris.JsonRpc.Newtonsoft.csproj b/AustinHarris.JsonRpc.Newtonsoft/AustinHarris.JsonRpc.Newtonsoft.csproj new file mode 100644 index 0000000..26d67c3 --- /dev/null +++ b/AustinHarris.JsonRpc.Newtonsoft/AustinHarris.JsonRpc.Newtonsoft.csproj @@ -0,0 +1,15 @@ + + + netstandard2.1 + + + false + + + + + + + + + \ No newline at end of file diff --git a/AustinHarris.JsonRpc.Newtonsoft/JsonRequest.cs b/AustinHarris.JsonRpc.Newtonsoft/JsonRequest.cs new file mode 100644 index 0000000..b464ab7 --- /dev/null +++ b/AustinHarris.JsonRpc.Newtonsoft/JsonRequest.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json; + +namespace AustinHarris.JsonRpc.Newtonsoft +{ + /// + /// Represents a JsonRpc request + /// + [JsonObject(MemberSerialization.OptIn)] + public class JsonRequest : IJsonRequest + { + public JsonRequest() + { + } + + public JsonRequest(string method, string raw) + { + Method = method; + Raw = raw; + } + + [JsonProperty("method")] + public string Method { get; set; } + + public string Raw { get; set; } + } +} diff --git a/AustinHarris.JsonRpc.Newtonsoft/JsonResponseErrorObject.cs b/AustinHarris.JsonRpc.Newtonsoft/JsonResponseErrorObject.cs new file mode 100644 index 0000000..c10ce87 --- /dev/null +++ b/AustinHarris.JsonRpc.Newtonsoft/JsonResponseErrorObject.cs @@ -0,0 +1,48 @@ +using System; +using Newtonsoft.Json; + +namespace AustinHarris.JsonRpc.Newtonsoft +{ + /// + /// 5.1 Error object + /// + /// When a rpc call encounters an error, the Response Object MUST contain the error member with a value that is a Object with the following members: + /// codeA Number that indicates the error type that occurred. + /// This MUST be an integer.messageA String providing a short description of the error. + /// The message SHOULD be limited to a concise single sentence.dataA Primitive or Structured value that contains additional information about the error. + /// This may be omitted. + /// The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.). + /// The error codes from and including -32768 to -32000 are reserved for pre-defined errors. Any code within this range, but not defined explicitly below is reserved for future use. The error codes are nearly the same as those suggested for XML-RPC at the following url: http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php + /// + /// code message meaning + /// + /// -32700 Parse error Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text. + /// -32600 Invalid Request The JSON sent is not a valid Request object. + /// -32601 Method not found The method does not exist / is not available. + /// -32602 Invalid params Invalid method parameter(s). + /// -32603 Internal error Internal JSON-RPC error. + /// -32000 to -32099 Server error Reserved for implementation-defined server-errors. + /// + /// The remainder of the space is available for application defined errors. + /// + [Serializable] + [JsonObject(MemberSerialization.OptIn)] + public class JsonRpcException : System.Exception, IJsonRpcException + { + [JsonProperty] + public int Code { get; set; } + + [JsonProperty] + public string Message { get; set; } + + [JsonProperty] + public object Data { get; set; } + + public JsonRpcException(int code, string message, object data) + { + this.Code = code; + this.Message = message; + this.Data = data; + } + } +} diff --git a/AustinHarris.JsonRpc.Newtonsoft/ObjectFactory.cs b/AustinHarris.JsonRpc.Newtonsoft/ObjectFactory.cs new file mode 100644 index 0000000..5a03aa8 --- /dev/null +++ b/AustinHarris.JsonRpc.Newtonsoft/ObjectFactory.cs @@ -0,0 +1,486 @@ +using System; +using System.Buffers; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Linq; +using AustinHarris.JsonRpc.Jsmn; + +namespace AustinHarris.JsonRpc.Newtonsoft +{ + public class ObjectFactory : IObjectFactory + { + public IJsonRpcException CreateException(int code, string message, object data) + { + return new JsonRpcException(code, message, data); + } + + public object DeserializeJson(string json, Type type) + { + return JsonConvert.DeserializeObject(json, type); + } + + public IJsonRequest CreateRequest() + { + return new JsonRequest(); + } +// // JSON.Net implementation +// public string MethodName(string json) +// { +// JsonTextReader reader = new JsonTextReader(new StringReader(json)); +// using (reader) +// while (reader.Read()) +// { +// if (reader.TokenType == JsonToken.PropertyName) +// { +// var name = (string)reader.Value; +// if (name == "method") +// { +// reader.Read(); +// return (string)reader.Value; +// } +// continue; +// } +// } +// return String.Empty; +// } + + // JSMN implementation + private static string METHOD = "method"; + public string MethodName(string json) + { + string value; + if (jsmn.parseFirstField(json,METHOD,out value)) + { + return value; + } + + throw new KeyNotFoundException(METHOD); + } + + const string envelopeResult1 = "{\"jsonrpc\":\"2.0\",\"result\":"; + const string envelopeError1 = "{\"jsonrpc\":\"2.0\",\"error\":"; + const string envelope2 = ",\"id\":"; + const string envelope3 = "}"; + static int LenEnvelopeResult = envelopeResult1.Length + envelope2.Length + envelope3.Length; + static int LenEnvelopeError = envelopeError1.Length + envelope2.Length + envelope3.Length; + + public string ToJsonRpcResponse(ref InvokeResult response) + { + if (String.IsNullOrEmpty(response.SerializedError)) + { + var respLen = response.SerializedResult.Length + + (response.SerializedId !=null ? response.SerializedId.Length : 0) + + LenEnvelopeResult; + string outs = string.Create(respLen, response, (span, result) => + { + var newC = 0; + for (int i = 0; i < envelopeResult1.Length; i++) + { + span[newC++] = envelopeResult1[i]; + } + + for (int i = 0; i < result.SerializedResult.Length; i++) + { + span[newC++] = result.SerializedResult[i]; + } + + for (int i = 0; i < envelope2.Length; i++) + { + span[newC++] = envelope2[i]; + } + + if (result.SerializedId != null) + { + for (int i = 0; i < result.SerializedId.Length; i++) + { + span[newC++] = result.SerializedId[i]; + } + } + + for (int i = 0; i < envelope3.Length; i++) + { + span[newC++] = envelope3[i]; + } + }); + return outs; + } + else + { + var respLen = response.SerializedError.Length + + response.SerializedId.Length + + LenEnvelopeError; + + string outs = string.Create(respLen, response, (span, result) => + { + var newC = 0; + for (int i = 0; i < envelopeError1.Length; i++) + { + span[newC++] = envelopeError1[i]; + } + + for (int i = 0; i < result.SerializedError.Length; i++) + { + span[newC++] = result.SerializedError[i]; + } + + for (int i = 0; i < envelope2.Length; i++) + { + span[newC++] = envelope2[i]; + } + + for (int i = 0; i < result.SerializedId.Length; i++) + { + span[newC++] = result.SerializedId[i]; + } + + for (int i = 0; i < envelope3.Length; i++) + { + span[newC++] = envelope3[i]; + } + }); + return outs; + + } + } + static JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings(); + public string Serialize(T data) + { + return JsonConvert.SerializeObject(data, jsonSerializerSettings); + } + + public IJsonRequest[] DeserializeRequests(string requests) + { + return JsonConvert.DeserializeObject(requests); + } + + public void DeserializeJsonRef(string json, ref ValueTuple functionParameters, ref string rawId, KeyValuePair[] info) + { + jsmn.DeserializeJsonRef(json,ref functionParameters, ref rawId, info); + } + + public void DeserializeJsonRef(string json, ref (T1, T2) functionParameters, ref string rawId, + KeyValuePair[] info) + { + jsmn.DeserializeJsonRef(json,ref functionParameters, ref rawId, info); + } + +// public void DeserializeJsonRef(string json, ref ValueTuple functionParameters, ref string rawId, KeyValuePair[] info) +// { +// var prop = new Stack(); +// var ptype = String.Empty; +// JsonTextReader reader = new JsonTextReader(new StringReader(json)); +// var pidx = 0; +// while (reader.Read()) +// { +// if (reader.Value != null) +// { +// if (reader.TokenType == JsonToken.PropertyName) +// { +// prop.Push((string)reader.Value); +// continue; +// } +// else if (prop.Peek() == "jsonrpc" && prop.Count == 1) +// { +// prop.Pop(); +// continue; +// } +// else if (prop.Peek() == "method" && prop.Count == 1) +// { +// prop.Pop(); +// continue; +// } +// else if (prop.Peek() == "id" && prop.Count == 1) +// { +// rawId = reader.Value.ToString(); +// prop.Pop(); +// continue; +// } +// else +// { +// if (ptype == "Array") +// { +// if (reader.TokenType == JsonToken.Null) +// functionParameters.Item1 = default(T); +// else +// functionParameters.Item1 = new JValue(reader.Value).ToObject(); +// pidx++; +// } +// else if (ptype == "Object") +// { +// var propName = prop.Pop(); +// for (int i = 0; i < info.Length; i++) +// { +// if (info[i].Key == propName) +// { +// if (reader.TokenType == JsonToken.Null) +// functionParameters.Item1 = default(T); +// else +// functionParameters.Item1 = new JValue(reader.Value).ToObject(); +// } +// } +// } +// } +// } +// else +// { +// if (prop.Count > 0 && prop.Peek() == "params") +// { +// if (reader.TokenType == JsonToken.StartArray) +// { +// ptype = "Array"; +// } +// else if (reader.TokenType == JsonToken.StartObject) +// { +// ptype = "Object"; +// } +// else if (reader.TokenType == JsonToken.EndArray +// || reader.TokenType == JsonToken.EndObject) +// { +// prop.Pop(); +// continue; +// } +// } +// } +// } +// } + +// public void DeserializeJsonRef(string json, ref (T1, T2) functionParameters, ref string rawId, KeyValuePair[] info) +// { +// var prop = new Stack(); +// var ptype = String.Empty; +// JsonTextReader reader = new JsonTextReader(new StringReader(json)); +// var pidx = 0; +// while (reader.Read()) +// { +// if (reader.Value != null) +// { +// if (reader.TokenType == JsonToken.PropertyName) +// { +// prop.Push((string)reader.Value); +// continue; +// } +// else if (prop.Peek() == "jsonrpc" && prop.Count == 1) +// { +// prop.Pop(); +// continue; +// } +// else if (prop.Peek() == "method" && prop.Count == 1) +// { +// prop.Pop(); +// continue; +// } +// else if (prop.Peek() == "id" && prop.Count == 1) +// { +// rawId = reader.Value.ToString(); +// prop.Pop(); +// continue; +// } +// else +// { +// if (ptype == "Array") +// { +// if (pidx == 0) +// { +// if (reader.TokenType == JsonToken.Null) +// functionParameters.Item1 = default(T1); +// else +// functionParameters.Item1 = new JValue(reader.Value).ToObject(); +// } +// else +// { +// if (reader.TokenType == JsonToken.Null) +// functionParameters.Item2 = default(T2); +// else +// functionParameters.Item2 = new JValue(reader.Value).ToObject(); +// } +// pidx++; +// } +// else if (ptype == "Object") +// { +// var propName = prop.Pop(); +// for (int i = 0; i < info.Length; i++) +// { +// if (info[i].Key == propName) +// { +// if (i == 0) +// { +// if (reader.TokenType == JsonToken.Null) +// functionParameters.Item1 = default(T1); +// else +// functionParameters.Item1 = new JValue(reader.Value).ToObject(); +// } +// else +// { +// if (reader.TokenType == JsonToken.Null) +// functionParameters.Item2 = default(T2); +// else +// functionParameters.Item2 = new JValue(reader.Value).ToObject(); +// } +// } +// } +// } +// } +// } +// else +// { +// if (prop.Count > 0 && prop.Peek() == "params") +// { +// if (reader.TokenType == JsonToken.StartArray) +// { +// ptype = "Array"; +// } +// else if (reader.TokenType == JsonToken.StartObject) +// { +// ptype = "Object"; +// } +// else if (reader.TokenType == JsonToken.EndArray +// || reader.TokenType == JsonToken.EndObject) +// { +// prop.Pop(); +// continue; +// } +// } +// } +// } +// } + + public void DeserializeJsonRef(string json, ref (T1, T2, T3) functionParameters, ref string rawId, KeyValuePair[] info) + { + var prop = new Stack(); + var ptype = String.Empty; + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + var pidx = 0; + while (reader.Read()) + { + if (reader.Value != null) + { + if (reader.TokenType == JsonToken.PropertyName) + { + prop.Push((string)reader.Value); + continue; + } + else if (prop.Peek() == "jsonrpc" && prop.Count == 1) + { + prop.Pop(); + continue; + } + else if (prop.Peek() == "method" && prop.Count == 1) + { + prop.Pop(); + continue; + } + else if (prop.Peek() == "id" && prop.Count == 1) + { + rawId = reader.Value.ToString(); + prop.Pop(); + continue; + } + else + { + if (ptype == "Array") + { + if (pidx == 0) + { + if (reader.TokenType == JsonToken.Null) + functionParameters.Item1 = default(T1); + else + functionParameters.Item1 = new JValue(reader.Value).ToObject(); + } + else if(pidx == 1) + { + if (reader.TokenType == JsonToken.Null) + functionParameters.Item2 = default(T2); + else + functionParameters.Item2 = new JValue(reader.Value).ToObject(); + } + else + { + if (reader.TokenType == JsonToken.Null) + functionParameters.Item3 = default(T3); + else + functionParameters.Item3 = new JValue(reader.Value).ToObject(); + } + pidx++; + } + else if (ptype == "Object") + { + var propName = prop.Pop(); + for (int i = 0; i < info.Length; i++) + { + if (info[i].Key == propName) + { + if (i == 0) + { + if (reader.TokenType == JsonToken.Null) + functionParameters.Item1 = default(T1); + else + functionParameters.Item1 = new JValue(reader.Value).ToObject(); + } + else if(i == 1) + { + if (reader.TokenType == JsonToken.Null) + functionParameters.Item2 = default(T2); + else + functionParameters.Item2 = new JValue(reader.Value).ToObject(); + } + else + { + if (reader.TokenType == JsonToken.Null) + functionParameters.Item3 = default(T3); + else + functionParameters.Item3 = new JValue(reader.Value).ToObject(); + } + } + } + } + } + } + else + { + if (prop.Count > 0 && prop.Peek() == "params") + { + if (reader.TokenType == JsonToken.StartArray) + { + ptype = "Array"; + } + else if (reader.TokenType == JsonToken.StartObject) + { + ptype = "Object"; + } + else if (reader.TokenType == JsonToken.EndArray + || reader.TokenType == JsonToken.EndObject) + { + prop.Pop(); + continue; + } + } + } + } + } + + public void DeserializeJsonRef(string json, ref (T1, T2, T3, T4) functionParameters, ref string rawId, KeyValuePair[] functionParameterInfo) + { + throw new NotImplementedException(); + } + + public void DeserializeJsonRef(string json, ref (T1, T2, T3, T4, T5) functionParameters, ref string rawId, KeyValuePair[] functionParameterInfo) + { + throw new NotImplementedException(); + } + + public void DeserializeJsonRef(string json, ref (T1, T2, T3, T4, T5, T6) functionParameters, ref string rawId, KeyValuePair[] functionParameterInfo) + { + throw new NotImplementedException(); + } + + public void DeserializeJsonRef(string json, ref (T1, T2, T3, T4, T5, T6, T7) functionParameters, ref string rawId, KeyValuePair[] functionParameterInfo) + { + throw new NotImplementedException(); + } + + } +} diff --git a/AustinHarris.JsonRpc.Standard/Attributes.cs b/AustinHarris.JsonRpc.Standard/Attributes.cs new file mode 100644 index 0000000..1592511 --- /dev/null +++ b/AustinHarris.JsonRpc.Standard/Attributes.cs @@ -0,0 +1,50 @@ +using System; + +namespace AustinHarris.JsonRpc +{ + /// + /// Required to expose a method to the JsonRpc service. + /// + [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)] + public sealed class JsonRpcMethodAttribute : Attribute + { + readonly string jsonMethodName; + + /// + /// Required to expose a method to the JsonRpc service. + /// + /// Lets you specify the method name as it will be referred to by JsonRpc. + public JsonRpcMethodAttribute(string jsonMethodName = "") + { + this.jsonMethodName = jsonMethodName; + } + + 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/AustinHarris.JsonRpc.Standard/AustinHarris.JsonRpc.Standard.csproj b/AustinHarris.JsonRpc.Standard/AustinHarris.JsonRpc.Standard.csproj new file mode 100644 index 0000000..7832d1f --- /dev/null +++ b/AustinHarris.JsonRpc.Standard/AustinHarris.JsonRpc.Standard.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.1 + + + + false + + + diff --git a/AustinHarris.JsonRpc.Standard/Config.cs b/AustinHarris.JsonRpc.Standard/Config.cs new file mode 100644 index 0000000..45eab83 --- /dev/null +++ b/AustinHarris.JsonRpc.Standard/Config.cs @@ -0,0 +1,113 @@ +using System; +using System.Threading.Tasks; + +namespace AustinHarris.JsonRpc +{ + /// + /// The PreProcessHandler is called after the request has been parsed and prior to calling the associated method on the JsonRpcService. + /// If any non-null result is returned from the PreProcessHandler, the operation is aborted and the error is returned to the caller. + /// + /// The jsonRpc Request that is pending processing. + /// The context associated with this request + /// Any non-null result causes the operation to be aborted, and the JsonRpcException is returned to the caller. + public delegate IJsonRpcException PreProcessHandler(IJsonRequest request, object context); + /// + /// The PostProcessHandler is called after the response has been created and prior to returning the data to the caller. + /// If any non-null result is returned from the PostProcessHandler, the current return value is discared and the new return value used + /// in preference. + /// + /// The jsonRpc Request that has been processed. + /// The jsonRpc Response that has been created. + /// The context associated with this request/response pair + /// Any non-null result causes the result to be discarded and the JsonRpcException is returned to the caller. + public delegate IJsonRpcException PostProcessHandler(IJsonRequest request, ref InvokeResult response, object context); + + /// + /// Global configurations for JsonRpc + /// + public static class Config + { + /// + /// Sets the object factory to be used. + /// + /// + public static void ConfigureFactory(IObjectFactory factory) + { + Handler.Configure(factory); + } + + /// + /// Sets the the PreProcessing Handler on the default session. + /// + /// + public static void SetPreProcessHandler(PreProcessHandler handler) + { + Handler.DefaultHandler.SetPreProcessHandler(handler); + } + + /// + /// Sets the the PostProcessing Handler on the default session. + /// + /// + public static void SetPostProcessHandler(PostProcessHandler handler) + { + Handler.DefaultHandler.SetPostProcessHandler(handler); + } + + /// + /// Sets the PreProcessing Handler on a specific session + /// + /// + /// + public static void SetBeforeProcessHandler(string sessionId, PreProcessHandler handler) + { + Handler.GetSessionHandler(sessionId).SetPreProcessHandler(handler); + } + + /// + /// For exceptions thrown after the routed method has been called. + /// Allows you to specify an error handler that will be invoked prior to returning the JsonResponse to the client. + /// You are able to modify the error that is returned inside the provided handler. + /// + /// + public static void SetErrorHandler(Func handler) + { + Handler.DefaultHandler.SetErrorHandler(handler); + } + + /// + /// For exceptions thrown after the routed method has been called. + /// Allows you to specify an error handler that will be invoked prior to returning the JsonResponse to the client. + /// You are able to modify the error that is returned inside the provided handler. + /// + /// + /// + public static void SetErrorHandler(string sessionId, Func handler) + { + Handler.GetSessionHandler(sessionId).SetErrorHandler(handler); + } + + /// + /// For exceptions thrown during parsing and prior to a routed method being called. + /// Allows you to specify an error handler that will be invoked prior to returning the JsonResponse to the client. + /// You are able to modify the error that is returned inside the provided handler. + /// + /// + public static void SetParseErrorHandler(Func handler) + { + Handler.DefaultHandler.SetParseErrorHandler(handler); + } + + /// + /// For exceptions thrown during parsing and prior to a routed method being called. + /// Allows you to specify an error handler that will be invoked prior to returning the JsonResponse to the client. + /// You are able to modify the error that is returned inside the provided handler. + /// + /// + /// + public static void SetParseErrorHandler(string sessionId, Func handler) + { + Handler.GetSessionHandler(sessionId).SetParseErrorHandler(handler); + } + } +} diff --git a/AustinHarris.JsonRpc.Standard/Handler.cs b/AustinHarris.JsonRpc.Standard/Handler.cs new file mode 100644 index 0000000..5719460 --- /dev/null +++ b/AustinHarris.JsonRpc.Standard/Handler.cs @@ -0,0 +1,325 @@ +namespace AustinHarris.JsonRpc +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using System.Collections.Concurrent; + using System.Threading; + + public class Handler + { + #region Members + 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; + internal static IObjectFactory _objectFactory; + private static volatile string _defaultSessionId; + #endregion + + #region Constructors + + static Handler() + { + //current = new Handler(Guid.NewGuid().ToString()); + _defaultSessionId = Guid.NewGuid().ToString(); + _sessionHandlersMaster = new ConcurrentDictionary(); + } + + private Handler(string sessionId) + { + SessionId = sessionId; + this.MetaData = new SMD(); + if(_objectFactory == null) + { + throw new InvalidOperationException("Configure must be called before Handler is created."); + } + } + + public static void Configure(IObjectFactory factory) + { + _objectFactory = factory ?? throw new ArgumentNullException("factory"); + } + + #endregion + + #region Properties + + /// + /// Returns the SessionID of the default session + /// + /// + public static string DefaultSessionId() { return _defaultSessionId; } + + /// + /// Gets a specific session + /// + /// The sessionId of the handler you want to retrieve. + /// + public static Handler GetSessionHandler(string 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)); + } + + /// + /// gets the default session + /// + /// The default Session Handler + public static Handler GetSessionHandler() + { + return GetSessionHandler(_defaultSessionId); + } + + /// + /// Removes and clears the Handler with the specific sessionID from the registry of Handlers + /// + /// + public static void DestroySession(string sessionId) + { + _sessionHandlersMaster.TryRemove(sessionId, out Handler h); + Interlocked.Increment(ref _sessionHandlerMasterVersion); + h.MetaData.Services.Clear(); + } + /// + /// Removes and clears the current Handler from the registry of Handlers + /// + public void Destroy() + { + DestroySession(SessionId); + } + + /// + /// Gets the default session handler + /// + public static Handler DefaultHandler { get { return GetSessionHandler(_defaultSessionId); } } + + /// + /// The sessionID of this Handler + /// + 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 IJsonRpcException __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(IJsonRpcException exception) + { + __currentRpcException = exception; + } + public static IJsonRpcException RpcGetAndRemoveRpcException() + { + var ex = __currentRpcException; + __currentRpcException = null ; + return ex; + } + + private AustinHarris.JsonRpc.PreProcessHandler externalPreProcessingHandler; + private AustinHarris.JsonRpc.PostProcessHandler externalPostProcessingHandler; + private Func externalErrorHandler; + private Func parseErrorHandler; + #endregion + + /// + /// This metadata contains all the types and mappings of all the methods in this handler. Warning: Modifying this directly could cause your handler to no longer function. + /// + public SMD MetaData { get; set; } + + #region Public Methods + + /// + /// Allows you to register all the functions on a Pojo Type that have been attributed as [JsonRpcMethod] to the specified sessionId + /// + /// The session to register against + /// The instance containing JsonRpcMethods to register + public static void RegisterInstance(string sessionID, object instance) + { + ServiceBinder.BindService(sessionID, instance); + } + + /// + /// 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); + } + + public void SetPreProcessHandler(AustinHarris.JsonRpc.PreProcessHandler handler) + { + externalPreProcessingHandler = handler; + } + + public void SetPostProcessHandler(AustinHarris.JsonRpc.PostProcessHandler handler) + { + externalPostProcessingHandler = handler; + } + + /// + /// Invokes a method to handle a JsonRpc request. + /// + /// JsonRpc Request to be processed + /// Optional context that will be available from within the jsonRpcMethod. + /// + public void Handle(IJsonRequest Rpc, ref InvokeResult response, Object RpcContext = null) + { + AddRpcContext(RpcContext); + + var preProcessingException = PreProcess(Rpc, RpcContext); + if (preProcessingException != null) + { + response.SerializedError = _objectFactory.Serialize(preProcessingException); + //callback is called - if it is empty then nothing will be done + //return response always- if callback is empty or not + + return ; + } + + if (this.MetaData.Services.TryGetValue(Rpc.Method, out SMDService metadata)) + { + } + else if (metadata == null) + { + response.SerializedError = _objectFactory.Serialize(_objectFactory.CreateException(-32601, "Method not found", "The method does not exist / is not available.")); + return; + } + + try + { + var results = metadata.Invoke(_objectFactory, Rpc.Raw); + response.SerializedResult = results.Item1; + response.SerializedId = results.Item2; + var contextException = RpcGetAndRemoveRpcException(); + + if (contextException != null) + { + response.SerializedError = _objectFactory.Serialize(ProcessException(Rpc, contextException)); + } + return ; + } + catch (Exception ex) + { + if (ex is TargetParameterCountException) + { + response.SerializedError = _objectFactory.Serialize(ProcessException(Rpc, _objectFactory.CreateException(-32602, "Invalid params", ex))); + } + else if (ex is IJsonRpcException) + { + response.SerializedError = _objectFactory.Serialize(ProcessException(Rpc, ex as IJsonRpcException)); + } + else if (ex.InnerException != null && ex.InnerException is IJsonRpcException) + { + response.SerializedError = _objectFactory.Serialize(ProcessException(Rpc, ex.InnerException as IJsonRpcException)); + } + else if (ex.InnerException != null) + { + response.SerializedError = _objectFactory.Serialize(ProcessException(Rpc, _objectFactory.CreateException(-32603, "Internal Error", ex.InnerException))); + } + else + { + response.SerializedError = _objectFactory.Serialize(ProcessException(Rpc, _objectFactory.CreateException(-32603, "Internal Error", ex))); + } + return; + } + finally + { + RemoveRpcContext(); + } + } + #endregion + + [ThreadStatic] + static object __currentRpcContext; + private void AddRpcContext(object RpcContext) + { + __currentRpcContext = RpcContext; + } + private void RemoveRpcContext() + { + __currentRpcContext = null; + } + + private IJsonRpcException ProcessException(IJsonRequest req, IJsonRpcException ex) + { + if (externalErrorHandler != null) + return externalErrorHandler(req, ex); + return ex; + } + internal IJsonRpcException ProcessParseException(string req, IJsonRpcException ex) + { + if (parseErrorHandler != null) + return parseErrorHandler(req, ex); + return ex; + } + internal void SetErrorHandler(Func handler) + { + externalErrorHandler = handler; + } + internal void SetParseErrorHandler(Func handler) + { + parseErrorHandler = handler; + } + + private IJsonRpcException PreProcess(IJsonRequest request, object context) + { + return externalPreProcessingHandler == null ? null : externalPreProcessingHandler(request, context); + } + + internal void PostProcess(IJsonRequest request, ref InvokeResult response, object context) + { + if (externalPostProcessingHandler != null) + { + try + { + externalPostProcessingHandler(request,ref response, context); + } + catch (Exception ex) + { + response.SerializedError = _objectFactory.Serialize( ProcessException(request, _objectFactory.CreateException(-32603, "Internal Error", ex)) ); + } + } + } + + } + +} + diff --git a/AustinHarris.JsonRpc.Standard/IJsonRequest.cs b/AustinHarris.JsonRpc.Standard/IJsonRequest.cs new file mode 100644 index 0000000..1b6919f --- /dev/null +++ b/AustinHarris.JsonRpc.Standard/IJsonRequest.cs @@ -0,0 +1,14 @@ +namespace AustinHarris.JsonRpc +{ + public interface IJsonRequest + { + string Raw { get; set; } + string Method { get; set; } + } + + public interface IJsonRequestFactory + { + IJsonRequest CreateRequest(); + IJsonRequest[] DeserializeRequests(string requests); + } +} \ No newline at end of file diff --git a/AustinHarris.JsonRpc.Standard/IJsonResponse.cs b/AustinHarris.JsonRpc.Standard/IJsonResponse.cs new file mode 100644 index 0000000..7b0a50f --- /dev/null +++ b/AustinHarris.JsonRpc.Standard/IJsonResponse.cs @@ -0,0 +1,9 @@ +namespace AustinHarris.JsonRpc +{ + public struct InvokeResult + { + public string SerializedError; + public string SerializedId; + public string SerializedResult; + } +} \ No newline at end of file diff --git a/AustinHarris.JsonRpc.Standard/IJsonRpcException.cs b/AustinHarris.JsonRpc.Standard/IJsonRpcException.cs new file mode 100644 index 0000000..100ac60 --- /dev/null +++ b/AustinHarris.JsonRpc.Standard/IJsonRpcException.cs @@ -0,0 +1,13 @@ +namespace AustinHarris.JsonRpc +{ + public interface IJsonRpcException + { + int Code { get; set; } + object Data { get; set; } + string Message { get; set; } + } + public interface IJsonRpcExceptionFactory + { + IJsonRpcException CreateException(int code, string message, object data); + } +} \ No newline at end of file diff --git a/AustinHarris.JsonRpc.Standard/IObjectFactory.cs b/AustinHarris.JsonRpc.Standard/IObjectFactory.cs new file mode 100644 index 0000000..9c23727 --- /dev/null +++ b/AustinHarris.JsonRpc.Standard/IObjectFactory.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; + +namespace AustinHarris.JsonRpc +{ + public interface IObjectFactory : IJsonRpcExceptionFactory, IJsonRequestFactory + { + string Serialize(T data); + string ToJsonRpcResponse(ref InvokeResult response); + object DeserializeJson(string json, Type type); + string MethodName(string json); + + void DeserializeJsonRef(string json, ref ValueTuple functionParameters, ref string rawId, KeyValuePair[] functionParameterInfo); + void DeserializeJsonRef(string json, ref ValueTuple functionParameters, ref string rawId, KeyValuePair[] functionParameterInfo); + void DeserializeJsonRef(string json, ref ValueTuple functionParameters, ref string rawId, KeyValuePair[] functionParameterInfo); + void DeserializeJsonRef(string json, ref ValueTuple functionParameters, ref string rawId, KeyValuePair[] functionParameterInfo); + void DeserializeJsonRef(string json, ref ValueTuple functionParameters, ref string rawId, KeyValuePair[] functionParameterInfo); + void DeserializeJsonRef(string json, ref ValueTuple functionParameters, ref string rawId, KeyValuePair[] functionParameterInfo); + void DeserializeJsonRef(string json, ref ValueTuple functionParameters, ref string rawId, KeyValuePair[] functionParameterInfo); + } +} \ No newline at end of file diff --git a/AustinHarris.JsonRpc.Standard/JsonRpcContext.cs b/AustinHarris.JsonRpc.Standard/JsonRpcContext.cs new file mode 100644 index 0000000..597a81f --- /dev/null +++ b/AustinHarris.JsonRpc.Standard/JsonRpcContext.cs @@ -0,0 +1,39 @@ +namespace AustinHarris.JsonRpc +{ + /// + /// Provides access to a context specific to each JsonRpc method invocation. + /// This is a convienence class that wraps calls to Context specific methods on AustinHarris.JsonRpc.Handler + /// + public class JsonRpcContext + { + private JsonRpcContext(object value) + { + Value = 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(IJsonRpcException 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/AustinHarris.JsonRpc.Standard/JsonRpcProcessor.cs b/AustinHarris.JsonRpc.Standard/JsonRpcProcessor.cs new file mode 100644 index 0000000..6bef5cc --- /dev/null +++ b/AustinHarris.JsonRpc.Standard/JsonRpcProcessor.cs @@ -0,0 +1,225 @@ +using System; +using System.Threading.Tasks; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using System.Collections.Concurrent; +using System.Threading; + +namespace AustinHarris.JsonRpc +{ + public static class JsonRpcProcessor + { + static JsonRpcProcessor() + { + for (int i = 0; i < _privatepool.Length; i++) + { + _privatepool[i] = new Thread(new ThreadStart(_workPool)); + _privatepool[i].IsBackground = true; + _privatepool[i].Start(); + } + } + + public static void ProcessAsync(JsonRpcStateAsync async, object context = null) + { + ProcessAsync(Handler.DefaultSessionId(), async, context); + } + + public static void ProcessAsync(string sessionId, JsonRpcStateAsync async, object context = null) + { + ProcessAsync(sessionId, async.JsonRpc, context) + .ContinueWith(t => + { + async.Result = t.Result; + async.SetCompleted(); + }); + } + public static Task ProcessAsync(string jsonRpc, object context = null) + { + return ProcessAsync(Handler.DefaultSessionId(), jsonRpc, context); + } + + struct ParamBox + { + public string sessionId; + public string jsonRpc; + public object context; + } + struct ParamBox2 + { + public Handler handler; + public string jsonRpc; + public object context; + } + struct ParamBox3 + { + public TaskCompletionSource tcs; + public Handler handler; + public string jsonRpc; + public object context; + } + + private static TaskFactory _tf = new TaskFactory(TaskScheduler.Default); + + public static Task ProcessAsync(string sessionId, string jsonRpc, object context = null) + { + ParamBox __pq; + __pq.sessionId = sessionId; + __pq.jsonRpc = jsonRpc; + __pq.context = context; + + return _tf.StartNew((_) => + { + return Process(Handler.GetSessionHandler(((ParamBox)_).sessionId), ((ParamBox)_).jsonRpc, ((ParamBox)_).context); + }, __pq); + } + public static Task ProcessAsync(Handler handler, string jsonRpc, object context = null) + { + ParamBox2 __pq; + __pq.handler = handler; + __pq.jsonRpc = jsonRpc; + __pq.context = context; + + return _tf.StartNew((_) => + { + return Process(((ParamBox2)_).handler, ((ParamBox2)_).jsonRpc, ((ParamBox2)_).context); + }, __pq); + } + + private static BlockingCollection _pendingWork = new BlockingCollection(); + private static Thread[] _privatepool = new Thread[8]; + private static void _workPool() + { + ParamBox3 item; + while (true) + { + item = _pendingWork.Take(); + item.tcs.SetResult(Process(item.handler, item.jsonRpc, item.context)); + } + } + + public static Task ProcessAsync2(Handler handler, string jsonRpc, object context = null) + { + var tcs = new TaskCompletionSource(); + var task = tcs.Task; + + ParamBox3 __pq; + __pq.tcs = tcs; + __pq.handler = handler; + __pq.jsonRpc = jsonRpc; + __pq.context = context; + + _pendingWork.Add(__pq); + return tcs.Task; + } + + [ThreadStatic] + static IJsonRequest[] array1 = null; + + public static string Process(Handler handler, string jsonRpc, object jsonRpcContext) + { + var singleBatch = true; + + IJsonRequest[] batch = null; + try + { + if (IsSingleRpc(jsonRpc)) + { + var name = Handler._objectFactory.MethodName(jsonRpc); + if (array1 == null) + array1 = new[] { Handler._objectFactory.CreateRequest() }; + array1[0].Method = name; + array1[0].Raw = jsonRpc; + batch = array1; + } + else + { + batch = Handler._objectFactory.DeserializeRequests(jsonRpc); + singleBatch = batch.Length == 1; + if (batch.Length == 0) + { + InvokeResult ir = new InvokeResult + { + SerializedResult = Handler._objectFactory.Serialize(handler.ProcessParseException(jsonRpc, Handler._objectFactory.CreateException(3200, "Invalid Request", "Batch of calls was empty."))) + }; + return Handler._objectFactory.ToJsonRpcResponse( ref ir ); + } + } + } + catch (Exception ex) + { + InvokeResult ir = new InvokeResult + { + SerializedResult = Handler._objectFactory.Serialize(handler.ProcessParseException(jsonRpc, Handler._objectFactory.CreateException(-32700, "Parse error", ex))) + }; + return Handler._objectFactory.ToJsonRpcResponse(ref ir); + } + + StringBuilder sbResult = null; + for (var i = 0; i < batch.Length; i++) + { + var jsonRequest = batch[i]; + InvokeResult jsonResponse = new InvokeResult(); + + if (jsonRequest == null) + { + jsonResponse.SerializedError = Handler._objectFactory.Serialize(handler.ProcessParseException(jsonRpc, + Handler._objectFactory.CreateException(-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.SerializedError = Handler._objectFactory.Serialize(handler.ProcessParseException(jsonRpc, + Handler._objectFactory.CreateException(-32600, "Invalid Request", "Missing property 'method'"))); + } + else + { + handler.Handle(jsonRequest, ref jsonResponse, jsonRpcContext); + handler.PostProcess(jsonRequest, ref jsonResponse, jsonRpcContext); + + if (jsonResponse.SerializedResult == null) continue; + } + + // special case optimization for single Item batch + if (singleBatch && (jsonResponse.SerializedId != null || jsonResponse.SerializedError != null)) + { + return Handler._objectFactory.ToJsonRpcResponse(ref jsonResponse); + } + if (jsonResponse.SerializedId == null && jsonResponse.SerializedError == null) + { + // do nothing + sbResult = new StringBuilder(0); + } + else + { + // write out the response + if (i == 0) + { + sbResult = new StringBuilder("["); + } + + sbResult.Append(Handler._objectFactory.ToJsonRpcResponse(ref jsonResponse)); + if (i < batch.Length - 1) + { + sbResult.Append(','); + } + else if (i == batch.Length - 1) + { + sbResult.Append(']'); + } + } + } + return sbResult.ToString(); + } + + private static bool IsSingleRpc(string json) + { + for (int i = 0; i < json.Length; i++) + { + if (json[i] == '{') return true; + if (json[i] == '[') return false; + } + return true; + } + } +} diff --git a/AustinHarris.JsonRpc.Standard/JsonRpcService.cs b/AustinHarris.JsonRpc.Standard/JsonRpcService.cs new file mode 100644 index 0000000..d8ec961 --- /dev/null +++ b/AustinHarris.JsonRpc.Standard/JsonRpcService.cs @@ -0,0 +1,22 @@ +namespace AustinHarris.JsonRpc +{ + /// + /// For routing use SessionId + /// + public abstract class JsonRpcService + { + protected JsonRpcService() + { + ServiceBinder.BindService(Handler.DefaultSessionId(), this); + } + + /// + /// Routing by SessionId + /// + /// + protected JsonRpcService(string sessionID) + { + ServiceBinder.BindService(sessionID, this); + } + } +} \ No newline at end of file diff --git a/AustinHarris.JsonRpc.Standard/JsonRpcStateAsync.cs b/AustinHarris.JsonRpc.Standard/JsonRpcStateAsync.cs new file mode 100644 index 0000000..5869c21 --- /dev/null +++ b/AustinHarris.JsonRpc.Standard/JsonRpcStateAsync.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading; + +namespace AustinHarris.JsonRpc +{ + public class JsonRpcStateAsync : IAsyncResult + { + public JsonRpcStateAsync(AsyncCallback cb, Object extraData) + { + this.cb = cb; + asyncState = extraData; + isCompleted = false; + } + + public string JsonRpc { get; set; } + public string Result { get; set; } + + private AsyncCallback cb = null; + private Object asyncState; + public object AsyncState + { + get + { + return asyncState; + } + } + + public bool CompletedSynchronously + { + get + { + return false; + } + } + + // If this object was not being used solely with ASP.Net this + // method would need an implementation. ASP.Net never uses the + // event, so it is not implemented here. + public WaitHandle AsyncWaitHandle + { + get + { + // not supported + return null; + } + } + + private Boolean isCompleted; + public bool IsCompleted + { + get + { + return isCompleted; + } + } + + internal void SetCompleted() + { + isCompleted = true; + if (cb != null) + { + cb(this); + } + } + } +} diff --git a/AustinHarris.JsonRpc.Standard/SMDService.cs b/AustinHarris.JsonRpc.Standard/SMDService.cs new file mode 100644 index 0000000..9a43812 --- /dev/null +++ b/AustinHarris.JsonRpc.Standard/SMDService.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace AustinHarris.JsonRpc +{ + public class SMD + { + public Dictionary Services { get; set; } + + public SMD () + { + Services = new Dictionary(); + } + + internal void AddService(string method, Dictionary parameters, Dictionary defaultValues, Delegate dele) + { + var newService = new SMDService(parameters, defaultValues, dele); + Services.Add(method,newService); + } + } + + public class SMDService + { + public Delegate dele; + + public KeyValuePair[] Parameters { get; } + + /// + /// Stores default values for optional parameters. + /// + public KeyValuePair[] DefaultValues { get; } + public Type Returns { get; } + + /// + /// Defines a service method http://dojotoolkit.org/reference-guide/1.8/dojox/rpc/smd.html + /// + /// + /// + public SMDService(Dictionary parameters, Dictionary defaultValues, Delegate dele) + { + // TODO: Complete member initialization + this.dele = dele; + this.Parameters = parameters.Take(parameters.Count-1).ToArray(); // last param is return type similar to Func<,> + + // create the default values storage for optional parameters. + this.DefaultValues = defaultValues.ToArray(); + + // this is getting the return type from the end of the param list + this.Returns = parameters.Values.LastOrDefault(); + + _internalFunc = BuildInternalFunc(); + } + + private Func BuildInternalFunc() + { + try + { + var plist = Parameters.Select(x => x.Value).ToArray(); + Type vtupType; + if (plist.Length == 0) vtupType = typeof(ValueTuple);//.MakeGenericType(plist); + else if (plist.Length == 1) vtupType = typeof(ValueTuple<>).MakeGenericType(plist); + else if (plist.Length == 2) vtupType = typeof(ValueTuple<,>).MakeGenericType(plist); + else if (plist.Length == 3) vtupType = typeof(ValueTuple<,,>).MakeGenericType(plist); + else if (plist.Length == 4) vtupType = typeof(ValueTuple<,,,>).MakeGenericType(plist); + else if (plist.Length == 5) vtupType = typeof(ValueTuple<,,,,>).MakeGenericType(plist); + else if (plist.Length == 6) vtupType = typeof(ValueTuple<,,,,,>).MakeGenericType(plist); + else if (plist.Length == 7) vtupType = typeof(ValueTuple<,,,,,,>).MakeGenericType(plist); + else throw new NotImplementedException("Functions with more then 7 parameters are not supported"); + // variables + var id = Expression.Parameter(typeof(string), "id"); + var vtup = Expression.Parameter(vtupType, "vtup"); + var vmeta = Expression.Parameter(typeof(KeyValuePair[]),"vmeta"); + var vtarget = Expression.Parameter(dele.Target.GetType(), "vtarget"); + var vreturns = Expression.Parameter(Returns, "vreturns"); + var vserializedResult = Expression.Parameter(typeof(string), "vserializedResult"); + var vserializedResultAndId = Expression.Parameter(typeof((string, string)), "vserializedResultAndId"); + Expression[]>> meta = () => Parameters; + Expression> target = () => dele.Target; + var meta2 = Expression.Constant(Parameters); + var target2 = Expression.Constant(dele.Target); + // vtupItems + var vtupItems = new Expression[Parameters.Length]; + for (int i = 0; i < vtupItems.Length; i++) + { + vtupItems[i] = Expression.PropertyOrField(vtup, "Item" + (i + 1).ToString()); + } + // functions + var deser = typeof(IObjectFactory).GetMethods().First(x => { + return x.Name == "DeserializeJsonRef" && + x.IsGenericMethod && + x.GetGenericArguments().Length == plist.Length; + }).MakeGenericMethod(plist); + var serialize = typeof(IObjectFactory).GetMethods().First(x => + { + return x.Name == "Serialize" && + x.IsGenericMethod && + x.GetGenericArguments().Length == 1; + }).MakeGenericMethod(Returns); + + // parameters + var factory = Expression.Parameter(typeof(IObjectFactory), "factory"); + var json = Expression.Parameter(typeof(string), "json"); + + BlockExpression setup = Expression.Block( + new[] { id , vmeta, vtarget, vtup, vreturns, vserializedResult, vserializedResultAndId }, + Expression.Assign(id, Expression.Constant(String.Empty)), + Expression.Assign(vtup, Expression.New(vtupType)), + Expression.Assign(vmeta, meta2), + Expression.Assign(vtarget, Expression.Convert( target2, dele.Target.GetType())), + // call deserialize + Expression.Call(factory, deser, new Expression[] { + json, + vtup, + id, + vmeta + }), + // call the jsonRpc function with the deserialized parameters + Expression.Assign(vreturns, Expression.Call(vtarget, dele.Method, vtupItems)), + // serialize the result of the jsonRpc function + Expression.Assign(vserializedResult, Expression.Call(factory, serialize, vreturns)), + Expression.Assign(Expression.PropertyOrField(vserializedResultAndId, "Item1"), vserializedResult), + Expression.Assign(Expression.PropertyOrField(vserializedResultAndId, "Item2"), id), + vserializedResultAndId + ); + + var lambda = Expression.Lambda>( + setup, + new ParameterExpression[] { factory, json } + ); + + Console.WriteLine(lambda.ToString()); + + return lambda.Compile(); + } + catch (Exception ex) + { + throw new Exception("parameters: " + Parameters.Length.ToString() +" :: " + String.Join(", ", Parameters.Select(x=> x.Key + " : " + x.Value.Name)), ex); + } + } + + Func _internalFunc; + + internal (string,string) Invoke(IObjectFactory objectFactory, string jsonRpc) + { + return _internalFunc(objectFactory, jsonRpc); + } + } +} diff --git a/AustinHarris.JsonRpc.Standard/ServiceBinder.cs b/AustinHarris.JsonRpc.Standard/ServiceBinder.cs new file mode 100644 index 0000000..45bb077 --- /dev/null +++ b/AustinHarris.JsonRpc.Standard/ServiceBinder.cs @@ -0,0 +1,70 @@ +namespace AustinHarris.JsonRpc +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + + public static class ServiceBinder + { + 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 item = instance.GetType(); // var item = typeof(T); + + var methods = item.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(m => m.GetCustomAttributes(typeof(JsonRpcMethodAttribute), false).Length > 0); + foreach (var meth in methods) + { + Dictionary paras = new Dictionary(); + Dictionary defaultValues = new Dictionary(); // dictionary that holds default values for optional params. + + var paramzs = meth.GetParameters(); + + 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(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(paramName, paramzs[i].DefaultValue); + } + + var resType = meth.ReturnType; + paras.Add("returns", resType); // add the return type to the generic parameters list. + + var atdata = meth.GetCustomAttributes(typeof(JsonRpcMethodAttribute), false); + foreach (JsonRpcMethodAttribute handlerAttribute in atdata) + { + 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); + } + } + } + } +} \ No newline at end of file diff --git a/AustinHarris.JsonRpc.sln b/AustinHarris.JsonRpc.sln index 858a102..7c23c05 100644 --- a/AustinHarris.JsonRpc.sln +++ b/AustinHarris.JsonRpc.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.40629.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2024 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BBFBBA8A-2F75-422C-ACCD-D05A6EF7244C}" ProjectSection(SolutionItems) = preProject @@ -11,15 +11,24 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AustinHarris.JsonRpc", "Json-Rpc\AustinHarris.JsonRpc.csproj", "{24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AustinHarris.JsonRpc.Standard", "AustinHarris.JsonRpc.Standard\AustinHarris.JsonRpc.Standard.csproj", "{01960B79-0B97-4B74-8D1F-99AFA8A0FDF0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AustinHarris.JsonRpc.AspNet", "AustinHarris.JsonRpc.AspNet\AustinHarris.JsonRpc.AspNet.csproj", "{FFFDEBBC-93F5-4A22-9EC5-D86A4A792DBB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestServer_Console.Core", "TestServer_Console.Core\TestServer_Console.Core.csproj", "{FDFB1B74-FBCA-4317-AFC8-7B8FC5489088}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AustinHarris.JsonRpcTestN", "AustinHarris.JsonRpcTestN\AustinHarris.JsonRpcTestN.csproj", "{8569B076-5A8B-4D6A-B75D-EF75A390AA5F}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".Net Core", ".Net Core", "{9FC11C57-5936-4804-A323-87D5572150EE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestServer_Console", "TestServer_Console\TestServer_Console.csproj", "{31AE59FC-B6F6-4AC7-A7B9-1E07630AE42B}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".Net Standard", ".Net Standard", "{CB659139-9876-4850-BBB7-BC48F0B5BA22}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AustinHarris.JsonRpc.Newtonsoft", "AustinHarris.JsonRpc.Newtonsoft\AustinHarris.JsonRpc.Newtonsoft.csproj", "{216F103E-B492-465E-ABBE-142A1491771C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AustinHarris.JsonRpcTestN.Core", "AustinHarris.JsonRpcTestN.Core\AustinHarris.JsonRpcTestN.Core.csproj", "{84DBDA32-A467-4902-9577-F7A90986272D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AustinHarris.JsonRpc.Jsmn", "AustinHarris.JsonRpc.Jsmn\AustinHarris.JsonRpc.Jsmn.csproj", "{4D4C9D5F-A675-4CAE-8145-C432D19C15F2}" EndProject Global + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|ARM = Debug|ARM @@ -31,68 +40,109 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}.Debug|Any CPU.Build.0 = Debug|Any CPU - {24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}.Debug|ARM.ActiveCfg = Debug|x86 - {24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}.Debug|x86.ActiveCfg = Debug|x86 - {24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}.Debug|x86.Build.0 = Debug|x86 - {24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}.Release|Any CPU.ActiveCfg = Release|Any CPU - {24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}.Release|Any CPU.Build.0 = Release|Any CPU - {24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}.Release|ARM.ActiveCfg = Release|x86 - {24FC1A2A-0BC3-43A7-9BFE-B628C2C4A307}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {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 - {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 - {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 - {8569B076-5A8B-4D6A-B75D-EF75A390AA5F}.Debug|ARM.Build.0 = Debug|Any CPU - {8569B076-5A8B-4D6A-B75D-EF75A390AA5F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {8569B076-5A8B-4D6A-B75D-EF75A390AA5F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {8569B076-5A8B-4D6A-B75D-EF75A390AA5F}.Debug|x86.ActiveCfg = Debug|Any CPU - {8569B076-5A8B-4D6A-B75D-EF75A390AA5F}.Debug|x86.Build.0 = Debug|Any CPU - {8569B076-5A8B-4D6A-B75D-EF75A390AA5F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8569B076-5A8B-4D6A-B75D-EF75A390AA5F}.Release|Any CPU.Build.0 = Release|Any CPU - {8569B076-5A8B-4D6A-B75D-EF75A390AA5F}.Release|ARM.ActiveCfg = Release|Any CPU - {8569B076-5A8B-4D6A-B75D-EF75A390AA5F}.Release|ARM.Build.0 = Release|Any CPU - {8569B076-5A8B-4D6A-B75D-EF75A390AA5F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {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 - {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 + {01960B79-0B97-4B74-8D1F-99AFA8A0FDF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {01960B79-0B97-4B74-8D1F-99AFA8A0FDF0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {01960B79-0B97-4B74-8D1F-99AFA8A0FDF0}.Debug|ARM.ActiveCfg = Debug|Any CPU + {01960B79-0B97-4B74-8D1F-99AFA8A0FDF0}.Debug|ARM.Build.0 = Debug|Any CPU + {01960B79-0B97-4B74-8D1F-99AFA8A0FDF0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {01960B79-0B97-4B74-8D1F-99AFA8A0FDF0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {01960B79-0B97-4B74-8D1F-99AFA8A0FDF0}.Debug|x86.ActiveCfg = Debug|Any CPU + {01960B79-0B97-4B74-8D1F-99AFA8A0FDF0}.Debug|x86.Build.0 = Debug|Any CPU + {01960B79-0B97-4B74-8D1F-99AFA8A0FDF0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {01960B79-0B97-4B74-8D1F-99AFA8A0FDF0}.Release|Any CPU.Build.0 = Release|Any CPU + {01960B79-0B97-4B74-8D1F-99AFA8A0FDF0}.Release|ARM.ActiveCfg = Release|Any CPU + {01960B79-0B97-4B74-8D1F-99AFA8A0FDF0}.Release|ARM.Build.0 = Release|Any CPU + {01960B79-0B97-4B74-8D1F-99AFA8A0FDF0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {01960B79-0B97-4B74-8D1F-99AFA8A0FDF0}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {01960B79-0B97-4B74-8D1F-99AFA8A0FDF0}.Release|x86.ActiveCfg = Release|Any CPU + {01960B79-0B97-4B74-8D1F-99AFA8A0FDF0}.Release|x86.Build.0 = Release|Any CPU + {FDFB1B74-FBCA-4317-AFC8-7B8FC5489088}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDFB1B74-FBCA-4317-AFC8-7B8FC5489088}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDFB1B74-FBCA-4317-AFC8-7B8FC5489088}.Debug|ARM.ActiveCfg = Debug|Any CPU + {FDFB1B74-FBCA-4317-AFC8-7B8FC5489088}.Debug|ARM.Build.0 = Debug|Any CPU + {FDFB1B74-FBCA-4317-AFC8-7B8FC5489088}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {FDFB1B74-FBCA-4317-AFC8-7B8FC5489088}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {FDFB1B74-FBCA-4317-AFC8-7B8FC5489088}.Debug|x86.ActiveCfg = Debug|Any CPU + {FDFB1B74-FBCA-4317-AFC8-7B8FC5489088}.Debug|x86.Build.0 = Debug|Any CPU + {FDFB1B74-FBCA-4317-AFC8-7B8FC5489088}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDFB1B74-FBCA-4317-AFC8-7B8FC5489088}.Release|Any CPU.Build.0 = Release|Any CPU + {FDFB1B74-FBCA-4317-AFC8-7B8FC5489088}.Release|ARM.ActiveCfg = Release|Any CPU + {FDFB1B74-FBCA-4317-AFC8-7B8FC5489088}.Release|ARM.Build.0 = Release|Any CPU + {FDFB1B74-FBCA-4317-AFC8-7B8FC5489088}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {FDFB1B74-FBCA-4317-AFC8-7B8FC5489088}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {FDFB1B74-FBCA-4317-AFC8-7B8FC5489088}.Release|x86.ActiveCfg = Release|Any CPU + {FDFB1B74-FBCA-4317-AFC8-7B8FC5489088}.Release|x86.Build.0 = Release|Any CPU + {216F103E-B492-465E-ABBE-142A1491771C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {216F103E-B492-465E-ABBE-142A1491771C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {216F103E-B492-465E-ABBE-142A1491771C}.Debug|ARM.ActiveCfg = Debug|Any CPU + {216F103E-B492-465E-ABBE-142A1491771C}.Debug|ARM.Build.0 = Debug|Any CPU + {216F103E-B492-465E-ABBE-142A1491771C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {216F103E-B492-465E-ABBE-142A1491771C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {216F103E-B492-465E-ABBE-142A1491771C}.Debug|x86.ActiveCfg = Debug|Any CPU + {216F103E-B492-465E-ABBE-142A1491771C}.Debug|x86.Build.0 = Debug|Any CPU + {216F103E-B492-465E-ABBE-142A1491771C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {216F103E-B492-465E-ABBE-142A1491771C}.Release|Any CPU.Build.0 = Release|Any CPU + {216F103E-B492-465E-ABBE-142A1491771C}.Release|ARM.ActiveCfg = Release|Any CPU + {216F103E-B492-465E-ABBE-142A1491771C}.Release|ARM.Build.0 = Release|Any CPU + {216F103E-B492-465E-ABBE-142A1491771C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {216F103E-B492-465E-ABBE-142A1491771C}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {216F103E-B492-465E-ABBE-142A1491771C}.Release|x86.ActiveCfg = Release|Any CPU + {216F103E-B492-465E-ABBE-142A1491771C}.Release|x86.Build.0 = Release|Any CPU + {84DBDA32-A467-4902-9577-F7A90986272D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {84DBDA32-A467-4902-9577-F7A90986272D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {84DBDA32-A467-4902-9577-F7A90986272D}.Debug|ARM.ActiveCfg = Debug|Any CPU + {84DBDA32-A467-4902-9577-F7A90986272D}.Debug|ARM.Build.0 = Debug|Any CPU + {84DBDA32-A467-4902-9577-F7A90986272D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {84DBDA32-A467-4902-9577-F7A90986272D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {84DBDA32-A467-4902-9577-F7A90986272D}.Debug|x86.ActiveCfg = Debug|Any CPU + {84DBDA32-A467-4902-9577-F7A90986272D}.Debug|x86.Build.0 = Debug|Any CPU + {84DBDA32-A467-4902-9577-F7A90986272D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {84DBDA32-A467-4902-9577-F7A90986272D}.Release|Any CPU.Build.0 = Release|Any CPU + {84DBDA32-A467-4902-9577-F7A90986272D}.Release|ARM.ActiveCfg = Release|Any CPU + {84DBDA32-A467-4902-9577-F7A90986272D}.Release|ARM.Build.0 = Release|Any CPU + {84DBDA32-A467-4902-9577-F7A90986272D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {84DBDA32-A467-4902-9577-F7A90986272D}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {84DBDA32-A467-4902-9577-F7A90986272D}.Release|x86.ActiveCfg = Release|Any CPU + {84DBDA32-A467-4902-9577-F7A90986272D}.Release|x86.Build.0 = Release|Any CPU + {4D4C9D5F-A675-4CAE-8145-C432D19C15F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D4C9D5F-A675-4CAE-8145-C432D19C15F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D4C9D5F-A675-4CAE-8145-C432D19C15F2}.Debug|ARM.ActiveCfg = Debug|Any CPU + {4D4C9D5F-A675-4CAE-8145-C432D19C15F2}.Debug|ARM.Build.0 = Debug|Any CPU + {4D4C9D5F-A675-4CAE-8145-C432D19C15F2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {4D4C9D5F-A675-4CAE-8145-C432D19C15F2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {4D4C9D5F-A675-4CAE-8145-C432D19C15F2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4D4C9D5F-A675-4CAE-8145-C432D19C15F2}.Debug|x86.Build.0 = Debug|Any CPU + {4D4C9D5F-A675-4CAE-8145-C432D19C15F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D4C9D5F-A675-4CAE-8145-C432D19C15F2}.Release|Any CPU.Build.0 = Release|Any CPU + {4D4C9D5F-A675-4CAE-8145-C432D19C15F2}.Release|ARM.ActiveCfg = Release|Any CPU + {4D4C9D5F-A675-4CAE-8145-C432D19C15F2}.Release|ARM.Build.0 = Release|Any CPU + {4D4C9D5F-A675-4CAE-8145-C432D19C15F2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {4D4C9D5F-A675-4CAE-8145-C432D19C15F2}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {4D4C9D5F-A675-4CAE-8145-C432D19C15F2}.Release|x86.ActiveCfg = Release|Any CPU + {4D4C9D5F-A675-4CAE-8145-C432D19C15F2}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {01960B79-0B97-4B74-8D1F-99AFA8A0FDF0} = {CB659139-9876-4850-BBB7-BC48F0B5BA22} + {FDFB1B74-FBCA-4317-AFC8-7B8FC5489088} = {9FC11C57-5936-4804-A323-87D5572150EE} + {216F103E-B492-465E-ABBE-142A1491771C} = {CB659139-9876-4850-BBB7-BC48F0B5BA22} + {84DBDA32-A467-4902-9577-F7A90986272D} = {9FC11C57-5936-4804-A323-87D5572150EE} + {4D4C9D5F-A675-4CAE-8145-C432D19C15F2} = {CB659139-9876-4850-BBB7-BC48F0B5BA22} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {895CB7D3-766C-490E-9686-57339AE17D6B} + EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection GlobalSection(TestCaseManagementSettings) = postSolution CategoryFile = AustinHarris.JsonRpc.vsmdi EndGlobalSection diff --git a/AustinHarris.JsonRpcTestN.Core/AustinHarris.JsonRpcTestN.Core.csproj b/AustinHarris.JsonRpcTestN.Core/AustinHarris.JsonRpcTestN.Core.csproj new file mode 100644 index 0000000..ca475de --- /dev/null +++ b/AustinHarris.JsonRpcTestN.Core/AustinHarris.JsonRpcTestN.Core.csproj @@ -0,0 +1,17 @@ + + + netcoreapp3.0 + false + + + + + + + + + + + + + \ No newline at end of file diff --git a/AustinHarris.JsonRpcTestN.Core/Test.cs b/AustinHarris.JsonRpcTestN.Core/Test.cs new file mode 100644 index 0000000..d760b2b --- /dev/null +++ b/AustinHarris.JsonRpcTestN.Core/Test.cs @@ -0,0 +1,1959 @@ +using Xunit; +using System; +using System.Linq; +using AustinHarris.JsonRpc; +using System.Text.RegularExpressions; +using Newtonsoft.Json.Linq; + +namespace AustinHarris.JsonRpcTestN.Core +{ + public class Poco + { + public static Poco WithOffset(int offset) + { + return new Poco(offset); + } + + readonly int _offset; + public Poco(int offset) + { + _offset = offset; + } + + [JsonRpcMethod("add")] + public int Add(int input) { return input + _offset; } + } + + public class Test + { + [Fact] + public void TestCase() + { + } + static object[] services; + + static Test() + { + Config.ConfigureFactory(new AustinHarris.JsonRpc.Newtonsoft.ObjectFactory()); + services = new object[] { + new CalculatorService()}; + } + + [Fact] + public void TestCanCreateMultipleServicesOfSameTypeInTheirOwnSessions() + { + string request(int param) => String.Format("{{\"method\":\"add\",\"params\":[{0}],\"id\":1}}", param); + string expectedResult(int param) => String.Format("{{\"jsonrpc\":\"2.0\",\"result\":{0},\"id\":1}}", param); + + for (int i = 0; i < 100; i++) + { + ServiceBinder.BindService(i.ToString(), Poco.WithOffset(i)); + } + + for (int i = 0; i < 100; i++) + { + var result = JsonRpcProcessor.ProcessAsync(i.ToString(), request(10)); + result.Wait(); + var actual1 = JObject.Parse(result.Result); + var expected1 = JObject.Parse(expectedResult(10 + i)); + Assert.Equal(expected1, actual1); + } + } + + [Fact] + public void TestCanCreateAndRemoveSession() + { + var h = JsonRpc.Handler.GetSessionHandler("this one"); + 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.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}"; + string expectedResultAfterDestroy = "{\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Method not found\",\"code\":-32601,\"data\":\"The method does not exist / is not available.\"},\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync("this one", request); + result.Wait(); + + + var actual1 = JObject.Parse(result.Result); + var expected1 = JObject.Parse(expectedResult); + Assert.True(JToken.DeepEquals(expected1, actual1)); + h.Destroy(); + + var result2 = JsonRpcProcessor.ProcessAsync("this one", request); + result2.Wait(); + + Assert.True(JToken.DeepEquals(JObject.Parse(expectedResultAfterDestroy), JObject.Parse(result2.Result))); + } + + [Fact] + public void TestInProcessClient() + { + string request = "{\"method\":\"NullableFloatToNullableFloat\",\"params\":[0.0],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":0.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + + Assert.Equal(result.Result, expectedResult); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void TestStringToString() + { + string request = "{\"method\":\"internal.echo\",\"params\":[\"hi\"],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"hi\",\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + + Assert.Equal(result.Result, expectedResult); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void NullableDateTimeToNullableDateTime() + { + string request = "{\"method\":\"NullableDateTimeToNullableDateTime\",\"params\":[\"2014-06-30T14:50:38.5208399+09:00\"],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"2014-06-30T14:50:38.5208399+09:00\",\"id\":1}"; + var expectedDate = DateTime.Parse("2014-06-30T14:50:38.5208399+09:00"); + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + var acutalDate = DateTime.Parse(result.Result.Substring(27, 33)); + Assert.Equal(expectedDate, acutalDate); + } + + [Theory] + [InlineData(@"{""method"":""NullableFloatToNullableFloat"",""params"":[1.2345],""id"":1}", "{\"jsonrpc\":\"2.0\",\"result\":1.2345,\"id\":1}")] + [InlineData(@"{""method"":""NullableFloatToNullableFloat"",""params"":[3.14159],""id"":1}", "{\"jsonrpc\":\"2.0\",\"result\":3.14159,\"id\":1}")] + [InlineData(@"{""method"":""NullableFloatToNullableFloat"",""params"":[null],""id"":1}", "{\"jsonrpc\":\"2.0\",\"result\":null,\"id\":1}")] + public void NullableFloatToNullableFloat(string request, string response) + { + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.Equal(response, result.Result); + } + + + [Fact] + public void DecimalToNullableDecimal() + { + string request = "{\"method\":\"DecimalToNullableDecimal\",\"params\":[0.0],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":0.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.Equal(result.Result, expectedResult); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void StringToListOfString() + { + string request = "{\"method\":\"StringToListOfString\",\"params\":[\"some string\"],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":[\"one\",\"two\",\"three\",\"some string\"],\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.Equal(result.Result, expectedResult); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void CustomStringToListOfString() + { + string request = "{\"method\":\"CustomStringToListOfString\",\"params\":[{\"str\":\"some string\"}],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":[\"one\",\"two\",\"three\",\"some string\"],\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void StringToThrowingException() + { + string request = "{\"method\":\"StringToThrowingException\",\"params\":[\"some string\"],\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.Contains("-32603", result.Result); + } + + [Fact] + public void StringToRefException() + { + string request = "{\"method\":\"StringToRefException\",\"params\":[\"some string\"],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"refException worked\",\"code\":-1,\"data\":null},\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.True(JToken.DeepEquals(JObject.Parse(expectedResult), JObject.Parse(result.Result))); + } + + [Fact] + public void StringToThrowJsonRpcException() + { + string request = "{\"method\":\"StringToThrowJsonRpcException\",\"params\":[\"some string\"],\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.Contains("-2700", result.Result); + } + + [Fact] + public void ReturnsDateTime() + { + string request = "{\"method\":\"ReturnsDateTime\",\"params\":[],\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + } + + [Fact] + public void ReturnsCustomRecursiveClass() + { + string request = "{\"method\":\"ReturnsCustomRecursiveClass\",\"params\":[],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":{\"Nested1\":{\"Nested1\":null,\"Value1\":5},\"Value1\":10},\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + + [Fact] + public void FloatToFloat() + { + string request = "{\"method\":\"FloatToFloat\",\"params\":[0.123],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":0.123,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + + [Fact] + public void IntToInt() + { + string request = "{\"method\":\"IntToInt\",\"params\":[789],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":789,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void OptionalParamInt16() + { + string request = "{\"method\":\"TestOptionalParamInt16\",\"params\":[789],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":789,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void OptionalParamInt16NoParam() + { + string request = "{\"method\":\"TestOptionalParamInt16\",\"params\":[],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":789,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void Int16ToInt16() + { + string request = "{\"method\":\"Int16ToInt16\",\"params\":[789],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":789,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void Int32ToInt32() + { + string request = "{\"method\":\"Int32ToInt32\",\"params\":[789],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":789,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void Int64ToInt64() + { + string request = "{\"method\":\"Int64ToInt64\",\"params\":[78915984515564],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":78915984515564,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + + [Fact] + public void TestOptionalParamByteMissing() + { + string request = "{\"method\":\"TestOptionalParambyte\",\"params\":[],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamSbyteMissing() + { + string request = "{\"method\":\"TestOptionalParamsbyte\",\"params\":[],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamShortMissing() + { + string request = "{\"method\":\"TestOptionalParamshort\",\"params\":[],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamintMissing() + { + string request = "{\"method\":\"TestOptionalParamint\",\"params\":[],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamLongMissing() + { + string request = "{\"method\":\"TestOptionalParamlong\",\"params\":[],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUshortMissing() + { + string request = "{\"method\":\"TestOptionalParamushort\",\"params\":[],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUintMissing() + { + string request = "{\"method\":\"TestOptionalParamuint\",\"params\":[],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUlongMissing() + { + string request = "{\"method\":\"TestOptionalParamulong\",\"params\":[],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamFloatMissing() + { + string request = "{\"method\":\"TestOptionalParamfloat\",\"params\":[],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamDoubleMissing() + { + string request = "{\"method\":\"TestOptionalParamdouble\",\"params\":[],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamBoolMissing() + { + string request = "{\"method\":\"TestOptionalParambool\",\"params\":[],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamCharMissing() + { + string request = "{\"method\":\"TestOptionalParamchar\",\"params\":[],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"a\",\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamDecimalMissing() + { + string request = "{\"method\":\"TestOptionalParamdecimal\",\"params\":[],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void TestOptionalParamBytePresent() + { + string request = "{\"method\":\"TestOptionalParambyte\",\"params\":[71],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamSbytePresent() + { + string request = "{\"method\":\"TestOptionalParamsbyte\",\"params\":[71],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamShortPresent() + { + string request = "{\"method\":\"TestOptionalParamshort\",\"params\":[71],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamintPresent() + { + string request = "{\"method\":\"TestOptionalParamint\",\"params\":[71],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamLongPresent() + { + string request = "{\"method\":\"TestOptionalParamlong\",\"params\":[71],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUshortPresent() + { + string request = "{\"method\":\"TestOptionalParamushort\",\"params\":[71],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUintPresent() + { + string request = "{\"method\":\"TestOptionalParamuint\",\"params\":[71],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUlongPresent() + { + string request = "{\"method\":\"TestOptionalParamulong\",\"params\":[71],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamFloatPresent() + { + string request = "{\"method\":\"TestOptionalParamfloat\",\"params\":[71],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamDoublePresent() + { + string request = "{\"method\":\"TestOptionalParamdouble\",\"params\":[71],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamBoolPresent() + { + string request = "{\"method\":\"TestOptionalParambool\",\"params\":[false],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":false,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamCharPresent() + { + string request = "{\"method\":\"TestOptionalParamchar\",\"params\":[" + (int)'b' + "],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"b\",\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamDecimalPresent() + { + string request = "{\"method\":\"TestOptionalParamdecimal\",\"params\":[71],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void TestOptionalParamBytePresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParambyte\",\"params\":{\"input\":71},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamSbytePresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamsbyte\",\"params\":{\"input\":71},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamShortPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamshort\",\"params\":{\"input\":71},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamintPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamint\",\"params\":{\"input\":71},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamLongPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamlong\",\"params\":{\"input\":71},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUshortPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamushort\",\"params\":{\"input\":71},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUintPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamuint\",\"params\":{\"input\":71},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUlongPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamulong\",\"params\":{\"input\":71},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamFloatPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamfloat\",\"params\":{\"input\":71},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamDoublePresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamdouble\",\"params\":{\"input\":71},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamBoolPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParambool\",\"params\":{\"input\":false},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":false,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamCharPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamchar\",\"params\":{\"input\":" + (int)'c' + "},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"c\",\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamDecimalPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamdecimal\",\"params\":{\"input\":71},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":71.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void TestOptionalParamByteMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParambyte\",\"params\":{},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamSbyteMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamsbyte\",\"params\":{},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamShortMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamshort\",\"params\":{},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamintMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamint\",\"params\":{},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamLongMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamlong\",\"params\":{},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUshortMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamushort\",\"params\":{},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUintMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamuint\",\"params\":{},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUlongMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamulong\",\"params\":{},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamFloatMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamfloat\",\"params\":{},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamDoubleMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamdouble\",\"params\":{},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamBoolMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParambool\",\"params\":{},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamCharMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamchar\",\"params\":{},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"a\",\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamDecimalMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamdecimal\",\"params\":{},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":1.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void TestOptionalParamByte_2ndMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParambyte_2x\",\"params\":{input1:123},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":98,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamSbyte_2ndMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamsbyte_2x\",\"params\":{input1:123},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":126,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamShort_2ndMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamshort_2x\",\"params\":{input1:123},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamint_2ndMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamint_2x\",\"params\":{input1:123},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamLong_2ndMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamlong_2x\",\"params\":{input1:123},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUshort_2ndMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamushort_2x\",\"params\":{input1:123},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUint_2ndMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamuint_2x\",\"params\":{input1:123},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUlong_2ndMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamulong_2x\",\"params\":{input1:123},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamFloat_2ndMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamfloat_2x\",\"params\":{input1:123},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamDouble_2ndMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamdouble_2x\",\"params\":{input1:123},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamBool_2ndMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParambool_2x\",\"params\":{input1:123},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamChar_2ndMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamchar_2x\",\"params\":{input1:123},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"d\",\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamDecimal_2ndMissingObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamdecimal_2x\",\"params\":{input1:123},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void TestOptionalParamByte_2ndPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParambyte_2x\",\"params\":{input1:123, input2: 67},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":67,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamByte_2ndPresentArraySyntax() + { + string request = "{\"method\":\"TestOptionalParambyte_2x\",\"params\":[123, 67],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":67,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamByte_2ndMissingArraySyntax() + { + string request = "{\"method\":\"TestOptionalParambyte_2x\",\"params\":[123],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":98,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamSbyte_2ndPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamsbyte_2x\",\"params\":{input1:123, input2: 97},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":97,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamSbyte_2ndPresentArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamsbyte_2x\",\"params\":[123, 98],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":98,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamSbyte_2ndMissingArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamsbyte_2x\",\"params\":[123],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":126,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamShort_2ndPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamshort_2x\",\"params\":{input1:123, input2: 671},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamShort_2ndPresentArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamshort_2x\",\"params\":[123, 671],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamShort_2ndMissingArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamshort_2x\",\"params\":[123],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamint_2ndPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamint_2x\",\"params\":{input1:123, input2: 671},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamint_2ndPresentArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamint_2x\",\"params\":[123, 671],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamint_2ndMissingArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamint_2x\",\"params\":[123],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamLong_2ndPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamlong_2x\",\"params\":{input1:123, input2: 671},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamLong_2ndPresentArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamlong_2x\",\"params\":[123, 671],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamLong_2ndMissingArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamlong_2x\",\"params\":[123],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUshort_2ndPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamushort_2x\",\"params\":{input1:123, input2: 671},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUshort_2ndPresentArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamushort_2x\",\"params\":[123, 671],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUshort_2ndMissingArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamushort_2x\",\"params\":[123],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUint_2ndPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamuint_2x\",\"params\":{input1:123, input2: 671},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUint_2ndPresentArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamuint_2x\",\"params\":[123, 671],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUint_2ndMissingArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamuint_2x\",\"params\":[123],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUlong_2ndPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamulong_2x\",\"params\":{input1:123, input2: 671},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUlong_2ndPresentArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamulong_2x\",\"params\":[123, 671],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamUlong_2ndMissingArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamulong_2x\",\"params\":[123],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamFloat_2ndPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamfloat_2x\",\"params\":{input1:123, input2: 671},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamFloat_2ndPresentArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamfloat_2x\",\"params\":[123, 671],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamFloat_2ndMissingArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamfloat_2x\",\"params\":[123],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamDouble_2ndPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamdouble_2x\",\"params\":{input1:123, input2: 671},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamDouble_2ndPresentArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamdouble_2x\",\"params\":[123, 671],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamDouble_2ndMissingArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamdouble_2x\",\"params\":[123],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamBool_2ndPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParambool_2x\",\"params\":{input1:123, input2: 671},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamBool_2ndPresentArraySyntax() + { + string request = "{\"method\":\"TestOptionalParambool_2x\",\"params\":[true, false],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":false,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamBool_2ndMissingArraySyntax() + { + string request = "{\"method\":\"TestOptionalParambool_2x\",\"params\":[123],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamChar_2ndPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamchar_2x\",\"params\":{\"input1\":" + (int)'c' + ", \"input2\":" + (int)'d' + "},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"d\",\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamChar_2ndPresentArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamchar_2x\",\"params\":[" + (int)'c' + ", " + (int)'d' + "],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"d\",\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamChar_2ndMissingArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamchar_2x\",\"params\":[" + (int)'c' + "],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"d\",\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamDecimal_2ndPresentObjectSyntax() + { + string request = "{\"method\":\"TestOptionalParamdecimal_2x\",\"params\":{input1:123, input2: 671},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamDecimal_2ndPresentArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamdecimal_2x\",\"params\":[123, 671],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":671.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + [Fact] + public void TestOptionalParamDecimal_2ndMissingArraySyntax() + { + string request = "{\"method\":\"TestOptionalParamdecimal_2x\",\"params\":[123],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":987.0,\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void TestOptionalParametersStrings_BothMissing() + { + string request = "{\"method\":\"TestOptionalParameters_Strings\",\"params\":[],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":[null,null],\"id\":1}"; + + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void TestOptionalParametersStrings_SecondMissing() + { + string request = "{\"method\":\"TestOptionalParameters_Strings\",\"params\":[\"first\"],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":[\"first\",null],\"id\":1}"; + + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void TestOptionalParametersStrings_BothExists() + { + string request = "{\"method\":\"TestOptionalParameters_Strings\",\"params\":[\"first\",\"second\"],\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":[\"first\",\"second\"],\"id\":1}"; + + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void TestOptionalParametersBoolsAndStrings() + { + string request = + "{\"jsonrpc\":\"2.0\",\"method\":\"TestOptionalParametersBoolsAndStrings\",\"params\":{\"input1\":\"murkel\"},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":1}"; + + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.DoesNotContain("error", result.Result); + Assert.Equal(expectedResult, result.Result); + } + + [Fact] + public void TestBatchResultWrongRequests() + { + string request = "[{},{\"jsonrpc\":\"2.0\",\"id\":4}]"; + + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + + Assert.True(Regex.IsMatch(result.Result, @"\[(\{.*""error"":.*?,""id"":.*?\}),(\{.*""error"":.*?,""id"":.*?\})\]"), "Should have two errors."); + } + + [Fact] + public void TestBatchResultMultipleMethodCallsNotificationAtLast() + { + string request = + @"[{""jsonrpc"":""2.0"",""method"":""ReturnsDateTime"",""params"":{},""id"":1},{""jsonrpc"":""2.0"",""method"":""Notify"",""params"":[""Hello World!""]}]"; + + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + + Assert.False(result.Result.EndsWith(@",]"), "result.Result.EndsWith(@\",]\")"); + + } + + [Fact] + public void TestEmptyBatchResult() + { + var secondrequest = "{\"jsonrpc\":\"2.0\",\"method\":\"Notify\",\"params\":[\"Hello World!\"]}"; + var result = JsonRpcProcessor.ProcessAsync(secondrequest); + result.Wait(); + + Assert.True(string.IsNullOrEmpty(result.Result)); + } + + + [Fact] + public void TestNotificationVoidResult() + { + var secondrequest = "{\"jsonrpc\":\"2.0\",\"method\":\"Notify\",\"params\":[\"Hello World!\"], \"id\":73}"; + var result = JsonRpcProcessor.ProcessAsync(secondrequest); + result.Wait(); + Console.WriteLine(result.Result); + Assert.True(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."); + } + + [Fact] + public void TestLeftOutParams() + { + var request = + @"{""jsonrpc"":""2.0"",""method"":""ReturnsDateTime"",""id"":1}"; + + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + + Assert.False(result.Result.Contains(@"error"":{""code"":-32602"), @"According to JSON-RPC 2.0 the ""params"" member MAY be omitted."); + } + + [Fact] + public void TestMultipleResults() + { + var result = + JsonRpcProcessor.ProcessAsync( + @"[{""jsonrpc"":""2.0"",""method"":""ReturnsDateTime"",""params"":{},""id"":1},{""jsonrpc"":""2.0"",""method"":""ReturnsDateTime"",""params"":{},""id"":1}]"); + result.Wait(); + + Assert.EndsWith("]", result.Result); + } + + [Fact] + public void TestSingleResultBatch() + { + var result = + JsonRpcProcessor.ProcessAsync(@"[{""jsonrpc"":""2.0"",""method"":""ReturnsDateTime"",""params"":{},""id"":1}]"); + result.Wait(); + Assert.False(result.Result.EndsWith("]")); + } + + class PreProcessHandlerLocal + { + public IJsonRequest rpc = null; + public object context = null; + public int run = 0; + + public IJsonRpcException PreProcess(IJsonRequest rpc, object context) + { + run++; + + this.rpc = rpc; + this.context = context; + + return null; + } + } + + [Fact] + public void TestPreProcessor() + { + try { + PreProcessHandlerLocal handler = new PreProcessHandlerLocal(); + Config.SetPreProcessHandler(new PreProcessHandler(handler.PreProcess)); + string request = "{\"method\":\"TestPreProcessor\",\"params\":{inputValue:\"some string\"},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"Success!\",\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + AssertJsonAreEqual(expectedResult, result.Result); + Assert.Equal(1, handler.run); + Assert.NotNull(handler.rpc); + Assert.Null(handler.context); + } finally { + Config.SetPreProcessHandler(null); + } + + } + + [Fact] + public void TestPreProcessorThrowsJsonRPCException() + { + try + { + PreProcessHandlerLocal handler = new PreProcessHandlerLocal(); + Config.SetPreProcessHandler(new PreProcessHandler(handler.PreProcess)); + string request = "{\"method\":\"TestPreProcessorThrowsJsonRPCException\",\"params\":{inputValue:\"some string\"},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-27000,\"message\":\"Just some testing\",\"data\":null},\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + AssertJsonAreEqual(expectedResult, result.Result); + Assert.Equal(1, handler.run); + Assert.NotNull(handler.rpc); + Assert.Null(handler.context); + } + finally + { + Config.SetPreProcessHandler(null); + } + } + + [Fact] + public void TestPreProcessorThrowsException() + { + try + { + PreProcessHandlerLocal handler = new PreProcessHandlerLocal(); + Config.SetPreProcessHandler(new PreProcessHandler(handler.PreProcess)); + string request = "{\"method\":\"TestPreProcessorThrowsException\",\"params\":{inputValue:\"some string\"},\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.Contains("-32603", result.Result); + Assert.Equal(1, handler.run); + Assert.NotNull(handler.rpc); + Assert.Null(handler.context); + } + finally + { + Config.SetPreProcessHandler(null); + } + } + + [Fact] + public void TestPreProcessorSetsException() + { + try + { + PreProcessHandlerLocal handler = new PreProcessHandlerLocal(); + Config.SetPreProcessHandler(new PreProcessHandler(handler.PreProcess)); + string request = "{\"method\":\"TestPreProcessorSetsException\",\"params\":{inputValue:\"some string\"},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-27000,\"message\":\"This exception was thrown using: JsonRpcContext.SetException()\",\"data\":null},\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + AssertJsonAreEqual(expectedResult, result.Result); + Assert.Equal(1, handler.run); + Assert.NotNull(handler.rpc); + Assert.Null(handler.context); + } + finally + { + Config.SetPreProcessHandler(null); + } + } + + [Fact] + public void TestPreProcessOnSession() + { + var sessionId = "my session"; + var h = JsonRpc.Handler.GetSessionHandler(sessionId); + PreProcessHandlerLocal preHandler = new PreProcessHandlerLocal(); + h.SetPreProcessHandler(new PreProcessHandler(preHandler.PreProcess)); + + 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.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}"; + string expectedResultAfterDestroy = "{\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Method not found\",\"code\":-32601,\"data\":\"The method does not exist / is not available.\"},\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(sessionId, request); + result.Wait(); + + var actual1 = JObject.Parse(result.Result); + var expected1 = JObject.Parse(expectedResult); + Assert.True(JToken.DeepEquals(expected1, actual1)); + Assert.Equal(1, preHandler.run); + + h.Destroy(); + + var result2 = JsonRpcProcessor.ProcessAsync(sessionId, request); + result2.Wait(); + + Assert.Equal(1, preHandler.run); + Assert.True(JToken.DeepEquals(JObject.Parse(expectedResultAfterDestroy), JObject.Parse(result2.Result))); + } + + class PostProcessHandlerLocal + { + public IJsonRequest rpc = null; + public InvokeResult response; + public object context = null; + public int run = 0; + private bool changeResponse_; + + public PostProcessHandlerLocal(bool changeResponse) + { + changeResponse_ = changeResponse; + } + + public IJsonRpcException PostProcess(IJsonRequest rpc,ref InvokeResult response, object context) + { + run++; + + this.rpc = rpc; + this.response = response; + this.context = context; + + if (changeResponse_) + { + return new JsonRpc.Newtonsoft.JsonRpcException(-123, "Test error", null); + } + return null; + } + } + + [Fact] + public void TestPostProcessor() + { + try + { + PostProcessHandlerLocal handler = new PostProcessHandlerLocal(false); + Config.SetPostProcessHandler(new PostProcessHandler(handler.PostProcess)); + string request = "{\"method\":\"TestPostProcessor\",\"params\":{inputValue:\"some string\"},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"result\":\"Success!\",\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + AssertJsonAreEqual(expectedResult, result.Result); + Assert.Equal(1, handler.run); + Assert.NotNull(handler.rpc); + Assert.NotNull(handler.response); + Assert.Equal("Success!", (string)handler.response.SerializedResult); + Assert.Null(handler.context); + } + finally + { + Config.SetPostProcessHandler(null); + } + } + + [Fact] + public void TestPostProcessorThrowsJsonRPCException() + { + try + { + PostProcessHandlerLocal handler = new PostProcessHandlerLocal(false); + Config.SetPostProcessHandler(new PostProcessHandler(handler.PostProcess)); + string request = "{\"method\":\"TestPostProcessorThrowsJsonRPCException\",\"params\":{inputValue:\"some string\"},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-27000,\"message\":\"Just some testing\",\"data\":null},\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + AssertJsonAreEqual(expectedResult, result.Result); + Assert.Equal(1, handler.run); + Assert.NotNull(handler.rpc); + Assert.Null(handler.response.SerializedResult); + Assert.NotNull(handler.response.SerializedError); + Assert.Contains("-27000", handler.response.SerializedError); + Assert.Contains("Just some testing", handler.response.SerializedError); + Assert.Null(handler.context); + } + finally + { + Config.SetPostProcessHandler(null); + } + } + + [Fact] + public void TestPostProcessorThrowsException() + { + try + { + PostProcessHandlerLocal handler = new PostProcessHandlerLocal(false); + Config.SetPostProcessHandler(new PostProcessHandler(handler.PostProcess)); + string request = "{\"method\":\"TestPostProcessorThrowsException\",\"params\":{inputValue:\"some string\"},\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.Contains("-32603", result.Result); + Assert.Equal(1, handler.run); + Assert.NotNull(handler.rpc); + Assert.Null(handler.response.SerializedResult); + Assert.NotNull(handler.response.SerializedError); + Assert.Contains("-32603", handler.response.SerializedError); + Assert.Null(handler.context); + } + finally + { + Config.SetPostProcessHandler(null); + } + } + + [Fact] + public void TestPostProcessorSetsException() + { + try + { + PostProcessHandlerLocal handler = new PostProcessHandlerLocal(false); + Config.SetPostProcessHandler(new PostProcessHandler(handler.PostProcess)); + string request = "{\"method\":\"TestPostProcessorSetsException\",\"params\":{inputValue:\"some string\"},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-27001,\"message\":\"This exception was thrown using: JsonRpcContext.SetException()\",\"data\":null},\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + AssertJsonAreEqual(expectedResult, result.Result); + Assert.Equal(1, handler.run); + Assert.NotNull(handler.rpc); + Assert.Null(handler.context); + } + finally + { + Config.SetPreProcessHandler(null); + } + } + + + [Fact] + public void TestPostProcessorChangesReturn() + { + try + { + PostProcessHandlerLocal handler = new PostProcessHandlerLocal(true); + Config.SetPostProcessHandler(new PostProcessHandler(handler.PostProcess)); + string request = "{\"method\":\"TestPostProcessor\",\"params\":{inputValue:\"some string\"},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-123,\"message\":\"Test error\",\"data\":null},\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + AssertJsonAreEqual(expectedResult, result.Result); + Assert.Equal(1, handler.run); + Assert.Equal(1, handler.run); + Assert.NotNull(handler.rpc); + Assert.NotNull(handler.response); + Assert.Equal("Success!", (string)handler.response.SerializedResult); + Assert.Null(handler.context); + } + finally + { + Config.SetPostProcessHandler(null); + } + } + + [Fact] + public void TestPostProcessorThrowsJsonRPCExceptionChangesReturn() + { + try + { + PostProcessHandlerLocal handler = new PostProcessHandlerLocal(true); + Config.SetPostProcessHandler(new PostProcessHandler(handler.PostProcess)); + string request = "{\"method\":\"TestPostProcessorThrowsJsonRPCException\",\"params\":{inputValue:\"some string\"},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-123,\"message\":\"Test error\",\"data\":null},\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + AssertJsonAreEqual(expectedResult, result.Result); + Assert.Equal(1, handler.run); + Assert.NotNull(handler.rpc); + Assert.NotNull(handler.response); + Assert.Null(handler.response.SerializedResult); + Assert.NotNull(handler.response.SerializedError); + Assert.Contains("-27000", handler.response.SerializedError); + Assert.Contains("Just some testing", handler.response.SerializedError); + Assert.Null(handler.context); + } + finally + { + Config.SetPostProcessHandler(null); + } + } + + [Fact] + public void TestPostProcessorThrowsExceptionChangesReturn() + { + try + { + PostProcessHandlerLocal handler = new PostProcessHandlerLocal(true); + Config.SetPostProcessHandler(new PostProcessHandler(handler.PostProcess)); + string request = "{\"method\":\"TestPostProcessorThrowsException\",\"params\":{inputValue:\"some string\"},\"id\":1}"; + string expectedResult = "{\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Test error\",\"code\":-123,\"data\":null},\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + AssertJsonAreEqual(expectedResult, result.Result); + Assert.Equal(1, handler.run); + Assert.NotNull(handler.rpc); + Assert.Null(handler.response.SerializedResult); + Assert.NotNull(handler.response.SerializedError); + Assert.Contains("-32603", handler.response.SerializedError); + Assert.Null(handler.context); + } + finally + { + Config.SetPostProcessHandler(null); + } + } + + [Fact] + public void TestPostProcessOnSession() + { + var sessionId = "my first session"; + var h = JsonRpc.Handler.GetSessionHandler(sessionId); + PostProcessHandlerLocal postHandler = new PostProcessHandlerLocal(false); + h.SetPostProcessHandler(new PostProcessHandler(postHandler.PostProcess)); + + 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.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}"; + string expectedResultAfterDestroy = "{\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"Method not found\",\"code\":-32601,\"data\":\"The method does not exist / is not available.\"},\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(sessionId, request); + result.Wait(); + + var actual1 = JObject.Parse(result.Result); + var expected1 = JObject.Parse(expectedResult); + Assert.True(JToken.DeepEquals(expected1, actual1)); + Assert.Equal(1, postHandler.run); + + h.Destroy(); + + var result2 = JsonRpcProcessor.ProcessAsync(sessionId, request); + result2.Wait(); + + Assert.Equal(1, postHandler.run); + Assert.True(JToken.DeepEquals(JObject.Parse(expectedResultAfterDestroy), JObject.Parse(result2.Result))); + } + + [Fact] + public void TestExtraParameters() + { + string request = "{\"method\":\"ReturnsDateTime\",\"params\":{extra:\"mytext\"},\"id\":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.Contains("error", result.Result); + Assert.Contains("\"code\":-32602", result.Result); + } + + [Fact] + public void TestCustomParameterName() + { + string 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.ProcessAsync(request("myCustomParameter")); + result.Wait(); + Assert.Equal(JObject.Parse(expectedResult), JObject.Parse(result.Result)); + // Check method can\"t be used with its actual parameter name + result = JsonRpcProcessor.ProcessAsync(request("arg")); + result.Wait(); + Assert.Contains("-32602", result.Result); // check for \"invalid params\" error code + } + + [Fact] + public void TestCustomParameterWithNoSpecificName() + { + string 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.ProcessAsync(request("arg")); + result.Wait(); + Assert.Equal(JObject.Parse(expectedResult), JObject.Parse(result.Result)); + } + + [Fact] + public void TestNestedReturnType() + { + var request = "{\"jsonrpc\":\"2.0\",\"method\":\"TestNestedReturnType\",\"id\":1}"; + var expected = @"{""jsonrpc"":""2.0"",""result"":{""NodeId"":1,""Leafs"":[{""NodeId"":2,""Leafs"":[]},{""NodeId"":3,""Leafs"":[]}]},""id"":1}"; + var result = JsonRpcProcessor.ProcessAsync(request); + result.Wait(); + Assert.Equal(expected, result.Result); + } + + private static void AssertJsonAreEqual(string expectedJson, string actualJson) + { + Newtonsoft.Json.Linq.JObject expected = (Newtonsoft.Json.Linq.JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(expectedJson); + Newtonsoft.Json.Linq.JObject actual = (Newtonsoft.Json.Linq.JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(actualJson); + AssertJsonAreEqual(expected, actual, "root"); + } + + private static void AssertJsonAreEqual(JToken expectedJson, JToken actualJson, string path) + { + Assert.Equal(expectedJson.GetType(), actualJson.GetType()); + if (expectedJson is JObject) + { + AssertJsonAreEqual((JObject)expectedJson, (JObject)actualJson, path); + } else if (expectedJson is JObject) + { + AssertJsonAreEqual((JArray)expectedJson, (JArray)actualJson, path); + } else if (expectedJson is JValue) + { + AssertJsonAreEqual((JValue)expectedJson, (JValue)actualJson, path); + } else + { + throw new Exception("I don\"t know how to handle " + expectedJson.GetType().ToString()); + } + } + + private static void AssertJsonAreEqual(JObject expectedJson, JObject actualJson, string path) + { + Console.WriteLine("expected: {0}", expectedJson); + Console.WriteLine("actual : {0}", actualJson); + Assert.Equal(expectedJson.Count, actualJson.Count); + for (var expectedElementsEnumerator = expectedJson.GetEnumerator(); expectedElementsEnumerator.MoveNext(); ) + { + Assert.True(actualJson.TryGetValue(expectedElementsEnumerator.Current.Key, out JToken actualElement), "Couldn\"t find " + path + "[" + expectedElementsEnumerator.Current.Key + "]"); + AssertJsonAreEqual(expectedElementsEnumerator.Current.Value, actualElement, path + "[" + expectedElementsEnumerator.Current.Key + "]"); + } + } + + private static void AssertJsonAreEqual(JArray expectedJson, JArray actualJson, string path) + { + Assert.Equal(expectedJson.Count, actualJson.Count); + for (int jsonIndex = 0; jsonIndex < expectedJson.Count; jsonIndex++) + { + AssertJsonAreEqual(expectedJson[jsonIndex], actualJson[jsonIndex], path + "[" + jsonIndex.ToString() + "]"); + } + } + + private static void AssertJsonAreEqual(JValue expectedJson, JValue actualJson, string path) + { + Assert.Equal(expectedJson.Type, actualJson.Type); + switch (expectedJson.Type) + { + case JTokenType.Boolean: + Assert.Equal((bool)expectedJson.Value, (bool)actualJson.Value); + break; + case JTokenType.Integer: + Assert.Equal((System.Int64)expectedJson.Value, (System.Int64)actualJson.Value); + break; + case JTokenType.String: + Assert.Equal((string)expectedJson.Value, (string)actualJson.Value); + break; + case JTokenType.Null: + //Not used + break; + default: + throw new Exception("I don\"t know how to handle type " + expectedJson.Type.ToString()); + } + } + } +} + diff --git a/AustinHarris.JsonRpcTestN.Core/jsmn.cs b/AustinHarris.JsonRpcTestN.Core/jsmn.cs new file mode 100644 index 0000000..14cfa91 --- /dev/null +++ b/AustinHarris.JsonRpcTestN.Core/jsmn.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using AustinHarris.JsonRpc.Jsmn; +using Xunit; + +namespace AustinHarris.JsonRpcTestN.Jsmn +{ + public class Test + { + [Fact] + public void SmallParseWorks() + { + var input = "{\"method\":\"Add\",\"params\":[1,2],\"id\":1}"; + var tokens = new jsmntok_t[100]; + var success = AustinHarris.JsonRpc.Jsmn.jsmn.parse(input,tokens) > 0; + Assert.True(success); + } + + [Fact] + public void CanGetMethod() + { + var json = "{\"method\":\"Add\",\"params\":[1,2],\"id\":1}"; + string value; + Assert.True((jsmn.parseFirstField(json, "method", out value))); + Assert.Equal("Add",value); + } + + [Fact] + public void CanGetParamsArrayPrimitives1() + { + var json = "{\"method\":\"Add\",\"params\":[1],\"id\":1}"; + var functionParameters = new ValueTuple(); + string rawId = string.Empty; + var info = new KeyValuePair[] {new KeyValuePair("p1", typeof(int))}; + jsmn.DeserializeJsonRef(json,ref functionParameters, ref rawId, info); + Assert.Equal(functionParameters.Item1, 1); + Assert.Equal(rawId, "1"); + } + + [Fact] + public void CanGetParamsArrayPrimitives2() + { + var json = "{\"method\":\"Add\",\"params\":[1,2],\"id\":3}"; + var functionParameters = new ValueTuple(); + string rawId = string.Empty; + var info = new KeyValuePair[] {new KeyValuePair("p1", typeof(int))}; + jsmn.DeserializeJsonRef(json,ref functionParameters, ref rawId, info); + Assert.Equal(functionParameters.Item1, 1); + Assert.Equal(functionParameters.Item2, 2); + Assert.Equal(rawId, "3"); + } + + [Fact] + public void CanGetParamsArrayPrimitives1Nullable() + { + var json = "{\"method\":\"NullableFloatToNullableFloat\",\"params\":[1.23],\"id\":3}"; + var functionParameters = new ValueTuple(); + string rawId = string.Empty; + var info = new KeyValuePair[] { new KeyValuePair("p1", typeof(float?)) }; + jsmn.DeserializeJsonRef(json, ref functionParameters, ref rawId, info); + Assert.Equal(functionParameters.Item1, (float?)(1.23)); + Assert.Equal(rawId, "3"); + } + } +} \ No newline at end of file diff --git a/AustinHarris.JsonRpcTestN.Core/service.cs b/AustinHarris.JsonRpcTestN.Core/service.cs new file mode 100644 index 0000000..7111559 --- /dev/null +++ b/AustinHarris.JsonRpcTestN.Core/service.cs @@ -0,0 +1,394 @@ +using AustinHarris.JsonRpc; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; + +namespace AustinHarris.JsonRpcTestN +{ + public class TreeNode + { + public int NodeId { get; set; } + + public IList Leafs { get; set; } + } + + public class CalculatorService : JsonRpcService + { + [JsonRpcMethod] + private double Add(double l, double r) + { + return l + r; + } + + [JsonRpcMethod] + public DateTime? NullableDateTimeToNullableDateTime(DateTime? dt) + { + return dt; + } + + [JsonRpcMethod] + public float? NullableFloatToNullableFloat(float? a) + { + return a; + } + + [JsonRpcMethod] + public decimal? DecimalToNullableDecimal(decimal x) + { + return x; + } + + [JsonRpcMethod] + private List StringToListOfString(string input) + { + return new List() { "one", "two", "three", input }; + } + + [JsonRpcMethod] + private List StringToThrowingException(string input) + { + throw new Exception("Throwing Exception"); + } + + public class CustomString + { + public string str; + } + + [JsonRpcMethod] + private List CustomStringToListOfString(CustomString input) + { + return new List() { "one", "two", "three", input.str }; + } + + + + [JsonRpcMethod("internal.echo")] + private string Handle_Echo(string s) + { + return s; + } + + [JsonRpcMethod("error1")] + private string DevideByZero(string s) + { + var i = 0; + var j = 15; + 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 JsonRpc.Newtonsoft.JsonRpcException refException) + //{ + // refException = new JsonRpc.Newtonsoft.JsonRpcException(-1, "refException worked", null); + // return s; + //} + [JsonRpcMethod] + private string StringToThrowJsonRpcException(string s) + { + throw new JsonRpc.Newtonsoft.JsonRpcException(-27000, "Just some testing", null); + return s; + } + + //[JsonRpcMethod] + //private DateTime ReturnsDateTime() + //{ + // return DateTime.Now; + //} + + //[JsonRpcMethod] + //private recursiveClass ReturnsCustomRecursiveClass() + //{ + // var obj = new recursiveClass() { Value1 = 10, Nested1 = new recursiveClass() { Value1 = 5 } }; + // //obj.Nested1.Nested1 = obj; + // return obj; + //} + + [JsonRpcMethod] + private float FloatToFloat(float input) + { + return input; + } + + [JsonRpcMethod] + private double DoubleToDouble(double input) + { + return input; + } + + + + [JsonRpcMethod] + private int IntToInt(int input) + { + return input; + } + + [JsonRpcMethod] + private Int32 Int32ToInt32(Int32 input) + { + return input; + } + + [JsonRpcMethod] + private Int16 Int16ToInt16(Int16 input) + { + return input; + } + + [JsonRpcMethod] + private Int16 TestOptionalParamInt16(Int16 input = 789) + { + return input; + } + + [JsonRpcMethod] + private Int64 Int64ToInt64(Int64 input) + { + return input; + } + + private class RecursiveClass + { + public RecursiveClass Nested1 { get; set; } + public int Value1 { get; set; } + } + + #region OptionalParams Tests + [JsonRpcMethod] + private byte TestOptionalParambyte(byte input = 1) + { + return input; + } + [JsonRpcMethod] + private sbyte TestOptionalParamsbyte(sbyte input = 1) + { + return input; + } + [JsonRpcMethod] + private short TestOptionalParamshort(short input = 1) + { + return input; + } + [JsonRpcMethod] + private int TestOptionalParamint(int input = 1) + { + return input; + } + [JsonRpcMethod] + private long TestOptionalParamlong(long input = 1) + { + return input; + } + [JsonRpcMethod] + private ushort TestOptionalParamushort(ushort input = 1) + { + return input; + } + [JsonRpcMethod] + private uint TestOptionalParamuint(uint input = 1) + { + return input; + } + [JsonRpcMethod] + private ulong TestOptionalParamulong(ulong input = 1) + { + return input; + } + [JsonRpcMethod] + private float TestOptionalParamfloat(float input = 1) + { + return input; + } + [JsonRpcMethod] + private double TestOptionalParamdouble(double input = 1) + { + return input; + } + [JsonRpcMethod] + private bool TestOptionalParambool(bool input = true) + { + return input; + } + [JsonRpcMethod] + private char TestOptionalParamchar(char input = 'a') + { + return input; + } + [JsonRpcMethod] + private decimal TestOptionalParamdecimal(decimal input = 1) + { + return input; + } + + #endregion + + [JsonRpcMethod] + public byte TestOptionalParambyte_2x(byte input1, byte input2 = 98) + { + return input2; + } + [JsonRpcMethod] + public sbyte TestOptionalParamsbyte_2x(sbyte input1, sbyte input2 = 126) + { + return input2; + } + [JsonRpcMethod] + public short TestOptionalParamshort_2x(short input1, short input2 = 987) + { + return input2; + } + [JsonRpcMethod] + public int TestOptionalParamint_2x(int input1, int input2 = 987) + { + return input2; + } + [JsonRpcMethod] + public long TestOptionalParamlong_2x(long input1, long input2 = 987) + { + return input2; + } + [JsonRpcMethod] + public ushort TestOptionalParamushort_2x(ushort input1, ushort input2 = 987) + { + return input2; + } + [JsonRpcMethod] + public uint TestOptionalParamuint_2x(uint input1, uint input2 = 987) + { + return input2; + } + [JsonRpcMethod] + public ulong TestOptionalParamulong_2x(ulong input1, ulong input2 = 987) + { + return input2; + } + [JsonRpcMethod] + public float TestOptionalParamfloat_2x(float input1, float input2 = 987) + { + return input2; + } + [JsonRpcMethod] + public double TestOptionalParamdouble_2x(double input1, double input2 = 987) + { + return input2; + } + [JsonRpcMethod] + public bool TestOptionalParambool_2x(bool input1, bool input2 = true) + { + return input2; + } + [JsonRpcMethod] + public char TestOptionalParamchar_2x(char input1, char input2 = 'd') + { + return input2; + } + [JsonRpcMethod] + public decimal TestOptionalParamdecimal_2x(decimal input1, decimal input2 = 987) + { + return input2; + } + [JsonRpcMethod] + private IList TestOptionalParameters_Strings(string input1 = null, string input2 = null) + { + return new List() + { + input1, + input2 + }; + } + + [JsonRpcMethod] + public bool TestOptionalParametersBoolsAndStrings(string input1, bool input2 = true, string input3 = "") + { + return input2; + } + + //[JsonRpcMethod] + //public void Notify(string message) + //{ + // Trace.WriteLine(string.Format("Notified about: {0}", message)); + //} + + [JsonRpcMethod] + public string TestPreProcessor(string inputValue) + { + return "Success!"; + } + + [JsonRpcMethod] + public string TestPreProcessorThrowsJsonRPCException(string inputValue) + { + throw new JsonRpc.Newtonsoft.JsonRpcException(-27000, "Just some testing", null); + } + + [JsonRpcMethod] + public string TestPreProcessorThrowsException(string inputValue) + { + throw new Exception("TestException"); + } + + [JsonRpcMethod] + public string TestPreProcessorSetsException(string inputValue) + { + JsonRpcContext.SetException(new JsonRpc.Newtonsoft.JsonRpcException(-27000, "This exception was thrown using: JsonRpcContext.SetException()", null)); + return null; + } + + [JsonRpcMethod] + public string TestPostProcessor(string inputValue) + { + return "Success!"; + } + + [JsonRpcMethod] + public string TestPostProcessorThrowsJsonRPCException(string inputValue) + { + throw new JsonRpc.Newtonsoft.JsonRpcException(-27000, "Just some testing", null); + } + + [JsonRpcMethod] + public string TestPostProcessorThrowsException(string inputValue) + { + throw new Exception("TestException"); + } + + [JsonRpcMethod] + public string TestPostProcessorSetsException(string inputValue) + { + JsonRpcContext.SetException(new JsonRpc.Newtonsoft.JsonRpcException(-27001, "This exception was thrown using: JsonRpcContext.SetException()", null)); + return null; + } + + //[JsonRpcMethod] + //public TreeNode TestNestedReturnType() + //{ + // return new TreeNode + // { + // NodeId = 1, + // Leafs = + // new[] + // { + // new TreeNode {NodeId = 2, Leafs = new List()}, + // new TreeNode {NodeId = 3, Leafs = new List()} + // } + // }; + //} + + } +} diff --git a/TestServer_Console.Core/Program.cs b/TestServer_Console.Core/Program.cs new file mode 100644 index 0000000..aa88d0a --- /dev/null +++ b/TestServer_Console.Core/Program.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using AustinHarris.JsonRpc; +using System.Threading; +using System.Diagnostics; +using System.Threading.Tasks; +using AustinHarris.JsonRpc.Newtonsoft; + +namespace TestServer_Console +{ + class Program + { + static object[] services = new object[] { + new CalculatorService() + }; + + static void Main(string[] args) + { + PrintOptions(); + for (string line = Console.ReadLine(); line != "q"; line = Console.ReadLine()) + { + if (string.IsNullOrWhiteSpace(line)) + Benchmark(); + else if (line.StartsWith("c", StringComparison.CurrentCultureIgnoreCase)) + ConsoleInput(); + PrintOptions(); + } + } + + private static void PrintOptions() + { + Console.WriteLine("Hit Enter to run benchmark"); + Console.WriteLine("'c' to start reading console input"); + Console.WriteLine("'q' to quit"); + } + + private static void ConsoleInput() + { + for (string line = Console.ReadLine(); !string.IsNullOrEmpty(line); line = Console.ReadLine()) + { + JsonRpcProcessor.ProcessAsync(line).ContinueWith(response => Console.WriteLine( response.Result )); + } + } + + private static volatile int ctr; + + private static string payload1 = "{\"method\":\"Add\",\"params\":[1,2],\"id\":1}"; + private static string payload2 = "{\"method\":\"AddInt\",\"params\":[1,7],\"id\":2}"; + private static string payload3 = "{\"method\":\"NullableFloatToNullableFloat\",\"params\":[1.23],\"id\":3}"; + private static string payload4 = "{\"method\":\"Test2\",\"params\":[3.456],\"id\":4}"; + private static string payload5 = "{\"method\":\"StringMe\",\"params\":[\"Foo\"],\"id\":5}"; + + private static void Benchmark() + { + Console.WriteLine("Starting benchmark"); + AustinHarris.JsonRpc.Config.ConfigureFactory(new AustinHarris.JsonRpc.Newtonsoft.ObjectFactory()); + //AustinHarris.JsonRpc.Config.ConfigureFactory(new AustinHarris.JsonRpc.Jsmn.ObjectFactory()); + var cnt = 50; + var iterations = 8; + for (int iteration = 1; iteration <= iterations; iteration++) + { + cnt *= iteration; + ctr = 0; + var handler = Handler.GetSessionHandler(); + Task[] tasks = new Task[cnt]; + string[] results = new string[cnt]; + var sessionid = Handler.DefaultSessionId(); + +// GC.Collect(); +// var sw = Stopwatch.StartNew(); +// for (int i = 0; i < cnt; i += 5) +// { +// tasks[i] = JsonRpcProcessor.ProcessAsync(handler, "{\"method\":\"Add\",\"params\":[1,2],\"id\":1}"); +// tasks[i + 1] = JsonRpcProcessor.ProcessAsync(handler, "{\"method\":\"AddInt\",\"params\":[1,7],\"id\":2}"); +// tasks[i + 2] = JsonRpcProcessor.ProcessAsync(handler, "{\"method\":\"NullableFloatToNullableFloat\",\"params\":[1.23],\"id\":3}"); +// tasks[i + 3] = JsonRpcProcessor.ProcessAsync(handler, "{\"method\":\"Test2\",\"params\":[3.456],\"id\":4}"); +// tasks[i + 4] = JsonRpcProcessor.ProcessAsync(handler, "{\"method\":\"StringMe\",\"params\":[\"Foo\"],\"id\":5}"); +// } +// Task.WaitAll(tasks); +// sw.Stop(); +// Console.WriteLine("Async processed {0} rpc in {1}ms for {2} rpc/sec", cnt, sw.ElapsedMilliseconds, (double)cnt * 1000d / sw.ElapsedMilliseconds); +// +// for (int i = 0; i < tasks.Length; i++) +// { +// tasks[i] = null; +// } +// GC.Collect(); + + var sw = Stopwatch.StartNew(); + for (int i = 0; i < cnt; i+=5) + { + tasks[i] = JsonRpcProcessor.ProcessAsync2(handler, payload1); + tasks[i + 1] = JsonRpcProcessor.ProcessAsync2(handler, payload2); + tasks[i + 2] = JsonRpcProcessor.ProcessAsync2(handler, payload3); + tasks[i + 3] = JsonRpcProcessor.ProcessAsync2(handler, payload4); + tasks[i + 4] = JsonRpcProcessor.ProcessAsync2(handler, payload5); + } + Task.WaitAll(tasks); + sw.Stop(); + Console.WriteLine("Async processed {0} rpc in {1}ms for {2} rpc/sec", cnt, sw.ElapsedMilliseconds, (double)cnt * 1000d / sw.ElapsedMilliseconds); + + GC.Collect(); + sw = Stopwatch.StartNew(); + for (int i = 0; i < cnt; i += 5) + { + results[i] = JsonRpcProcessor.Process(handler, payload1, null); + results[i + 1] = JsonRpcProcessor.Process(handler, payload2, null); + results[i + 2] = JsonRpcProcessor.Process(handler, payload3, null); + results[i + 3] = JsonRpcProcessor.Process(handler, payload4, null); + results[i + 4] = JsonRpcProcessor.Process(handler, payload5, null); + } + sw.Stop(); + Console.WriteLine("Direct processed {0} rpc in {1}ms for {2} rpc/sec", cnt, sw.ElapsedMilliseconds, (double)cnt * 1000d / sw.ElapsedMilliseconds); + } + + + Console.WriteLine("Finished benchmark..."); + } + + } + + +} diff --git a/TestServer_Console.Core/TestServer_Console.Core.csproj b/TestServer_Console.Core/TestServer_Console.Core.csproj new file mode 100644 index 0000000..1a70e75 --- /dev/null +++ b/TestServer_Console.Core/TestServer_Console.Core.csproj @@ -0,0 +1,21 @@ + + + + Exe + netcoreapp3.0 + + + + false + + + + + + + + + + + + diff --git a/TestServer_Console.Core/service.cs b/TestServer_Console.Core/service.cs new file mode 100644 index 0000000..0b99c50 --- /dev/null +++ b/TestServer_Console.Core/service.cs @@ -0,0 +1,131 @@ +using AustinHarris.JsonRpc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TestServer_Console +{ + public class CalculatorService : JsonRpcService + { + [JsonRpcMethod] + private double Add(double l, double r) + { + return l + r; + } + + [JsonRpcMethod] + private int AddInt(int l, int r) + { + return l + r; + } + + [JsonRpcMethod] + public float? NullableFloatToNullableFloat(float? a) + { + return a; + } + + [JsonRpcMethod] + public decimal? Test2(decimal x) + { + return x; + } + + [JsonRpcMethod] + 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; + } + } +}