Пример #1
0
 /// <param name="serverConfig">Configuration data for the server</param>
 /// <param name="invoker">Component that invokes Rpc requests target methods and returns a response</param>
 /// <param name="parser">Component that parses Http requests into Rpc requests</param>
 /// <param name="compressor">Component that compresses Rpc responses</param>
 /// <param name="logger">Component that logs actions from the router</param>
 /// <param name="routeProvider">Provider that allows the retrieval of all configured routes</param>
 public RpcRouter(IOptions <RpcServerConfiguration> serverConfig, IRpcInvoker invoker, IRpcParser parser, IRpcCompressor compressor, ILogger <RpcRouter> logger,
                  IRpcRouteProvider routeProvider)
 {
     if (serverConfig == null)
     {
         throw new ArgumentNullException(nameof(serverConfig));
     }
     if (invoker == null)
     {
         throw new ArgumentNullException(nameof(invoker));
     }
     if (parser == null)
     {
         throw new ArgumentNullException(nameof(parser));
     }
     if (compressor == null)
     {
         throw new ArgumentNullException(nameof(compressor));
     }
     if (routeProvider == null)
     {
         throw new ArgumentNullException(nameof(routeProvider));
     }
     this.serverConfig  = serverConfig;
     this.invoker       = invoker;
     this.parser        = parser;
     this.compressor    = compressor;
     this.logger        = logger;
     this.routeProvider = routeProvider;
 }
Пример #2
0
        /// <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);
        }
Пример #3
0
		/// <param name="serverConfig">Configuration data for the server</param>
		/// <param name="invoker">Component that invokes Rpc requests target methods and returns a response</param>
		/// <param name="parser">Component that parses Http requests into Rpc requests</param>
		/// <param name="compressor">Component that compresses Rpc responses</param>
		/// <param name="logger">Component that logs actions from the router</param>
		/// <param name="routeProvider">Provider that allows the retrieval of all configured routes</param>
		public RpcRouter(IOptions<RpcServerConfiguration> serverConfig, IRpcInvoker invoker, IRpcParser parser, IRpcCompressor compressor, ILogger<RpcRouter> logger,
			IRpcRouteProvider routeProvider)
		{
			if (serverConfig == null)
			{
				throw new ArgumentNullException(nameof(serverConfig));
			}
			if (invoker == null)
			{
				throw new ArgumentNullException(nameof(invoker));
			}
			if (parser == null)
			{
				throw new ArgumentNullException(nameof(parser));
			}
			if (compressor == null)
			{
				throw new ArgumentNullException(nameof(compressor));
			}
			if (routeProvider == null)
			{
				throw new ArgumentNullException(nameof(routeProvider));
			}
			this.serverConfig = serverConfig;
			this.invoker = invoker;
			this.parser = parser;
			this.compressor = compressor;
			this.logger = logger;
			this.routeProvider = routeProvider;
		}
Пример #4
0
        /// <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;
        }
Пример #5
0
 /// <param name="compressor">Component that compresses Rpc responses</param>
 /// <param name="logger">Component that logs actions from the router</param>
 /// <param name="routeProvider">Provider that allows the retrieval of all configured routes</param>
 public RpcRouter(ILogger <RpcRouter> logger, IRpcCompressor compressor, IRpcRouteProvider routeProvider, IRpcRequestHandler routeHandler)
 {
     this.logger        = logger;
     this.compressor    = compressor ?? throw new ArgumentNullException(nameof(compressor));
     this.routeProvider = routeProvider ?? throw new ArgumentNullException(nameof(routeProvider));
     this.routeHandler  = routeHandler ?? throw new ArgumentNullException(nameof(routeHandler));
 }
Пример #6
0
        /// <summary>
        /// Gets all the predefined Rpc methods for a Rpc route
        /// </summary>
        /// <param name="path">The route to get Rpc methods for</param>
        /// <param name="serviceProvider">(Optional) IoC Container for rpc method controllers</param>
        /// <returns>List of Rpc methods for the specified Rpc route</returns>
        private List <MethodInfo> GetRpcMethods(RpcPath path, IRpcRouteProvider routeProvider)
        {
            var methods = new List <MethodInfo>();

            foreach (IRpcMethodProvider methodProvider in routeProvider.GetMethodsByPath(path))
            {
                foreach (MethodInfo methodInfo in methodProvider.GetRouteMethods())
                {
                    methods.Add(methodInfo);
                }
            }
            return(methods);
        }
