using Edjcase.JsonRpc.Router; using EdjCase.JsonRpc.Core; using EdjCase.JsonRpc.Router.Abstractions; using EdjCase.JsonRpc.Router.Defaults; using EdjCase.JsonRpc.Router.Utilities; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EdjCase.JsonRpc.Router { public class RpcRequestHandler : IRpcRequestHandler { /// /// Configuration data for the server /// private IOptions serverConfig { get; } /// /// Component that invokes Rpc requests target methods and returns a response /// private IRpcInvoker invoker { get; } /// /// Component that parses Http requests into Rpc requests /// private IRpcParser parser { get; } /// /// Component that logs actions from the handler /// private ILogger logger { get; } /// /// Component that serializes all the responses to json /// private IRpcResponseSerializer responseSerializer { get; } /// Configuration data for the server /// Component that invokes Rpc requests target methods and returns a response /// Component that parses Http requests into Rpc requests /// Component that serializes all the responses to json /// Component that logs actions from the router public RpcRequestHandler(IOptions serverConfig, IRpcInvoker invoker, IRpcParser parser, IRpcResponseSerializer responseSerializer, ILogger logger) { this.serverConfig = serverConfig ?? throw new ArgumentNullException(nameof(serverConfig)); this.invoker = invoker ?? throw new ArgumentNullException(nameof(invoker)); this.parser = parser ?? throw new ArgumentNullException(nameof(parser)); this.responseSerializer = responseSerializer ?? throw new ArgumentNullException(nameof(responseSerializer)); this.logger = logger; } public async Task 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 responses = new List(); if (batchLimit > 0 && result.RequestCount > batchLimit) { string batchLimitError = $"Request count exceeded batch request limit ({batchLimit})."; responses = new List { 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(); } 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); } } } }