/// <summary> /// Call the incoming Rpc requests methods and gives the appropriate respones /// </summary> /// <param name="requests">List of Rpc requests</param> /// <param name="route">Rpc route that applies to the current request</param> /// <param name="httpContext">The context of the current http request</param> /// <param name="jsonSerializerSettings">Json serialization settings that will be used in serialization and deserialization for rpc requests</param> /// <returns>List of Rpc responses for the requests</returns> public async Task<List<RpcResponse>> InvokeBatchRequestAsync(List<RpcRequest> requests, RpcRoute route, HttpContext httpContext, JsonSerializerSettings jsonSerializerSettings = null) { this.logger?.LogDebug($"Invoking '{requests.Count}' batch requests"); var invokingTasks = new List<Task<RpcResponse>>(); foreach (RpcRequest request in requests) { Task<RpcResponse> invokingTask = Task.Run(async () => await this.InvokeRequestAsync(request, route, httpContext, jsonSerializerSettings)); if (request.Id != null) { //Only wait for non-notification requests invokingTasks.Add(invokingTask); } } await Task.WhenAll(invokingTasks.ToArray()); List<RpcResponse> responses = invokingTasks .Select(t => t.Result) .Where(r => r != null) .ToList(); this.logger?.LogDebug($"Finished '{requests.Count}' batch requests"); return responses; }
/// <summary> /// Call the incoming Rpc requests methods and gives the appropriate respones /// </summary> /// <param name="requests">List of Rpc requests</param> /// <param name="route">Rpc route that applies to the current request</param> /// <param name="serviceProvider">(Optional)IoC Container for rpc method controllers</param> /// <param name="jsonSerializerSettings">Json serialization settings that will be used in serialization and deserialization for rpc requests</param> /// <returns>List of Rpc responses for the requests</returns> public List <RpcResponse> InvokeBatchRequest(List <RpcRequest> requests, RpcRoute route, IServiceProvider serviceProvider = null, JsonSerializerSettings jsonSerializerSettings = null) { this.Logger?.LogDebug($"Invoking '{requests.Count}' batch requests"); var invokingTasks = new List <Task <RpcResponse> >(); foreach (RpcRequest request in requests) { Task <RpcResponse> invokingTask = Task.Run(() => this.InvokeRequest(request, route, serviceProvider, jsonSerializerSettings)); if (request.Id != null) { //Only wait for non-notification requests invokingTasks.Add(invokingTask); } } Task.WaitAll(invokingTasks.Cast <Task>().ToArray()); List <RpcResponse> responses = invokingTasks .Select(t => t.Result) .Where(r => r != null) .ToList(); this.Logger?.LogDebug($"Finished '{requests.Count}' batch requests"); return(responses); }
/// <summary> /// Indicates if the incoming request matches any predefined routes /// </summary> /// <param name="requestUrl">The current request url</param> /// <param name="route">The matching route corresponding to the request url if found, otherwise it is null</param> /// <param name="routeProvider">Provider that allows the retrieval of all configured routes</param> /// <returns>True if the request url matches any Rpc routes, otherwise False</returns> public bool MatchesRpcRoute(IRpcRouteProvider routeProvider, string requestUrl, out RpcRoute route) { if (requestUrl == null) { throw new ArgumentNullException(nameof(requestUrl)); } this.logger?.LogDebug($"Attempting to match Rpc route for the request url '{requestUrl}'"); RpcPath requestPath = RpcPath.Parse(requestUrl); this.logger?.LogTrace($"Request path: {requestPath}"); foreach (RpcRoute rpcRoute in routeProvider.GetRoutes()) { RpcPath routePath = RpcPath.Parse(rpcRoute.Name); this.logger?.LogTrace($"Trying to match against route - Name: {rpcRoute.Name}, Path: {routePath}"); if (requestPath == routePath) { this.logger?.LogDebug($"Matched the request url '{requestUrl}' to the route '{rpcRoute.Name}'"); route = rpcRoute; return true; } } this.logger?.LogDebug($"Failed to match the request url '{requestUrl}' to a route"); route = null; return false; }
public bool MatchesRpcRoute(RpcRouteCollection routes, string requestUrl, out RpcRoute route) { if (routes == null) { throw new ArgumentNullException(nameof(routes)); } if (requestUrl == null) { throw new ArgumentNullException(nameof(requestUrl)); } this.Logger?.LogVerbose($"Attempting to match Rpc route for the request url '{requestUrl}'"); RpcPath requestPath = RpcPath.Parse(requestUrl); RpcPath routePrefix = RpcPath.Parse(routes.RoutePrefix); foreach (RpcRoute rpcRoute in routes) { RpcPath routePath = RpcPath.Parse(rpcRoute.Name); routePath = routePrefix.Add(routePath); if (requestPath == routePath) { this.Logger?.LogVerbose($"Matched the request url '{requestUrl}' to the route '{rpcRoute.Name}'"); route = rpcRoute; return(true); } } this.Logger?.LogVerbose($"Failed to match the request url '{requestUrl}' to a route"); route = null; return(false); }
public RpcResponseBase InvokeRequest(RpcRequest request, RpcRoute route) { try { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (route == null) { throw new ArgumentNullException(nameof(route)); } } catch (ArgumentNullException ex) // Dont want to throw any exceptions when doing async requests { return(this.GetUnknownExceptionReponse(request, ex)); } this.Logger?.LogVerbose($"Invoking request with id '{request.Id}'"); RpcResponseBase rpcResponse; try { if (!string.Equals(request.JsonRpcVersion, "2.0")) { throw new RpcInvalidRequestException("Request must be jsonrpc version '2.0'"); } object[] parameterList; RpcMethod rpcMethod = this.GetMatchingMethod(route, request, out parameterList); this.Logger?.LogVerbose($"Attempting to invoke method '{request.Method}'"); object result = rpcMethod.Invoke(parameterList); this.Logger?.LogVerbose($"Finished invoking method '{request.Method}'"); rpcResponse = new RpcResultResponse(request.Id, result); } catch (RpcException ex) { this.Logger?.LogError("An Rpc error occurred. Returning an Rpc error response", ex); RpcError error = new RpcError(ex); rpcResponse = new RpcErrorResponse(request.Id, error); } catch (Exception ex) { rpcResponse = this.GetUnknownExceptionReponse(request, ex); } if (request.Id != null) { this.Logger?.LogVerbose($"Finished request with id '{request.Id}'"); //Only give a response if there is an id return(rpcResponse); } this.Logger?.LogVerbose($"Finished request with no id. Not returning a response"); return(null); }
public void InvokeRequest_AmbiguousRequest_ErrorResponse() { RpcRequest stringRequest = new RpcRequest("1", "AmbiguousMethod", 1); RpcRoute route = new RpcRoute(); route.AddClass <TestRouteClass>(); IRpcInvoker invoker = new DefaultRpcInvoker(); RpcResponse response = invoker.InvokeRequest(stringRequest, route); Assert.NotNull(response.Error); Assert.Equal(response.Error.Code, RpcErrorCode.AmbiguousMethod); }
public void InvokeRequest_StringParam_ParseAsGuidType() { Guid randomGuid = Guid.NewGuid(); RpcRequest stringRequest = new RpcRequest("1", "GuidTypeMethod", randomGuid.ToString()); RpcRoute route = new RpcRoute(); route.AddClass <TestRouteClass>(); IRpcInvoker invoker = new DefaultRpcInvoker(); RpcResponse stringResponse = invoker.InvokeRequest(stringRequest, route); Assert.Equal(stringResponse.Result, randomGuid); }
/// <summary> /// Gets all the predefined Rpc methods for a Rpc route /// </summary> /// <param name="route">The route to get Rpc methods for</param> /// <param name="serviceProvider">(Optional) IoC Container for rpc method controllers</param> /// <param name="jsonSerializerSettings">Json serialization settings that will be used in serialization and deserialization for rpc requests</param> /// <returns>List of Rpc methods for the specified Rpc route</returns> private static List <RpcMethod> GetRpcMethods(RpcRoute route, IServiceProvider serviceProvider = null, JsonSerializerSettings jsonSerializerSettings = null) { List <RpcMethod> rpcMethods = new List <RpcMethod>(); foreach (Type type in route.GetClasses()) { MethodInfo[] publicMethods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance); foreach (MethodInfo publicMethod in publicMethods) { RpcMethod rpcMethod = new RpcMethod(type, route, publicMethod, serviceProvider, jsonSerializerSettings); rpcMethods.Add(rpcMethod); } } return(rpcMethods); }
public void MatchesRpcRoute_DifferentRoutes_Valid(string requestUrl, string availableRouteName, bool shouldMatch) { RpcRoute route = new RpcRoute(availableRouteName); RpcRouteCollection routes = new RpcRouteCollection { route }; DefaultRpcParser parser = new DefaultRpcParser(); RpcRoute matchedRoute; bool isMatch = parser.MatchesRpcRoute(routes, requestUrl, out matchedRoute); Assert.Equal(isMatch, shouldMatch); Assert.Equal(matchedRoute != null, shouldMatch); Assert.Equal(route == matchedRoute, shouldMatch); }
private static List <RpcMethod> GetRpcMethods(RpcRoute route) { List <RpcMethod> rpcMethods = new List <RpcMethod>(); foreach (Type type in route.GetClasses()) { MethodInfo[] publicMethods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); foreach (MethodInfo publicMethod in publicMethods) { RpcMethod rpcMethod = new RpcMethod(type, route, publicMethod); rpcMethods.Add(rpcMethod); } } return(rpcMethods); }
public void InvokeRequest_AsyncMethod_Valid() { RpcRequest stringRequest = new RpcRequest("1", "2.0", "AddAsync", 1, 1); RpcRoute route = new RpcRoute(); route.AddClass <TestRouteClass>(); IRpcInvoker invoker = new DefaultRpcInvoker(); RpcResponseBase response = invoker.InvokeRequest(stringRequest, route); RpcResultResponse resultResponse = Assert.IsType <RpcResultResponse>(response); Assert.NotNull(resultResponse.Result); Assert.Equal(resultResponse.Result, 2); }
public async Task InvokeRequest_AmbiguousRequest_ErrorResponse() { RpcRequest stringRequest = new RpcRequest("1", "AmbiguousMethod", 1); var routeCriteria = new List <RouteCriteria>(); routeCriteria.Add(new RouteCriteria(typeof(TestRouteClass))); RpcRoute route = new RpcRoute(routeCriteria); IRouteContext routeContext = this.GetRouteContext(); DefaultRpcInvoker invoker = this.GetInvoker(); RpcResponse response = await invoker.InvokeRequestAsync(stringRequest, route, routeContext); Assert.NotNull(response.Error); Assert.Equal(response.Error.Code, (int)RpcErrorCode.MethodNotFound); }
public void InvokeRequest_Int64RequestParam_ConvertToInt32Param() { RpcRequest stringRequest = new RpcRequest("1", "2.0", "IntParameter", (long)1); RpcRoute route = new RpcRoute(); route.AddClass <TestRouteClass>(); IRpcInvoker invoker = new DefaultRpcInvoker(); RpcResponseBase response = invoker.InvokeRequest(stringRequest, route); RpcResultResponse resultResponse = Assert.IsType <RpcResultResponse>(response); Assert.NotNull(resultResponse.Result); Assert.IsType <int>(resultResponse.Result); Assert.Equal(resultResponse.Result, 1); }
public async Task InvokeRequest_StringParam_ParseAsGuidType() { Guid randomGuid = Guid.NewGuid(); RpcRequest stringRequest = new RpcRequest("1", "GuidTypeMethod", randomGuid.ToString()); var routeCriteria = new List <RouteCriteria>(); routeCriteria.Add(new RouteCriteria(typeof(TestRouteClass))); RpcRoute route = new RpcRoute(routeCriteria); IRouteContext routeContext = this.GetRouteContext(); DefaultRpcInvoker invoker = this.GetInvoker(); RpcResponse stringResponse = await invoker.InvokeRequestAsync(stringRequest, route, routeContext); Assert.Equal(stringResponse.Result, randomGuid); }
public IPromise <string> Call(RpcRoute route, string argument) { var message = new RpcMessage { Type = RpcMessageType.ActionRequest, Id = Id(), ControllerName = route.ControllerName, ActionName = route.ActionName, Data = argument, ReturnTag = new Tag(Aos.Node.NodeId) }; var promise = new Promise <string>(); _waitingForResult.Add(message.Id, promise); _sender.DispatchMessage(route.TargetTag, message.ToString()); return(promise); }
public async Task InvokeRequest_ServiceProvider_Pass() { RpcRequest stringRequest = new RpcRequest("1", "Test"); var routeCriteria = new List <RouteCriteria>(); routeCriteria.Add(new RouteCriteria(typeof(TestIoCRouteClass))); RpcRoute route = new RpcRoute(routeCriteria); DefaultRpcInvoker invoker = this.GetInvoker(); IServiceProvider serviceProvider = this.GetServiceProvider(); IRouteContext routeContext = this.GetRouteContext(); RpcResponse response = await invoker.InvokeRequestAsync(stringRequest, route, routeContext); RpcResponse resultResponse = Assert.IsType <RpcResponse>(response); Assert.NotNull(resultResponse.Result); Assert.Equal(JTokenType.Integer, resultResponse.Result.Type); Assert.Equal(resultResponse.Result.Value <int>(), 1); }
public void InvokeRequest_ServiceProvider_Pass() { RpcRequest stringRequest = new RpcRequest("1", "Test"); RpcRoute route = new RpcRoute(); route.AddClass <TestIoCRouteClass>(); IRpcInvoker invoker = new DefaultRpcInvoker(); IServiceCollection serviceCollection = new ServiceCollection(); serviceCollection.AddScoped <TestInjectionClass>(); serviceCollection.AddScoped <TestIoCRouteClass>(); IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); RpcResponse response = invoker.InvokeRequest(stringRequest, route, serviceProvider); RpcResponse resultResponse = Assert.IsType <RpcResponse>(response); Assert.NotNull(resultResponse.Result); Assert.Equal(JTokenType.Integer, resultResponse.Result.Type); Assert.Equal(resultResponse.Result.Value <int>(), 1); }
public async Task InvokeRequest_Int64RequestParam_ConvertToInt32Param() { RpcRequest stringRequest = new RpcRequest("1", "IntParameter", (long)1); var routeCriteria = new List <RouteCriteria>(); routeCriteria.Add(new RouteCriteria(typeof(TestRouteClass))); RpcRoute route = new RpcRoute(routeCriteria); IRouteContext routeContext = this.GetRouteContext(); DefaultRpcInvoker invoker = this.GetInvoker(); RpcResponse response = await invoker.InvokeRequestAsync(stringRequest, route, routeContext); RpcResponse resultResponse = Assert.IsType <RpcResponse>(response); Assert.NotNull(resultResponse.Result); Assert.Equal(JTokenType.Integer, resultResponse.Result.Type); Assert.Equal(resultResponse.Result.Value <int>(), 1); }
public async Task InvokeRequest_AsyncMethod_Valid() { RpcRequest stringRequest = new RpcRequest("1", "AddAsync", 1, 1); var routeCriteria = new List <RouteCriteria>(); routeCriteria.Add(new RouteCriteria(typeof(TestRouteClass))); RpcRoute route = new RpcRoute(routeCriteria); IRouteContext routeContext = this.GetRouteContext(); DefaultRpcInvoker invoker = this.GetInvoker(); RpcResponse response = await invoker.InvokeRequestAsync(stringRequest, route, routeContext); RpcResponse resultResponse = Assert.IsType <RpcResponse>(response); Assert.NotNull(resultResponse.Result); Assert.Equal(resultResponse.Result, 2); }
/// <summary> /// Gets all the predefined Rpc methods for a Rpc route /// </summary> /// <param name="route">The route to get Rpc methods for</param> /// <param name="serviceProvider">(Optional) IoC Container for rpc method controllers</param> /// <param name="jsonSerializerSettings">Json serialization settings that will be used in serialization and deserialization for rpc requests</param> /// <returns>List of Rpc methods for the specified Rpc route</returns> private static List <RpcMethod> GetRpcMethods(RpcRoute route, IServiceProvider serviceProvider = null, JsonSerializerSettings jsonSerializerSettings = null) { List <RpcMethod> rpcMethods = new List <RpcMethod>(); foreach (RouteCriteria routeCriteria in route.RouteCriteria) { foreach (Type type in routeCriteria.Types) { List <MethodInfo> publicMethods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance) //Ignore ToString, GetHashCode and Equals .Where(m => m.DeclaringType != typeof(object)) .ToList(); foreach (MethodInfo publicMethod in publicMethods) { RpcMethod rpcMethod = new RpcMethod(type, route, publicMethod, serviceProvider, jsonSerializerSettings); rpcMethods.Add(rpcMethod); } } } return(rpcMethods); }
public async Task InvokeRequest_OptionalParameter_Valid() { var routeCriteria = new List <RouteCriteria>(); routeCriteria.Add(new RouteCriteria(typeof(TestRouteClass))); RpcRoute route = new RpcRoute(routeCriteria); DefaultRpcInvoker invoker = this.GetInvoker(); IServiceProvider serviceProvider = this.GetServiceProvider(); IRouteContext routeContext = this.GetRouteContext(); //No params specified RpcRequest stringRequest = new RpcRequest("1", "Optional"); RpcResponse response = await invoker.InvokeRequestAsync(stringRequest, route, routeContext); RpcResponse resultResponse = Assert.IsType <RpcResponse>(response); Assert.Null(resultResponse.Result); Assert.False(resultResponse.HasError); //Param is null stringRequest = new RpcRequest("1", "Optional", parameterList: null); response = await invoker.InvokeRequestAsync(stringRequest, route, routeContext); resultResponse = Assert.IsType <RpcResponse>(response); Assert.Null(resultResponse.Result); Assert.False(resultResponse.HasError); //Param is a string stringRequest = new RpcRequest("1", "Optional", parameterList: "Test"); response = await invoker.InvokeRequestAsync(stringRequest, route, routeContext); resultResponse = Assert.IsType <RpcResponse>(response); Assert.NotNull(resultResponse.Result); Assert.Equal(JTokenType.String, resultResponse.Result.Type); Assert.Equal(resultResponse.Result.Value <string>(), "Test"); }
public List <RpcResponseBase> InvokeBatchRequest(List <RpcRequest> requests, RpcRoute route) { this.Logger?.LogVerbose($"Invoking '{requests.Count}' batch requests"); var invokingTasks = new List <Task <RpcResponseBase> >(); foreach (RpcRequest request in requests) { Task <RpcResponseBase> invokingTask = Task.Run(() => this.InvokeRequest(request, route)); invokingTasks.Add(invokingTask); } Task.WaitAll(invokingTasks.Cast <Task>().ToArray()); List <RpcResponseBase> responses = invokingTasks .Select(t => t.Result) .Where(r => r != null) .ToList(); this.Logger?.LogVerbose($"Finished '{requests.Count}' batch requests"); return(responses); }
/// <summary> /// Call the incoming Rpc requests methods and gives the appropriate respones /// </summary> /// <param name="requests">List of Rpc requests</param> /// <param name="route">Rpc route that applies to the current request</param> /// <param name="httpContext">The context of the current http request</param> /// <param name="jsonSerializerSettings">Json serialization settings that will be used in serialization and deserialization for rpc requests</param> /// <returns>List of Rpc responses for the requests</returns> public async Task <List <RpcResponse> > InvokeBatchRequestAsync(List <RpcRequest> requests, RpcRoute route, IRouteContext routeContext, JsonSerializerSettings jsonSerializerSettings = null) { this.logger?.LogDebug($"Invoking '{requests.Count}' batch requests"); var invokingTasks = new List <Task <RpcResponse> >(); foreach (RpcRequest request in requests) { Task <RpcResponse> invokingTask = Task.Run(async() => await this.InvokeRequestAsync(request, route, routeContext, jsonSerializerSettings)); if (request.Id != null) { //Only wait for non-notification requests invokingTasks.Add(invokingTask); } } await Task.WhenAll(invokingTasks.ToArray()); List <RpcResponse> responses = invokingTasks .Select(t => t.Result) .Where(r => r != null) .ToList(); this.logger?.LogDebug($"Finished '{requests.Count}' batch requests"); return(responses); }
/// <summary> /// Finds the matching Rpc method for the current request /// </summary> /// <param name="route">Rpc route for the current request</param> /// <param name="request">Current Rpc request</param> /// <param name="parameterList">Paramter list parsed from the request</param> /// <param name="serviceProvider">(Optional)IoC Container for rpc method controllers</param> /// <param name="jsonSerializerSettings">Json serialization settings that will be used in serialization and deserialization for rpc requests</param> /// <returns>The matching Rpc method to the current request</returns> private RpcMethod GetMatchingMethod(RpcRoute route, RpcRequest request, out object[] parameterList, IServiceProvider serviceProvider = null, JsonSerializerSettings jsonSerializerSettings = null) { if (route == null) { throw new ArgumentNullException(nameof(route)); } if (request == null) { throw new ArgumentNullException(nameof(request)); } this.logger?.LogDebug($"Attempting to match Rpc request to a method '{request.Method}'"); List <RpcMethod> methods = DefaultRpcInvoker.GetRpcMethods(route, serviceProvider, jsonSerializerSettings); //Case insenstive check for hybrid approach. Will check for case sensitive if there is ambiguity methods = methods .Where(m => string.Equals(m.Method, request.Method, StringComparison.OrdinalIgnoreCase)) .ToList(); RpcMethod rpcMethod = null; parameterList = null; int originalMethodCount = methods.Count; if (methods.Count > 0) { List <RpcMethod> potentialMatches = new List <RpcMethod>(); foreach (RpcMethod method in methods) { bool matchingMethod; if (request.ParameterMap != null) { matchingMethod = method.HasParameterSignature(request.ParameterMap, out parameterList); } else { matchingMethod = method.HasParameterSignature(request.ParameterList, out parameterList); } if (matchingMethod) { potentialMatches.Add(method); } } if (potentialMatches.Count > 1) { //Try to remove ambiguity with case sensitive check potentialMatches = potentialMatches .Where(m => string.Equals(m.Method, request.Method, StringComparison.Ordinal)) .ToList(); if (potentialMatches.Count != 1) { this.logger?.LogError("More than one method matched the rpc request. Unable to invoke due to ambiguity."); throw new RpcMethodNotFoundException(); } } if (potentialMatches.Count == 1) { rpcMethod = potentialMatches.First(); } } if (rpcMethod == null) { this.logger?.LogError("No methods matched request."); throw new RpcMethodNotFoundException(); } this.logger?.LogDebug("Request was matched to a method"); return(rpcMethod); }
/// <summary> /// Call the incoming Rpc request method and gives the appropriate response /// </summary> /// <param name="request">Rpc request</param> /// <param name="route">Rpc route that applies to the current request</param> /// <param name="httpContext">The context of the current http request</param> /// <param name="jsonSerializerSettings">Json serialization settings that will be used in serialization and deserialization for rpc requests</param> /// <returns>An Rpc response for the request</returns> public async Task <RpcResponse> InvokeRequestAsync(RpcRequest request, RpcRoute route, IRouteContext routeContext, JsonSerializerSettings jsonSerializerSettings = null) { try { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (route == null) { throw new ArgumentNullException(nameof(route)); } } catch (ArgumentNullException ex) // Dont want to throw any exceptions when doing async requests { return(this.GetUnknownExceptionReponse(request, ex)); } this.logger?.LogDebug($"Invoking request with id '{request.Id}'"); RpcResponse rpcResponse; try { if (!string.Equals(request.JsonRpcVersion, JsonRpcContants.JsonRpcVersion)) { throw new RpcInvalidRequestException($"Request must be jsonrpc version '{JsonRpcContants.JsonRpcVersion}'"); } object[] parameterList; RpcMethod rpcMethod = this.GetMatchingMethod(route, request, out parameterList, routeContext.RequestServices, jsonSerializerSettings); bool isAuthorized = await this.IsAuthorizedAsync(rpcMethod, routeContext); if (isAuthorized) { this.logger?.LogDebug($"Attempting to invoke method '{request.Method}'"); object result = await rpcMethod.InvokeAsync(parameterList); this.logger?.LogDebug($"Finished invoking method '{request.Method}'"); JsonSerializer jsonSerializer = JsonSerializer.Create(jsonSerializerSettings); if (result is IRpcMethodResult) { this.logger?.LogTrace($"Result is {nameof(IRpcMethodResult)}."); rpcResponse = ((IRpcMethodResult)result).ToRpcResponse(request.Id, obj => JToken.FromObject(obj, jsonSerializer)); } else { this.logger?.LogTrace($"Result is plain object."); JToken resultJToken = result != null?JToken.FromObject(result, jsonSerializer) : null; rpcResponse = new RpcResponse(request.Id, resultJToken); } } else { var authError = new RpcError(RpcErrorCode.InvalidRequest, "Unauthorized"); rpcResponse = new RpcResponse(request.Id, authError); } } catch (RpcException ex) { this.logger?.LogException(ex, "An Rpc error occurred. Returning an Rpc error response"); RpcError error = new RpcError(ex, this.serverConfig.Value.ShowServerExceptions); rpcResponse = new RpcResponse(request.Id, error); } catch (Exception ex) { rpcResponse = this.GetUnknownExceptionReponse(request, ex); } if (request.Id != null) { this.logger?.LogDebug($"Finished request with id '{request.Id}'"); //Only give a response if there is an id return(rpcResponse); } this.logger?.LogDebug($"Finished request with no id. Not returning a response"); return(null); }
/// <summary> /// Indicates if the incoming request matches any predefined routes /// </summary> /// <param name="requestUrl">The current request url</param> /// <param name="route">The matching route corresponding to the request url if found, otherwise it is null</param> /// <param name="routeProvider">Provider that allows the retrieval of all configured routes</param> /// <returns>True if the request url matches any Rpc routes, otherwise False</returns> public bool MatchesRpcRoute(IRpcRouteProvider routeProvider, string requestUrl, out RpcRoute route) { if (requestUrl == null) { throw new ArgumentNullException(nameof(requestUrl)); } this.logger?.LogDebug($"Attempting to match Rpc route for the request url '{requestUrl}'"); RpcPath requestPath = RpcPath.Parse(requestUrl); this.logger?.LogTrace($"Request path: {requestPath}"); foreach (RpcRoute rpcRoute in routeProvider.GetRoutes()) { RpcPath routePath = RpcPath.Parse(rpcRoute.Name); this.logger?.LogTrace($"Trying to match against route - Name: {rpcRoute.Name}, Path: {routePath}"); if (requestPath == routePath) { this.logger?.LogDebug($"Matched the request url '{requestUrl}' to the route '{rpcRoute.Name}'"); route = rpcRoute; return(true); } } this.logger?.LogDebug($"Failed to match the request url '{requestUrl}' to a route"); route = null; return(false); }
/// <summary> /// Finds the matching Rpc method for the current request /// </summary> /// <param name="route">Rpc route for the current request</param> /// <param name="request">Current Rpc request</param> /// <param name="parameterList">Paramter list parsed from the request</param> /// <param name="serviceProvider">(Optional)IoC Container for rpc method controllers</param> /// <param name="jsonSerializerSettings">Json serialization settings that will be used in serialization and deserialization for rpc requests</param> /// <returns>The matching Rpc method to the current request</returns> private RpcMethod GetMatchingMethod(RpcRoute route, RpcRequest request, out object[] parameterList, IServiceProvider serviceProvider = null, JsonSerializerSettings jsonSerializerSettings = null) { if (route == null) { throw new ArgumentNullException(nameof(route)); } if (request == null) { throw new ArgumentNullException(nameof(request)); } this.logger?.LogDebug($"Attempting to match Rpc request to a method '{request.Method}'"); List<RpcMethod> methods = DefaultRpcInvoker.GetRpcMethods(route, serviceProvider, jsonSerializerSettings); //Case insenstive check for hybrid approach. Will check for case sensitive if there is ambiguity methods = methods .Where(m => string.Equals(m.Method, request.Method, StringComparison.OrdinalIgnoreCase)) .ToList(); RpcMethod rpcMethod = null; parameterList = null; int originalMethodCount = methods.Count; if (methods.Count > 0) { List<RpcMethod> potentialMatches = new List<RpcMethod>(); foreach (RpcMethod method in methods) { bool matchingMethod; if (request.ParameterMap != null) { matchingMethod = method.HasParameterSignature(request.ParameterMap, out parameterList); } else { matchingMethod = method.HasParameterSignature(request.ParameterList); parameterList = request.ParameterList; } if (matchingMethod) { potentialMatches.Add(method); } } if (potentialMatches.Count > 1) { //Try to remove ambiguity with case sensitive check potentialMatches = potentialMatches .Where(m => string.Equals(m.Method, request.Method, StringComparison.Ordinal)) .ToList(); if (potentialMatches.Count != 1) { this.logger?.LogError("More than one method matched the rpc request. Unable to invoke due to ambiguity."); throw new RpcMethodNotFoundException(); } } if (potentialMatches.Count == 1) { rpcMethod = potentialMatches.First(); } } if (rpcMethod == null) { this.logger?.LogError("No methods matched request."); throw new RpcMethodNotFoundException(); } this.logger?.LogDebug("Request was matched to a method"); return rpcMethod; }
/// <summary> /// Call the incoming Rpc request method and gives the appropriate response /// </summary> /// <param name="request">Rpc request</param> /// <param name="route">Rpc route that applies to the current request</param> /// <param name="serviceProvider">(Optional)IoC Container for rpc method controllers</param> /// <param name="jsonSerializerSettings">Json serialization settings that will be used in serialization and deserialization for rpc requests</param> /// <returns>An Rpc response for the request</returns> public RpcResponse InvokeRequest(RpcRequest request, RpcRoute route, IServiceProvider serviceProvider = null, JsonSerializerSettings jsonSerializerSettings = null) { try { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (route == null) { throw new ArgumentNullException(nameof(route)); } } catch (ArgumentNullException ex) // Dont want to throw any exceptions when doing async requests { return(this.GetUnknownExceptionReponse(request, ex)); } this.Logger?.LogDebug($"Invoking request with id '{request.Id}'"); RpcResponse rpcResponse; try { if (!string.Equals(request.JsonRpcVersion, JsonRpcContants.JsonRpcVersion)) { throw new RpcInvalidRequestException($"Request must be jsonrpc version '{JsonRpcContants.JsonRpcVersion}'"); } object[] parameterList; RpcMethod rpcMethod = this.GetMatchingMethod(route, request, out parameterList, serviceProvider, jsonSerializerSettings); this.Logger?.LogDebug($"Attempting to invoke method '{request.Method}'"); object result = rpcMethod.Invoke(parameterList); this.Logger?.LogDebug($"Finished invoking method '{request.Method}'"); JsonSerializer jsonSerializer = JsonSerializer.Create(jsonSerializerSettings); JToken resultJToken = result != null?JToken.FromObject(result, jsonSerializer) : null; rpcResponse = new RpcResponse(request.Id, resultJToken); } catch (RpcException ex) { this.Logger?.LogException(ex, "An Rpc error occurred. Returning an Rpc error response"); RpcError error = new RpcError(ex, this.ShowServerExceptions); rpcResponse = new RpcResponse(request.Id, error); } catch (Exception ex) { rpcResponse = this.GetUnknownExceptionReponse(request, ex); } if (request.Id != null) { this.Logger?.LogDebug($"Finished request with id '{request.Id}'"); //Only give a response if there is an id return(rpcResponse); } this.Logger?.LogDebug($"Finished request with no id. Not returning a response"); return(null); }
/// <summary> /// Gets all the predefined Rpc methods for a Rpc route /// </summary> /// <param name="route">The route to get Rpc methods for</param> /// <param name="serviceProvider">(Optional) IoC Container for rpc method controllers</param> /// <param name="jsonSerializerSettings">Json serialization settings that will be used in serialization and deserialization for rpc requests</param> /// <returns>List of Rpc methods for the specified Rpc route</returns> private static List<RpcMethod> GetRpcMethods(RpcRoute route, IServiceProvider serviceProvider = null, JsonSerializerSettings jsonSerializerSettings = null) { List<RpcMethod> rpcMethods = new List<RpcMethod>(); foreach (RouteCriteria routeCriteria in route.RouteCriteria) { foreach (Type type in routeCriteria.Types) { MethodInfo[] publicMethods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance); foreach (MethodInfo publicMethod in publicMethods) { RpcMethod rpcMethod = new RpcMethod(type, route, publicMethod, serviceProvider, jsonSerializerSettings); rpcMethods.Add(rpcMethod); } } } return rpcMethods; }
/// <summary> /// Call the incoming Rpc request method and gives the appropriate response /// </summary> /// <param name="request">Rpc request</param> /// <param name="route">Rpc route that applies to the current request</param> /// <param name="httpContext">The context of the current http request</param> /// <param name="jsonSerializerSettings">Json serialization settings that will be used in serialization and deserialization for rpc requests</param> /// <returns>An Rpc response for the request</returns> public async Task<RpcResponse> InvokeRequestAsync(RpcRequest request, RpcRoute route, HttpContext httpContext, JsonSerializerSettings jsonSerializerSettings = null) { try { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (route == null) { throw new ArgumentNullException(nameof(route)); } } catch (ArgumentNullException ex) // Dont want to throw any exceptions when doing async requests { return this.GetUnknownExceptionReponse(request, ex); } this.logger?.LogDebug($"Invoking request with id '{request.Id}'"); RpcResponse rpcResponse; try { if (!string.Equals(request.JsonRpcVersion, JsonRpcContants.JsonRpcVersion)) { throw new RpcInvalidRequestException($"Request must be jsonrpc version '{JsonRpcContants.JsonRpcVersion}'"); } object[] parameterList; RpcMethod rpcMethod = this.GetMatchingMethod(route, request, out parameterList, httpContext.RequestServices, jsonSerializerSettings); bool isAuthorized = await this.IsAuthorizedAsync(rpcMethod, httpContext); if (isAuthorized) { this.logger?.LogDebug($"Attempting to invoke method '{request.Method}'"); object result = await rpcMethod.InvokeAsync(parameterList); this.logger?.LogDebug($"Finished invoking method '{request.Method}'"); JsonSerializer jsonSerializer = JsonSerializer.Create(jsonSerializerSettings); if (result is IRpcMethodResult) { this.logger?.LogTrace($"Result is {nameof(IRpcMethodResult)}."); rpcResponse = ((IRpcMethodResult)result).ToRpcResponse(request.Id, obj => JToken.FromObject(obj, jsonSerializer)); } else { this.logger?.LogTrace($"Result is plain object."); JToken resultJToken = result != null ? JToken.FromObject(result, jsonSerializer) : null; rpcResponse = new RpcResponse(request.Id, resultJToken); } } else { var authError = new RpcError(RpcErrorCode.InvalidRequest, "Unauthorized"); rpcResponse = new RpcResponse(request.Id, authError); } } catch (RpcException ex) { this.logger?.LogException(ex, "An Rpc error occurred. Returning an Rpc error response"); RpcError error = new RpcError(ex, this.serverConfig.Value.ShowServerExceptions); rpcResponse = new RpcResponse(request.Id, error); } catch (Exception ex) { rpcResponse = this.GetUnknownExceptionReponse(request, ex); } if (request.Id != null) { this.logger?.LogDebug($"Finished request with id '{request.Id}'"); //Only give a response if there is an id return rpcResponse; } this.logger?.LogDebug($"Finished request with no id. Not returning a response"); return null; }
/// <summary> /// Finds the matching Rpc method for the current request /// </summary> /// <param name="route">Rpc route for the current request</param> /// <param name="request">Current Rpc request</param> /// <param name="parameterList">Paramter list parsed from the request</param> /// <param name="serviceProvider">(Optional)IoC Container for rpc method controllers</param> /// <param name="jsonSerializerSettings">Json serialization settings that will be used in serialization and deserialization for rpc requests</param> /// <returns>The matching Rpc method to the current request</returns> private RpcMethod GetMatchingMethod(RpcRoute route, RpcRequest request, out object[] parameterList, IServiceProvider serviceProvider = null, JsonSerializerSettings jsonSerializerSettings = null) { if (route == null) { throw new ArgumentNullException(nameof(route)); } if (request == null) { throw new ArgumentNullException(nameof(request)); } this.Logger?.LogDebug($"Attempting to match Rpc request to a method '{request.Method}'"); List <RpcMethod> methods = DefaultRpcInvoker.GetRpcMethods(route, serviceProvider, jsonSerializerSettings); methods = methods .Where(m => string.Equals(m.Method, request.Method, StringComparison.OrdinalIgnoreCase)) .ToList(); RpcMethod rpcMethod = null; parameterList = null; if (methods.Count > 1) { foreach (RpcMethod method in methods) { bool matchingMethod; if (request.ParameterMap != null) { matchingMethod = method.HasParameterSignature(request.ParameterMap, out parameterList); } else { matchingMethod = method.HasParameterSignature(request.ParameterList); parameterList = request.ParameterList; } if (matchingMethod) { if (rpcMethod != null) //If already found a match { throw new RpcAmbiguousMethodException(); } rpcMethod = method; } } } else if (methods.Count == 1) { //Only signature check for methods that have the same name for performance reasons rpcMethod = methods.First(); if (request.ParameterMap != null) { bool signatureMatch = rpcMethod.TryParseParameterList(request.ParameterMap, out parameterList); if (!signatureMatch) { throw new RpcMethodNotFoundException(); } } else { parameterList = request.ParameterList; } } if (rpcMethod == null) { throw new RpcMethodNotFoundException(); } this.Logger?.LogDebug("Request was matched to a method"); return(rpcMethod); }