Пример #7
0
        /// <summary>
        /// Extension method to use the JsonRpc router in the Asp.Net pipeline
        /// </summary>
        /// <param name="app"><see cref="IApplicationBuilder"/> that is supplied by Asp.Net</param>
        /// <param name="routeProvider">Action to configure route provider</param>
        /// <returns><see cref="IApplicationBuilder"/> that includes the Basic auth middleware</returns>
        public static IApplicationBuilder UseJsonRpc(this IApplicationBuilder app, IRpcRouteProvider routeProvider)
        {
            if (app == null)
            {
                throw new ArgumentNullException(nameof(app));
            }
            if(routeProvider == null)
            {
                throw new ArgumentNullException(nameof(routeProvider));
            }

            RpcRouter router = ActivatorUtilities.CreateInstance<RpcRouter>(app.ApplicationServices, routeProvider);
            return app.UseRouter(router);
        }
Пример #8
0
        /// <summary>
        /// Extension method to use the JsonRpc router in the Asp.Net pipeline
        /// </summary>
        /// <param name="app"><see cref="IApplicationBuilder"/> that is supplied by Asp.Net</param>
        /// <param name="routeProvider">Action to configure route provider</param>
        /// <returns><see cref="IApplicationBuilder"/> that includes the Basic auth middleware</returns>
        public static IApplicationBuilder UseJsonRpc(this IApplicationBuilder app, IRpcRouteProvider routeProvider)
        {
            if (app == null)
            {
                throw new ArgumentNullException(nameof(app));
            }
            if (routeProvider == null)
            {
                throw new ArgumentNullException(nameof(routeProvider));
            }

            RpcRouter router = ActivatorUtilities.CreateInstance <RpcRouter>(app.ApplicationServices, routeProvider);

            return(app.UseRouter(router));
        }
Пример #9
0
        /// <summary>
        /// Extension method to use the JsonRpc router in the Asp.Net pipeline
        /// </summary>
        /// <param name="app"><see cref="IApplicationBuilder"/> that is supplied by Asp.Net</param>
        /// <param name="options">Auto routing configuration</param>
        /// <returns><see cref="IApplicationBuilder"/> that includes the Basic auth middleware</returns>
        public static IApplicationBuilder UseJsonRpc(this IApplicationBuilder app, IRpcRouteProvider routeProvider)
        {
            if (app == null)
            {
                throw new ArgumentNullException(nameof(app));
            }
            if (routeProvider == null)
            {
                throw new ArgumentNullException(nameof(routeProvider));
            }
            if (app.ApplicationServices.GetService <RpcServicesMarker>() == null)
            {
                throw new InvalidOperationException("AddJsonRpc() needs to be called in the ConfigureServices method.");
            }
            var router = new RpcHttpRouter(routeProvider);

            return(app.UseRouter(router));
        }
Пример #10
0
 public static void RegisterTypeRoute <T>(this IRpcRouteProvider routeProvider, string name = null)
 {
     routeProvider.RegisterRoute(new List <RouteCriteria> {
         new RouteCriteria(typeof(T))
     }, name);
 }
Пример #11
0
 public static void RegisterRoute(this IRpcRouteProvider routeProvider, RouteCriteria criteria, string name = null)
 {
     routeProvider.RegisterRoute(new List <RouteCriteria> {
         criteria
     }, name);
 }
