DefaultRequestMatcher.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. using JsonRPC4.Common;
  2. using JsonRPC4.Router.Abstractions;
  3. using JsonRPC4.Router.Utilities;
  4. using Microsoft.Extensions.Logging;
  5. using Microsoft.Extensions.Options;
  6. using System;
  7. using System.Buffers;
  8. using System.Collections.Concurrent;
  9. using System.Collections.Generic;
  10. using System.Linq;
  11. using System.Reflection;
  12. using System.Threading.Tasks;
  13. namespace JsonRPC4.Router.Defaults
  14. {
  15. public class DefaultRequestMatcher : IRpcRequestMatcher
  16. {
  17. private class CompiledMethodInfo
  18. {
  19. public MethodInfo MethodInfo
  20. {
  21. get;
  22. }
  23. public CompiledParameterInfo[] Parameters
  24. {
  25. get;
  26. }
  27. public CompiledMethodInfo(MethodInfo methodInfo, CompiledParameterInfo[] parameters)
  28. {
  29. MethodInfo = methodInfo;
  30. Parameters = parameters;
  31. }
  32. }
  33. private class CompiledParameterInfo
  34. {
  35. public string Name
  36. {
  37. get;
  38. }
  39. public RpcParameterType Type
  40. {
  41. get;
  42. }
  43. public Type RawType
  44. {
  45. get;
  46. }
  47. public bool IsOptional
  48. {
  49. get;
  50. }
  51. public CompiledParameterInfo(string name, RpcParameterType type, Type rawType, bool isOptional)
  52. {
  53. Name = name;
  54. Type = type;
  55. RawType = rawType;
  56. IsOptional = isOptional;
  57. }
  58. }
  59. private static ConcurrentDictionary<MethodInfo, CompiledMethodInfo> compiledMethodCache
  60. {
  61. get;
  62. } = new ConcurrentDictionary<MethodInfo, CompiledMethodInfo>();
  63. private static ConcurrentDictionary<string, RpcMethodInfo[]> requestToMethodCache
  64. {
  65. get;
  66. } = new ConcurrentDictionary<string, RpcMethodInfo[]>();
  67. private ILogger<DefaultRequestMatcher> logger
  68. {
  69. get;
  70. }
  71. private IOptions<RpcServerConfiguration> serverConfig
  72. {
  73. get;
  74. }
  75. public DefaultRequestMatcher(ILogger<DefaultRequestMatcher> logger, IOptions<RpcServerConfiguration> serverConfig)
  76. {
  77. this.logger = logger;
  78. this.serverConfig = serverConfig;
  79. }
  80. public RpcMethodInfo GetMatchingMethod(RpcRequest request, IReadOnlyList<MethodInfo> methods)
  81. {
  82. //IL_0041: Unknown result type (might be due to invalid IL or missing references)
  83. //IL_0046: Unknown result type (might be due to invalid IL or missing references)
  84. //IL_0051: Unknown result type (might be due to invalid IL or missing references)
  85. //IL_0056: Unknown result type (might be due to invalid IL or missing references)
  86. //IL_0098: Unknown result type (might be due to invalid IL or missing references)
  87. //IL_0099: Unknown result type (might be due to invalid IL or missing references)
  88. if (request == null)
  89. {
  90. throw new ArgumentNullException("request");
  91. }
  92. logger.AttemptingToMatchMethod(request.Method);
  93. CompiledMethodInfo[] array = ArrayPool<CompiledMethodInfo>.Shared.Rent(methods.Count);
  94. Span<RpcMethodInfo> val;
  95. try
  96. {
  97. FillCompiledMethodInfos(methods, array);
  98. val = FilterAndBuildMethodInfoByRequest(MemoryExtensions.AsSpan<CompiledMethodInfo>(array, 0, methods.Count), request);
  99. }
  100. finally
  101. {
  102. ArrayPool<CompiledMethodInfo>.Shared.Return(array, true);
  103. }
  104. if (val.Length == 1)
  105. {
  106. logger.RequestMatchedMethod();
  107. return val[0];
  108. }
  109. string message;
  110. if (val.Length > 1)
  111. {
  112. List<string> list = new List<string>();
  113. Span<RpcMethodInfo> val2 = val;
  114. for (int i = 0; i < val2.Length; i++)
  115. {
  116. RpcMethodInfo rpcMethodInfo = val2[i];
  117. List<string> list2 = new List<string>();
  118. ParameterInfo[] parameters = rpcMethodInfo.Method.GetParameters();
  119. foreach (ParameterInfo parameterInfo in parameters)
  120. {
  121. string text = parameterInfo.Name + ": " + parameterInfo.ParameterType.Name;
  122. if (parameterInfo.IsOptional)
  123. {
  124. text += "(Optional)";
  125. }
  126. list2.Add(text);
  127. }
  128. string text2 = string.Join(", ", list2);
  129. list.Add("{Name: '" + rpcMethodInfo.Method.Name + "', Parameters: [" + text2 + "]}");
  130. }
  131. message = "More than one method matched the rpc request. Unable to invoke due to ambiguity. Methods that matched the same name: " + string.Join(", ", list);
  132. }
  133. else
  134. {
  135. logger.MethodsInRoute(methods);
  136. message = "No methods matched request.";
  137. }
  138. throw new RpcException(RpcErrorCode.MethodNotFound, message);
  139. }
  140. private void FillCompiledMethodInfos(IReadOnlyList<MethodInfo> methods, CompiledMethodInfo[] compiledMethods)
  141. {
  142. for (int i = 0; i < methods.Count; i++)
  143. {
  144. MethodInfo methodInfo = methods[i];
  145. if (!compiledMethodCache.TryGetValue(methodInfo, out CompiledMethodInfo value))
  146. {
  147. CompiledParameterInfo[] parameters = methodInfo.GetParameters().Select(ExtractParam).ToArray();
  148. value = new CompiledMethodInfo(methodInfo, parameters);
  149. }
  150. compiledMethods[i] = value;
  151. }
  152. CompiledParameterInfo ExtractParam(ParameterInfo parameterInfo)
  153. {
  154. Type type = parameterInfo.ParameterType;
  155. if (type.IsGenericType)
  156. {
  157. Type underlyingType = Nullable.GetUnderlyingType(type);
  158. if (underlyingType != null)
  159. {
  160. type = underlyingType;
  161. }
  162. }
  163. 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);
  164. }
  165. }
  166. private RpcMethodInfo[] FilterAndBuildMethodInfoByRequest(ReadOnlySpan<CompiledMethodInfo> methods, RpcRequest request)
  167. {
  168. int num = 0;
  169. int num2 = 200;
  170. int num3 = request.Method.Length;
  171. if (request.Parameters != null)
  172. {
  173. num3 += 3 + num2;
  174. }
  175. char[] array = ArrayPool<char>.Shared.Rent(num3);
  176. RpcMethodInfo[] result;
  177. try
  178. {
  179. for (int i = 0; i < request.Method.Length; i++)
  180. {
  181. array[num++] = request.Method[i];
  182. }
  183. if (request.Parameters != null)
  184. {
  185. array[num++] = ' ';
  186. array[num++] = (request.Parameters.IsDictionary ? 'd' : 'a');
  187. array[num++] = ' ';
  188. if (request.Parameters.IsDictionary)
  189. {
  190. using (Dictionary<string, IRpcParameter>.Enumerator enumerator = request.Parameters.AsDictionary.GetEnumerator())
  191. {
  192. while (enumerator.MoveNext())
  193. {
  194. KeyValuePair<string, IRpcParameter> keyValuePair = enumerator.Current;
  195. if (num + keyValuePair.Key.Length + 1 >= keyValuePair.Key.Length)
  196. {
  197. ArrayPool<char>.Shared.Return(array, false);
  198. array = ArrayPool<char>.Shared.Rent(array.Length + 30);
  199. }
  200. array[num++] = ' ';
  201. for (int j = 0; j < keyValuePair.Key.Length; j++)
  202. {
  203. array[num++] = keyValuePair.Key[j];
  204. }
  205. }
  206. goto IL_1E3;
  207. }
  208. }
  209. List<IRpcParameter> asList = request.Parameters.AsList;
  210. for (int k = 0; k < asList.Count; k++)
  211. {
  212. char c;
  213. switch (asList[k].Type)
  214. {
  215. case RpcParameterType.Null:
  216. c = '-';
  217. break;
  218. case RpcParameterType.Number:
  219. c = 'n';
  220. break;
  221. case RpcParameterType.String:
  222. c = 's';
  223. break;
  224. case RpcParameterType.Object:
  225. c = 'o';
  226. break;
  227. default:
  228. throw new InvalidOperationException(string.Format("Unimplemented parameter type '{0}'", asList[k].Type));
  229. }
  230. array[num++] = c;
  231. }
  232. }
  233. IL_1E3:
  234. string text = new string(array, 0, num);
  235. RpcMethodInfo[] array2;
  236. if (DefaultRequestMatcher.requestToMethodCache.TryGetValue(text, out array2))
  237. {
  238. result = array2;
  239. }
  240. else
  241. {
  242. DefaultRequestMatcher.CompiledMethodInfo[] array3 = ArrayPool<DefaultRequestMatcher.CompiledMethodInfo>.Shared.Rent(methods.Length);
  243. try
  244. {
  245. int num4 = 0;
  246. for (int l = 0; l < methods.Length; l++)
  247. {
  248. DefaultRequestMatcher.CompiledMethodInfo compiledMethodInfo = methods[l];
  249. if (RpcUtil.NamesMatch(MemoryExtensions.AsSpan(compiledMethodInfo.MethodInfo.Name), MemoryExtensions.AsSpan(request.Method)))
  250. {
  251. array3[num4++] = compiledMethodInfo;
  252. }
  253. }
  254. Span<RpcMethodInfo> span;
  255. if (num4 < 1)
  256. {
  257. span = Span<RpcMethodInfo>.Empty;
  258. }
  259. else
  260. {
  261. RpcMethodInfo[] array4 = ArrayPool<RpcMethodInfo>.Shared.Rent(num4);
  262. try
  263. {
  264. int num5 = 0;
  265. for (int m = 0; m < num4; m++)
  266. {
  267. DefaultRequestMatcher.CompiledMethodInfo method = array3[m];
  268. ValueTuple<bool, RpcMethodInfo> valueTuple = this.HasParameterSignature(method, request);
  269. bool item = valueTuple.Item1;
  270. RpcMethodInfo item2 = valueTuple.Item2;
  271. if (item)
  272. {
  273. array4[num5++] = item2;
  274. }
  275. }
  276. if (num5 <= 1)
  277. {
  278. span = MemoryExtensions.AsSpan<RpcMethodInfo>(array4, 0, num5);
  279. }
  280. else
  281. {
  282. RpcMethodInfo[] array5 = ArrayPool<RpcMethodInfo>.Shared.Rent(num5);
  283. try
  284. {
  285. int num6 = 0;
  286. for (int n = 0; n < num5; n++)
  287. {
  288. bool flag = true;
  289. RpcMethodInfo rpcMethodInfo = array4[n];
  290. ParameterInfo[] parameters = rpcMethodInfo.Method.GetParameters();
  291. if (rpcMethodInfo.Parameters.Length == parameters.Length)
  292. {
  293. for (int num7 = 0; num7 < rpcMethodInfo.Parameters.Length; num7++)
  294. {
  295. object value = rpcMethodInfo.Parameters[num7];
  296. ParameterInfo parameterInfo = parameters[num7];
  297. if (!RpcUtil.TypesMatch(value, parameterInfo.ParameterType))
  298. {
  299. flag = false;
  300. break;
  301. }
  302. }
  303. }
  304. else
  305. {
  306. flag = false;
  307. }
  308. if (flag)
  309. {
  310. array5[num6++] = array4[n];
  311. }
  312. }
  313. RpcMethodInfo[] array7;
  314. int num9;
  315. if (num6 <= 0)
  316. {
  317. RpcMethodInfo[] array6 = array4;
  318. int num8 = num5;
  319. array7 = array6;
  320. num9 = num8;
  321. }
  322. else
  323. {
  324. RpcMethodInfo[] array8 = array5;
  325. int num8 = num6;
  326. array7 = array8;
  327. num9 = num8;
  328. }
  329. if (num9 <= 1)
  330. {
  331. span = MemoryExtensions.AsSpan<RpcMethodInfo>(array4, 0, num9);
  332. }
  333. else
  334. {
  335. RpcMethodInfo[] array9 = ArrayPool<RpcMethodInfo>.Shared.Rent(num9);
  336. try
  337. {
  338. int num10 = 0;
  339. for (int num11 = 0; num11 < num9; num11++)
  340. {
  341. RpcMethodInfo rpcMethodInfo2 = array7[num11];
  342. if (string.Equals(rpcMethodInfo2.Method.Name, request.Method, StringComparison.Ordinal))
  343. {
  344. array9[num10++] = rpcMethodInfo2;
  345. }
  346. }
  347. span = MemoryExtensions.AsSpan<RpcMethodInfo>(array9, 0, num10);
  348. }
  349. finally
  350. {
  351. ArrayPool<RpcMethodInfo>.Shared.Return(array9, false);
  352. }
  353. }
  354. }
  355. finally
  356. {
  357. ArrayPool<RpcMethodInfo>.Shared.Return(array5, false);
  358. }
  359. }
  360. }
  361. finally
  362. {
  363. ArrayPool<RpcMethodInfo>.Shared.Return(array4, false);
  364. }
  365. }
  366. RpcMethodInfo[] array10 = span.ToArray();
  367. DefaultRequestMatcher.requestToMethodCache.TryAdd(text, array10);
  368. result = array10;
  369. }
  370. finally
  371. {
  372. ArrayPool<DefaultRequestMatcher.CompiledMethodInfo>.Shared.Return(array3, false);
  373. }
  374. }
  375. }
  376. finally
  377. {
  378. ArrayPool<char>.Shared.Return(array, false);
  379. }
  380. return result;
  381. }
  382. private (bool Matches, RpcMethodInfo MethodInfo) HasParameterSignature(CompiledMethodInfo method, RpcRequest rpcRequest)
  383. {
  384. IList<IRpcParameter> parameterList;
  385. if (rpcRequest.Parameters == null)
  386. {
  387. parameterList = new IRpcParameter[0];
  388. }
  389. else if (rpcRequest.Parameters.IsDictionary)
  390. {
  391. Dictionary<string, IRpcParameter> asDictionary = rpcRequest.Parameters.AsDictionary;
  392. if (!TryParseParameterList(method, asDictionary, out parameterList))
  393. {
  394. return (false, null);
  395. }
  396. }
  397. else
  398. {
  399. parameterList = rpcRequest.Parameters.AsList;
  400. }
  401. if (parameterList.Count() > method.Parameters.Length)
  402. {
  403. return (false, null);
  404. }
  405. for (int i = 0; i < parameterList.Count(); i++)
  406. {
  407. CompiledParameterInfo compiledParameterInfo = method.Parameters[i];
  408. IRpcParameter rpcParameter = parameterList[i];
  409. if (rpcParameter.Type != compiledParameterInfo.Type && compiledParameterInfo.Type != RpcParameterType.Object && (rpcParameter.Type != 0 || !compiledParameterInfo.RawType.IsNullableType()))
  410. {
  411. return (false, null);
  412. }
  413. }
  414. object[] array = new object[method.Parameters.Length];
  415. for (int j = 0; j < parameterList.Count(); j++)
  416. {
  417. CompiledParameterInfo compiledParameterInfo2 = method.Parameters[j];
  418. if (!parameterList[j].TryGetValue(compiledParameterInfo2.RawType, out object value))
  419. {
  420. return (false, null);
  421. }
  422. array[j] = value;
  423. }
  424. RpcMethodInfo item = new RpcMethodInfo(method.MethodInfo, array);
  425. return (true, item);
  426. }
  427. private bool TryParseParameterList(CompiledMethodInfo method, Dictionary<string, IRpcParameter> requestParameters, out IList<IRpcParameter> parameterList)
  428. {
  429. //IL_0036: Unknown result type (might be due to invalid IL or missing references)
  430. //IL_0042: Unknown result type (might be due to invalid IL or missing references)
  431. parameterList = new IRpcParameter[method.Parameters.Count()];
  432. for (int i = 0; i < method.Parameters.Length; i++)
  433. {
  434. CompiledParameterInfo compiledParameterInfo = method.Parameters[i];
  435. foreach (KeyValuePair<string, IRpcParameter> requestParameter in requestParameters)
  436. {
  437. if (RpcUtil.NamesMatch(MemoryExtensions.AsSpan(compiledParameterInfo.Name), MemoryExtensions.AsSpan(requestParameter.Key)))
  438. {
  439. parameterList[i] = requestParameter.Value;
  440. }
  441. }
  442. if (!compiledParameterInfo.IsOptional)
  443. {
  444. parameterList = null;
  445. return false;
  446. }
  447. }
  448. return true;
  449. }
  450. }
  451. }