131 lines
3.3 KiB
C#
131 lines
3.3 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;
|
||
|
|
||
|
public JRpcClient(JRpcTransport transport)
|
||
|
{
|
||
|
this.Transport = transport;
|
||
|
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);
|
||
|
await this.Transport.Write(request.ToJsonString());
|
||
|
|
||
|
return await task.Task;
|
||
|
}
|
||
|
|
||
|
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) { }
|
||
|
}
|