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 JRpcClient { private JRpcTransport Transport; private IDictionary> Requests; private int? Timeout = null; public JRpcClient(JRpcTransport transport, int? timeout = null) { this.Transport = transport; this.Timeout = timeout; this.Requests = new Dictionary>(); 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 if (parsed["id"] != null) // Requests are not supported on the Client return; //TODO: implement Notifications } else if (parsed["id"] != null && parsed["method"] == null) { // Response //TODO: Somehow remove this warning, since it has no meaning in this context. // ID has to be something, that was checked before var id = (string)parsed["id"]!; var task = this.Requests[id]; if (task == null) return; //This Request was not from here if (parsed["error"] != null) { var err = parsed["error"].Deserialize(); if (err == null) { task.SetException(new JRpcException("Internal Server Error")); } else { task.SetException(err.ToException()); } } else { task.SetResult(parsed["result"]); } } else { // Ignoring invalid packet! return; } } catch (Exception) { //TODO: Maybe log exception, but don't break! } } public async Task SendRequestRaw(string method, JsonArray param) { var id = Guid.NewGuid().ToString(); var request = new JsonObject { ["jsonrpc"] = "2.0", ["id"] = id, ["method"] = method, ["params"] = param }; var task = new TaskCompletionSource(); this.Requests.Add(id, task); _ = Task.Run(async () => { await this.Transport.Write(request.ToJsonString()); }); if (this.Timeout.HasValue) { if (await Task.WhenAny(task.Task, Task.Delay(this.Timeout.Value)) == task.Task) { return await task.Task; } else { this.Requests.Remove(id); throw new JRpcTimeoutException(); } } else { return await task.Task; } } public async Task SendRequest(string method, JsonArray param) { var result = await this.SendRequestRaw(method, param); return result.Deserialize(); } 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()); } } class JRpcError { public int code { get; set; } public string? message { get; set; } public JsonNode? data { get; set; } public JRpcException ToException() { return new JRpcException(this.message!); } } public class JRpcException : Exception { public JRpcException(string message) : base(message) { } } public class JRpcTimeoutException : JRpcException { public JRpcTimeoutException() : base("Request Timeout") { } }