Пример #12
0
        /// <summary>
        /// Finds the matching Rpc method for the current request
        /// </summary>
        /// <param name="path">Rpc route for the current request</param>
        /// <param name="request">Current Rpc request</param>
        /// <param name="parameterList">Parameter list parsed from the request</param>
        /// <param name="serviceProvider">(Optional)IoC Container for rpc method controllers</param>
        /// <returns>The matching Rpc method to the current request</returns>
        private RpcMethodInfo GetMatchingMethod(RpcPath path, RpcRequest request, IRpcRouteProvider routeProvider, IServiceProvider serviceProvider)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }
            this.logger?.LogDebug($"Attempting to match Rpc request to a method '{request.Method}'");
            List <MethodInfo> allMethods = this.GetRpcMethods(path, routeProvider);

            //Case insenstive check for hybrid approach. Will check for case sensitive if there is ambiguity
            var requestMethodName = this.convertSnakeCaseToCamelCase ? this.ConvertSnakeCaseToCamelCase(request.Method) : request.Method;
            List <MethodInfo> methodsWithSameName = allMethods
                                                    .Where(m => string.Equals(m.Name, requestMethodName, StringComparison.OrdinalIgnoreCase))
                                                    .ToList();

            var potentialMatches = new List <RpcMethodInfo>();

            foreach (MethodInfo method in methodsWithSameName)
            {
                (bool isMatch, RpcMethodInfo methodInfo) = this.HasParameterSignature(method, request);
                if (isMatch)
                {
                    potentialMatches.Add(methodInfo);
                }
            }

            if (potentialMatches.Count > 1)
            {
                //Try to remove ambiguity with 'perfect matching' (usually optional params and types)
                List <RpcMethodInfo> exactMatches = potentialMatches
                                                    .Where(p => p.HasExactParameterMatch())
                                                    .ToList();
                if (exactMatches.Any())
                {
                    potentialMatches = exactMatches;
                }
                if (potentialMatches.Count > 1)
                {
                    //Try to remove ambiguity with case sensitive check
                    potentialMatches = potentialMatches
                                       .Where(m => string.Equals(m.Method.Name, requestMethodName, 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();
                    }
                }
            }

            RpcMethodInfo rpcMethod = null;

            if (potentialMatches.Count == 1)
            {
                rpcMethod = potentialMatches.First();
            }

            if (rpcMethod == null)
            {
                //Log diagnostics
                string methodsString = string.Join(", ", allMethods.Select(m => m.Name));
                this.logger?.LogTrace("Methods in route: " + methodsString);

                var methodInfoList = new List <string>();
                foreach (MethodInfo matchedMethod in methodsWithSameName)
                {
                    var parameterTypeList = new List <string>();
                    foreach (ParameterInfo parameterInfo in matchedMethod.GetParameters())
                    {
                        string parameterType = parameterInfo.Name + ": " + parameterInfo.ParameterType.Name;
                        if (parameterInfo.IsOptional)
                        {
                            parameterType += "(Optional)";
                        }
                        parameterTypeList.Add(parameterType);
                    }
                    string parameterString = string.Join(", ", parameterTypeList);
                    methodInfoList.Add($"{{Name: '{matchedMethod.Name}', Parameters: [{parameterString}]}}");
                }
                this.logger?.LogTrace("Methods that matched the same name: " + string.Join(", ", methodInfoList));
                this.logger?.LogError("No methods matched request.");
                throw new RpcMethodNotFoundException();
            }
            this.logger?.LogDebug("Request was matched to a method");
            return(rpcMethod);
        }
Пример #13
0
 /// <param name="compressor">Component that compresses Rpc responses</param>
 /// <param name="logger">Component that logs actions from the router</param>
 /// <param name="routeProvider">Provider that allows the retrieval of all configured routes</param>
 public RpcRouter(IRpcRouteProvider routeProvider)
 {
     this.routeProvider = routeProvider ?? throw new ArgumentNullException(nameof(routeProvider));
 }
Пример #14
0
 public static IRouteContext FromHttpContext(HttpContext httpContext, IRpcRouteProvider routeProvider)
 {
     return(new DefaultRouteContext(httpContext.RequestServices, httpContext.User, routeProvider));
 }
Пример #15
0
 public DefaultRouteContext(IServiceProvider serviceProvider, ClaimsPrincipal user, IRpcRouteProvider routeProvider)
 {
     this.RequestServices = serviceProvider;
     this.User            = user;
     this.RouteProvider   = routeProvider;
 }
        public static async Task HandleJsonRpcWebSocketRequest(this WebSocket webSocket, HttpContext context, IRpcRequestHandler rpcRequestHandler, IRpcRouteProvider rpcRouteProvider)
        {
            byte[]        buffer       = new byte[1024 * 4];
            IRouteContext routeContext = DefaultRouteContext.FromHttpContext(context, rpcRouteProvider);

            while (webSocket.State == WebSocketState.Open)
            {
                WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment <byte>(buffer), CancellationToken.None);

                if (result.MessageType != WebSocketMessageType.Text)
                {
                    continue;
                }
                string requestBody = Encoding.ASCII.GetString(buffer);
                string response    = await rpcRequestHandler.HandleRequestAsync(RpcPath.Parse(context.Request.Path), requestBody, routeContext);

                await webSocket.SendAsync(
                    new ArraySegment <byte>(Encoding.ASCII.GetBytes(response), 0, response.Length),
                    WebSocketMessageType.Text,
                    true,
                    CancellationToken.None
                    );
            }
        }