JsonRPC/templates/CSharp/JRpcClient.cs

171 lines
4.2 KiB
C#

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<string, TaskCompletionSource<JsonNode?>> Requests;
private int? Timeout = null;
public JRpcClient(JRpcTransport transport, int? timeout = null)
{
this.Transport = transport;
this.Timeout = timeout;
this.Requests = new Dictionary<string, TaskCompletionSource<JsonNode?>>();
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<JRpcError>();
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<JsonNode?> 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<JsonNode?>();
this.Requests.Add(id, task);
try
{
_ = Task.Run(async () =>
{
try
{
await this.Transport.Write(request.ToJsonString());
}
catch (Exception e)
{
task.SetException(e);
}
});
if (this.Timeout.HasValue)
{
if (await Task.WhenAny(task.Task, Task.Delay(this.Timeout.Value)) == task.Task)
{
return await task.Task;
}
else
{
throw new JRpcTimeoutException();
}
}
else
{
return await task.Task;
}
}
finally
{
this.Requests.Remove(id);
}
}
public async Task<TResult?> SendRequest<TResult>(string method, JsonArray param)
{
var result = await this.SendRequestRaw(method, param);
return result.Deserialize<TResult>();
}
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") { }
}