Пример #1
0
        /// <summary>
        /// Takes a route/http contexts and attempts to parse, invoke, respond to an Rpc request
        /// </summary>
        /// <param name="context">Route context</param>
        /// <returns>Task for async routing</returns>
        public async Task RouteAsync(RouteContext context)
        {
            ILogger <RpcHttpRouter> logger = context.HttpContext.RequestServices.GetService <ILogger <RpcHttpRouter> >();

            try
            {
                RpcPath?requestPath;
                if (!context.HttpContext.Request.Path.HasValue)
                {
                    requestPath = null;
                }
                else
                {
                    if (!RpcPath.TryParse(context.HttpContext.Request.Path.Value.AsSpan(), out requestPath))
                    {
                        logger?.LogInformation($"Could not parse the path '{context.HttpContext.Request.Path.Value}' for the " +
                                               $"request into an rpc path. Skipping rpc router middleware.");
                        return;
                    }
                }
                logger?.LogInformation($"Rpc request with route '{requestPath}' started.");


                IRpcRequestHandler requestHandler = context.HttpContext.RequestServices.GetRequiredService <IRpcRequestHandler>();
                var routeContext = DefaultRpcContext.Build(context.HttpContext.RequestServices, requestPath);
                context.HttpContext.RequestServices.GetRequiredService <IRpcContextAccessor>().Value = routeContext;
                Stream writableStream = this.BuildWritableResponseStream(context.HttpContext);
                using (var requestBody = new MemoryStream())
                {
                    await context.HttpContext.Request.Body.CopyToAsync(requestBody);

                    requestBody.Position = 0;
                    bool hasResponse = await requestHandler.HandleRequestAsync(requestBody, writableStream);

                    if (!hasResponse)
                    {
                        //No response required, but status code must be 204
                        context.HttpContext.Response.StatusCode = 204;
                        context.MarkAsHandled();
                        return;
                    }
                }


                context.MarkAsHandled();

                logger?.LogInformation("Rpc request complete");
            }
            catch (Exception ex)
            {
                string errorMessage = "Unknown exception occurred when trying to process Rpc request. Marking route unhandled";
                logger?.LogException(ex, errorMessage);
                context.MarkAsHandled();
            }
        }
Пример #2
0
        public async Task <string> HandleRequestAsync(RpcPath requestPath, string requestBody, IRouteContext routeContext)
        {
            try
            {
                List <RpcRequest> requests = this.parser.ParseRequests(requestBody, out bool isBulkRequest, this.serverConfig.Value.JsonSerializerSettings);
                this.logger?.LogInformation($"Processing {requests.Count} Rpc requests");

                int batchLimit = this.serverConfig.Value.BatchRequestLimit;
                List <RpcResponse> responses;
                if (batchLimit > 0 && requests.Count > batchLimit)
                {
                    string batchLimitError = $"Request count exceeded batch request limit ({batchLimit}).";
                    responses = new List <RpcResponse>
                    {
                        new RpcResponse(null, new RpcError(RpcErrorCode.InvalidRequest, batchLimitError))
                    };
                    this.logger?.LogError(batchLimitError + " Returning error response.");
                }
                else
                {
                    responses = await this.invoker.InvokeBatchRequestAsync(requests, requestPath, routeContext);
                }
                if (responses == null || !responses.Any())
                {
                    this.logger?.LogInformation("No rpc responses created.");
                    return(null);
                }

                string resultJson = !isBulkRequest
                                        ? JsonConvert.SerializeObject(responses.Single(), this.serverConfig.Value.JsonSerializerSettings)
                                        : JsonConvert.SerializeObject(responses, this.serverConfig.Value.JsonSerializerSettings);


                this.logger?.LogInformation($"{responses.Count} rpc response(s) created.");
                return(resultJson);
            }
            catch (RpcException ex)
            {
                this.logger?.LogException(ex, "Error occurred when proccessing Rpc request. Sending Rpc error response");
                var response = new RpcResponse(null, new RpcError(ex, this.serverConfig.Value.ShowServerExceptions));
                return(JsonConvert.SerializeObject(response, this.serverConfig.Value.JsonSerializerSettings));
            }
        }
