Refactor: generate SS/SSR start arguments

This commit is contained in:
ChsBuffer
2021-03-16 15:55:46 +08:00
parent 15f4895c0f
commit eb713db867
4 changed files with 310 additions and 32 deletions

View File

@@ -0,0 +1,121 @@
using System;
using System.Linq;
using System.Reflection;
using Netch.Utils;
namespace Netch.Models
{
public abstract class ParameterBase
{
// null value par
private readonly bool _full;
protected readonly string ParametersSeparate = " ";
protected readonly string Separate = " ";
protected readonly string VerbPrefix = "-";
protected readonly string FullPrefix = "--";
protected ParameterBase()
{
_full = !GetType().IsDefined(typeof(VerbAttribute));
}
public override string ToString()
{
var parameters = GetType().GetProperties().Select(PropToParameter).Where(s => s != null).Cast<string>();
return string.Join(ParametersSeparate, parameters).Trim();
}
private string? PropToParameter(PropertyInfo p)
{
// prefix
bool full;
if (p.IsDefined(typeof(VerbAttribute)))
full = false;
else if (p.IsDefined(typeof(FullAttribute)))
full = true;
else
full = _full;
var prefix = full ? FullPrefix : VerbPrefix;
// key
var key = p.GetCustomAttribute<RealNameAttribute>()?.Name ?? p.Name;
// build
var value = p.GetValue(this);
switch (value)
{
case bool b:
return b ? $"{prefix}{key}" : null;
default:
if ((value?.ToString() ?? null).IsNullOrWhiteSpace())
return p.IsDefined(typeof(OptionalAttribute)) ? null : throw new RequiredArgumentValueInvalidException(p.Name, this, null);
if (p.IsDefined(typeof(QuoteAttribute)))
value = $"\"{value}\"";
return $"{prefix}{key}{Separate}{value}";
}
}
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)]
public class VerbAttribute : Attribute
{
// Don't use verb and full both on one class or property
// if you did, will take verb
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)]
public class FullAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
public class OptionalAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
public class QuoteAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
public class RealNameAttribute : Attribute
{
public string Name { get; }
public RealNameAttribute(string name)
{
Name = name;
}
}
[Serializable]
public class RequiredArgumentValueInvalidException : Exception
{
public string? ArgumentName { get; }
public object? ArgumentObject { get; }
private readonly string? _message;
private const string DefaultMessage = "{0}'s Argument \"{1}\" value invalid. A required argument's value can't be null or empty.";
public override string Message => _message ?? string.Format(DefaultMessage, ArgumentObject!.GetType(), ArgumentName);
public RequiredArgumentValueInvalidException()
{
_message = "Some Argument value invalid. A required argument value's can't be null or empty.";
}
public RequiredArgumentValueInvalidException(string argumentName, object argumentObject, string? message)
{
ArgumentName = argumentName;
ArgumentObject = argumentObject;
_message = message;
}
}
}

View File

@@ -24,26 +24,60 @@ namespace Netch.Servers.Shadowsocks
{
var server = (Shadowsocks) s;
#region Argument
var argument = new StringBuilder();
argument.Append($"-s {server.AutoResolveHostname()} " + $"-p {server.Port} " + $"-b {this.LocalAddress()} " +
$"-l {this.Socks5LocalPort()} " + $"-m {server.EncryptMethod} " + $"-k \"{server.Password}\" " + "-u");
if (!string.IsNullOrWhiteSpace(server.Plugin) && !string.IsNullOrWhiteSpace(server.PluginOption))
argument.Append($" --plugin {server.Plugin}" + $" --plugin-opts \"{server.PluginOption}\"");
var command = new SSParameter
{
s = server.AutoResolveHostname(),
p = server.Port.ToString(),
b = this.LocalAddress(),
l = this.Socks5LocalPort().ToString(),
m = server.EncryptMethod,
k = server.Password,
u = true,
plugin = server.Plugin,
plugin_opts = server.PluginOption
};
if (mode.BypassChina)
argument.Append($" --acl \"{Path.GetFullPath(File.Exists(Global.UserACL) ? Global.UserACL : Global.BuiltinACL)}\"");
command.acl = $"{Path.GetFullPath(File.Exists(Global.UserACL) ? Global.UserACL : Global.BuiltinACL)}";
#endregion
StartInstanceAuto(command.ToString());
}
StartInstanceAuto(argument.ToString());
[Verb]
private class SSParameter : ParameterBase
{
public string? s { get; set; }
public string? p { get; set; }
public string? b { get; set; }
public string? l { get; set; }
public string? m { get; set; }
public string? k { get; set; }
public bool u { get; set; }
[Full]
[Optional]
public string? plugin { get; set; }
[Full]
[Optional]
[RealName("plugin-opts")]
public string? plugin_opts { get; set; }
[Full]
[Quote]
[Optional]
public string? acl { get; set; }
}
public override void Stop()
{
StopInstance();
StopInstance();
}
}
}

