Added outbound HTTP support (support group prefix proxy)

https://github.com/2dust/v2rayN/discussions/4550
This commit is contained in:
2dust 2024-04-08 14:02:56 +08:00
parent b5cb9ce67e
commit 5683df2fc0
15 changed files with 336 additions and 273 deletions

View file

@ -143,6 +143,7 @@ namespace v2rayN
{EConfigType.VMess,"vmess"}, {EConfigType.VMess,"vmess"},
{EConfigType.Shadowsocks,"shadowsocks"}, {EConfigType.Shadowsocks,"shadowsocks"},
{EConfigType.Socks,"socks"}, {EConfigType.Socks,"socks"},
{EConfigType.Http,"http"},
{EConfigType.VLESS,"vless"}, {EConfigType.VLESS,"vless"},
{EConfigType.Trojan,"trojan"}, {EConfigType.Trojan,"trojan"},
{EConfigType.Hysteria2,"hysteria2"}, {EConfigType.Hysteria2,"hysteria2"},

View file

@ -664,6 +664,23 @@ namespace v2rayN.Handler
return 0; return 0;
} }
/// <summary>
/// Add or edit server
/// </summary>
/// <param name="config"></param>
/// <param name="profileItem"></param>
/// <returns></returns>
public static int AddHttpServer(Config config, ProfileItem profileItem, bool toFile = true)
{
profileItem.configType = EConfigType.Http;
profileItem.address = profileItem.address.TrimEx();
AddServerCommon(config, profileItem, toFile);
return 0;
}
/// <summary> /// <summary>
/// Add or edit server /// Add or edit server
/// </summary> /// </summary>

View file

