171 lines
4.2 KiB
C#
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") { }
|
|
}
|