Compare commits

...

4 commits

Author SHA1 Message Date
DHR60
366daef7c5 Fix
Fix raw http ui

Fill xhttp default mode

Fix share uri

Remove RawHost

Fix singbox tcp http path

Fix vmess share uri
2026-03-29 17:22:58 +08:00
DHR60
8de4ba082d Fix 2026-03-29 17:22:58 +08:00
DHR60
0458df7b69 Rename tcp to raw 2026-03-29 17:22:58 +08:00
DHR60
40770dd250 Refactor transport 2026-03-29 17:22:58 +08:00
20 changed files with 1418 additions and 653 deletions

View file

@ -2,7 +2,7 @@ namespace ServiceLib.Enums;
public enum ETransport
{
tcp,
raw,
kcp,
ws,
httpupgrade,

View file

@ -42,9 +42,11 @@ public class Global
public const string SingboxFakeIPFilterFileName = NamespaceSample + "singbox_fakeip_filter";
public const string DefaultSecurity = "auto";
public const string DefaultNetwork = "tcp";
public const string TcpHeaderHttp = "http";
public const string DefaultNetwork = "raw";
public const string RawHeaderHttp = "http";
public const string None = "none";
public const string RawNetworkAlias = "tcp";
public const string DefaultXhttpMode = "auto";
public const string ProxyTag = "proxy";
public const string DirectTag = "direct";
public const string BlockTag = "block";
@ -182,7 +184,7 @@ public class Global
@"https://raw.githubusercontent.com/Chocolate4U/Iran-v2ray-rules/main/v2rayN/"
];
public static readonly Dictionary<string, string> TcpHttpUserAgentTexts = new()
public static readonly Dictionary<string, string> RawHttpUserAgentTexts = new()
{
{"chrome","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36" },
{"firefox","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" },
@ -292,14 +294,12 @@ public class Global
public static readonly List<string> Networks =
[
"tcp",
"kcp",
"ws",
"httpupgrade",
"raw",
"xhttp",
"h2",
"quic",
"grpc"
"kcp",
"grpc",
"ws",
"httpupgrade"
];
public static readonly List<string> KcpHeaderTypes =

View file

@ -340,8 +340,9 @@ public class CoreConfigContextBuilder
}
// xhttp downloadSettings address protect
if (!string.IsNullOrEmpty(node.Extra)
&& JsonUtils.ParseJson(node.Extra) is JsonObject extra
var xhttpExtra = node.GetTransportExtra().XhttpExtra;
if (!string.IsNullOrEmpty(xhttpExtra)
&& JsonUtils.ParseJson(xhttpExtra) is JsonObject extra
&& extra.TryGetPropertyValue("downloadSettings", out var dsNode)
&& dsNode is JsonObject downloadSettings
&& downloadSettings.TryGetPropertyValue("address", out var dAddrNode)

View file

@ -20,7 +20,7 @@ public class NodeValidator
[EConfigType.VMess, EConfigType.VLESS, EConfigType.Trojan, EConfigType.Shadowsocks];
private static readonly HashSet<string> SingboxShadowsocksAllowedTransports =
[nameof(ETransport.tcp), nameof(ETransport.ws), nameof(ETransport.quic)];
[nameof(ETransport.raw), nameof(ETransport.ws)];
public static NodeValidatorResult Validate(ProfileItem item, ECoreType coreType)
{
@ -141,9 +141,10 @@ public class NodeValidator
v.Assert(!item.PublicKey.IsNullOrEmpty(), string.Format(ResUI.MsgInvalidProperty, "PublicKey"));
}
if (item.Network == nameof(ETransport.xhttp) && !item.Extra.IsNullOrEmpty())
var transport = item.GetTransportExtra();
if (item.Network == nameof(ETransport.xhttp) && !transport.XhttpExtra.IsNullOrEmpty())
{
if (JsonUtils.ParseJson(item.Extra) is null)
if (JsonUtils.ParseJson(transport.XhttpExtra) is null)
{
v.Error(string.Format(ResUI.MsgInvalidProperty, "XHTTP Extra"));
}
@ -159,7 +160,7 @@ public class NodeValidator
}
// sing-box does not support non-tcp transports for protocols other than vmess/trojan/vless/shadowsocks
if (!SingboxTransportSupportedProtocols.Contains(configType) && net != nameof(ETransport.tcp))
if (!SingboxTransportSupportedProtocols.Contains(configType) && net != nameof(ETransport.raw))
{
return string.Format(ResUI.MsgCoreNotSupportProtocolTransport,
nameof(ECoreType.sing_box), configType.ToString(), net);

View file

@ -235,9 +235,6 @@ public static class ConfigHandler
item.Password = profileItem.Password;
item.Network = profileItem.Network;
item.HeaderType = profileItem.HeaderType;
item.RequestHost = profileItem.RequestHost;
item.Path = profileItem.Path;
item.StreamSecurity = profileItem.StreamSecurity;
item.Sni = profileItem.Sni;
@ -249,7 +246,6 @@ public static class ConfigHandler
item.ShortId = profileItem.ShortId;
item.SpiderX = profileItem.SpiderX;
item.Mldsa65Verify = profileItem.Mldsa65Verify;
item.Extra = profileItem.Extra;
item.MuxEnabled = profileItem.MuxEnabled;
item.Cert = profileItem.Cert;
item.CertSha = profileItem.CertSha;
@ -296,9 +292,6 @@ public static class ConfigHandler
VmessSecurity = profileItem.GetProtocolExtra().VmessSecurity?.TrimEx()
});
profileItem.Network = profileItem.Network.TrimEx();
profileItem.HeaderType = profileItem.HeaderType.TrimEx();
profileItem.RequestHost = profileItem.RequestHost.TrimEx();
profileItem.Path = profileItem.Path.TrimEx();
profileItem.StreamSecurity = profileItem.StreamSecurity.TrimEx();
if (!Global.VmessSecurities.Contains(profileItem.GetProtocolExtra().VmessSecurity))
@ -750,10 +743,12 @@ public static class ConfigHandler
profileItem.Password = profileItem.Password.TrimEx();
profileItem.Network = string.Empty;
if (!Global.TuicCongestionControls.Contains(profileItem.HeaderType))
var congestionControl = profileItem.GetProtocolExtra().CongestionControl;
if (!Global.TuicCongestionControls.Contains(congestionControl))
{
profileItem.HeaderType = Global.TuicCongestionControls.FirstOrDefault()!;
congestionControl = Global.TuicCongestionControls.FirstOrDefault()!;
}
profileItem.SetProtocolExtra(profileItem.GetProtocolExtra() with { CongestionControl = congestionControl });
if (profileItem.StreamSecurity.IsNullOrEmpty())
{
@ -995,9 +990,6 @@ public static class ConfigHandler
profileItem.Address = profileItem.Address.TrimEx();
profileItem.Password = profileItem.Password.TrimEx();
profileItem.Network = profileItem.Network.TrimEx();
profileItem.HeaderType = profileItem.HeaderType.TrimEx();
profileItem.RequestHost = profileItem.RequestHost.TrimEx();
profileItem.Path = profileItem.Path.TrimEx();
profileItem.StreamSecurity = profileItem.StreamSecurity.TrimEx();
var vlessEncryption = profileItem.GetProtocolExtra().VlessEncryption?.TrimEx();
@ -1066,7 +1058,7 @@ public static class ConfigHandler
/// <returns>0 if successful</returns>
public static async Task<int> AddServerCommon(Config config, ProfileItem profileItem, bool toFile = true)
{
profileItem.ConfigVersion = 3;
profileItem.ConfigVersion = 4;
if (profileItem.StreamSecurity.IsNotEmpty())
{
@ -1134,6 +1126,8 @@ public static class ConfigHandler
var oProtocolExtra = o.GetProtocolExtra();
var nProtocolExtra = n.GetProtocolExtra();
var oTransport = oProtocolExtra.Transport ?? new TransportExtra();
var nTransport = nProtocolExtra.Transport ?? new TransportExtra();
return o.ConfigType == n.ConfigType
&& AreEqual(o.Address, n.Address)
@ -1144,9 +1138,16 @@ public static class ConfigHandler
&& AreEqual(oProtocolExtra.SsMethod, nProtocolExtra.SsMethod)
&& AreEqual(oProtocolExtra.VmessSecurity, nProtocolExtra.VmessSecurity)
&& AreEqual(o.Network, n.Network)
&& AreEqual(o.HeaderType, n.HeaderType)
&& AreEqual(o.RequestHost, n.RequestHost)
&& AreEqual(o.Path, n.Path)
&& AreEqual(oTransport.RawHeaderType, nTransport.RawHeaderType)
&& AreEqual(oTransport.Host, nTransport.Host)
&& AreEqual(oTransport.Path, nTransport.Path)
&& AreEqual(oTransport.XhttpMode, nTransport.XhttpMode)
&& AreEqual(oTransport.XhttpExtra, nTransport.XhttpExtra)
&& AreEqual(oTransport.GrpcAuthority, nTransport.GrpcAuthority)
&& AreEqual(oTransport.GrpcServiceName, nTransport.GrpcServiceName)
&& AreEqual(oTransport.GrpcMode, nTransport.GrpcMode)
&& AreEqual(oTransport.KcpHeaderType, nTransport.KcpHeaderType)
&& AreEqual(oTransport.KcpSeed, nTransport.KcpSeed)
&& (o.ConfigType == EConfigType.Trojan || o.StreamSecurity == n.StreamSecurity)
&& AreEqual(oProtocolExtra.Flow, nProtocolExtra.Flow)
&& AreEqual(oProtocolExtra.SalamanderPass, nProtocolExtra.SalamanderPass)

View file

@ -5,6 +5,7 @@ namespace ServiceLib.Handler.Fmt;
public class BaseFmt
{
private static readonly string[] _allowInsecureArray = new[] { "insecure", "allowInsecure", "allow_insecure" };
private static string UrlEncodeSafe(string? value) => Utils.UrlEncode(value ?? string.Empty);
protected static string GetIpv6(string address)
{
@ -21,6 +22,8 @@ public class BaseFmt
protected static int ToUriQuery(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
{
var transport = item.GetTransportExtra();
if (item.StreamSecurity.IsNotEmpty())
{
dicQuery.Add("security", item.StreamSecurity);
@ -87,54 +90,65 @@ public class BaseFmt
dicQuery.Add("fm", Utils.UrlEncode(finalmask));
}
dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp));
switch (item.Network)
var network = item.GetNetwork();
if (!Global.Networks.Contains(network))
{
case nameof(ETransport.tcp):
dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
if (item.RequestHost.IsNotEmpty())
network = nameof(ETransport.raw);
}
//dicQuery.Add("type", network);
dicQuery.Add("type", network == nameof(ETransport.raw) ? Global.RawNetworkAlias : network);
switch (network)
{
case nameof(ETransport.raw):
dicQuery.Add("headerType", transport.RawHeaderType.IsNotEmpty() ? transport.RawHeaderType : Global.None);
if (transport.Host.IsNotEmpty())
{
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("host", UrlEncodeSafe(transport.Host));
}
if (transport.Path.IsNotEmpty())
{
dicQuery.Add("path", UrlEncodeSafe(transport.Path));
}
break;
case nameof(ETransport.kcp):
dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
if (item.Path.IsNotEmpty())
dicQuery.Add("headerType", transport.KcpHeaderType.IsNotEmpty() ? transport.KcpHeaderType : Global.None);
if (transport.KcpSeed.IsNotEmpty())
{
dicQuery.Add("seed", Utils.UrlEncode(item.Path));
dicQuery.Add("seed", UrlEncodeSafe(transport.KcpSeed));
}
break;
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
if (item.RequestHost.IsNotEmpty())
if (transport.Host.IsNotEmpty())
{
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("host", UrlEncodeSafe(transport.Host));
}
if (item.Path.IsNotEmpty())
if (transport.Path.IsNotEmpty())
{
dicQuery.Add("path", Utils.UrlEncode(item.Path));
dicQuery.Add("path", UrlEncodeSafe(transport.Path));
}
break;
case nameof(ETransport.xhttp):
if (item.RequestHost.IsNotEmpty())
if (transport.Host.IsNotEmpty())
{
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("host", UrlEncodeSafe(transport.Host));
}
if (item.Path.IsNotEmpty())
if (transport.Path.IsNotEmpty())
{
dicQuery.Add("path", Utils.UrlEncode(item.Path));
dicQuery.Add("path", UrlEncodeSafe(transport.Path));
}
if (item.HeaderType.IsNotEmpty() && Global.XhttpMode.Contains(item.HeaderType))
if (transport.XhttpMode.IsNotEmpty() && Global.XhttpMode.Contains(transport.XhttpMode))
{
dicQuery.Add("mode", Utils.UrlEncode(item.HeaderType));
dicQuery.Add("mode", UrlEncodeSafe(transport.XhttpMode));
}
if (item.Extra.IsNotEmpty())
if (transport.XhttpExtra.IsNotEmpty())
{
var node = JsonUtils.ParseJson(item.Extra);
var node = JsonUtils.ParseJson(transport.XhttpExtra);
var extra = node != null
? JsonUtils.Serialize(node, new JsonSerializerOptions
{
@ -142,38 +156,19 @@ public class BaseFmt
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
})
: item.Extra;
dicQuery.Add("extra", Utils.UrlEncode(extra));
: transport.XhttpExtra;
dicQuery.Add("extra", UrlEncodeSafe(extra));
}
break;
case nameof(ETransport.http):
case nameof(ETransport.h2):
dicQuery["type"] = nameof(ETransport.http);
if (item.RequestHost.IsNotEmpty())
{
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
}
if (item.Path.IsNotEmpty())
{
dicQuery.Add("path", Utils.UrlEncode(item.Path));
}
break;
case nameof(ETransport.quic):
dicQuery.Add("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
dicQuery.Add("quicSecurity", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("key", Utils.UrlEncode(item.Path));
break;
case nameof(ETransport.grpc):
if (item.Path.IsNotEmpty())
if (transport.GrpcServiceName.IsNotEmpty())
{
dicQuery.Add("authority", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("serviceName", Utils.UrlEncode(item.Path));
if (item.HeaderType is Global.GrpcGunMode or Global.GrpcMultiMode)
dicQuery.Add("authority", UrlEncodeSafe(transport.GrpcAuthority));
dicQuery.Add("serviceName", UrlEncodeSafe(transport.GrpcServiceName));
if (transport.GrpcMode is Global.GrpcGunMode or Global.GrpcMultiMode)
{
dicQuery.Add("mode", Utils.UrlEncode(item.HeaderType));
dicQuery.Add("mode", UrlEncodeSafe(transport.GrpcMode));
}
}
break;
@ -216,6 +211,8 @@ public class BaseFmt
protected static int ResolveUriQuery(NameValueCollection query, ref ProfileItem item)
{
var transport = item.GetTransportExtra();
item.StreamSecurity = GetQueryValue(query, "security");
item.Sni = GetQueryValue(query, "sni");
item.Alpn = GetQueryDecoded(query, "alpn");
@ -258,36 +255,54 @@ public class BaseFmt
item.AllowInsecure = string.Empty;
}
item.Network = GetQueryValue(query, "type", nameof(ETransport.tcp));
var net = GetQueryValue(query, "type", nameof(ETransport.raw));
if (net == Global.RawNetworkAlias)
{
net = nameof(ETransport.raw);
}
if (!Global.Networks.Contains(net))
{
net = nameof(ETransport.raw);
}
item.Network = net;
switch (item.Network)
{
case nameof(ETransport.tcp):
item.HeaderType = GetQueryValue(query, "headerType", Global.None);
item.RequestHost = GetQueryDecoded(query, "host");
case nameof(ETransport.raw):
transport = transport with
{
RawHeaderType = GetQueryValue(query, "headerType", Global.None),
Host = GetQueryDecoded(query, "host"),
Path = GetQueryDecoded(query, "path"),
};
break;
case nameof(ETransport.kcp):
item.HeaderType = GetQueryValue(query, "headerType", Global.None);
item.Path = GetQueryDecoded(query, "seed");
var kcpSeed = GetQueryDecoded(query, "seed");
transport = transport with
{
KcpHeaderType = GetQueryValue(query, "headerType", Global.None),
KcpSeed = kcpSeed,
};
break;
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
item.RequestHost = GetQueryDecoded(query, "host");
item.Path = GetQueryDecoded(query, "path", "/");
transport = transport with
{
Host = GetQueryDecoded(query, "host"),
Path = GetQueryDecoded(query, "path", "/"),
};
break;
case nameof(ETransport.xhttp):
item.RequestHost = GetQueryDecoded(query, "host");
item.Path = GetQueryDecoded(query, "path", "/");
item.HeaderType = GetQueryDecoded(query, "mode");
var extraDecoded = GetQueryDecoded(query, "extra");
if (extraDecoded.IsNotEmpty())
var xhttpExtra = GetQueryDecoded(query, "extra");
if (xhttpExtra.IsNotEmpty())
{
var node = JsonUtils.ParseJson(extraDecoded);
var node = JsonUtils.ParseJson(xhttpExtra);
if (node != null)
{
extraDecoded = JsonUtils.Serialize(node, new JsonSerializerOptions
xhttpExtra = JsonUtils.Serialize(node, new JsonSerializerOptions
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
@ -295,31 +310,32 @@ public class BaseFmt
});
}
}
item.Extra = extraDecoded;
break;
case nameof(ETransport.http):
case nameof(ETransport.h2):
item.Network = nameof(ETransport.h2);
item.RequestHost = GetQueryDecoded(query, "host");
item.Path = GetQueryDecoded(query, "path", "/");
break;
case nameof(ETransport.quic):
item.HeaderType = GetQueryValue(query, "headerType", Global.None);
item.RequestHost = GetQueryValue(query, "quicSecurity", Global.None);
item.Path = GetQueryDecoded(query, "key");
transport = transport with
{
Host = GetQueryDecoded(query, "host"),
Path = GetQueryDecoded(query, "path", "/"),
XhttpMode = GetQueryDecoded(query, "mode"),
XhttpExtra = xhttpExtra,
};
break;
case nameof(ETransport.grpc):
item.RequestHost = GetQueryDecoded(query, "authority");
item.Path = GetQueryDecoded(query, "serviceName");
item.HeaderType = GetQueryDecoded(query, "mode", Global.GrpcGunMode);
transport = transport with
{
GrpcAuthority = GetQueryDecoded(query, "authority"),
GrpcServiceName = GetQueryDecoded(query, "serviceName"),
GrpcMode = GetQueryDecoded(query, "mode", Global.GrpcGunMode),
};
break;
default:
item.Network = nameof(ETransport.raw);
break;
}
item.SetTransportExtra(transport);
return 0;
}

View file

@ -42,31 +42,28 @@ public class ShadowsocksFmt : BaseFmt
//url = Utile.Base64Encode(url);
//new Sip002
var pw = Utils.Base64Encode($"{item.GetProtocolExtra().SsMethod}:{item.Password}", true);
var transport = item.GetTransportExtra();
// plugin
var plugin = string.Empty;
var pluginArgs = string.Empty;
if (item.Network == nameof(ETransport.tcp) && item.HeaderType == Global.TcpHeaderHttp)
if (item.Network == nameof(ETransport.raw) && transport.RawHeaderType == Global.RawHeaderHttp)
{
plugin = "obfs-local";
pluginArgs = $"obfs=http;obfs-host={item.RequestHost};";
pluginArgs = $"obfs=http;obfs-host={transport.Host};";
}
else
{
if (item.Network == nameof(ETransport.ws))
{
pluginArgs += "mode=websocket;";
pluginArgs += $"host={item.RequestHost};";
pluginArgs += $"host={transport.Host};";
// https://github.com/shadowsocks/v2ray-plugin/blob/e9af1cdd2549d528deb20a4ab8d61c5fbe51f306/args.go#L172
// Equal signs and commas [and backslashes] must be escaped with a backslash.
var path = item.Path.Replace("\\", "\\\\").Replace("=", "\\=").Replace(",", "\\,");
var path = (transport.Path ?? string.Empty).Replace("\\", "\\\\").Replace("=", "\\=").Replace(",", "\\,");
pluginArgs += $"path={path};";
}
else if (item.Network == nameof(ETransport.quic))
{
pluginArgs += "mode=quic;";
}
if (item.StreamSecurity == Global.StreamSecurity)
{
pluginArgs += "tls;";
@ -213,8 +210,11 @@ public class ShadowsocksFmt : BaseFmt
{
obfsHost = obfsHost.Replace("obfs-host=", "");
item.Network = Global.DefaultNetwork;
item.HeaderType = Global.TcpHeaderHttp;
item.RequestHost = obfsHost;
item.SetTransportExtra(item.GetTransportExtra() with
{
RawHeaderType = Global.RawHeaderHttp,
Host = obfsHost,
});
}
}
// Parse v2ray-plugin
@ -231,21 +231,20 @@ public class ShadowsocksFmt : BaseFmt
if (modeValue == "websocket")
{
item.Network = nameof(ETransport.ws);
var t = item.GetTransportExtra();
if (!host.IsNullOrEmpty())
{
item.RequestHost = host.Replace("host=", "");
item.Sni = item.RequestHost;
var wsHost = host.Replace("host=", "");
t = t with { Host = wsHost };
item.Sni = wsHost;
}
if (!path.IsNullOrEmpty())
{
var pathValue = path.Replace("path=", "");
pathValue = pathValue.Replace("\\=", "=").Replace("\\,", ",").Replace("\\\\", "\\");
item.Path = pathValue;
t = t with { Path = pathValue };
}
}
else if (modeValue == "quic")
{
item.Network = nameof(ETransport.quic);
item.SetTransportExtra(t);
}
if (hasTls)

View file

@ -26,6 +26,7 @@ public class VmessFmt : BaseFmt
var vmessQRCode = new VmessQRCode
{
// vmess link keeps shared transport keys; map from new transport model on export.
v = 2,
ps = item.Remarks.TrimEx(),
add = item.Address,
@ -33,10 +34,34 @@ public class VmessFmt : BaseFmt
id = item.Password,
aid = int.TryParse(item.GetProtocolExtra()?.AlterId, out var result) ? result : 0,
scy = item.GetProtocolExtra().VmessSecurity ?? "",
net = item.Network,
type = item.HeaderType,
host = item.RequestHost,
path = item.Path,
net = item.GetNetwork() == nameof(ETransport.raw) ? Global.RawNetworkAlias : item.Network,
type = item.GetNetwork() switch
{
nameof(ETransport.raw) => item.GetTransportExtra().RawHeaderType,
nameof(ETransport.kcp) => item.GetTransportExtra().KcpHeaderType,
nameof(ETransport.xhttp) => item.GetTransportExtra().XhttpMode,
nameof(ETransport.grpc) => item.GetTransportExtra().GrpcMode,
_ => Global.None,
},
host = item.GetNetwork() switch
{
nameof(ETransport.raw) => item.GetTransportExtra().Host,
nameof(ETransport.ws) => item.GetTransportExtra().Host,
nameof(ETransport.httpupgrade) => item.GetTransportExtra().Host,
nameof(ETransport.xhttp) => item.GetTransportExtra().Host,
nameof(ETransport.grpc) => item.GetTransportExtra().GrpcAuthority,
_ => null,
},
path = item.GetNetwork() switch
{
nameof(ETransport.raw) => item.GetTransportExtra().Path,
nameof(ETransport.kcp) => item.GetTransportExtra().KcpSeed,
nameof(ETransport.ws) => item.GetTransportExtra().Path,
nameof(ETransport.httpupgrade) => item.GetTransportExtra().Path,
nameof(ETransport.xhttp) => item.GetTransportExtra().Path,
nameof(ETransport.grpc) => item.GetTransportExtra().GrpcServiceName,
_ => null,
},
tls = item.StreamSecurity,
sni = item.Sni,
alpn = item.Alpn,
@ -70,7 +95,10 @@ public class VmessFmt : BaseFmt
}
item.Network = Global.DefaultNetwork;
item.HeaderType = Global.None;
var transport = new TransportExtra
{
RawHeaderType = Global.None,
};
//item.ConfigVersion = vmessQRCode.v;
item.Remarks = Utils.ToString(vmessQRCode.ps);
@ -84,15 +112,30 @@ public class VmessFmt : BaseFmt
});
if (vmessQRCode.net.IsNotEmpty())
{
item.Network = vmessQRCode.net;
item.Network = vmessQRCode.net == Global.RawNetworkAlias ? nameof(ETransport.raw) : vmessQRCode.net;
}
if (vmessQRCode.type.IsNotEmpty())
{
item.HeaderType = vmessQRCode.type;
transport = item.GetNetwork() switch
{
nameof(ETransport.raw) => transport with { RawHeaderType = vmessQRCode.type },
nameof(ETransport.kcp) => transport with { KcpHeaderType = vmessQRCode.type },
nameof(ETransport.xhttp) => transport with { XhttpMode = vmessQRCode.type },
nameof(ETransport.grpc) => transport with { GrpcMode = vmessQRCode.type },
_ => transport,
};
}
item.RequestHost = Utils.ToString(vmessQRCode.host);
item.Path = Utils.ToString(vmessQRCode.path);
transport = item.GetNetwork() switch
{
nameof(ETransport.raw) => transport with { Host = Utils.ToString(vmessQRCode.host), Path = Utils.ToString(vmessQRCode.path) },
nameof(ETransport.kcp) => transport with { KcpSeed = Utils.ToString(vmessQRCode.path) },
nameof(ETransport.ws) => transport with { Host = Utils.ToString(vmessQRCode.host), Path = Utils.ToString(vmessQRCode.path) },
nameof(ETransport.httpupgrade) => transport with { Host = Utils.ToString(vmessQRCode.host), Path = Utils.ToString(vmessQRCode.path) },
nameof(ETransport.xhttp) => transport with { Host = Utils.ToString(vmessQRCode.host), Path = Utils.ToString(vmessQRCode.path) },
nameof(ETransport.grpc) => transport with { GrpcAuthority = Utils.ToString(vmessQRCode.host), GrpcServiceName = Utils.ToString(vmessQRCode.path) },
_ => transport,
};
item.SetTransportExtra(transport);
item.StreamSecurity = Utils.ToString(vmessQRCode.tls);
item.Sni = Utils.ToString(vmessQRCode.sni);
item.Alpn = Utils.ToString(vmessQRCode.alpn);
@ -122,7 +165,7 @@ public class VmessFmt : BaseFmt
item.SetProtocolExtra(new ProtocolExtraItem
{
VmessSecurity = "auto",
VmessSecurity = Global.DefaultSecurity,
});
var query = Utils.ParseQueryString(url.Query);

View file

@ -308,8 +308,15 @@ public sealed class AppManager
#pragma warning disable CS0618
public async Task MigrateProfileExtra()
{
await MigrateProfileExtraGroup();
await MigrateProfileExtraGroupV2ToV3();
await MigrateProfileExtraV2ToV3();
await MigrateProfileTransportV3ToV4();
}
private async Task MigrateProfileExtraV2ToV3()
{
const int pageSize = 100;
var offset = 0;
@ -325,7 +332,7 @@ public sealed class AppManager
break;
}
var batchSuccessCount = await MigrateProfileExtraSub(batch);
var batchSuccessCount = await MigrateProfileExtraV2ToV3Sub(batch);
// Only increment offset by the number of failed items that remain in the result set
// Successfully updated items are automatically excluded from future queries due to ConfigVersion = 3
@ -335,7 +342,121 @@ public sealed class AppManager
//await ProfileGroupItemManager.Instance.ClearAll();
}
private async Task<int> MigrateProfileExtraSub(List<ProfileItem> batch)
private async Task MigrateProfileTransportV3ToV4()
{
const int pageSize = 100;
var offset = 0;
while (true)
{
var sql = $"SELECT * FROM ProfileItem WHERE ConfigVersion = 3 LIMIT {pageSize} OFFSET {offset}";
var batch = await SQLiteHelper.Instance.QueryAsync<ProfileItem>(sql);
if (batch is null || batch.Count == 0)
{
break;
}
var updateProfileItems = new List<ProfileItem>();
foreach (var item in batch)
{
try
{
if (item.Network == Global.RawNetworkAlias)
{
item.Network = nameof(ETransport.raw);
}
var extra = item.GetProtocolExtra();
var transport = extra.Transport ?? new TransportExtra();
var network = item.GetNetwork();
switch (network)
{
case nameof(ETransport.raw):
transport = transport with
{
RawHeaderType = item.HeaderType.NullIfEmpty(),
Host = item.RequestHost.NullIfEmpty(),
Path = item.Path.NullIfEmpty(),
};
break;
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
transport = transport with
{
Host = item.RequestHost.NullIfEmpty(),
Path = item.Path.NullIfEmpty(),
};
break;
case nameof(ETransport.xhttp):
transport = transport with
{
Host = item.RequestHost.NullIfEmpty(),
Path = item.Path.NullIfEmpty(),
XhttpMode = item.HeaderType.NullIfEmpty(),
XhttpExtra = item.Extra.NullIfEmpty(),
};
break;
case nameof(ETransport.grpc):
transport = transport with
{
GrpcAuthority = item.RequestHost.NullIfEmpty(),
GrpcServiceName = item.Path.NullIfEmpty(),
GrpcMode = item.HeaderType.NullIfEmpty(),
};
break;
case nameof(ETransport.kcp):
transport = transport with
{
KcpHeaderType = item.HeaderType.NullIfEmpty(),
KcpSeed = item.Path.NullIfEmpty(),
};
break;
default:
item.Network = Global.DefaultNetwork;
transport = transport with
{
RawHeaderType = item.HeaderType.NullIfEmpty(),
Host = item.RequestHost.NullIfEmpty(),
};
break;
}
item.SetProtocolExtra(extra with { Transport = transport });
item.ConfigVersion = 4;
updateProfileItems.Add(item);
}
catch (Exception ex)
{
Logging.SaveLog($"MigrateProfileTransportV3ToV4 Error: {ex}");
}
}
if (updateProfileItems.Count > 0)
{
try
{
var count = await SQLiteHelper.Instance.UpdateAllAsync(updateProfileItems);
offset += batch.Count - count;
}
catch (Exception ex)
{
Logging.SaveLog($"MigrateProfileTransportV3ToV4 update error: {ex}");
offset += batch.Count;
}
}
else
{
offset += batch.Count;
}
}
}
private async Task<int> MigrateProfileExtraV2ToV3Sub(List<ProfileItem> batch)
{
var updateProfileItems = new List<ProfileItem>();
@ -433,7 +554,7 @@ public sealed class AppManager
}
}
private async Task<bool> MigrateProfileExtraGroup()
private async Task<bool> MigrateProfileExtraGroupV2ToV3()
{
var list = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().ToListAsync();
var groupItems = new ConcurrentDictionary<string, ProfileGroupItem>(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!));

View file

@ -9,7 +9,7 @@ public class ProfileItem
{
IndexId = string.Empty;
ConfigType = EConfigType.VMess;
ConfigVersion = 3;
ConfigVersion = 4;
Subid = string.Empty;
Address = string.Empty;
Port = 0;
@ -17,9 +17,6 @@ public class ProfileItem
Username = string.Empty;
Network = string.Empty;
Remarks = string.Empty;
HeaderType = string.Empty;
RequestHost = string.Empty;
Path = string.Empty;
StreamSecurity = string.Empty;
AllowInsecure = string.Empty;
}
@ -142,6 +139,16 @@ public class ProfileItem
return _protocolExtraCache ??= JsonUtils.Deserialize<ProtocolExtraItem>(ProtoExtra) ?? new ProtocolExtraItem();
}
public TransportExtra GetTransportExtra()
{
return GetProtocolExtra().Transport ?? new TransportExtra();
}
public void SetTransportExtra(TransportExtra transportExtra)
{
SetProtocolExtra(GetProtocolExtra() with { Transport = transportExtra });
}
#endregion function
[PrimaryKey]
@ -160,8 +167,11 @@ public class ProfileItem
public string Password { get; set; }
public string Username { get; set; }
public string Network { get; set; }
[Obsolete("Use TransportExtra.RawHeaderType/XhttpMode/GrpcMode/KcpHeaderType instead.")]
public string HeaderType { get; set; }
[Obsolete("Use TransportExtra.Host/GrpcAuthority instead.")]
public string RequestHost { get; set; }
[Obsolete("Use TransportExtra.Path/GrpcServiceName/KcpSeed instead.")]
public string Path { get; set; }
public string StreamSecurity { get; set; }
public string AllowInsecure { get; set; }
@ -172,6 +182,7 @@ public class ProfileItem
public string ShortId { get; set; }
public string SpiderX { get; set; }
public string Mldsa65Verify { get; set; }
[Obsolete("Use TransportExtra.XhttpExtra instead.")]
public string Extra { get; set; }
public bool? MuxEnabled { get; set; }
public string Cert { get; set; }

View file

@ -2,6 +2,8 @@ namespace ServiceLib.Models;
public record ProtocolExtraItem
{
public TransportExtra? Transport { get; init; }
public bool? Uot { get; init; }
public string? CongestionControl { get; init; }

View file

@ -0,0 +1,18 @@
namespace ServiceLib.Models;
public record TransportExtra
{
public string? RawHeaderType { get; init; }
public string? Host { get; init; }
public string? Path { get; init; }
public string? XhttpMode { get; init; }
public string? XhttpExtra { get; init; }
public string? GrpcAuthority { get; init; }
public string? GrpcServiceName { get; init; }
public string? GrpcMode { get; init; }
public string? KcpHeaderType { get; init; }
public string? KcpSeed { get; init; }
}

View file

@ -317,7 +317,7 @@ public class StreamSettings4Ray
public TlsSettings4Ray? tlsSettings { get; set; }
public TcpSettings4Ray? tcpSettings { get; set; }
public RawSettings4Ray? rawSettings { get; set; }
public KcpSettings4Ray? kcpSettings { get; set; }
@ -371,7 +371,7 @@ public class CertificateSettings4Ray
public string? usage { get; set; }
}
public class TcpSettings4Ray
public class RawSettings4Ray
{
public Header4Ray header { get; set; }
}

View file

@ -84,6 +84,8 @@ public partial class CoreConfigSingboxService
try
{
var protocolExtra = _node.GetProtocolExtra();
var transportExtra = _node.GetTransportExtra();
var network = _node.GetNetwork();
outbound.server = _node.Address;
outbound.server_port = _node.Port;
outbound.type = Global.ProtocolTypes[_node.ConfigType];
@ -114,27 +116,23 @@ public partial class CoreConfigSingboxService
outbound.password = _node.Password;
outbound.udp_over_tcp = protocolExtra.Uot == true ? true : null;
if (_node.Network == nameof(ETransport.tcp) && _node.HeaderType == Global.TcpHeaderHttp)
if (network == nameof(ETransport.raw) && transportExtra.RawHeaderType == Global.RawHeaderHttp)
{
outbound.plugin = "obfs-local";
outbound.plugin_opts = $"obfs=http;obfs-host={_node.RequestHost};";
outbound.plugin_opts = $"obfs=http;obfs-host={transportExtra.Host};";
}
else
{
var pluginArgs = string.Empty;
if (_node.Network == nameof(ETransport.ws))
if (network == nameof(ETransport.ws))
{
pluginArgs += "mode=websocket;";
pluginArgs += $"host={_node.RequestHost};";
pluginArgs += $"host={transportExtra.Host};";
// https://github.com/shadowsocks/v2ray-plugin/blob/e9af1cdd2549d528deb20a4ab8d61c5fbe51f306/args.go#L172
// Equal signs and commas [and backslashes] must be escaped with a backslash.
var path = _node.Path.Replace("\\", "\\\\").Replace("=", "\\=").Replace(",", "\\,");
var path = (transportExtra.Path ?? string.Empty).Replace("\\", "\\\\").Replace("=", "\\=").Replace(",", "\\,");
pluginArgs += $"path={path};";
}
else if (_node.Network == nameof(ETransport.quic))
{
pluginArgs += "mode=quic;";
}
if (_node.StreamSecurity == Global.StreamSecurity)
{
pluginArgs += "tls;";
@ -381,9 +379,18 @@ public partial class CoreConfigSingboxService
{
serverName = _node.Sni;
}
else if (_node.RequestHost.IsNotEmpty())
else
{
serverName = Utils.String2List(_node.RequestHost)?.First();
var host = _node.GetNetwork() switch
{
nameof(ETransport.raw) => _node.GetTransportExtra().Host,
nameof(ETransport.ws) => _node.GetTransportExtra().Host,
nameof(ETransport.httpupgrade) => _node.GetTransportExtra().Host,
nameof(ETransport.xhttp) => _node.GetTransportExtra().Host,
nameof(ETransport.grpc) => _node.GetTransportExtra().GrpcAuthority,
_ => null,
};
serverName = Utils.String2List(host)?.First();
}
var tls = new Tls4Sbox()
{
@ -438,27 +445,22 @@ public partial class CoreConfigSingboxService
try
{
var transport = new Transport4Sbox();
var transportExtra = _node.GetTransportExtra();
switch (_node.GetNetwork())
{
case nameof(ETransport.h2):
transport.type = nameof(ETransport.http);
transport.host = _node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(_node.RequestHost);
transport.path = _node.Path.NullIfEmpty();
break;
case nameof(ETransport.tcp): //http
if (_node.HeaderType == Global.TcpHeaderHttp)
case nameof(ETransport.raw): //http
if (transportExtra.RawHeaderType == Global.RawHeaderHttp)
{
transport.type = nameof(ETransport.http);
transport.host = _node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(_node.RequestHost);
transport.path = _node.Path.NullIfEmpty();
transport.host = transportExtra.Host.IsNullOrEmpty() ? null : Utils.String2List(transportExtra.Host);
transport.path = transportExtra.Path.NullIfEmpty();
}
break;
case nameof(ETransport.ws):
transport.type = nameof(ETransport.ws);
var wsPath = _node.Path;
var wsPath = transportExtra.Path;
// Parse eh and ed parameters from path using regex
if (!wsPath.IsNullOrEmpty())
@ -487,29 +489,25 @@ public partial class CoreConfigSingboxService
}
transport.path = wsPath.NullIfEmpty();
if (_node.RequestHost.IsNotEmpty())
if (transportExtra.Host.IsNotEmpty())
{
transport.headers = new()
{
Host = _node.RequestHost
Host = transportExtra.Host
};
}
break;
case nameof(ETransport.httpupgrade):
transport.type = nameof(ETransport.httpupgrade);
transport.path = _node.Path.NullIfEmpty();
transport.host = _node.RequestHost.NullIfEmpty();
transport.path = transportExtra.Path.NullIfEmpty();
transport.host = transportExtra.Host.NullIfEmpty();
break;
case nameof(ETransport.quic):
transport.type = nameof(ETransport.quic);
break;
case nameof(ETransport.grpc):
transport.type = nameof(ETransport.grpc);
transport.service_name = _node.Path;
transport.service_name = transportExtra.GrpcServiceName;
transport.idle_timeout = _config.GrpcItem.IdleTimeout?.ToString("##s");
transport.ping_timeout = _config.GrpcItem.HealthCheckTimeout?.ToString("##s");
transport.permit_without_stream = _config.GrpcItem.PermitWithoutStream;

View file

@ -348,8 +348,49 @@ public partial class CoreConfigV2rayService
network = "hysteria";
}
streamSettings.network = network;
var host = _node.RequestHost.TrimEx();
var path = _node.Path.TrimEx();
var transport = _node.GetTransportExtra();
var host = string.Empty;
var path = string.Empty;
var kcpSeed = string.Empty;
var headerType = string.Empty;
var xhttpExtra = string.Empty;
switch (network)
{
case nameof(ETransport.raw):
host = transport.Host?.TrimEx() ?? string.Empty;
path = transport.Path?.TrimEx() ?? string.Empty;
headerType = transport.RawHeaderType?.TrimEx() ?? string.Empty;
break;
case nameof(ETransport.kcp):
kcpSeed = transport.KcpSeed?.TrimEx() ?? string.Empty;
headerType = transport.KcpHeaderType?.TrimEx() ?? string.Empty;
break;
case nameof(ETransport.ws):
host = transport.Host?.TrimEx() ?? string.Empty;
path = transport.Path?.TrimEx() ?? string.Empty;
break;
case nameof(ETransport.httpupgrade):
host = transport.Host?.TrimEx() ?? string.Empty;
path = transport.Path?.TrimEx() ?? string.Empty;
break;
case nameof(ETransport.xhttp):
host = transport.Host?.TrimEx() ?? string.Empty;
path = transport.Path?.TrimEx() ?? string.Empty;
headerType = transport.XhttpMode?.TrimEx() ?? string.Empty;
xhttpExtra = transport.XhttpExtra?.TrimEx() ?? string.Empty;
break;
case nameof(ETransport.grpc):
host = transport.GrpcAuthority?.TrimEx() ?? string.Empty;
path = transport.GrpcServiceName?.TrimEx() ?? string.Empty;
headerType = transport.GrpcMode?.TrimEx() ?? string.Empty;
break;
}
var sni = _node.Sni.TrimEx();
var useragent = _config.CoreBasicItem.DefUserAgent ?? string.Empty;
@ -435,19 +476,19 @@ public partial class CoreConfigV2rayService
kcpSettings.readBufferSize = _config.KcpItem.ReadBufferSize;
kcpSettings.writeBufferSize = _config.KcpItem.WriteBufferSize;
var kcpFinalmask = new Finalmask4Ray();
if (Global.KcpHeaderMaskMap.TryGetValue(_node.HeaderType, out var header))
if (Global.KcpHeaderMaskMap.TryGetValue(headerType, out var header))
{
kcpFinalmask.udp =
[
new Mask4Ray
{
type = header,
settings = _node.HeaderType == "dns" && !host.IsNullOrEmpty() ? new MaskSettings4Ray { domain = host } : null
settings = null
}
];
}
kcpFinalmask.udp ??= [];
if (path.IsNullOrEmpty())
if (kcpSeed.IsNullOrEmpty())
{
kcpFinalmask.udp.Add(new Mask4Ray
{
@ -459,7 +500,7 @@ public partial class CoreConfigV2rayService
kcpFinalmask.udp.Add(new Mask4Ray
{
type = "mkcp-aes128gcm",
settings = new MaskSettings4Ray { password = path }
settings = new MaskSettings4Ray { password = kcpSeed }
});
}
streamSettings.kcpSettings = kcpSettings;
@ -517,63 +558,25 @@ public partial class CoreConfigV2rayService
{
xhttpSettings.host = host;
}
if (_node.HeaderType.IsNotEmpty() && Global.XhttpMode.Contains(_node.HeaderType))
if (headerType.IsNotEmpty() && Global.XhttpMode.Contains(headerType))
{
xhttpSettings.mode = _node.HeaderType;
xhttpSettings.mode = headerType;
}
if (_node.Extra.IsNotEmpty())
if (xhttpExtra.IsNotEmpty())
{
xhttpSettings.extra = JsonUtils.ParseJson(_node.Extra);
xhttpSettings.extra = JsonUtils.ParseJson(xhttpExtra);
}
streamSettings.xhttpSettings = xhttpSettings;
FillOutboundMux(outbound);
break;
//h2
case nameof(ETransport.h2):
HttpSettings4Ray httpSettings = new();
if (host.IsNotEmpty())
{
httpSettings.host = Utils.String2List(host);
}
httpSettings.path = path;
streamSettings.httpSettings = httpSettings;
break;
//quic
case nameof(ETransport.quic):
QuicSettings4Ray quicsettings = new()
{
security = host,
key = path,
header = new Header4Ray
{
type = _node.HeaderType
}
};
streamSettings.quicSettings = quicsettings;
if (_node.StreamSecurity == Global.StreamSecurity)
{
if (sni.IsNotEmpty())
{
streamSettings.tlsSettings.serverName = sni;
}
else
{
streamSettings.tlsSettings.serverName = _node.Address;
}
}
break;
case nameof(ETransport.grpc):
GrpcSettings4Ray grpcSettings = new()
{
authority = host.NullIfEmpty(),
serviceName = path,
multiMode = _node.HeaderType == Global.GrpcMultiMode,
multiMode = headerType == Global.GrpcMultiMode,
idle_timeout = _config.GrpcItem.IdleTimeout,
health_check_timeout = _config.GrpcItem.HealthCheckTimeout,
permit_without_stream = _config.GrpcItem.PermitWithoutStream,
@ -639,21 +642,20 @@ public partial class CoreConfigV2rayService
streamSettings.finalmask = hy2Finalmask;
break;
default:
//tcp
if (_node.HeaderType == Global.TcpHeaderHttp)
case nameof(ETransport.raw):
if (headerType == Global.RawHeaderHttp)
{
TcpSettings4Ray tcpSettings = new()
RawSettings4Ray rawSettings = new()
{
header = new Header4Ray
{
type = _node.HeaderType
type = headerType
}
};
//request Host
var request = EmbedUtils.GetEmbedText(Global.V2raySampleHttpRequestFileName);
var useragentValue = Global.TcpHttpUserAgentTexts.GetValueOrDefault(useragent, useragent);
var useragentValue = Global.RawHttpUserAgentTexts.GetValueOrDefault(useragent, useragent);
var arrHost = host.Split(',');
var host2 = string.Join(",".AppendQuotes(), arrHost);
request = request.Replace("$requestHost$", $"{host2.AppendQuotes()}");
@ -666,11 +668,14 @@ public partial class CoreConfigV2rayService
pathHttp = string.Join(",".AppendQuotes(), arrPath);
}
request = request.Replace("$requestPath$", $"{pathHttp.AppendQuotes()}");
tcpSettings.header.request = JsonUtils.Deserialize<object>(request);
rawSettings.header.request = JsonUtils.Deserialize<object>(request);
streamSettings.tcpSettings = tcpSettings;
streamSettings.rawSettings = rawSettings;
}
break;
default:
break;
}
if (!_node.Finalmask.IsNullOrEmpty())

View file

@ -73,6 +73,152 @@ public class AddServerViewModel : MyReactiveObject
[Reactive]
public bool NaiveQuic { get; set; }
[Reactive]
public string RawHeaderType { get; set; }
[Reactive]
public string Host { get; set; }
[Reactive]
public string Path { get; set; }
[Reactive]
public string XhttpMode { get; set; }
[Reactive]
public string XhttpExtra { get; set; }
[Reactive]
public string GrpcAuthority { get; set; }
[Reactive]
public string GrpcServiceName { get; set; }
[Reactive]
public string GrpcMode { get; set; }
[Reactive]
public string KcpHeaderType { get; set; }
[Reactive]
public string KcpSeed { get; set; }
public string TransportHeaderType
{
get => SelectedSource.GetNetwork() switch
{
nameof(ETransport.raw) => RawHeaderType,
nameof(ETransport.kcp) => KcpHeaderType,
nameof(ETransport.xhttp) => XhttpMode,
nameof(ETransport.grpc) => GrpcMode,
_ => string.Empty,
};
set
{
switch (SelectedSource.GetNetwork())
{
case nameof(ETransport.raw):
RawHeaderType = value;
break;
case nameof(ETransport.kcp):
KcpHeaderType = value;
break;
case nameof(ETransport.xhttp):
XhttpMode = value;
break;
case nameof(ETransport.grpc):
GrpcMode = value;
break;
}
this.RaisePropertyChanged();
}
}
public string TransportHost
{
get => SelectedSource.GetNetwork() switch
{
nameof(ETransport.raw) => Host,
nameof(ETransport.ws) => Host,
nameof(ETransport.httpupgrade) => Host,
nameof(ETransport.xhttp) => Host,
nameof(ETransport.grpc) => GrpcAuthority,
_ => string.Empty,
};
set
{
switch (SelectedSource.GetNetwork())
{
case nameof(ETransport.raw):
Host = value;
break;
case nameof(ETransport.ws):
Host = value;
break;
case nameof(ETransport.httpupgrade):
Host = value;
break;
case nameof(ETransport.xhttp):
Host = value;
break;
case nameof(ETransport.grpc):
GrpcAuthority = value;
break;
}
this.RaisePropertyChanged();
}
}
public string TransportPath
{
get => SelectedSource.GetNetwork() switch
{
nameof(ETransport.kcp) => KcpSeed,
nameof(ETransport.ws) => Path,
nameof(ETransport.httpupgrade) => Path,
nameof(ETransport.xhttp) => Path,
nameof(ETransport.grpc) => GrpcServiceName,
_ => string.Empty,
};
set
{
switch (SelectedSource.GetNetwork())
{
case nameof(ETransport.kcp):
KcpSeed = value;
break;
case nameof(ETransport.ws):
Path = value;
break;
case nameof(ETransport.httpupgrade):
Path = value;
break;
case nameof(ETransport.xhttp):
Path = value;
break;
case nameof(ETransport.grpc):
GrpcServiceName = value;
break;
}
this.RaisePropertyChanged();
}
}
public string TransportExtraText
{
get => SelectedSource.GetNetwork() == nameof(ETransport.xhttp)
? XhttpExtra
: string.Empty;
set
{
if (SelectedSource.GetNetwork() == nameof(ETransport.xhttp))
{
XhttpExtra = value;
}
this.RaisePropertyChanged();
}
}
public ReactiveCommand<Unit, Unit> FetchCertCmd { get; }
public ReactiveCommand<Unit, Unit> FetchCertChainCmd { get; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
@ -101,14 +247,21 @@ public class AddServerViewModel : MyReactiveObject
this.WhenAnyValue(x => x.CertSha)
.Subscribe(_ => UpdateCertTip());
this.WhenAnyValue(x => x.SelectedSource.Network)
.Subscribe(_ =>
{
this.RaisePropertyChanged(nameof(TransportHeaderType));
this.RaisePropertyChanged(nameof(TransportHost));
this.RaisePropertyChanged(nameof(TransportPath));
this.RaisePropertyChanged(nameof(TransportExtraText));
});
this.WhenAnyValue(x => x.Cert)
.Subscribe(_ => UpdateCertSha());
if (profileItem.IndexId.IsNullOrEmpty())
{
profileItem.Network = Global.DefaultNetwork;
profileItem.HeaderType = Global.None;
profileItem.RequestHost = "";
profileItem.StreamSecurity = "";
SelectedSource = profileItem;
}
@ -121,6 +274,7 @@ public class AddServerViewModel : MyReactiveObject
CertSha = SelectedSource?.CertSha?.ToString() ?? string.Empty;
var protocolExtra = SelectedSource?.GetProtocolExtra();
var transport = protocolExtra?.Transport ?? new TransportExtra();
Ports = protocolExtra?.Ports ?? string.Empty;
AlterId = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0;
Flow = protocolExtra?.Flow ?? string.Empty;
@ -139,6 +293,18 @@ public class AddServerViewModel : MyReactiveObject
CongestionControl = protocolExtra?.CongestionControl ?? string.Empty;
InsecureConcurrency = protocolExtra?.InsecureConcurrency > 0 ? protocolExtra.InsecureConcurrency : null;
NaiveQuic = protocolExtra?.NaiveQuic ?? false;
RawHeaderType = transport.RawHeaderType ?? Global.None;
Host = transport.Host ?? string.Empty;
Path = transport.Path ?? string.Empty;
XhttpMode = transport.XhttpMode ?? Global.DefaultXhttpMode;
XhttpExtra = transport.XhttpExtra ?? string.Empty;
GrpcAuthority = transport.GrpcAuthority ?? string.Empty;
GrpcServiceName = transport.GrpcServiceName ?? string.Empty;
GrpcMode = transport.GrpcMode.IsNullOrEmpty() ? Global.GrpcGunMode : transport.GrpcMode;
KcpHeaderType = transport.KcpHeaderType.IsNullOrEmpty() ? Global.None : transport.KcpHeaderType;
KcpSeed = transport.KcpSeed ?? string.Empty;
}
private async Task SaveServerAsync()
@ -185,8 +351,28 @@ public class AddServerViewModel : MyReactiveObject
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
SelectedSource.Cert = Cert.IsNullOrEmpty() ? string.Empty : Cert;
SelectedSource.CertSha = CertSha.IsNullOrEmpty() ? string.Empty : CertSha;
if (!Global.Networks.Contains(SelectedSource.Network))
{
SelectedSource.Network = Global.DefaultNetwork;
}
var transport = new TransportExtra
{
RawHeaderType = RawHeaderType.NullIfEmpty(),
Host = Host.NullIfEmpty(),
Path = Path.NullIfEmpty(),
XhttpMode = XhttpMode.NullIfEmpty(),
XhttpExtra = XhttpExtra.NullIfEmpty(),
GrpcAuthority = GrpcAuthority.NullIfEmpty(),
GrpcServiceName = GrpcServiceName.NullIfEmpty(),
GrpcMode = GrpcMode.NullIfEmpty(),
KcpHeaderType = KcpHeaderType.NullIfEmpty(),
KcpSeed = KcpSeed.NullIfEmpty(),
};
SelectedSource.SetProtocolExtra(SelectedSource.GetProtocolExtra() with
{
Transport = transport,
Ports = Ports.NullIfEmpty(),
AlterId = AlterId > 0 ? AlterId.ToString() : null,
Flow = Flow.NullIfEmpty(),
@ -261,7 +447,7 @@ public class AddServerViewModel : MyReactiveObject
var serverName = SelectedSource.Sni;
if (serverName.IsNullOrEmpty())
{
serverName = SelectedSource.RequestHost;
serverName = GetCurrentTransportHost();
}
if (serverName.IsNullOrEmpty())
{
@ -286,7 +472,7 @@ public class AddServerViewModel : MyReactiveObject
var serverName = SelectedSource.Sni;
if (serverName.IsNullOrEmpty())
{
serverName = SelectedSource.RequestHost;
serverName = GetCurrentTransportHost();
}
if (serverName.IsNullOrEmpty())
{
@ -301,4 +487,17 @@ public class AddServerViewModel : MyReactiveObject
Cert = CertPemManager.ConcatenatePemChain(certs);
UpdateCertTip(certError);
}
private string GetCurrentTransportHost()
{
return SelectedSource.GetNetwork() switch
{
nameof(ETransport.raw) => Host,
nameof(ETransport.ws) => Host,
nameof(ETransport.httpupgrade) => Host,
nameof(ETransport.xhttp) => Host,
nameof(ETransport.grpc) => GrpcAuthority,
_ => string.Empty,
};
}
}

View file

@ -690,128 +690,279 @@
<Grid
x:Name="gridTransport"
Grid.Row="4"
ColumnDefinitions="300,Auto,Auto"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
RowDefinitions="Auto,Auto,Auto">
<TextBlock
Grid.Row="0"
Grid.ColumnSpan="2"
Margin="{StaticResource Margin4}"
Text="{x:Static resx:ResUI.GbTransport}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbNetwork}" />
<ComboBox
x:Name="cmbNetwork"
Grid.Row="1"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="1"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TipNetwork}" />
<TextBlock
x:Name="labHeaderType"
Grid.Row="2"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbHeaderType}" />
<StackPanel
Grid.Row="2"
Grid.Column="1"
VerticalAlignment="Center"
Orientation="Horizontal">
<Grid Grid.Row="1" ColumnDefinitions="300,Auto,Auto">
<TextBlock
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbNetwork}" />
<ComboBox
x:Name="cmbHeaderType"
x:Name="cmbNetwork"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TipNetwork}" />
</Grid>
<Button
x:Name="btnExtra"
Margin="{StaticResource MarginLr8}"
Classes="IconButton">
<Button.Content>
<PathIcon Data="{StaticResource SemiIconMore}">
<PathIcon.RenderTransform>
<RotateTransform Angle="90" />
</PathIcon.RenderTransform>
</PathIcon>
</Button.Content>
<Button.Flyout>
<Flyout>
<StackPanel>
<TextBlock
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TransportExtraTip}" />
<views:JsonEditor
x:Name="txtExtra"
Width="400"
MinHeight="100"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Stretch"
VerticalAlignment="Center" />
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
</StackPanel>
<TextBlock
x:Name="tipHeaderType"
Grid.Row="2"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbHeaderType}" />
<Grid Grid.Row="2">
<Grid
x:Name="gridTransportRaw"
IsVisible="False"
RowDefinitions="Auto,Auto">
<Grid Grid.Row="0" ColumnDefinitions="300,Auto">
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbHeaderType}" />
<ComboBox
x:Name="cmbHeaderTypeRaw"
Grid.Row="0"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}" />
</Grid>
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRequestHost}" />
<TextBox
x:Name="txtRequestHost"
Grid.Row="3"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
<TextBlock
x:Name="tipRequestHost"
Grid.Row="3"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRequestHost}" />
<Grid
x:Name="gridTransportRawHttp"
Grid.Row="1"
ColumnDefinitions="300,Auto"
IsVisible="False"
RowDefinitions="Auto,Auto">
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRequestHost}" />
<TextBox
x:Name="txtRequestHostRaw"
Grid.Row="0"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbPath}" />
<TextBox
x:Name="txtPathRaw"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
</Grid>
</Grid>
<TextBlock
Grid.Row="4"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbPath}" />
<TextBox
x:Name="txtPath"
Grid.Row="4"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
<TextBlock
x:Name="tipPath"
Grid.Row="4"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbPath}" />
<Grid
x:Name="gridTransportXhttp"
ColumnDefinitions="300,Auto"
IsVisible="False"
RowDefinitions="Auto,Auto,Auto,Auto">
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbHeaderType}" />
<ComboBox
x:Name="cmbHeaderTypeXhttp"
Grid.Row="0"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRequestHost}" />
<TextBox
x:Name="txtRequestHostXhttp"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbPath}" />
<TextBox
x:Name="txtPathXhttp"
Grid.Row="2"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Top"
Text="{x:Static resx:ResUI.TransportExtraTip}"
TextWrapping="Wrap" />
<views:JsonEditor
x:Name="txtExtraXhttp"
Grid.Row="3"
Grid.Column="1"
Width="400"
MinHeight="100"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Stretch"
VerticalAlignment="Center" />
</Grid>
<Grid
x:Name="gridTransportKcp"
ColumnDefinitions="300,Auto"
IsVisible="False"
RowDefinitions="Auto,Auto">
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbHeaderType}" />
<ComboBox
x:Name="cmbHeaderTypeKcp"
Grid.Row="0"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="Seed" />
<TextBox
x:Name="txtKcpSeed"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
</Grid>
<Grid
x:Name="gridTransportGrpc"
ColumnDefinitions="300,Auto"
IsVisible="False"
RowDefinitions="Auto,Auto,Auto">
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbHeaderType}" />
<ComboBox
x:Name="cmbHeaderTypeGrpc"
Grid.Row="0"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRequestHost}" />
<TextBox
x:Name="txtRequestHostGrpc"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbPath}" />
<TextBox
x:Name="txtPathGrpc"
Grid.Row="2"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
</Grid>
<Grid
x:Name="gridTransportWs"
ColumnDefinitions="300,Auto"
IsVisible="False"
RowDefinitions="Auto,Auto">
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRequestHost}" />
<TextBox
x:Name="txtRequestHostWs"
Grid.Row="0"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbPath}" />
<TextBox
x:Name="txtPathWs"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
</Grid>
<Grid
x:Name="gridTransportHttpupgrade"
ColumnDefinitions="300,Auto"
IsVisible="False"
RowDefinitions="Auto,Auto">
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbRequestHost}" />
<TextBox
x:Name="txtRequestHostHttpupgrade"
Grid.Row="0"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbPath}" />
<TextBox
x:Name="txtPathHttpupgrade"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
</Grid>
</Grid>
</Grid>
<Grid

