using System; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; using System.Threading.Tasks; using System.Collections.Generic; namespace __NAMESPACE__; public class JRpcServer { public IDictionary> Services; public JRpcServer() { this.Services = new Dictionary>(); } public void AddService(JRpcService service) { this.Services.Add(service.Name, service); } public JRpcServerSession GetSession(JRpcTransport transport, TContext context) { return new JRpcServerSession(this, transport, context); } } public class JRpcServerSession { public JRpcServer Server { get; private set; } public JRpcTransport Transport { get; private set; } public TContext Context { get; private set; } public JRpcServerSession(JRpcServer server, JRpcTransport transport, TContext context) { this.Server = server; this.Transport = transport; this.Context = context; this.Transport.OnPacket += this.HandlePacket; } private void HandlePacket(string packet) { try { var parsed = JsonNode.Parse(packet); if (parsed == null || (string?)parsed["jsonrpc"] != "2.0") return; if (parsed["method"] != null) // Request or Notification { var id = (string?)parsed["id"]; var splitted = ((string)parsed["method"]!).Split(".", 2); var serviceName = splitted[0]; var functionName = splitted[1]; if (serviceName == null || functionName == null) { if (id != null) { var response = new JsonObject { ["jsonrpc"] = "2.0", ["id"] = id, ["error"] = new JsonObject { ["code"] = -32700, ["message"] = "Method not found!", } }; _ = this.Transport.Write(response.ToJsonString()!); } return; } var service = this.Server.Services[serviceName]; if (service == null) { if (id != null) { var response = new JsonObject { ["jsonrpc"] = "2.0", ["id"] = id, ["error"] = new JsonObject { ["code"] = -32700, ["message"] = "Method not found!", } }; _ = this.Transport.Write(response.ToJsonString()!); } return; } if (!service.Functions.Contains(functionName)) { if (id != null) { var response = new JsonObject { ["jsonrpc"] = "2.0", ["id"] = id, ["error"] = new JsonObject { ["code"] = -32700, ["message"] = "Method not found!", } }; _ = this.Transport.Write(response.ToJsonString()!); } return; } if (!(parsed["params"] is JsonArray || parsed["params"] is JsonObject)) { if (id != null) { var response = new JsonObject { ["jsonrpc"] = "2.0", ["id"] = id, ["error"] = new JsonObject { ["code"] = -32602, ["message"] = "Invalid Parameters!", } }; _ = this.Transport.Write(response.ToJsonString()!); } return; } _ = service.HandleRequest(functionName, parsed["params"]!, this.Context).ContinueWith(result => { if (id != null) { var response = new JsonObject { ["jsonrpc"] = "2.0", ["id"] = id, ["error"] = result.IsFaulted ? new JsonObject { ["code"] = -32603, ["message"] = result.Exception!.InnerException!.Message } : null, ["result"] = !result.IsFaulted ? result.Result : null, }; _ = this.Transport.Write(response.ToJsonString()!); } }); //TODO: implement Notifications } else { // Ignoring everyting else. Don't care! return; } } catch (Exception) { //TODO: Maybe log exception, but don't break! } } public async void SendNotification(string method, JsonArray param) { var not = new JsonObject { ["jsonrpc"] = "2.0", ["method"] = method, ["params"] = param, }; await this.Transport.Write(not.ToJsonString()); } } public abstract class JRpcService { public abstract string Name { get; } public HashSet Functions = new HashSet(); protected void RegisterFunction(string name) { this.Functions.Add(name); } public abstract Task HandleRequest(string function, JsonNode param, TContext context); // public abstract Task HandleNotification(string notification, JsonNode param); }