View File

@@ -24,31 +24,64 @@ namespace Netch.Servers.ShadowsocksR
{
var server = (ShadowsocksR) s;
#region Argument
var argument = new StringBuilder();
argument.Append($"-s {server.AutoResolveHostname()} -p {server.Port} -k \"{server.Password}\" -m {server.EncryptMethod} -t 120");
if (!string.IsNullOrEmpty(server.Protocol))
var command = new SSRParameter
{
argument.Append($" -O {server.Protocol}");
if (!string.IsNullOrEmpty(server.ProtocolParam))
argument.Append($" -G \"{server.ProtocolParam}\"");
}
s = server.AutoResolveHostname(),
p = server.Port.ToString(),
k = server.Password,
m = server.EncryptMethod,
t = "120",
O = server.Protocol,
G = server.ProtocolParam,
o = server.OBFS,
g = server.OBFSParam,
b = this.LocalAddress(),
l = this.Socks5LocalPort().ToString(),
u = true
};
if (!string.IsNullOrEmpty(server.OBFS))
{
argument.Append($" -o {server.OBFS}");
if (!string.IsNullOrEmpty(server.OBFSParam))
argument.Append($" -g \"{server.OBFSParam}\"");
}
argument.Append($" -b {this.LocalAddress()} -l {this.Socks5LocalPort()} -u");
if (mode.BypassChina)
argument.Append($" --acl \"{Path.GetFullPath(File.Exists(Global.UserACL) ? Global.UserACL : Global.BuiltinACL)}\"");
command.acl = $"{Path.GetFullPath(File.Exists(Global.UserACL) ? Global.UserACL : Global.BuiltinACL)}";
#endregion
StartInstanceAuto(command.ToString());
}
StartInstanceAuto(argument.ToString());
[Verb]
class SSRParameter : ParameterBase
{
public string? s { get; set; }
public string? p { get; set; }
[Quote]
public string? k { get; set; }
public string? m { get; set; }
public string? t { get; set; }
[Optional]
public string? O { get; set; }
[Optional]
public string? G { get; set; }
[Optional]
public string? o { get; set; }
[Optional]
public string? g { get; set; }
public string? b { get; set; }
public string? l { get; set; }
public bool u { get; set; }
[Full]
[Quote]
[Optional]
public string? acl { get; set; }
}
public override void Stop()

90
UnitTest/ParameterTest.cs Normal file
View File

@@ -0,0 +1,90 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Netch.Models;
namespace UnitTest
{
[TestClass]
public class ParameterTest
{
[Verb]
private class VerbAndRealName : ParameterBase
{
[RealName("v")]
public string v1 { get; } = "a";
[RealName("v")]
public string v2 { get; } = "b";
}
private class Full : ParameterBase
{
[RealName("f")]
public string f1 { get; } = "a";
[RealName("f")]
public string f2 { get; } = "b";
}
private class FullWithVerb : ParameterBase
{
public string f { get; } = "a";
[Verb]
public string v { get; } = "b";
}
[Verb]
private class VerbWithFull : ParameterBase
{
public string v { get; } = "a";
[Full]
public string f { get; } = "b";
}
private class QuoteValue : ParameterBase
{
public static string pathValue = @"C:\Programe Files\Damn thats space";
[Quote]
public string path { get; set; } = pathValue;
}
private class FlagAndOptional : ParameterBase
{
public bool a { get; set; } = true;
public bool b { get; set; } = false;
[Optional]
public string c { get; set; } = string.Empty;
[Optional]
public string? d { get; set; } = null;
}
private class RequiredEmpty : ParameterBase
{
public string? udp { get; set; } = string.Empty;
}
private class RequiredNull : ParameterBase
{
public string? udp { get; set; } = null;
}
[TestMethod]
public void Test()
{
Assert.AreEqual(new VerbAndRealName().ToString(), "-v a -v b");
Assert.AreEqual(new Full().ToString(), "--f a --f b");
Assert.AreEqual(new FullWithVerb().ToString(), "--f a -v b");
Assert.AreEqual(new VerbWithFull().ToString(), "-v a --f b");
Assert.AreEqual(new QuoteValue().ToString(), $"--path \"{QuoteValue.pathValue}\"");
Assert.AreEqual(new FlagAndOptional().ToString(), "--a");
Assert.ThrowsException<RequiredArgumentValueInvalidException>(() => { _ = new RequiredEmpty().ToString(); });
Assert.ThrowsException<RequiredArgumentValueInvalidException>(() => { _ = new RequiredNull().ToString(); });
}
}
}