|
@@ -0,0 +1,473 @@
|
|
|
+using JsonRPC4.Common;
|
|
|
+using JsonRPC4.Router.Abstractions;
|
|
|
+using JsonRPC4.Router.Utilities;
|
|
|
+using Microsoft.Extensions.Logging;
|
|
|
+using Microsoft.Extensions.Options;
|
|
|
+using System;
|
|
|
+using System.Buffers;
|
|
|
+using System.Collections.Concurrent;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.Linq;
|
|
|
+using System.Reflection;
|
|
|
+using System.Threading.Tasks;
|
|
|
+
|
|
|
+namespace JsonRPC4.Router.Defaults
|
|
|
+{
|
|
|
+ public class DefaultRequestMatcher : IRpcRequestMatcher
|
|
|
+ {
|
|
|
+ private class CompiledMethodInfo
|
|
|
+ {
|
|
|
+ public MethodInfo MethodInfo
|
|
|
+ {
|
|
|
+ get;
|
|
|
+ }
|
|
|
+
|
|
|
+ public CompiledParameterInfo[] Parameters
|
|
|
+ {
|
|
|
+ get;
|
|
|
+ }
|
|
|
+
|
|
|
+ public CompiledMethodInfo(MethodInfo methodInfo, CompiledParameterInfo[] parameters)
|
|
|
+ {
|
|
|
+ MethodInfo = methodInfo;
|
|
|
+ Parameters = parameters;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private class CompiledParameterInfo
|
|
|
+ {
|
|
|
+ public string Name
|
|
|
+ {
|
|
|
+ get;
|
|
|
+ }
|
|
|
+
|
|
|
+ public RpcParameterType Type
|
|
|
+ {
|
|
|
+ get;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Type RawType
|
|
|
+ {
|
|
|
+ get;
|
|
|
+ }
|
|
|
+
|
|
|
+ public bool IsOptional
|
|
|
+ {
|
|
|
+ get;
|
|
|
+ }
|
|
|
+
|
|
|
+ public CompiledParameterInfo(string name, RpcParameterType type, Type rawType, bool isOptional)
|
|
|
+ {
|
|
|
+ Name = name;
|
|
|
+ Type = type;
|
|
|
+ RawType = rawType;
|
|
|
+ IsOptional = isOptional;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static ConcurrentDictionary<MethodInfo, CompiledMethodInfo> compiledMethodCache
|
|
|
+ {
|
|
|
+ get;
|
|
|
+ } = new ConcurrentDictionary<MethodInfo, CompiledMethodInfo>();
|
|
|
+
|
|
|
+
|
|
|
+ private static ConcurrentDictionary<string, RpcMethodInfo[]> requestToMethodCache
|
|
|
+ {
|
|
|
+ get;
|
|
|
+ } = new ConcurrentDictionary<string, RpcMethodInfo[]>();
|
|
|
+
|
|
|
+
|
|
|
+ private ILogger<DefaultRequestMatcher> logger
|
|
|
+ {
|
|
|
+ get;
|
|
|
+ }
|
|
|
+
|
|
|
+ private IOptions<RpcServerConfiguration> serverConfig
|
|
|
+ {
|
|
|
+ get;
|
|
|
+ }
|
|
|
+
|
|
|
+ public DefaultRequestMatcher(ILogger<DefaultRequestMatcher> logger, IOptions<RpcServerConfiguration> serverConfig)
|
|
|
+ {
|
|
|
+ this.logger = logger;
|
|
|
+ this.serverConfig = serverConfig;
|
|
|
+ }
|
|
|
+
|
|
|
+ public RpcMethodInfo GetMatchingMethod(RpcRequest request, IReadOnlyList<MethodInfo> methods)
|
|
|
+ {
|
|
|
+ //IL_0041: Unknown result type (might be due to invalid IL or missing references)
|
|
|
+ //IL_0046: Unknown result type (might be due to invalid IL or missing references)
|
|
|
+ //IL_0051: Unknown result type (might be due to invalid IL or missing references)
|
|
|
+ //IL_0056: Unknown result type (might be due to invalid IL or missing references)
|
|
|
+ //IL_0098: Unknown result type (might be due to invalid IL or missing references)
|
|
|
+ //IL_0099: Unknown result type (might be due to invalid IL or missing references)
|
|
|
+ if (request == null)
|
|
|
+ {
|
|
|
+ throw new ArgumentNullException("request");
|
|
|
+ }
|
|
|
+ logger.AttemptingToMatchMethod(request.Method);
|
|
|
+ CompiledMethodInfo[] array = ArrayPool<CompiledMethodInfo>.Shared.Rent(methods.Count);
|
|
|
+ Span<RpcMethodInfo> val;
|
|
|
+ try
|
|
|
+ {
|
|
|
+ FillCompiledMethodInfos(methods, array);
|
|
|
+ val = FilterAndBuildMethodInfoByRequest(MemoryExtensions.AsSpan<CompiledMethodInfo>(array, 0, methods.Count), request);
|
|
|
+
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ ArrayPool<CompiledMethodInfo>.Shared.Return(array, true);
|
|
|
+ }
|
|
|
+ if (val.Length == 1)
|
|
|
+ {
|
|
|
+ logger.RequestMatchedMethod();
|
|
|
+ return val[0];
|
|
|
+ }
|
|
|
+ string message;
|
|
|
+ if (val.Length > 1)
|
|
|
+ {
|
|
|
+ List<string> list = new List<string>();
|
|
|
+ Span<RpcMethodInfo> val2 = val;
|
|
|
+ for (int i = 0; i < val2.Length; i++)
|
|
|
+ {
|
|
|
+ RpcMethodInfo rpcMethodInfo = val2[i];
|
|
|
+ List<string> list2 = new List<string>();
|
|
|
+ ParameterInfo[] parameters = rpcMethodInfo.Method.GetParameters();
|
|
|
+ foreach (ParameterInfo parameterInfo in parameters)
|
|
|
+ {
|
|
|
+ string text = parameterInfo.Name + ": " + parameterInfo.ParameterType.Name;
|
|
|
+ if (parameterInfo.IsOptional)
|
|
|
+ {
|
|
|
+ text += "(Optional)";
|
|
|
+ }
|
|
|
+ list2.Add(text);
|
|
|
+ }
|
|
|
+ string text2 = string.Join(", ", list2);
|
|
|
+ list.Add("{Name: '" + rpcMethodInfo.Method.Name + "', Parameters: [" + text2 + "]}");
|
|
|
+ }
|
|
|
+ message = "More than one method matched the rpc request. Unable to invoke due to ambiguity. Methods that matched the same name: " + string.Join(", ", list);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ logger.MethodsInRoute(methods);
|
|
|
+ message = "No methods matched request.";
|
|
|
+ }
|
|
|
+ throw new RpcException(RpcErrorCode.MethodNotFound, message);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void FillCompiledMethodInfos(IReadOnlyList<MethodInfo> methods, CompiledMethodInfo[] compiledMethods)
|
|
|
+ {
|
|
|
+ for (int i = 0; i < methods.Count; i++)
|
|
|
+ {
|
|
|
+ MethodInfo methodInfo = methods[i];
|
|
|
+ if (!compiledMethodCache.TryGetValue(methodInfo, out CompiledMethodInfo value))
|
|
|
+ {
|
|
|
+ CompiledParameterInfo[] parameters = methodInfo.GetParameters().Select(ExtractParam).ToArray();
|
|
|
+ value = new CompiledMethodInfo(methodInfo, parameters);
|
|
|
+ }
|
|
|
+ compiledMethods[i] = value;
|
|
|
+ }
|
|
|
+ CompiledParameterInfo ExtractParam(ParameterInfo parameterInfo)
|
|
|
+ {
|
|
|
+ Type type = parameterInfo.ParameterType;
|
|
|
+ if (type.IsGenericType)
|
|
|
+ {
|
|
|
+ Type underlyingType = Nullable.GetUnderlyingType(type);
|
|
|
+ if (underlyingType != null)
|
|
|
+ {
|
|
|
+ type = underlyingType;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return new CompiledParameterInfo(type: (type == typeof(short) || type == typeof(ushort) || type == typeof(int) || type == typeof(uint) || type == typeof(long) || type == typeof(ulong) || type == typeof(float) || type == typeof(double) || type == typeof(decimal)) ? RpcParameterType.Number : ((!(type == typeof(string))) ? RpcParameterType.Object : RpcParameterType.String), name: parameterInfo.Name, rawType: parameterInfo.ParameterType, isOptional: parameterInfo.IsOptional);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private RpcMethodInfo[] FilterAndBuildMethodInfoByRequest(ReadOnlySpan<CompiledMethodInfo> methods, RpcRequest request)
|
|
|
+ {
|
|
|
+ int num = 0;
|
|
|
+ int num2 = 200;
|
|
|
+ int num3 = request.Method.Length;
|
|
|
+ if (request.Parameters != null)
|
|
|
+ {
|
|
|
+ num3 += 3 + num2;
|
|
|
+ }
|
|
|
+ char[] array = ArrayPool<char>.Shared.Rent(num3);
|
|
|
+ RpcMethodInfo[] result;
|
|
|
+ try
|
|
|
+ {
|
|
|
+ for (int i = 0; i < request.Method.Length; i++)
|
|
|
+ {
|
|
|
+ array[num++] = request.Method[i];
|
|
|
+ }
|
|
|
+ if (request.Parameters != null)
|
|
|
+ {
|
|
|
+ array[num++] = ' ';
|
|
|
+ array[num++] = (request.Parameters.IsDictionary ? 'd' : 'a');
|
|
|
+ array[num++] = ' ';
|
|
|
+ if (request.Parameters.IsDictionary)
|
|
|
+ {
|
|
|
+ using (Dictionary<string, IRpcParameter>.Enumerator enumerator = request.Parameters.AsDictionary.GetEnumerator())
|
|
|
+ {
|
|
|
+ while (enumerator.MoveNext())
|
|
|
+ {
|
|
|
+ KeyValuePair<string, IRpcParameter> keyValuePair = enumerator.Current;
|
|
|
+ if (num + keyValuePair.Key.Length + 1 >= keyValuePair.Key.Length)
|
|
|
+ {
|
|
|
+ ArrayPool<char>.Shared.Return(array, false);
|
|
|
+ array = ArrayPool<char>.Shared.Rent(array.Length + 30);
|
|
|
+ }
|
|
|
+ array[num++] = ' ';
|
|
|
+ for (int j = 0; j < keyValuePair.Key.Length; j++)
|
|
|
+ {
|
|
|
+ array[num++] = keyValuePair.Key[j];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ goto IL_1E3;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ List<IRpcParameter> asList = request.Parameters.AsList;
|
|
|
+ for (int k = 0; k < asList.Count; k++)
|
|
|
+ {
|
|
|
+ char c;
|
|
|
+ switch (asList[k].Type)
|
|
|
+ {
|
|
|
+ case RpcParameterType.Null:
|
|
|
+ c = '-';
|
|
|
+ break;
|
|
|
+ case RpcParameterType.Number:
|
|
|
+ c = 'n';
|
|
|
+ break;
|
|
|
+ case RpcParameterType.String:
|
|
|
+ c = 's';
|
|
|
+ break;
|
|
|
+ case RpcParameterType.Object:
|
|
|
+ c = 'o';
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new InvalidOperationException(string.Format("Unimplemented parameter type '{0}'", asList[k].Type));
|
|
|
+ }
|
|
|
+ array[num++] = c;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ IL_1E3:
|
|
|
+ string text = new string(array, 0, num);
|
|
|
+ RpcMethodInfo[] array2;
|
|
|
+ if (DefaultRequestMatcher.requestToMethodCache.TryGetValue(text, out array2))
|
|
|
+ {
|
|
|
+ result = array2;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ DefaultRequestMatcher.CompiledMethodInfo[] array3 = ArrayPool<DefaultRequestMatcher.CompiledMethodInfo>.Shared.Rent(methods.Length);
|
|
|
+ try
|
|
|
+ {
|
|
|
+ int num4 = 0;
|
|
|
+ for (int l = 0; l < methods.Length; l++)
|
|
|
+ {
|
|
|
+ DefaultRequestMatcher.CompiledMethodInfo compiledMethodInfo = methods[l];
|
|
|
+ if (RpcUtil.NamesMatch(MemoryExtensions.AsSpan(compiledMethodInfo.MethodInfo.Name), MemoryExtensions.AsSpan(request.Method)))
|
|
|
+ {
|
|
|
+ array3[num4++] = compiledMethodInfo;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Span<RpcMethodInfo> span;
|
|
|
+ if (num4 < 1)
|
|
|
+ {
|
|
|
+ span = Span<RpcMethodInfo>.Empty;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ RpcMethodInfo[] array4 = ArrayPool<RpcMethodInfo>.Shared.Rent(num4);
|
|
|
+ try
|
|
|
+ {
|
|
|
+ int num5 = 0;
|
|
|
+ for (int m = 0; m < num4; m++)
|
|
|
+ {
|
|
|
+ DefaultRequestMatcher.CompiledMethodInfo method = array3[m];
|
|
|
+ ValueTuple<bool, RpcMethodInfo> valueTuple = this.HasParameterSignature(method, request);
|
|
|
+ bool item = valueTuple.Item1;
|
|
|
+ RpcMethodInfo item2 = valueTuple.Item2;
|
|
|
+ if (item)
|
|
|
+ {
|
|
|
+ array4[num5++] = item2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (num5 <= 1)
|
|
|
+ {
|
|
|
+ span = MemoryExtensions.AsSpan<RpcMethodInfo>(array4, 0, num5);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ RpcMethodInfo[] array5 = ArrayPool<RpcMethodInfo>.Shared.Rent(num5);
|
|
|
+ try
|
|
|
+ {
|
|
|
+ int num6 = 0;
|
|
|
+ for (int n = 0; n < num5; n++)
|
|
|
+ {
|
|
|
+ bool flag = true;
|
|
|
+ RpcMethodInfo rpcMethodInfo = array4[n];
|
|
|
+ ParameterInfo[] parameters = rpcMethodInfo.Method.GetParameters();
|
|
|
+ if (rpcMethodInfo.Parameters.Length == parameters.Length)
|
|
|
+ {
|
|
|
+ for (int num7 = 0; num7 < rpcMethodInfo.Parameters.Length; num7++)
|
|
|
+ {
|
|
|
+ object value = rpcMethodInfo.Parameters[num7];
|
|
|
+ ParameterInfo parameterInfo = parameters[num7];
|
|
|
+ if (!RpcUtil.TypesMatch(value, parameterInfo.ParameterType))
|
|
|
+ {
|
|
|
+ flag = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ flag = false;
|
|
|
+ }
|
|
|
+ if (flag)
|
|
|
+ {
|
|
|
+ array5[num6++] = array4[n];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ RpcMethodInfo[] array7;
|
|
|
+ int num9;
|
|
|
+ if (num6 <= 0)
|
|
|
+ {
|
|
|
+ RpcMethodInfo[] array6 = array4;
|
|
|
+ int num8 = num5;
|
|
|
+ array7 = array6;
|
|
|
+ num9 = num8;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ RpcMethodInfo[] array8 = array5;
|
|
|
+ int num8 = num6;
|
|
|
+ array7 = array8;
|
|
|
+ num9 = num8;
|
|
|
+ }
|
|
|
+ if (num9 <= 1)
|
|
|
+ {
|
|
|
+ span = MemoryExtensions.AsSpan<RpcMethodInfo>(array4, 0, num9);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ RpcMethodInfo[] array9 = ArrayPool<RpcMethodInfo>.Shared.Rent(num9);
|
|
|
+ try
|
|
|
+ {
|
|
|
+ int num10 = 0;
|
|
|
+ for (int num11 = 0; num11 < num9; num11++)
|
|
|
+ {
|
|
|
+ RpcMethodInfo rpcMethodInfo2 = array7[num11];
|
|
|
+ if (string.Equals(rpcMethodInfo2.Method.Name, request.Method, StringComparison.Ordinal))
|
|
|
+ {
|
|
|
+ array9[num10++] = rpcMethodInfo2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ span = MemoryExtensions.AsSpan<RpcMethodInfo>(array9, 0, num10);
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ ArrayPool<RpcMethodInfo>.Shared.Return(array9, false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ ArrayPool<RpcMethodInfo>.Shared.Return(array5, false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ ArrayPool<RpcMethodInfo>.Shared.Return(array4, false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ RpcMethodInfo[] array10 = span.ToArray();
|
|
|
+ DefaultRequestMatcher.requestToMethodCache.TryAdd(text, array10);
|
|
|
+ result = array10;
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ ArrayPool<DefaultRequestMatcher.CompiledMethodInfo>.Shared.Return(array3, false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ ArrayPool<char>.Shared.Return(array, false);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private (bool Matches, RpcMethodInfo MethodInfo) HasParameterSignature(CompiledMethodInfo method, RpcRequest rpcRequest)
|
|
|
+ {
|
|
|
+ IList<IRpcParameter> parameterList;
|
|
|
+ if (rpcRequest.Parameters == null)
|
|
|
+ {
|
|
|
+ parameterList = new IRpcParameter[0];
|
|
|
+ }
|
|
|
+ else if (rpcRequest.Parameters.IsDictionary)
|
|
|
+ {
|
|
|
+ Dictionary<string, IRpcParameter> asDictionary = rpcRequest.Parameters.AsDictionary;
|
|
|
+ if (!TryParseParameterList(method, asDictionary, out parameterList))
|
|
|
+ {
|
|
|
+ return (false, null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ parameterList = rpcRequest.Parameters.AsList;
|
|
|
+ }
|
|
|
+ if (parameterList.Count() > method.Parameters.Length)
|
|
|
+ {
|
|
|
+ return (false, null);
|
|
|
+ }
|
|
|
+ for (int i = 0; i < parameterList.Count(); i++)
|
|
|
+ {
|
|
|
+ CompiledParameterInfo compiledParameterInfo = method.Parameters[i];
|
|
|
+ IRpcParameter rpcParameter = parameterList[i];
|
|
|
+ if (rpcParameter.Type != compiledParameterInfo.Type && compiledParameterInfo.Type != RpcParameterType.Object && (rpcParameter.Type != 0 || !compiledParameterInfo.RawType.IsNullableType()))
|
|
|
+ {
|
|
|
+ return (false, null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ object[] array = new object[method.Parameters.Length];
|
|
|
+ for (int j = 0; j < parameterList.Count(); j++)
|
|
|
+ {
|
|
|
+ CompiledParameterInfo compiledParameterInfo2 = method.Parameters[j];
|
|
|
+ if (!parameterList[j].TryGetValue(compiledParameterInfo2.RawType, out object value))
|
|
|
+ {
|
|
|
+ return (false, null);
|
|
|
+ }
|
|
|
+ array[j] = value;
|
|
|
+ }
|
|
|
+ RpcMethodInfo item = new RpcMethodInfo(method.MethodInfo, array);
|
|
|
+ return (true, item);
|
|
|
+ }
|
|
|
+
|
|
|
+ private bool TryParseParameterList(CompiledMethodInfo method, Dictionary<string, IRpcParameter> requestParameters, out IList<IRpcParameter> parameterList)
|
|
|
+ {
|
|
|
+ //IL_0036: Unknown result type (might be due to invalid IL or missing references)
|
|
|
+ //IL_0042: Unknown result type (might be due to invalid IL or missing references)
|
|
|
+ parameterList = new IRpcParameter[method.Parameters.Count()];
|
|
|
+ for (int i = 0; i < method.Parameters.Length; i++)
|
|
|
+ {
|
|
|
+ CompiledParameterInfo compiledParameterInfo = method.Parameters[i];
|
|
|
+ foreach (KeyValuePair<string, IRpcParameter> requestParameter in requestParameters)
|
|
|
+ {
|
|
|
+ if (RpcUtil.NamesMatch(MemoryExtensions.AsSpan(compiledParameterInfo.Name), MemoryExtensions.AsSpan(requestParameter.Key)))
|
|
|
+ {
|
|
|
+ parameterList[i] = requestParameter.Value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!compiledParameterInfo.IsOptional)
|
|
|
+ {
|
|
|
+ parameterList = null;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|