Adding C# Support. Badly tested currently, but kindof working

This commit is contained in:
K35
2022-01-05 21:16:17 +00:00
parent 94832ef682
commit 49425cab39
20 changed files with 3825 additions and 71 deletions

View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,130 @@
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) { }
}

View File

@ -0,0 +1,196 @@
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<TContext>
{
public IDictionary<string, JRpcService<TContext>> Services;
public JRpcServer()
{
this.Services = new Dictionary<string, JRpcService<TContext>>();
}
public void AddService(string name, JRpcService<TContext> service)
{
this.Services.Add(name, service);
}
public JRpcServerSession<TContext> GetSession(JRpcTransport transport, TContext context)
{
return new JRpcServerSession<TContext>(this, transport, context);
}
}
public class JRpcServerSession<TContext>
{
JRpcServer<TContext> Server;
JRpcTransport Transport;
TContext Context;
public JRpcServerSession(JRpcServer<TContext> 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<TContext>
{
public HashSet<string> Functions = new HashSet<string>();
protected void RegisterFunction(string name)
{
this.Functions.Add(name);
}
public abstract Task<JsonNode?> HandleRequest(string function, JsonNode param, TContext context);
// public abstract Task HandleNotification(string notification, JsonNode param);
}

View File

@ -0,0 +1,14 @@
using System.Threading.Tasks;
namespace __NAMESPACE__;
public delegate void NotifyPacket(string data);
public abstract class JRpcTransport {
public event NotifyPacket OnPacket;
public abstract Task Write(string data);
public void DevSendPacket(string data) {
this.OnPacket.Invoke(data);
}
}

View File

@ -9,10 +9,10 @@ export const Logging = {
export enum ErrorCodes {
ParseError = -32700,
InvalidRequest = -32700,
MethodNotFound = -32700,
InvalidParams = -32700,
InternalError = -32700,
InvalidRequest = -32600,
MethodNotFound = -32601,
InvalidParams = -32602,
InternalError = -32603,
}
export interface RequestObject {