View file

@ -16,6 +16,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => Close();
cmbNetwork.SelectionChanged += CmbNetwork_SelectionChanged;
cmbHeaderTypeRaw.SelectionChanged += CmbHeaderTypeRaw_SelectionChanged;
cmbStreamSecurity.SelectionChanged += CmbStreamSecurity_SelectionChanged;
btnGUID.Click += btnGUID_Click;
btnGUID5.Click += btnGUID_Click;
@ -24,6 +25,16 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
cmbCoreType.ItemsSource = Global.CoreTypes.AppendEmpty();
cmbNetwork.ItemsSource = Global.Networks;
cmbHeaderTypeRaw.ItemsSource = new List<string> { Global.None, Global.RawHeaderHttp };
var kcpHeaderTypes = new List<string> { Global.None };
kcpHeaderTypes.AddRange(Global.KcpHeaderTypes);
cmbHeaderTypeKcp.ItemsSource = kcpHeaderTypes;
cmbHeaderTypeXhttp.ItemsSource = Global.XhttpMode;
cmbHeaderTypeGrpc.ItemsSource = new List<string> { Global.GrpcGunMode, Global.GrpcMultiMode };
cmbFingerprint.ItemsSource = Global.Fingerprints;
cmbFingerprint2.ItemsSource = Global.Fingerprints;
cmbAllowInsecure.ItemsSource = Global.AllowInsecure;
@ -201,10 +212,27 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
break;
}
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Extra, v => v.txtExtra.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RawHeaderType, v => v.cmbHeaderTypeRaw.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostRaw.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathRaw.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.KcpHeaderType, v => v.cmbHeaderTypeKcp.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.KcpSeed, v => v.txtKcpSeed.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostWs.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathWs.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostHttpupgrade.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathHttpupgrade.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.XhttpMode, v => v.cmbHeaderTypeXhttp.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostXhttp.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathXhttp.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.XhttpExtra, v => v.txtExtraXhttp.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.GrpcMode, v => v.cmbHeaderTypeGrpc.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.GrpcAuthority, v => v.txtRequestHostGrpc.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.GrpcServiceName, v => v.txtPathGrpc.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.StreamSecurity, v => v.cmbStreamSecurity.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI.Text).DisposeWith(disposables);
@ -253,8 +281,12 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
private void CmbNetwork_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
SetHeaderType();
SetTips();
SetTransportGridVisibility();
}
private void CmbHeaderTypeRaw_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
SetRawHttpFieldsVisibility();
}
private void CmbStreamSecurity_SelectionChanged(object? sender, SelectionChangedEventArgs e)
@ -283,102 +315,60 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
txtId5.Text = Utils.GetGuid();
}
private void SetHeaderType()
private void SetTransportGridVisibility()
{
var lstHeaderType = new List<string>();
var network = cmbNetwork.SelectedItem.ToString();
if (network.IsNullOrEmpty())
{
lstHeaderType.Add(Global.None);
cmbHeaderType.ItemsSource = lstHeaderType;
cmbHeaderType.SelectedIndex = 0;
return;
}
if (network == nameof(ETransport.tcp))
{
lstHeaderType.Add(Global.None);
lstHeaderType.Add(Global.TcpHeaderHttp);
}
else if (network is nameof(ETransport.kcp) or nameof(ETransport.quic))
{
lstHeaderType.Add(Global.None);
lstHeaderType.AddRange(Global.KcpHeaderTypes);
}
else if (network is nameof(ETransport.xhttp))
{
lstHeaderType.AddRange(Global.XhttpMode);
}
else if (network == nameof(ETransport.grpc))
{
lstHeaderType.Add(Global.GrpcGunMode);
lstHeaderType.Add(Global.GrpcMultiMode);
}
else
{
lstHeaderType.Add(Global.None);
}
cmbHeaderType.ItemsSource = lstHeaderType;
cmbHeaderType.SelectedIndex = 0;
}
private void SetTips()
{
var network = cmbNetwork.SelectedItem.ToString();
var network = cmbNetwork.SelectedItem?.ToString();
if (network.IsNullOrEmpty())
{
network = Global.DefaultNetwork;
}
labHeaderType.IsVisible = true;
btnExtra.IsVisible = false;
tipRequestHost.Text =
tipPath.Text =
tipHeaderType.Text = string.Empty;
gridTransportRaw.IsVisible = false;
gridTransportKcp.IsVisible = false;
gridTransportWs.IsVisible = false;
gridTransportHttpupgrade.IsVisible = false;
gridTransportXhttp.IsVisible = false;
gridTransportGrpc.IsVisible = false;
switch (network)
{
case nameof(ETransport.tcp):
tipRequestHost.Text = ResUI.TransportRequestHostTip1;
tipHeaderType.Text = ResUI.TransportHeaderTypeTip1;
case nameof(ETransport.raw):
gridTransportRaw.IsVisible = true;
break;
case nameof(ETransport.kcp):
tipHeaderType.Text = ResUI.TransportHeaderTypeTip2;
tipPath.Text = ResUI.TransportPathTip5;
gridTransportKcp.IsVisible = true;
break;
case nameof(ETransport.ws):
gridTransportWs.IsVisible = true;
break;
case nameof(ETransport.httpupgrade):
tipRequestHost.Text = ResUI.TransportRequestHostTip2;
tipPath.Text = ResUI.TransportPathTip1;
gridTransportHttpupgrade.IsVisible = true;
break;
case nameof(ETransport.xhttp):
tipRequestHost.Text = ResUI.TransportRequestHostTip2;
tipPath.Text = ResUI.TransportPathTip1;
tipHeaderType.Text = ResUI.TransportHeaderTypeTip5;
labHeaderType.IsVisible = false;
btnExtra.IsVisible = true;
gridTransportXhttp.IsVisible = true;
break;
case nameof(ETransport.h2):
tipRequestHost.Text = ResUI.TransportRequestHostTip3;
tipPath.Text = ResUI.TransportPathTip2;
break;
case nameof(ETransport.quic):
tipRequestHost.Text = ResUI.TransportRequestHostTip4;
tipPath.Text = ResUI.TransportPathTip3;
tipHeaderType.Text = ResUI.TransportHeaderTypeTip3;
break;
case nameof(ETransport.grpc):
tipRequestHost.Text = ResUI.TransportRequestHostTip5;
tipPath.Text = ResUI.TransportPathTip4;
tipHeaderType.Text = ResUI.TransportHeaderTypeTip4;
labHeaderType.IsVisible = false;
gridTransportGrpc.IsVisible = true;
break;
default:
gridTransportRaw.IsVisible = true;
break;
}
SetRawHttpFieldsVisibility();
}
private void SetRawHttpFieldsVisibility()
{
var network = cmbNetwork.SelectedItem?.ToString();
if (network.IsNullOrEmpty())
{
network = Global.DefaultNetwork;
}
var rawHeaderType = cmbHeaderTypeRaw.SelectedItem?.ToString();
var showRawHttpFields = network == nameof(ETransport.raw)
&& rawHeaderType == Global.RawHeaderHttp;
gridTransportRawHttp.IsVisible = showRawHttpFields;
}
}

