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);
}
}
}
}