@ -213,11 +213,12 @@ namespace v2rayN.Handler
{ {
outbound.server = node.address; outbound.server = node.address;
outbound.server_port = node.port; outbound.server_port = node.port;
outbound.type = Global.ProtocolTypes[node.configType];
if (node.configType == EConfigType.VMess) switch (node.configType)
{
case EConfigType.VMess:
{ {
outbound.type = Global.ProtocolTypes[EConfigType.VMess];
outbound.uuid = node.id; outbound.uuid = node.id;
outbound.alter_id = node.alterId; outbound.alter_id = node.alterId;
if (Global.VmessSecurities.Contains(node.security)) if (Global.VmessSecurities.Contains(node.security))
@ -230,20 +231,18 @@ namespace v2rayN.Handler
} }
GenOutboundMux(node, outbound); GenOutboundMux(node, outbound);
break;
} }
else if (node.configType == EConfigType.Shadowsocks) case EConfigType.Shadowsocks:
{ {
outbound.type = Global.ProtocolTypes[EConfigType.Shadowsocks];
outbound.method = LazyConfig.Instance.GetShadowsocksSecurities(node).Contains(node.security) ? node.security : Global.None; outbound.method = LazyConfig.Instance.GetShadowsocksSecurities(node).Contains(node.security) ? node.security : Global.None;
outbound.password = node.id; outbound.password = node.id;
GenOutboundMux(node, outbound); GenOutboundMux(node, outbound);
break;
} }
else if (node.configType == EConfigType.Socks) case EConfigType.Socks:
{ {
outbound.type = Global.ProtocolTypes[EConfigType.Socks];
outbound.version = "5"; outbound.version = "5";
if (!Utils.IsNullOrEmpty(node.security) if (!Utils.IsNullOrEmpty(node.security)
&& !Utils.IsNullOrEmpty(node.id)) && !Utils.IsNullOrEmpty(node.id))
@ -251,11 +250,20 @@ namespace v2rayN.Handler
outbound.username = node.security; outbound.username = node.security;
outbound.password = node.id; outbound.password = node.id;
} }
break;
} }
else if (node.configType == EConfigType.VLESS) case EConfigType.Http:
{
if (!Utils.IsNullOrEmpty(node.security)
&& !Utils.IsNullOrEmpty(node.id))
{
outbound.username = node.security;
outbound.password = node.id;
}
break;
}
case EConfigType.VLESS:
{ {
outbound.type = Global.ProtocolTypes[EConfigType.VLESS];
outbound.uuid = node.id; outbound.uuid = node.id;
outbound.packet_encoding = "xudp"; outbound.packet_encoding = "xudp";
@ -268,19 +276,17 @@ namespace v2rayN.Handler
{ {
outbound.flow = node.flow; outbound.flow = node.flow;
} }
break;
} }
else if (node.configType == EConfigType.Trojan) case EConfigType.Trojan:
{ {
outbound.type = Global.ProtocolTypes[EConfigType.Trojan];
outbound.password = node.id; outbound.password = node.id;
GenOutboundMux(node, outbound); GenOutboundMux(node, outbound);
break;
} }
else if (node.configType == EConfigType.Hysteria2) case EConfigType.Hysteria2:
{ {
outbound.type = Global.ProtocolTypes[EConfigType.Hysteria2];
outbound.password = node.id; outbound.password = node.id;
if (!Utils.IsNullOrEmpty(node.path)) if (!Utils.IsNullOrEmpty(node.path))
@ -294,24 +300,24 @@ namespace v2rayN.Handler
outbound.up_mbps = _config.hysteriaItem.up_mbps > 0 ? _config.hysteriaItem.up_mbps : null; outbound.up_mbps = _config.hysteriaItem.up_mbps > 0 ? _config.hysteriaItem.up_mbps : null;
outbound.down_mbps = _config.hysteriaItem.down_mbps > 0 ? _config.hysteriaItem.down_mbps : null; outbound.down_mbps = _config.hysteriaItem.down_mbps > 0 ? _config.hysteriaItem.down_mbps : null;
break;
} }
else if (node.configType == EConfigType.Tuic) case EConfigType.Tuic:
{ {
outbound.type = Global.ProtocolTypes[EConfigType.Tuic];
outbound.uuid = node.id; outbound.uuid = node.id;
outbound.password = node.security; outbound.password = node.security;
outbound.congestion_control = node.headerType; outbound.congestion_control = node.headerType;
break;
} }
else if (node.configType == EConfigType.Wireguard) case EConfigType.Wireguard:
{ {
outbound.type = Global.ProtocolTypes[EConfigType.Wireguard];
outbound.private_key = node.id; outbound.private_key = node.id;
outbound.peer_public_key = node.publicKey; outbound.peer_public_key = node.publicKey;
outbound.reserved = Utils.String2List(node.path).Select(int.Parse).ToArray(); outbound.reserved = Utils.String2List(node.path).Select(int.Parse).ToArray();
outbound.local_address = [.. Utils.String2List(node.requestHost)]; outbound.local_address = [.. Utils.String2List(node.requestHost)];
outbound.mtu = Utils.ToInt(node.shortId.IsNullOrEmpty() ? Global.TunMtus.FirstOrDefault() : node.shortId); outbound.mtu = Utils.ToInt(node.shortId.IsNullOrEmpty() ? Global.TunMtus.FirstOrDefault() : node.shortId);
break;
}
} }
GenOutboundTls(node, outbound); GenOutboundTls(node, outbound);

View file