View file

@ -150,7 +150,7 @@
<Grid
x:Name="gridVMess"
Grid.Row="2"
Visibility="Hidden">
Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
@ -234,7 +234,7 @@
<Grid
x:Name="gridSs"
Grid.Row="2"
Visibility="Hidden">
Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
@ -308,7 +308,7 @@
<Grid
x:Name="gridSocks"
Grid.Row="2"
Visibility="Hidden">
Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
@ -353,7 +353,7 @@
<Grid
x:Name="gridVLESS"
Grid.Row="2"
Visibility="Hidden">
Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
@ -437,7 +437,7 @@
<Grid
x:Name="gridTrojan"
Grid.Row="2"
Visibility="Hidden">
Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
@ -497,7 +497,7 @@
<Grid
x:Name="gridHysteria2"
Grid.Row="2"
Visibility="Hidden">
Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
@ -610,7 +610,7 @@
<Grid
x:Name="gridTuic"
Grid.Row="2"
Visibility="Hidden">
Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
@ -670,7 +670,7 @@
<Grid
x:Name="gridWireguard"
Grid.Row="2"
Visibility="Hidden">
Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
@ -767,7 +767,7 @@
<Grid
x:Name="gridAnytls"
Grid.Row="2"
Visibility="Hidden">
Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
@ -796,7 +796,7 @@
<Grid
x:Name="gridNaive"
Grid.Row="2"
Visibility="Hidden">
Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
@ -908,143 +908,356 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="0"
Grid.ColumnSpan="2"
Margin="{StaticResource Margin4}"
Style="{StaticResource ModuleTitle}"
Text="{x:Static resx:ResUI.GbTransport}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbNetwork}" />
<ComboBox
x:Name="cmbNetwork"
Grid.Row="1"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="1"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TipNetwork}" />
<TextBlock
x:Name="labHeaderType"
Grid.Row="2"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbHeaderType}" />
<StackPanel
Grid.Row="2"
Grid.Column="1"
VerticalAlignment="Center"
Orientation="Horizontal">
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbNetwork}" />
<ComboBox
x:Name="cmbHeaderType"
x:Name="cmbNetwork"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TipNetwork}" />
</Grid>
<materialDesign:PopupBox
x:Name="popExtra"
HorizontalAlignment="Right"
StaysOpen="True"
Style="{StaticResource MaterialDesignToolForegroundPopupBox}">
<StackPanel>
<Grid Grid.Row="2">
<Grid x:Name="gridTransportRaw" Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TransportExtraTip}" />
<TextBox
x:Name="txtExtra"
Width="400"
Text="{x:Static resx:ResUI.TbHeaderType}" />
<ComboBox
x:Name="cmbHeaderTypeRaw"
Grid.Row="0"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefComboBox}" />
</Grid>
<Grid
x:Name="gridTransportRawHttp"
Grid.Row="1"
Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
AcceptsReturn="True"
MinLines="6"
Style="{StaticResource MyOutlinedTextBox}"
TextWrapping="Wrap" />
</StackPanel>
</materialDesign:PopupBox>
</StackPanel>
<TextBlock
x:Name="tipHeaderType"
Grid.Row="2"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbHeaderType}" />
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbRequestHost}" />
<TextBox
x:Name="txtRequestHostRaw"
Grid.Row="0"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbPath}" />
<TextBox
x:Name="txtPathRaw"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
</Grid>
</Grid>
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbRequestHost}" />
<TextBox
x:Name="txtRequestHost"
Grid.Row="3"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
<TextBlock
x:Name="tipRequestHost"
Grid.Row="3"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbRequestHost}" />
<Grid x:Name="gridTransportXhttp" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbHeaderType}" />
<ComboBox
x:Name="cmbHeaderTypeXhttp"
Grid.Row="0"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbRequestHost}" />
<TextBox
x:Name="txtRequestHostXhttp"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbPath}" />
<TextBox
x:Name="txtPathXhttp"
Grid.Row="2"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Top"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TransportExtraTip}"
TextWrapping="Wrap" />
<TextBox
x:Name="txtExtraXhttp"
Grid.Row="3"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
AcceptsReturn="True"
MinLines="6"
Style="{StaticResource MyOutlinedTextBox}"
TextWrapping="Wrap" />
</Grid>
<TextBlock
Grid.Row="4"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbPath}" />
<TextBox
x:Name="txtPath"
Grid.Row="4"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
<TextBlock
x:Name="tipPath"
Grid.Row="4"
Grid.Column="2"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbPath}" />
<Grid x:Name="gridTransportKcp" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbHeaderType}" />
<ComboBox
x:Name="cmbHeaderTypeKcp"
Grid.Row="0"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="Seed" />
<TextBox
x:Name="txtKcpSeed"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
</Grid>
<Grid x:Name="gridTransportGrpc" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbHeaderType}" />
<ComboBox
x:Name="cmbHeaderTypeGrpc"
Grid.Row="0"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbRequestHost}" />
<TextBox
x:Name="txtRequestHostGrpc"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbPath}" />
<TextBox
x:Name="txtPathGrpc"
Grid.Row="2"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
</Grid>
<Grid x:Name="gridTransportWs" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbRequestHost}" />
<TextBox
x:Name="txtRequestHostWs"
Grid.Row="0"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbPath}" />
<TextBox
x:Name="txtPathWs"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
</Grid>
<Grid x:Name="gridTransportHttpupgrade" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbRequestHost}" />
<TextBox
x:Name="txtRequestHostHttpupgrade"
Grid.Row="0"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbPath}" />
<TextBox
x:Name="txtPathHttpupgrade"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
</Grid>
</Grid>
</Grid>
<Grid x:Name="gridFinalmask" Grid.Row="5">
@ -1118,7 +1331,7 @@
<Grid
x:Name="gridTlsMore"
Grid.Row="8"
Visibility="Hidden">
Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
@ -1307,7 +1520,7 @@
<Grid
x:Name="gridRealityMore"
Grid.Row="8"
Visibility="Hidden">
Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />

View file

@ -11,6 +11,7 @@ public partial class AddServerWindow
Owner = Application.Current.MainWindow;
Loaded += Window_Loaded;
cmbNetwork.SelectionChanged += CmbNetwork_SelectionChanged;
cmbHeaderTypeRaw.SelectionChanged += CmbHeaderTypeRaw_SelectionChanged;
cmbStreamSecurity.SelectionChanged += CmbStreamSecurity_SelectionChanged;
btnGUID.Click += btnGUID_Click;
btnGUID5.Click += btnGUID_Click;
@ -19,6 +20,20 @@ public partial class AddServerWindow
cmbCoreType.ItemsSource = Global.CoreTypes.AppendEmpty();
cmbNetwork.ItemsSource = Global.Networks;
if (ViewModel.SelectedSource.Network.IsNullOrEmpty() || !Global.Networks.Contains(ViewModel.SelectedSource.Network))
{
ViewModel.SelectedSource.Network = Global.DefaultNetwork;
}
cmbHeaderTypeRaw.ItemsSource = new List<string> { Global.None, Global.RawHeaderHttp };
var kcpHeaderTypes = new List<string> { Global.None };
kcpHeaderTypes.AddRange(Global.KcpHeaderTypes);
cmbHeaderTypeKcp.ItemsSource = kcpHeaderTypes;
cmbHeaderTypeXhttp.ItemsSource = Global.XhttpMode;
cmbHeaderTypeGrpc.ItemsSource = new List<string> { Global.GrpcGunMode, Global.GrpcMultiMode };
cmbFingerprint.ItemsSource = Global.Fingerprints;
cmbFingerprint2.ItemsSource = Global.Fingerprints;
cmbAllowInsecure.ItemsSource = Global.AllowInsecure;
@ -114,7 +129,7 @@ public partial class AddServerWindow
}
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
gridTlsMore.Visibility = Visibility.Hidden;
gridTlsMore.Visibility = Visibility.Collapsed;
this.WhenActivated(disposables =>
{
@ -195,10 +210,27 @@ public partial class AddServerWindow
break;
}
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Extra, v => v.txtExtra.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RawHeaderType, v => v.cmbHeaderTypeRaw.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostRaw.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathRaw.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.KcpHeaderType, v => v.cmbHeaderTypeKcp.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.KcpSeed, v => v.txtKcpSeed.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostWs.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathWs.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostHttpupgrade.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathHttpupgrade.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.XhttpMode, v => v.cmbHeaderTypeXhttp.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostXhttp.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathXhttp.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.XhttpExtra, v => v.txtExtraXhttp.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.GrpcMode, v => v.cmbHeaderTypeGrpc.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.GrpcAuthority, v => v.txtRequestHostGrpc.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.GrpcServiceName, v => v.txtPathGrpc.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.StreamSecurity, v => v.cmbStreamSecurity.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI.Text).DisposeWith(disposables);
@ -249,8 +281,12 @@ public partial class AddServerWindow
private void CmbNetwork_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SetHeaderType();
SetTips();
SetTransportGridVisibility();
}
private void CmbHeaderTypeRaw_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SetRawHttpFieldsVisibility();
}
private void CmbStreamSecurity_SelectionChanged(object sender, SelectionChangedEventArgs e)
@ -259,17 +295,17 @@ public partial class AddServerWindow
if (security == Global.StreamSecurityReality)
{
gridRealityMore.Visibility = Visibility.Visible;
gridTlsMore.Visibility = Visibility.Hidden;
gridTlsMore.Visibility = Visibility.Collapsed;
}
else if (security == Global.StreamSecurity)
{
gridRealityMore.Visibility = Visibility.Hidden;
gridRealityMore.Visibility = Visibility.Collapsed;
gridTlsMore.Visibility = Visibility.Visible;
}
else
{
gridRealityMore.Visibility = Visibility.Hidden;
gridTlsMore.Visibility = Visibility.Hidden;
gridRealityMore.Visibility = Visibility.Collapsed;
gridTlsMore.Visibility = Visibility.Collapsed;
}
}
@ -279,102 +315,62 @@ public partial class AddServerWindow
txtId5.Text = Utils.GetGuid();
}
private void SetHeaderType()
private void SetTransportGridVisibility()
{
var lstHeaderType = new List<string>();
var network = cmbNetwork.SelectedItem.ToString();
if (network.IsNullOrEmpty())
{
lstHeaderType.Add(Global.None);
cmbHeaderType.ItemsSource = lstHeaderType;
cmbHeaderType.SelectedIndex = 0;
return;
}
if (network == nameof(ETransport.tcp))
{
lstHeaderType.Add(Global.None);
lstHeaderType.Add(Global.TcpHeaderHttp);
}
else if (network is nameof(ETransport.kcp) or nameof(ETransport.quic))
{
lstHeaderType.Add(Global.None);
lstHeaderType.AddRange(Global.KcpHeaderTypes);
}
else if (network is nameof(ETransport.xhttp))
{
lstHeaderType.AddRange(Global.XhttpMode);
}
else if (network == nameof(ETransport.grpc))
{
lstHeaderType.Add(Global.GrpcGunMode);
lstHeaderType.Add(Global.GrpcMultiMode);
}
else
{
lstHeaderType.Add(Global.None);
}
cmbHeaderType.ItemsSource = lstHeaderType;
cmbHeaderType.SelectedIndex = 0;
}
private void SetTips()
{
var network = cmbNetwork.SelectedItem.ToString();
var network = cmbNetwork.SelectedItem?.ToString();
if (network.IsNullOrEmpty())
{
network = Global.DefaultNetwork;
}
labHeaderType.Visibility = Visibility.Visible;
popExtra.Visibility = Visibility.Hidden;
tipRequestHost.Text =
tipPath.Text =
tipHeaderType.Text = string.Empty;
gridTransportRaw.Visibility = Visibility.Collapsed;
gridTransportKcp.Visibility = Visibility.Collapsed;
gridTransportWs.Visibility = Visibility.Collapsed;
gridTransportHttpupgrade.Visibility = Visibility.Collapsed;
gridTransportXhttp.Visibility = Visibility.Collapsed;
gridTransportGrpc.Visibility = Visibility.Collapsed;
switch (network)
{
case nameof(ETransport.tcp):
tipRequestHost.Text = ResUI.TransportRequestHostTip1;
tipHeaderType.Text = ResUI.TransportHeaderTypeTip1;
case nameof(ETransport.raw):
gridTransportRaw.Visibility = Visibility.Visible;
break;
case nameof(ETransport.kcp):
tipHeaderType.Text = ResUI.TransportHeaderTypeTip2;
tipPath.Text = ResUI.TransportPathTip5;
gridTransportKcp.Visibility = Visibility.Visible;
break;
case nameof(ETransport.ws):
gridTransportWs.Visibility = Visibility.Visible;
break;
case nameof(ETransport.httpupgrade):
tipRequestHost.Text = ResUI.TransportRequestHostTip2;
tipPath.Text = ResUI.TransportPathTip1;
gridTransportHttpupgrade.Visibility = Visibility.Visible;
break;
case nameof(ETransport.xhttp):
tipRequestHost.Text = ResUI.TransportRequestHostTip2;
tipPath.Text = ResUI.TransportPathTip1;
tipHeaderType.Text = ResUI.TransportHeaderTypeTip5;
labHeaderType.Visibility = Visibility.Hidden;
popExtra.Visibility = Visibility.Visible;
gridTransportXhttp.Visibility = Visibility.Visible;
break;
case nameof(ETransport.h2):
tipRequestHost.Text = ResUI.TransportRequestHostTip3;
tipPath.Text = ResUI.TransportPathTip2;
break;
case nameof(ETransport.quic):
tipRequestHost.Text = ResUI.TransportRequestHostTip4;
tipPath.Text = ResUI.TransportPathTip3;
tipHeaderType.Text = ResUI.TransportHeaderTypeTip3;
break;
case nameof(ETransport.grpc):
tipRequestHost.Text = ResUI.TransportRequestHostTip5;
tipPath.Text = ResUI.TransportPathTip4;
tipHeaderType.Text = ResUI.TransportHeaderTypeTip4;
labHeaderType.Visibility = Visibility.Hidden;
gridTransportGrpc.Visibility = Visibility.Visible;
break;
default:
gridTransportRaw.Visibility = Visibility.Visible;
break;
}
SetRawHttpFieldsVisibility();
}
private void SetRawHttpFieldsVisibility()
{
var network = cmbNetwork.SelectedItem?.ToString();
if (network.IsNullOrEmpty())
{
network = Global.DefaultNetwork;
}
var rawHeaderType = cmbHeaderTypeRaw.SelectedItem?.ToString();
var showRawHttpFields = network == nameof(ETransport.raw)
&& rawHeaderType == Global.RawHeaderHttp;
gridTransportRawHttp.Visibility = showRawHttpFields
? Visibility.Visible
: Visibility.Collapsed;
}
}