fix: inherit targetStrategy for custom outbounds (#10597)

GenDns applies the DNS Strategy4Proxy setting to the generated proxy
outbound, but ApplyCustomOutboundReplace replaces the whole JSON object
and dropped targetStrategy when the custom outbound did not define one.
Keep the generated value unless the custom JSON sets it explicitly.

Add regression tests for the inherited and explicitly configured cases.
This commit is contained in:
Henglie
2026-11-14 10:53:16 +08:00
committed by GitHub
parent f2f336d97c
commit 9c7c169056
2 changed files with 82 additions and 0 deletions
@@ -857,6 +857,81 @@ public class CoreConfigV2rayServiceTests
await proxyOutbound!.protocol.Should().BeEqualTo("shadowsocks");
}
[Test]
public async Task GenerateClientConfigContent_CustomOutbound_ShouldInheritTargetStrategyFromDnsSettings()
{
var config = CoreConfigTestFactory.CreateConfig(ECoreType.Xray);
config.SimpleDNSItem.Strategy4Proxy = "UseIPv4";
CoreConfigTestFactory.BindAppManagerConfig(config);
var customNode = CoreConfigTestFactory.CreateCustomOutboundNode(ECoreType.Xray, "n-custom", "custom-xray");
var customJsonContent = """
{
"protocol": "shadowsocks",
"settings": {
"servers": [
{
"address": "1.2.3.4",
"port": 8388,
"method": "aes-128-gcm",
"password": "custom_password"
}
]
}
}
""";
var context = CoreConfigTestFactory.CreateContext(config, customNode, ECoreType.Xray);
context.CustomOutboundContent[customNode.IndexId] = customJsonContent;
var result = new CoreConfigV2rayService(context).GenerateClientConfigContent();
await result.Success.Should().BeTrue().Because($"ret msg: {result.Msg}");
var generatedConfig = JsonNode.Parse(result.Data!.ToString()!)!;
var proxy = generatedConfig["outbounds"]!.AsArray()
.First(outbound => outbound!["tag"]!.GetValue<string>() == Global.ProxyTag)!;
await proxy["targetStrategy"]?.GetValue<string>().Should().BeEqualTo("UseIPv4");
}
[Test]
public async Task GenerateClientConfigContent_CustomOutbound_ShouldKeepExplicitTargetStrategy()
{
var config = CoreConfigTestFactory.CreateConfig(ECoreType.Xray);
config.SimpleDNSItem.Strategy4Proxy = "UseIPv4";
CoreConfigTestFactory.BindAppManagerConfig(config);
var customNode = CoreConfigTestFactory.CreateCustomOutboundNode(ECoreType.Xray, "n-custom", "custom-xray");
var customJsonContent = """
{
"protocol": "shadowsocks",
"targetStrategy": "AsIs",
"settings": {
"servers": [
{
"address": "1.2.3.4",
"port": 8388,
"method": "aes-128-gcm",
"password": "custom_password"
}
]
}
}
""";
var context = CoreConfigTestFactory.CreateContext(config, customNode, ECoreType.Xray);
context.CustomOutboundContent[customNode.IndexId] = customJsonContent;
var result = new CoreConfigV2rayService(context).GenerateClientConfigContent();
await result.Success.Should().BeTrue().Because($"ret msg: {result.Msg}");
var generatedConfig = JsonNode.Parse(result.Data!.ToString()!)!;
var proxy = generatedConfig["outbounds"]!.AsArray()
.First(outbound => outbound!["tag"]!.GetValue<string>() == Global.ProxyTag)!;
await proxy["targetStrategy"]?.GetValue<string>().Should().BeEqualTo("AsIs");
}
[Test]
public async Task GenerateClientConfigContent_XdriveGoogleDrive_ShouldGenerateValidOutbound()
{
@@ -29,6 +29,7 @@ public partial class CoreConfigV2rayService
continue;
}
var outboundTag = outbound.tag;
var outboundTargetStrategy = outbound.targetStrategy;
var outboundDetour = outbound.streamSettings?.sockopt?.dialerProxy ?? string.Empty;
var outboundBindInterface = outbound.streamSettings?.sockopt?.Interface ?? string.Empty;
var customOutboundContent = context.CustomOutboundContent[customOutboundIndex];
@@ -44,6 +45,12 @@ public partial class CoreConfigV2rayService
{
customOutboundObj?["tag"] = outboundTag;
}
if (customOutboundObj is not null
&& customOutboundObj["targetStrategy"] is null
&& !outboundTargetStrategy.IsNullOrEmpty())
{
customOutboundObj["targetStrategy"] = outboundTargetStrategy;
}
if (!containDetourPlaceholder && !outboundDetour.IsNullOrEmpty())
{
customOutboundObj!["streamSettings"] ??= new JsonObject();