Пример #3
0
        /// <summary>
        /// Takes a route/http contexts and attempts to parse, invoke, respond to an Rpc request
        /// </summary>
        /// <param name="context">Route context</param>
        /// <returns>Task for async routing</returns>
        public async Task RouteAsync(RouteContext context)
        {
            if (!context.HttpContext.WebSockets.IsWebSocketRequest)
            {
                return;
            }
            ILogger <RpcWebSocketRouter> logger = context.HttpContext.RequestServices.GetService <ILogger <RpcWebSocketRouter> >();

            try
            {
                RpcPath requestPath;
                if (!context.HttpContext.Request.Path.HasValue)
                {
                    requestPath = RpcPath.Default;
                }
                else
                {
                    if (!RpcPath.TryParse(context.HttpContext.Request.Path.Value, out requestPath))
                    {
                        logger?.LogInformation($"Could not parse the path '{context.HttpContext.Request.Path.Value}' for the " +
                                               $"request into an rpc path. Skipping rpc router middleware.");
                        return;
                    }
                }
                if (!requestPath.TryRemoveBasePath(this.routeProvider.BaseRequestPath, out requestPath))
                {
                    logger?.LogTrace("Request did not match the base request path. Skipping rpc router.");
                    return;
                }
                logger?.LogInformation($"Rpc web socket request is attempting to open.");

                var scopeFactory = context.HttpContext.RequestServices.GetRequiredService <IServiceScopeFactory>();
                using (WebSocket socket = await context.HttpContext.WebSockets.AcceptWebSocketAsync())
                {
                    while (socket.State == WebSocketState.Open)
                    {
                        var buffer = new ArraySegment <byte>(new byte[1_000_000]);
Пример #4
0
        public RpcEndpointBuilder AddController(Type controllerType)
        {
            var attribute = controllerType.GetCustomAttribute <RpcRouteAttribute>(true);
            ReadOnlySpan <char> routePathString;

            if (attribute == null || attribute.RouteName == null)
            {
                if (controllerType.Name.EndsWith("Controller"))
                {
                    routePathString = controllerType.Name.AsSpan(0, controllerType.Name.IndexOf("Controller"));
                }
                else
                {
                    routePathString = controllerType.Name.AsSpan();
                }
            }
            else
            {
                routePathString = attribute.RouteName.AsSpan();
            }
            RpcPath routePath = RpcPath.Parse(routePathString);

            return(this.AddControllerWithCustomPath(controllerType, routePath));
        }
Пример #5
0
 internal RpcRouteInfo(RpcMethodInfo methodInfo, RpcPath path)
 {
     this.MethodInfo = methodInfo;
     this.Path       = path;
 }
Пример #6
0
 internal RpcRouteInfo(RpcMethodInfo methodInfo, RpcPath path, IServiceProvider requestServices)
 {
     this.MethodInfo      = methodInfo;
     this.Path            = path;
     this.RequestServices = requestServices;
 }
Пример #7
0
        /// <summary>
        /// Takes a route/http contexts and attempts to parse, invoke, respond to an Rpc request
        /// </summary>
        /// <param name="context">Route context</param>
        /// <returns>Task for async routing</returns>
        public async Task RouteAsync(RouteContext context)
        {
            ILogger <RpcRouter> logger = context.HttpContext.RequestServices.GetService <ILogger <RpcRouter> >();

            try
            {
                RpcPath requestPath;
                if (!context.HttpContext.Request.Path.HasValue)
                {
                    requestPath = RpcPath.Default;
                }
                else
                {
                    if (!RpcPath.TryParse(context.HttpContext.Request.Path.Value, out requestPath))
                    {
                        logger?.LogInformation($"Could not parse the path '{context.HttpContext.Request.Path.Value}' for the " +
                                               $"request into an rpc path. Skipping rpc router middleware.");
                        return;
                    }
                }
                if (!requestPath.TryRemoveBasePath(this.routeProvider.BaseRequestPath, out requestPath))
                {
                    logger?.LogTrace("Request did not match the base request path. Skipping rpc router.");
                    return;
                }
                HashSet <RpcPath> availableRoutes = this.routeProvider.GetRoutes();
                if (!availableRoutes.Any())
                {
                    logger?.LogDebug($"Request matched base request path but no routes.");
                    return;
                }
                logger?.LogInformation($"Rpc request route '{requestPath}' matched.");

                Stream contentStream = context.HttpContext.Request.Body;

                string jsonString;
                if (contentStream == null)
                {
                    jsonString = null;
                }
                else
                {
                    StreamReader streamReader = new StreamReader(contentStream);
                    jsonString = streamReader.ReadToEnd().Trim();
                }

                var    requestHandler = context.HttpContext.RequestServices.GetRequiredService <IRpcRequestHandler>();
                var    routeContext   = DefaultRouteContext.FromHttpContext(context.HttpContext, this.routeProvider);
                string responseJson   = await requestHandler.HandleRequestAsync(requestPath, jsonString, routeContext);

                if (responseJson == null)
                {
                    //No response required
                    return;
                }

                context.HttpContext.Response.ContentType = "application/json";

                bool   responseSet    = false;
                string acceptEncoding = context.HttpContext.Request.Headers["Accept-Encoding"];
                if (!string.IsNullOrWhiteSpace(acceptEncoding))
                {
                    IRpcCompressor compressor = context.HttpContext.RequestServices.GetService <IRpcCompressor>();
                    if (compressor != null)
                    {
                        string[] encodings = acceptEncoding.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
                        foreach (string encoding in encodings)
                        {
                            bool haveType = Enum.TryParse(encoding, true, out CompressionType compressionType);
                            if (!haveType)
                            {
                                continue;
                            }
                            context.HttpContext.Response.Headers.Add("Content-Encoding", new[] { encoding });
                            compressor.CompressText(context.HttpContext.Response.Body, responseJson, Encoding.UTF8, compressionType);
                            responseSet = true;
                            break;
                        }
                    }
                }
                if (!responseSet)
                {
                    await context.HttpContext.Response.WriteAsync(responseJson);
                }

                context.MarkAsHandled();

                logger?.LogInformation("Rpc request complete");
            }
            catch (Exception ex)
            {
                string errorMessage = "Unknown exception occurred when trying to process Rpc request. Marking route unhandled";
                logger?.LogException(ex, errorMessage);
                context.MarkAsHandled();
            }
        }
Пример #8
0
        public async Task <string> HandleRequestAsync(RpcPath requestPath, string requestBody, IRouteContext routeContext)
        {
            try
            {
                ParsingResult result = this.parser.ParseRequests(requestBody);
                this.logger?.LogInformation($"Processing {result.RequestCount} Rpc requests");

                int?batchLimit = this.serverConfig.Value.BatchRequestLimit;
                List <RpcResponse> responses = new List <RpcResponse>();
                if (batchLimit > 0 && result.RequestCount > batchLimit)
                {
                    string batchLimitError = $"Request count exceeded batch request limit ({batchLimit}).";
                    responses = new List <RpcResponse>
                    {
                        new RpcResponse(null, new RpcError(RpcErrorCode.InvalidRequest, batchLimitError))
                    };
                    this.logger?.LogError(batchLimitError + " Returning error response.");
                }
                else
                {
                    if (result.Requests.Any())
                    {
                        responses = await this.invoker.InvokeBatchRequestAsync(result.Requests, requestPath, routeContext);
                    }
                    else
                    {
                        responses = new List <RpcResponse>();
                    }
                    foreach ((RpcId id, RpcError error) in result.Errors)
                    {
                        if (id == default)
                        {
                            this.logger.LogError($"Request with no id failed and no response will be sent. Error - Code: {error.Code}, Message: {error.Message}");
                            continue;
                        }
                        responses.Add(new RpcResponse(id, error));
                    }
                }
                if (responses == null || !responses.Any())
                {
                    this.logger?.LogInformation("No rpc responses created.");
                    return(null);
                }
                this.logger?.LogInformation($"{responses.Count} rpc response(s) created.");

                if (result.IsBulkRequest)
                {
                    return(this.responseSerializer.SerializeBulk(responses));
                }
                else
                {
                    return(this.responseSerializer.Serialize(responses.Single()));
                }
            }
            catch (RpcException ex)
            {
                this.logger?.LogException(ex, "Error occurred when proccessing Rpc request. Sending Rpc error response");
                var response = new RpcResponse(null, ex.ToRpcError(this.serverConfig.Value.ShowServerExceptions));
                return(this.responseSerializer.Serialize(response));
            }
        }
Пример #9
0
        /// <summary>
        /// Takes a route/http contexts and attempts to parse, invoke, respond to an Rpc request
        /// </summary>
        /// <param name="context">Route context</param>
        /// <returns>Task for async routing</returns>
        public async Task RouteAsync(RouteContext context)
        {
            ILogger <RpcHttpRouter> logger = context.HttpContext.RequestServices.GetService <ILogger <RpcHttpRouter> >();

            try
            {
                RpcPath requestPath;
                if (!context.HttpContext.Request.Path.HasValue)
                {
                    requestPath = RpcPath.Default;
                }
                else
                {
                    if (!RpcPath.TryParse(context.HttpContext.Request.Path.Value, out requestPath))
                    {
                        logger?.LogInformation($"Could not parse the path '{context.HttpContext.Request.Path.Value}' for the " +
                                               $"request into an rpc path. Skipping rpc router middleware.");
                        return;
                    }
                }
                if (!requestPath.TryRemoveBasePath(this.routeProvider.BaseRequestPath, out requestPath))
                {
                    logger?.LogTrace("Request did not match the base request path. Skipping rpc router.");
                    return;
                }
                logger?.LogInformation($"Rpc request with route '{requestPath}' started.");

                string jsonString;
                if (context.HttpContext.Request.Body == null)
                {
                    jsonString = null;
                }
                else
                {
                    using (StreamReader streamReader = new StreamReader(context.HttpContext.Request.Body, Encoding.UTF8,
                                                                        detectEncodingFromByteOrderMarks: true,
                                                                        bufferSize: 1024,
                                                                        leaveOpen: true))
                    {
                        try
                        {
                            jsonString = await streamReader.ReadToEndAsync();
                        }
                        catch (TaskCanceledException ex)
                        {
                            throw new RpcCanceledRequestException("Cancelled while reading the request.", ex);
                        }
                        jsonString = jsonString.Trim();
                    }
                }

                IRpcRequestHandler requestHandler = context.HttpContext.RequestServices.GetRequiredService <IRpcRequestHandler>();
                var    routeContext = DefaultRouteContext.FromHttpContext(context.HttpContext, this.routeProvider);
                string responseJson = await requestHandler.HandleRequestAsync(requestPath, jsonString, routeContext);

                if (responseJson == null)
                {
                    //No response required, but status code must be 204
                    context.HttpContext.Response.StatusCode = 204;
                    context.MarkAsHandled();
                    return;
                }

                context.HttpContext.Response.ContentType = "application/json";

                bool   responseSet    = false;
                string acceptEncoding = context.HttpContext.Request.Headers["Accept-Encoding"];
                if (!string.IsNullOrWhiteSpace(acceptEncoding))
                {
                    IStreamCompressor compressor = context.HttpContext.RequestServices.GetService <IStreamCompressor>();
                    if (compressor != null)
                    {
                        string[] encodings = acceptEncoding.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
                        foreach (string encoding in encodings)
                        {
                            bool haveType = Enum.TryParse(encoding, true, out CompressionType compressionType);
                            if (!haveType)
                            {
                                continue;
                            }
                            context.HttpContext.Response.Headers.Add("Content-Encoding", new[] { encoding });
                            using (Stream responseStream = new MemoryStream(Encoding.UTF8.GetBytes(responseJson)))
                            {
                                compressor.Compress(responseStream, context.HttpContext.Response.Body, compressionType);
                            }
                            responseSet = true;
                            break;
                        }
                    }
                }
                if (!responseSet)
                {
                    await context.HttpContext.Response.WriteAsync(responseJson);
                }

                context.MarkAsHandled();

                logger?.LogInformation("Rpc request complete");
            }
            catch (Exception ex)
            {
                string errorMessage = "Unknown exception occurred when trying to process Rpc request. Marking route unhandled";
                logger?.LogException(ex, errorMessage);
                context.MarkAsHandled();
            }
        }