/// <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(); } }
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)); } }
/// <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]);
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)); }
internal RpcRouteInfo(RpcMethodInfo methodInfo, RpcPath path) { this.MethodInfo = methodInfo; this.Path = path; }
internal RpcRouteInfo(RpcMethodInfo methodInfo, RpcPath path, IServiceProvider requestServices) { this.MethodInfo = methodInfo; this.Path = path; this.RequestServices = requestServices; }
/// <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(); } }
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)); } }
/// <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(); } }