@ -295,7 +295,9 @@ namespace v2rayN.Handler
{ {
try try
{ {
if (node.configType == EConfigType.VMess) switch (node.configType)
{
case EConfigType.VMess:
{ {
VnextItem4Ray vnextItem; VnextItem4Ray vnextItem;
if (outbound.settings.vnext.Count <= 0) if (outbound.settings.vnext.Count <= 0)
@ -335,10 +337,10 @@ namespace v2rayN.Handler
GenOutboundMux(node, outbound, _config.coreBasicItem.muxEnabled); GenOutboundMux(node, outbound, _config.coreBasicItem.muxEnabled);
outbound.protocol = Global.ProtocolTypes[EConfigType.VMess];
outbound.settings.servers = null; outbound.settings.servers = null;
break;
} }
else if (node.configType == EConfigType.Shadowsocks) case EConfigType.Shadowsocks:
{ {
ServersItem4Ray serversItem; ServersItem4Ray serversItem;
if (outbound.settings.servers.Count <= 0) if (outbound.settings.servers.Count <= 0)
@ -360,10 +362,11 @@ namespace v2rayN.Handler
GenOutboundMux(node, outbound, false); GenOutboundMux(node, outbound, false);
outbound.protocol = Global.ProtocolTypes[EConfigType.Shadowsocks];
outbound.settings.vnext = null; outbound.settings.vnext = null;
break;
} }
else if (node.configType == EConfigType.Socks) case EConfigType.Socks:
case EConfigType.Http:
{ {
ServersItem4Ray serversItem; ServersItem4Ray serversItem;
if (outbound.settings.servers.Count <= 0) if (outbound.settings.servers.Count <= 0)
@ -395,13 +398,13 @@ namespace v2rayN.Handler
GenOutboundMux(node, outbound, false); GenOutboundMux(node, outbound, false);
outbound.protocol = Global.ProtocolTypes[EConfigType.Socks];
outbound.settings.vnext = null; outbound.settings.vnext = null;
break;
} }
else if (node.configType == EConfigType.VLESS) case EConfigType.VLESS:
{ {
VnextItem4Ray vnextItem; VnextItem4Ray vnextItem;
if (outbound.settings.vnext.Count <= 0) if (outbound.settings.vnext?.Count <= 0)
{ {
vnextItem = new VnextItem4Ray(); vnextItem = new VnextItem4Ray();
outbound.settings.vnext.Add(vnextItem); outbound.settings.vnext.Add(vnextItem);
@ -444,10 +447,10 @@ namespace v2rayN.Handler
GenOutboundMux(node, outbound, _config.coreBasicItem.muxEnabled); GenOutboundMux(node, outbound, _config.coreBasicItem.muxEnabled);
} }
outbound.protocol = Global.ProtocolTypes[EConfigType.VLESS];
outbound.settings.servers = null; outbound.settings.servers = null;
break;
} }
else if (node.configType == EConfigType.Trojan) case EConfigType.Trojan:
{ {
ServersItem4Ray serversItem; ServersItem4Ray serversItem;
if (outbound.settings.servers.Count <= 0) if (outbound.settings.servers.Count <= 0)
@ -468,9 +471,12 @@ namespace v2rayN.Handler
GenOutboundMux(node, outbound, false); GenOutboundMux(node, outbound, false);
outbound.protocol = Global.ProtocolTypes[EConfigType.Trojan];
outbound.settings.vnext = null; outbound.settings.vnext = null;
break;
} }
}
outbound.protocol = Global.ProtocolTypes[node.configType];
GenBoundStreamSettings(node, outbound.streamSettings); GenBoundStreamSettings(node, outbound.streamSettings);
} }
catch (Exception ex) catch (Exception ex)

View file

@ -10,6 +10,7 @@
Trojan = 6, Trojan = 6,
Hysteria2 = 7, Hysteria2 = 7,
Tuic = 8, Tuic = 8,
Wireguard = 9 Wireguard = 9,
Http = 10
} }
} }

View file

@ -232,7 +232,7 @@ namespace v2rayN.Models
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public List<VnextItem4Ray> vnext { get; set; } public List<VnextItem4Ray>? vnext { get; set; }
/// <summary> /// <summary>
/// ///
@ -288,17 +288,17 @@ namespace v2rayN.Models
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string method { get; set; } public string? method { get; set; }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public bool ota { get; set; } public bool? ota { get; set; }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public string password { get; set; } public string? password { get; set; }
/// <summary> /// <summary>
/// ///
@ -308,7 +308,7 @@ namespace v2rayN.Models
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public int level { get; set; } public int? level { get; set; }
/// <summary> /// <summary>
/// trojan /// trojan
@ -336,7 +336,7 @@ namespace v2rayN.Models
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public int level { get; set; } public int? level { get; set; }
} }
public class Mux4Ray public class Mux4Ray

