[feat] Ctl framework

This commit is contained in:
acite
2025-10-05 03:00:26 +08:00
parent dcdd9d840e
commit af6dfbac8c
26 changed files with 578 additions and 79 deletions

49
abyssctl/App/App.cs Normal file
View File

@@ -0,0 +1,49 @@
using System.Net.Sockets;
using System.Text;
using abyssctl.App.Modules;
using abyssctl.Model;
using abyssctl.Static;
using CommandLine;
using Newtonsoft.Json;
namespace abyssctl.App;
public class App
{
private static readonly string SocketPath = "ctl.sock";
public static async Task<Ctl> CtlWriteRead(Ctl ctl)
{
var endPoint = new UnixDomainSocketEndPoint(SocketPath);
using var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);
try
{
await socket.ConnectAsync(endPoint);
await socket.WriteBase64Async(Ctl.MakeBase64(ctl.Head, ctl.Params));
var s = Encoding.UTF8.GetString(
Convert.FromBase64String(await socket.ReadBase64Async()));
return JsonConvert.DeserializeObject<Ctl>(s)!;
}
catch (Exception e)
{
return new Ctl
{
Head = 500,
Params = [e.Message]
};
}
}
public async Task<int> RunAsync(string[] args)
{
return await Task.Run(() =>
{
return Parser.Default.ParseArguments<HelloOptions, VersionOptions>(args)
.MapResult(
(HelloOptions opt) => HelloOptions.Run(opt),
(VersionOptions opt) => VersionOptions.Run(opt),
_ => 1);
});
}
}

View File

@@ -0,0 +1,20 @@
using abyssctl.Model;
using CommandLine;
namespace abyssctl.App.Modules;
[Verb("hello", HelpText = "Say hello to abyss server")]
public class HelloOptions
{
public static int Run(HelloOptions opts)
{
var r = App.CtlWriteRead(new Ctl
{
Head = 100,
Params = []
}).GetAwaiter().GetResult();
Console.WriteLine($"Response Code: {r.Head}");
Console.WriteLine($"Params: {string.Join(",", r.Params)}");
return 0;
}
}

View File

@@ -0,0 +1,13 @@
using CommandLine;
namespace abyssctl.App.Modules;
[Verb("ver", HelpText = "Get server version")]
public class VersionOptions
{
public static int Run(VersionOptions opts)
{
Console.WriteLine("Version");
return 0;
}
}

19
abyssctl/Model/Ctl.cs Normal file
View File

@@ -0,0 +1,19 @@
using System.Text;
using Newtonsoft.Json;
namespace abyssctl.Model;
public class Ctl
{
[JsonProperty("head")] public int Head { get; set; }
[JsonProperty("params")] public string[] Params { get; set; } = [];
public static string MakeBase64(int head, string[] param)
{
return Convert.ToBase64String(
Encoding.UTF8.GetBytes(
JsonConvert.SerializeObject(new Ctl
{ Head = head, Params = param })));
}
}

13
abyssctl/Program.cs Normal file
View File

@@ -0,0 +1,13 @@

namespace abyssctl;
static class Program
{
static async Task<int> Main(string[] args)
{
var app = new App.App();
return await app.RunAsync(args);
}
}

View File

@@ -0,0 +1,50 @@
using System.Net.Sockets;
using System.Text;
namespace abyssctl.Static;
public static class SocketExtensions
{
public static async Task<string> ReadBase64Async(this Socket socket, CancellationToken cancellationToken = default)
{
var buffer = new byte[4096];
var sb = new StringBuilder();
while (true)
{
int bytesRead = await socket.ReceiveAsync(buffer, SocketFlags.None, cancellationToken).ConfigureAwait(false);
if (bytesRead == 0)
throw new SocketException((int)SocketError.ConnectionReset);
string chunk = Encoding.UTF8.GetString(buffer, 0, bytesRead);
sb.Append(chunk);
int newlineIndex = sb.ToString().IndexOf('\n');
if (newlineIndex >= 0)
{
string base64 = sb.ToString(0, newlineIndex).Trim();
sb.Remove(0, newlineIndex + 1);
return base64;
}
}
}
public static async Task WriteBase64Async(this Socket socket, string base64, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(base64))
throw new ArgumentException("Base64 string cannot be null or empty.", nameof(base64));
string message = base64 + "\n";
byte[] data = Encoding.UTF8.GetBytes(message);
int totalSent = 0;
while (totalSent < data.Length)
{
int sent = await socket.SendAsync(data.AsMemory(totalSent), SocketFlags.None, cancellationToken).ConfigureAwait(false);
if (sent == 0)
throw new SocketException((int)SocketError.ConnectionReset);
totalSent += sent;
}
}
}

24
abyssctl/abyssctl.csproj Normal file
View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>13</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PublishAot>false</PublishAot>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<OutputPath>../build/</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="9.0.9" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="System.CommandLine" Version="2.0.0-rc.1.25451.107" />
</ItemGroup>
</Project>