View file

@ -618,6 +618,15 @@ namespace v2rayN.Resx {
} }
} }
/// <summary>
/// 查找类似 Add [Http] server 的本地化字符串。
/// </summary>
public static string menuAddHttpServer {
get {
return ResourceManager.GetString("menuAddHttpServer", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Add [Hysteria2] server 的本地化字符串。 /// 查找类似 Add [Hysteria2] server 的本地化字符串。
/// </summary> /// </summary>

View file

@ -1198,4 +1198,7 @@
<data name="TransportRequestHostTip5" xml:space="preserve"> <data name="TransportRequestHostTip5" xml:space="preserve">
<value>*grpc Authority</value> <value>*grpc Authority</value>
</data> </data>
<data name="menuAddHttpServer" xml:space="preserve">
<value>Add [Http] server</value>
</data>
</root> </root>

View file

@ -1195,4 +1195,7 @@
<data name="TransportRequestHostTip5" xml:space="preserve"> <data name="TransportRequestHostTip5" xml:space="preserve">
<value>*grpc Authority</value> <value>*grpc Authority</value>
</data> </data>
<data name="menuAddHttpServer" xml:space="preserve">
<value>添加[Http]服务器</value>
</data>
</root> </root>

View file

@ -1168,4 +1168,7 @@
<data name="TransportRequestHostTip5" xml:space="preserve"> <data name="TransportRequestHostTip5" xml:space="preserve">
<value>*grpc Authority</value> <value>*grpc Authority</value>
</data> </data>
<data name="menuAddHttpServer" xml:space="preserve">
<value>新增[Http]伺服器</value>
</data>
</root> </root>

View file

@ -80,7 +80,8 @@ namespace v2rayN.ViewModels
return; return;
} }
} }
if (SelectedSource.configType != EConfigType.Socks) if (SelectedSource.configType != EConfigType.Socks
&& SelectedSource.configType != EConfigType.Http)
{ {
if (Utils.IsNullOrEmpty(SelectedSource.id)) if (Utils.IsNullOrEmpty(SelectedSource.id))
{ {
@ -127,6 +128,7 @@ namespace v2rayN.ViewModels
EConfigType.VMess => ConfigHandler.AddServer(_config, item), EConfigType.VMess => ConfigHandler.AddServer(_config, item),
EConfigType.Shadowsocks => ConfigHandler.AddShadowsocksServer(_config, item), EConfigType.Shadowsocks => ConfigHandler.AddShadowsocksServer(_config, item),
EConfigType.Socks => ConfigHandler.AddSocksServer(_config, item), EConfigType.Socks => ConfigHandler.AddSocksServer(_config, item),
EConfigType.Http => ConfigHandler.AddHttpServer(_config, item),
EConfigType.Trojan => ConfigHandler.AddTrojanServer(_config, item), EConfigType.Trojan => ConfigHandler.AddTrojanServer(_config, item),
EConfigType.VLESS => ConfigHandler.AddVlessServer(_config, item), EConfigType.VLESS => ConfigHandler.AddVlessServer(_config, item),
EConfigType.Hysteria2 => ConfigHandler.AddHysteria2Server(_config, item), EConfigType.Hysteria2 => ConfigHandler.AddHysteria2Server(_config, item),

View file

@ -86,6 +86,7 @@ namespace v2rayN.ViewModels
public ReactiveCommand<Unit, Unit> AddVlessServerCmd { get; } public ReactiveCommand<Unit, Unit> AddVlessServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddShadowsocksServerCmd { get; } public ReactiveCommand<Unit, Unit> AddShadowsocksServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddSocksServerCmd { get; } public ReactiveCommand<Unit, Unit> AddSocksServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddHttpServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddTrojanServerCmd { get; } public ReactiveCommand<Unit, Unit> AddTrojanServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddHysteria2ServerCmd { get; } public ReactiveCommand<Unit, Unit> AddHysteria2ServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddTuicServerCmd { get; } public ReactiveCommand<Unit, Unit> AddTuicServerCmd { get; }
@ -333,6 +334,10 @@ namespace v2rayN.ViewModels
{ {
EditServer(true, EConfigType.Socks); EditServer(true, EConfigType.Socks);
}); });
AddHttpServerCmd = ReactiveCommand.Create(() =>
{
EditServer(true, EConfigType.Http);
});
AddTrojanServerCmd = ReactiveCommand.Create(() => AddTrojanServerCmd = ReactiveCommand.Create(() =>
{ {
EditServer(true, EConfigType.Trojan); EditServer(true, EConfigType.Trojan);

View file

@ -92,6 +92,7 @@ namespace v2rayN.Views
break; break;
case EConfigType.Socks: case EConfigType.Socks:
case EConfigType.Http:
gridSocks.Visibility = Visibility.Visible; gridSocks.Visibility = Visibility.Visible;
break; break;
@ -174,6 +175,7 @@ namespace v2rayN.Views
break; break;
case EConfigType.Socks: case EConfigType.Socks:
case EConfigType.Http:
this.Bind(ViewModel, vm => vm.SelectedSource.id, v => v.txtId4.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.id, v => v.txtId4.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.security, v => v.txtSecurity4.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.security, v => v.txtSecurity4.Text).DisposeWith(disposables);
break; break;

View file

@ -1,17 +1,17 @@
<reactiveui:ReactiveWindow <reactiveui:ReactiveWindow
x:Class="v2rayN.Views.MainWindow" x:Class="v2rayN.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:reactiveui="http://reactiveui.net"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:tb="clr-namespace:H.NotifyIcon;assembly=H.NotifyIcon.Wpf"
xmlns:base="clr-namespace:v2rayN.Base" xmlns:base="clr-namespace:v2rayN.Base"
xmlns:conv="clr-namespace:v2rayN.Converters" xmlns:conv="clr-namespace:v2rayN.Converters"
xmlns:resx="clr-namespace:v2rayN.Resx" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vms="clr-namespace:v2rayN.ViewModels"
xmlns:local="clr-namespace:v2rayN.Views" xmlns:local="clr-namespace:v2rayN.Views"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveui="http://reactiveui.net"
xmlns:resx="clr-namespace:v2rayN.Resx"
xmlns:tb="clr-namespace:H.NotifyIcon;assembly=H.NotifyIcon.Wpf"
xmlns:vms="clr-namespace:v2rayN.ViewModels"
Title="v2rayN" Title="v2rayN"
Width="900" Width="900"
Height="700" Height="700"
@ -79,6 +79,10 @@
x:Name="menuAddSocksServer" x:Name="menuAddSocksServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddSocksServer}" /> Header="{x:Static resx:ResUI.menuAddSocksServer}" />
<MenuItem
x:Name="menuAddHttpServer"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddHttpServer}" />
<MenuItem <MenuItem
x:Name="menuAddTrojanServer" x:Name="menuAddTrojanServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"

View file

@ -86,6 +86,7 @@ namespace v2rayN.Views
this.BindCommand(ViewModel, vm => vm.AddVlessServerCmd, v => v.menuAddVlessServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddVlessServerCmd, v => v.menuAddVlessServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddShadowsocksServerCmd, v => v.menuAddShadowsocksServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddShadowsocksServerCmd, v => v.menuAddShadowsocksServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddSocksServerCmd, v => v.menuAddSocksServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddSocksServerCmd, v => v.menuAddSocksServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddHttpServerCmd, v => v.menuAddHttpServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddTrojanServerCmd, v => v.menuAddTrojanServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddTrojanServerCmd, v => v.menuAddTrojanServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddHysteria2ServerCmd, v => v.menuAddHysteria2Server).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddHysteria2ServerCmd, v => v.menuAddHysteria2Server).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables);