Filter unsupported XHTTP subscription outbounds
This commit is contained in:
@@ -19,6 +19,7 @@ let uci = null;
|
||||
let fixture_uci_data = null;
|
||||
let runtime_settings_cache = null;
|
||||
let runtime_ruleset_folder = runtime_constants.TMP_RULESET_FOLDER;
|
||||
let runtime_supports_xhttp = true;
|
||||
|
||||
let as_string = common.as_string;
|
||||
let read_json_file = common.read_json_file;
|
||||
@@ -509,6 +510,102 @@ function supported_subscription_outbound(outbound) {
|
||||
t == "socks" || t == "http" || t == "hysteria2";
|
||||
}
|
||||
|
||||
function outbound_uses_xhttp(outbound) {
|
||||
return type(outbound) == "object" && type(outbound.transport) == "object" &&
|
||||
lc(as_string(outbound.transport.type || "")) == "xhttp";
|
||||
}
|
||||
|
||||
function ensure_explicit_outbound_supported(outbound, source, name) {
|
||||
if (!runtime_supports_xhttp && outbound_uses_xhttp(outbound))
|
||||
runtime_generate_unsupported(as_string(source) + " '" + as_string(name) + "' uses XHTTP transport, but sing-box-extended is not installed");
|
||||
}
|
||||
|
||||
function subscription_outbound_display_name(outbound) {
|
||||
return type(outbound) == "object"
|
||||
? as_string(outbound.remark || outbound.tag || "unknown")
|
||||
: "unknown";
|
||||
}
|
||||
|
||||
function add_subscription_reference(refs, value) {
|
||||
value = as_string(value);
|
||||
if (value != "")
|
||||
refs[value] = true;
|
||||
}
|
||||
|
||||
function subscription_reference_set(outbounds) {
|
||||
let refs = {};
|
||||
for (let outbound in array_or_empty(outbounds)) {
|
||||
if (type(outbound) != "object")
|
||||
continue;
|
||||
add_subscription_reference(refs, outbound.tag);
|
||||
add_subscription_reference(refs, outbound.remark);
|
||||
}
|
||||
return refs;
|
||||
}
|
||||
|
||||
function subscription_reference_available(reference, source_refs, retained_refs) {
|
||||
reference = as_string(reference);
|
||||
return reference == "" || !source_refs[reference] || retained_refs[reference];
|
||||
}
|
||||
|
||||
function subscription_group_has_retained_member(outbound, retained_refs) {
|
||||
for (let reference in array_or_empty(outbound.outbounds))
|
||||
if (retained_refs[as_string(reference)])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function warn_skipped_subscription_outbound(section_name, outbound, reason) {
|
||||
warn("skipped incompatible subscription outbound for rule '", section_name, "': ",
|
||||
subscription_outbound_display_name(outbound), " (", reason, ")\n");
|
||||
}
|
||||
|
||||
function compatible_subscription_outbounds(outbounds, section_name) {
|
||||
let source_refs = subscription_reference_set(outbounds);
|
||||
let retained = [];
|
||||
for (let outbound in array_or_empty(outbounds)) {
|
||||
if (!supported_subscription_outbound(outbound))
|
||||
continue;
|
||||
if (!runtime_supports_xhttp && outbound_uses_xhttp(outbound)) {
|
||||
warn_skipped_subscription_outbound(section_name, outbound, "XHTTP requires sing-box-extended");
|
||||
continue;
|
||||
}
|
||||
push(retained, outbound);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
let retained_refs = subscription_reference_set(retained);
|
||||
let next = [];
|
||||
let changed = false;
|
||||
for (let outbound in retained) {
|
||||
let detour = as_string(outbound.detour || "");
|
||||
if (!subscription_reference_available(detour, source_refs, retained_refs)) {
|
||||
warn_skipped_subscription_outbound(section_name, outbound,
|
||||
"detour depends on unavailable outbound '" + detour + "'");
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
if (subscription_group_outbound(outbound) &&
|
||||
!subscription_group_has_retained_member(outbound, retained_refs)) {
|
||||
warn_skipped_subscription_outbound(section_name, outbound, "group has no compatible outbounds");
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
push(next, outbound);
|
||||
}
|
||||
retained = next;
|
||||
if (!changed)
|
||||
return retained;
|
||||
}
|
||||
}
|
||||
|
||||
function subscription_source_outbound_index(source_outbounds, outbound) {
|
||||
for (let i = 0; i < length(source_outbounds); i++)
|
||||
if (source_outbounds[i] === outbound)
|
||||
return i + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function copy_subscription_outbound(outbound, new_tag) {
|
||||
let copy = {};
|
||||
for (let key, value in outbound) {
|
||||
@@ -530,16 +627,23 @@ function copy_subscription_outbound(outbound, new_tag) {
|
||||
return copy;
|
||||
}
|
||||
|
||||
function rewrite_subscription_outbound_references(outbounds, tag_map) {
|
||||
function string_array_contains(values, needle) {
|
||||
for (let value in array_or_empty(values))
|
||||
if (as_string(value) == as_string(needle))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function rewrite_subscription_outbound_references(outbounds, tag_map, source_refs) {
|
||||
for (let outbound in outbounds) {
|
||||
if (type(outbound) != "object")
|
||||
continue;
|
||||
|
||||
for (let key in [ "detour", "default" ]) {
|
||||
let reference = as_string(outbound[key] || "");
|
||||
if (reference != "" && tag_map[reference])
|
||||
outbound[key] = tag_map[reference];
|
||||
}
|
||||
let detour = as_string(outbound.detour || "");
|
||||
if (detour != "" && tag_map[detour])
|
||||
outbound.detour = tag_map[detour];
|
||||
else if (detour != "" && source_refs[detour])
|
||||
delete outbound.detour;
|
||||
|
||||
if (type(outbound.outbounds) == "array") {
|
||||
let rewritten = [];
|
||||
@@ -549,6 +653,16 @@ function rewrite_subscription_outbound_references(outbounds, tag_map) {
|
||||
push(rewritten, tag_map[tag_name]);
|
||||
}
|
||||
outbound.outbounds = rewritten;
|
||||
|
||||
let default_tag = as_string(outbound.default || "");
|
||||
if (default_tag != "" && tag_map[default_tag])
|
||||
default_tag = tag_map[default_tag];
|
||||
if (default_tag == "" || !string_array_contains(rewritten, default_tag))
|
||||
default_tag = length(rewritten) > 0 ? rewritten[0] : "";
|
||||
if (default_tag != "")
|
||||
outbound.default = default_tag;
|
||||
else
|
||||
delete outbound.default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -628,10 +742,20 @@ function add_subscription_source_with_state(config, section, source_index, sourc
|
||||
))
|
||||
return 0;
|
||||
|
||||
let outbounds = runtime_subscription.read_source_outbounds(source_section);
|
||||
if (length(outbounds) == 0)
|
||||
let source_outbounds = runtime_subscription.read_source_outbounds(source_section);
|
||||
if (length(source_outbounds) == 0)
|
||||
return 0;
|
||||
|
||||
let skipped = {};
|
||||
for (let outbound in source_outbounds) {
|
||||
if (supported_subscription_outbound(outbound))
|
||||
continue;
|
||||
let t = type(outbound) == "object" ? as_string(outbound.type || "missing-type") : "non-object";
|
||||
if (reportable_skipped_subscription_type(t))
|
||||
skipped[t] = (skipped[t] || 0) + 1;
|
||||
}
|
||||
let outbounds = compatible_subscription_outbounds(source_outbounds, section_name);
|
||||
|
||||
if (show_metadata !== false)
|
||||
runtime_subscription.merge_source_metadata(state, section_name, source_section, source_index, source_entry);
|
||||
let visibility_refs = subscription_visibility_refs(outbounds);
|
||||
@@ -645,15 +769,8 @@ function add_subscription_source_with_state(config, section, source_index, sourc
|
||||
let group_flags = [];
|
||||
let hidden_flags = [];
|
||||
let tag_map = {};
|
||||
let skipped = {};
|
||||
for (let i = 0; i < length(outbounds); i++) {
|
||||
let outbound = outbounds[i];
|
||||
if (!supported_subscription_outbound(outbound)) {
|
||||
let t = type(outbound) == "object" ? as_string(outbound.type || "missing-type") : "non-object";
|
||||
if (reportable_skipped_subscription_type(t))
|
||||
skipped[t] = (skipped[t] || 0) + 1;
|
||||
continue;
|
||||
}
|
||||
if (include_urltest_groups === false && subscription_urltest_group_outbound(outbound))
|
||||
continue;
|
||||
let display_name = as_string(outbound.remark || outbound.tag || ("server-" + (i + 1)));
|
||||
@@ -670,7 +787,7 @@ function add_subscription_source_with_state(config, section, source_index, sourc
|
||||
if (as_string(outbound.remark || "") != "")
|
||||
tag_map[as_string(outbound.remark)] = new_tag;
|
||||
push(prepared, copy_subscription_outbound(outbound, new_tag));
|
||||
push(source_indices, i + 1);
|
||||
push(source_indices, subscription_source_outbound_index(source_outbounds, outbound));
|
||||
push(display_names, display_name);
|
||||
push(source_links, as_string(outbound.share_link || ""));
|
||||
push(group_flags, subscription_group_outbound(outbound));
|
||||
@@ -680,7 +797,7 @@ function add_subscription_source_with_state(config, section, source_index, sourc
|
||||
if (length(keys(skipped)) > 0)
|
||||
warn("skipped unsupported subscription outbounds for rule '", section_name, "': ", subscription_skip_summary(skipped), "\n");
|
||||
|
||||
rewrite_subscription_outbound_references(prepared, tag_map);
|
||||
rewrite_subscription_outbound_references(prepared, tag_map, subscription_reference_set(source_outbounds));
|
||||
let added = 0;
|
||||
for (let i = 0; i < length(prepared); i++) {
|
||||
let outbound = prepared[i];
|
||||
@@ -1780,13 +1897,14 @@ function add_manual_proxy_link(config, state, section_name, manual_index, link,
|
||||
taken[tag_name] = true;
|
||||
|
||||
let outbound = manual_link_outbound(link, tag_name);
|
||||
let display_name = url_fragment(link);
|
||||
if (display_name == "")
|
||||
display_name = tag_name;
|
||||
ensure_explicit_outbound_supported(outbound, "manual outbound", display_name);
|
||||
push(config.outbounds, outbound);
|
||||
push(selector_tags, tag_name);
|
||||
push(urltest_candidate_tags, tag_name);
|
||||
|
||||
let display_name = url_fragment(link);
|
||||
if (display_name == "")
|
||||
display_name = tag_name;
|
||||
state.links[tag_name] = as_string(link);
|
||||
state.outboundMetadata.names[tag_name] = display_name;
|
||||
if (as_string(outbound.server || "") != "")
|
||||
@@ -1913,6 +2031,7 @@ function prepare_json_connection_outbounds(section, taken) {
|
||||
let legacy_tag = connection_item_tag(section[".name"], "json", i + 1);
|
||||
let base = display_name != "" ? display_name : legacy_tag;
|
||||
let tag_name = unique_tag(base, taken);
|
||||
ensure_explicit_outbound_supported(outbound, "JSON outbound", display_name != "" ? display_name : legacy_tag);
|
||||
taken[tag_name] = true;
|
||||
if (display_name != "" && !tag_map[display_name])
|
||||
tag_map[display_name] = tag_name;
|
||||
@@ -2575,7 +2694,10 @@ function add_server_routes(config, servers, sections) {
|
||||
}
|
||||
}
|
||||
|
||||
function generate_config(output_path, service_address, mwan3_active) {
|
||||
function generate_config(output_path, service_address, mwan3_active, supports_xhttp) {
|
||||
runtime_supports_xhttp = supports_xhttp == null || as_string(supports_xhttp) == ""
|
||||
? true
|
||||
: cli_bool(supports_xhttp);
|
||||
let cursor = uci_cursor();
|
||||
cursor.load(CONFIG_NAME);
|
||||
runtime_settings_cache = object_or_empty(cursor.get_all(CONFIG_NAME, "settings"));
|
||||
@@ -2610,11 +2732,11 @@ function generate_config(output_path, service_address, mwan3_active) {
|
||||
}
|
||||
}
|
||||
|
||||
function generate_config_fixture(fixture_path, output_path, service_address, mwan3_active) {
|
||||
function generate_config_fixture(fixture_path, output_path, service_address, mwan3_active, supports_xhttp) {
|
||||
use_fixture_cursor(fixture_path);
|
||||
runtime_subscription.set_section_cache_dir(output_path + ".section-cache");
|
||||
runtime_ruleset_folder = output_path + ".rulesets";
|
||||
generate_config(output_path, service_address, mwan3_active);
|
||||
generate_config(output_path, service_address, mwan3_active, supports_xhttp);
|
||||
}
|
||||
|
||||
function merge_object_values(target, source) {
|
||||
@@ -3028,9 +3150,9 @@ function route_rule_has_resolve_matchers(service_tag, tag) {
|
||||
let mode = ARGV[0] || "";
|
||||
|
||||
if (mode == "generate-config")
|
||||
generate_config(ARGV[1], ARGV[2], ARGV[3]);
|
||||
generate_config(ARGV[1], ARGV[2], ARGV[3], ARGV[4]);
|
||||
else if (mode == "generate-config-fixture")
|
||||
generate_config_fixture(ARGV[1], ARGV[2], ARGV[3], ARGV[4]);
|
||||
generate_config_fixture(ARGV[1], ARGV[2], ARGV[3], ARGV[4], ARGV[5]);
|
||||
else if (mode == "stdin-length")
|
||||
stdin_length();
|
||||
else if (mode == "stdin-contains")
|
||||
|
||||
@@ -770,7 +770,8 @@ function init_config(populate_nft, caches_prepared, no_refresh) {
|
||||
"generate-config",
|
||||
temp_config,
|
||||
service_listen_address_value(settings),
|
||||
mwan3_active ? "1" : "0"
|
||||
mwan3_active ? "1" : "0",
|
||||
sing_box_is_extended(sing_box_version()) ? "1" : "0"
|
||||
]) + " >" + shell_quote(runtime_log) + " 2>&1"
|
||||
);
|
||||
if (generate_status != 0) {
|
||||
|
||||
+218
-1
@@ -99,12 +99,13 @@ generate_config() {
|
||||
generate_config_with_subscription_cache() {
|
||||
local fixture="$1"
|
||||
local output="$2"
|
||||
local supports_xhttp="${3:-1}"
|
||||
|
||||
mkdir -p "$output.section-cache" "$output.rulesets"
|
||||
TMP_SUBSCRIPTION_FOLDER="$WORK_DIR/subscriptions" \
|
||||
FORKOP_PERSISTENT_SUBSCRIPTION_CACHE_DIR="$WORK_DIR/persistent-subscription-cache" \
|
||||
ucode -L "$FORKOP_LIB" "$FORKOP_LIB/singbox/generator.uc" generate-config-fixture \
|
||||
"$fixture" "$output" "127.0.0.1" "0"
|
||||
"$fixture" "$output" "127.0.0.1" "0" "$supports_xhttp"
|
||||
}
|
||||
|
||||
cat >"$WORK_DIR/no-enabled-fixture.json" <<'JSON'
|
||||
@@ -614,6 +615,112 @@ cat >"$WORK_DIR/subscription-group-disabled-fixture.json" <<'JSON'
|
||||
}
|
||||
JSON
|
||||
|
||||
cat >"$WORK_DIR/subscription-xhttp-fixture.json" <<'JSON'
|
||||
{
|
||||
"settings": {
|
||||
".name": "settings",
|
||||
".type": "settings",
|
||||
"config_path": "/tmp/sing-box/config.json",
|
||||
"dns_server": "1.1.1.1",
|
||||
"service_listen_address": "127.0.0.1"
|
||||
},
|
||||
"section": [
|
||||
{
|
||||
".name": "xhttp",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "connection",
|
||||
"subscription_urls": [ "https://example.com/xhttp.json" ],
|
||||
"subscription_url_settings": "{\"https://example.com/xhttp.json\":{\"user_agent\":\"Happ\"}}",
|
||||
"domain_suffix": [ "xhttp.example" ]
|
||||
}
|
||||
],
|
||||
"urltest": [
|
||||
{
|
||||
".name": "ut_xhttp",
|
||||
".type": "urltest",
|
||||
"section": "xhttp",
|
||||
"name": "Compatible URLTest"
|
||||
}
|
||||
],
|
||||
"priority_group": [
|
||||
{
|
||||
".name": "pg_xhttp",
|
||||
".type": "priority_group",
|
||||
"section": "xhttp",
|
||||
"name": "Compatible Priority"
|
||||
}
|
||||
],
|
||||
"priority_level": [
|
||||
{
|
||||
".name": "pl_xhttp",
|
||||
".type": "priority_level",
|
||||
"group": "pg_xhttp",
|
||||
"name": "All compatible",
|
||||
"order": "0",
|
||||
"filter_mode": "disabled"
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
|
||||
cat >"$WORK_DIR/subscription-only-xhttp-fixture.json" <<'JSON'
|
||||
{
|
||||
"settings": {
|
||||
".name": "settings",
|
||||
".type": "settings",
|
||||
"config_path": "/tmp/sing-box/config.json",
|
||||
"dns_server": "1.1.1.1",
|
||||
"service_listen_address": "127.0.0.1"
|
||||
},
|
||||
"section": [
|
||||
{
|
||||
".name": "only_xhttp",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "connection",
|
||||
"subscription_urls": [ "https://example.com/only-xhttp.json" ],
|
||||
"subscription_url_settings": "{\"https://example.com/only-xhttp.json\":{\"user_agent\":\"Happ\"}}",
|
||||
"domain_suffix": [ "only-xhttp.example" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
|
||||
cat >"$WORK_DIR/manual-xhttp-fixture.json" <<'JSON'
|
||||
{
|
||||
"settings": { ".name": "settings", ".type": "settings", "dns_server": "1.1.1.1" },
|
||||
"section": [
|
||||
{
|
||||
".name": "manual_xhttp",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "connection",
|
||||
"selector_proxy_links": [
|
||||
"vless://00000000-0000-4000-8000-000000000001@xhttp.example:443?encryption=none&security=tls&sni=xhttp.example&type=xhttp#Manual XHTTP"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
|
||||
cat >"$WORK_DIR/json-xhttp-fixture.json" <<'JSON'
|
||||
{
|
||||
"settings": { ".name": "settings", ".type": "settings", "dns_server": "1.1.1.1" },
|
||||
"section": [
|
||||
{
|
||||
".name": "json_xhttp",
|
||||
".type": "section",
|
||||
"enabled": "1",
|
||||
"action": "connection",
|
||||
"outbound_jsons": [
|
||||
"{\"type\":\"vless\",\"tag\":\"JSON XHTTP\",\"server\":\"xhttp.example\",\"server_port\":443,\"uuid\":\"00000000-0000-4000-8000-000000000002\",\"transport\":{\"type\":\"xhttp\"}}"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
|
||||
mkdir -p "$WORK_DIR/subscriptions" "$WORK_DIR/persistent-subscription-cache"
|
||||
for source in proxy-subscription-1 proxy-subscription-2 test-subscription-1; do
|
||||
cat >"$WORK_DIR/subscriptions/$source.json" <<'JSON'
|
||||
@@ -675,6 +782,74 @@ cat >"$WORK_DIR/subscriptions/grouped-subscription-1.json" <<'JSON'
|
||||
JSON
|
||||
printf '%s' 'https://example.com/group.json' >"$WORK_DIR/subscriptions/grouped-subscription-1.url"
|
||||
printf '%s' 'Happ' >"$WORK_DIR/subscriptions/grouped-subscription-1.user_agent"
|
||||
cat >"$WORK_DIR/subscriptions/xhttp-subscription-1.json" <<'JSON'
|
||||
{
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "urltest",
|
||||
"tag": "Provider XHTTP Group",
|
||||
"outbounds": [ "xhttp-node", "plain-node" ],
|
||||
"default": "xhttp-node",
|
||||
"url": "https://www.gstatic.com/generate_204",
|
||||
"remark": "Provider XHTTP Group",
|
||||
"__forkop_allow_group": true
|
||||
},
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "xhttp-node",
|
||||
"remark": "Moscow XHTTP",
|
||||
"server": "127.0.0.10",
|
||||
"server_port": 443,
|
||||
"uuid": "00000000-0000-4000-8000-000000000010",
|
||||
"transport": { "type": "xhttp" },
|
||||
"__forkop_hidden": true
|
||||
},
|
||||
{
|
||||
"type": "socks",
|
||||
"tag": "plain-node",
|
||||
"remark": "Plain node",
|
||||
"server": "127.0.0.11",
|
||||
"server_port": 1080,
|
||||
"__forkop_hidden": true
|
||||
},
|
||||
{
|
||||
"type": "socks",
|
||||
"tag": "depends-on-xhttp",
|
||||
"remark": "Depends on XHTTP",
|
||||
"server": "127.0.0.12",
|
||||
"server_port": 1080,
|
||||
"detour": "xhttp-node"
|
||||
},
|
||||
{
|
||||
"type": "socks",
|
||||
"tag": "recursive-dependent",
|
||||
"remark": "Recursive dependent",
|
||||
"server": "127.0.0.13",
|
||||
"server_port": 1080,
|
||||
"detour": "depends-on-xhttp"
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
printf '%s' 'https://example.com/xhttp.json' >"$WORK_DIR/subscriptions/xhttp-subscription-1.url"
|
||||
printf '%s' 'Happ' >"$WORK_DIR/subscriptions/xhttp-subscription-1.user_agent"
|
||||
cat >"$WORK_DIR/subscriptions/only_xhttp-subscription-1.json" <<'JSON'
|
||||
{
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "vless",
|
||||
"tag": "only-xhttp-node",
|
||||
"remark": "Only XHTTP node",
|
||||
"server": "127.0.0.20",
|
||||
"server_port": 443,
|
||||
"uuid": "00000000-0000-4000-8000-000000000020",
|
||||
"transport": { "type": "xhttp" }
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
printf '%s' 'https://example.com/only-xhttp.json' >"$WORK_DIR/subscriptions/only_xhttp-subscription-1.url"
|
||||
printf '%s' 'Happ' >"$WORK_DIR/subscriptions/only_xhttp-subscription-1.user_agent"
|
||||
|
||||
generate_config "$WORK_DIR/disabled-updates-fixture.json" "$WORK_DIR/disabled.json"
|
||||
generate_config "$WORK_DIR/default-updates-fixture.json" "$WORK_DIR/default.json"
|
||||
@@ -692,6 +867,31 @@ generate_config "$WORK_DIR/domain-ip-rulesets-fixture.json" "$WORK_DIR/domain-ip
|
||||
generate_config_with_subscription_cache "$WORK_DIR/subscription-metadata-fixture.json" "$WORK_DIR/subscription-metadata.json"
|
||||
generate_config_with_subscription_cache "$WORK_DIR/subscription-group-fixture.json" "$WORK_DIR/subscription-group.json"
|
||||
generate_config_with_subscription_cache "$WORK_DIR/subscription-group-disabled-fixture.json" "$WORK_DIR/subscription-group-disabled.json"
|
||||
generate_config_with_subscription_cache "$WORK_DIR/subscription-xhttp-fixture.json" "$WORK_DIR/subscription-xhttp-extended.json" 1
|
||||
generate_config_with_subscription_cache "$WORK_DIR/subscription-xhttp-fixture.json" "$WORK_DIR/subscription-xhttp-stable.json" 0 \
|
||||
2>"$WORK_DIR/subscription-xhttp-stable.stderr"
|
||||
grep -Fq "Moscow XHTTP (XHTTP requires sing-box-extended)" "$WORK_DIR/subscription-xhttp-stable.stderr" ||
|
||||
fail "stable sing-box XHTTP warning should identify the incompatible subscription outbound"
|
||||
grep -Fq "Recursive dependent (detour depends on unavailable outbound 'depends-on-xhttp')" "$WORK_DIR/subscription-xhttp-stable.stderr" ||
|
||||
fail "stable sing-box XHTTP filtering should report recursively pruned detours"
|
||||
|
||||
if generate_config_with_subscription_cache "$WORK_DIR/subscription-only-xhttp-fixture.json" "$WORK_DIR/subscription-only-xhttp.json" 0 \
|
||||
>"$WORK_DIR/subscription-only-xhttp.stdout" 2>"$WORK_DIR/subscription-only-xhttp.stderr"; then
|
||||
fail "stable sing-box should reject a subscription section with only XHTTP outbounds"
|
||||
fi
|
||||
grep -Fq "Only XHTTP node (XHTTP requires sing-box-extended)" "$WORK_DIR/subscription-only-xhttp.stderr" ||
|
||||
fail "all-XHTTP subscription failure should identify the incompatible outbound"
|
||||
|
||||
for fixture in manual-xhttp json-xhttp; do
|
||||
if ucode -L "$FORKOP_LIB" "$SINGBOX_GENERATOR_UC" generate-config-fixture \
|
||||
"$WORK_DIR/$fixture-fixture.json" "$WORK_DIR/$fixture-stable.json" "127.0.0.1" "0" "0" \
|
||||
>"$WORK_DIR/$fixture-stable.stdout" 2>"$WORK_DIR/$fixture-stable.stderr"; then
|
||||
fail "stable sing-box should reject explicit $fixture configuration"
|
||||
fi
|
||||
grep -Fq "uses XHTTP transport, but sing-box-extended is not installed" "$WORK_DIR/$fixture-stable.stderr" ||
|
||||
fail "explicit $fixture XHTTP failure should explain the extended requirement"
|
||||
generate_config "$WORK_DIR/$fixture-fixture.json" "$WORK_DIR/$fixture-extended.json"
|
||||
done
|
||||
|
||||
ucode -e '
|
||||
let fs = require("fs");
|
||||
@@ -938,6 +1138,23 @@ let disabled_grouped_state = cfg("subscription-group-disabled.json.section-cache
|
||||
assert(disabled_grouped_state.urltestGroups["Provider Group"] == null, "skipped provider group is not cached as URLTest");
|
||||
assert(disabled_grouped_state.linkRefs["grouped-out-1"].sourceIndex == 2, "visible leaf keeps source link ref when group import is disabled");
|
||||
|
||||
let xhttp_stable = cfg("subscription-xhttp-stable");
|
||||
assert(outbound(xhttp_stable, "xhttp-node") == null, "stable sing-box excludes XHTTP subscription leaf");
|
||||
assert(outbound(xhttp_stable, "depends-on-xhttp") == null, "stable sing-box excludes outbound detouring through XHTTP");
|
||||
assert(outbound(xhttp_stable, "recursive-dependent") == null, "stable sing-box recursively excludes XHTTP-dependent detours");
|
||||
let stable_provider_group = outbound(xhttp_stable, "Provider XHTTP Group");
|
||||
assert(stable_provider_group && length(stable_provider_group.outbounds) == 1 && stable_provider_group.outbounds[0] == "plain-node", "provider group removes XHTTP member");
|
||||
assert(stable_provider_group.default == "plain-node", "provider group default falls back to a compatible member");
|
||||
let stable_urltest = outbound(xhttp_stable, "xhttp-urltest-ut_xhttp-out");
|
||||
assert(stable_urltest && length(stable_urltest.outbounds) == 1 && stable_urltest.outbounds[0] == "plain-node", "URLTest receives only compatible subscription leaves");
|
||||
let stable_priority = outbound(xhttp_stable, "xhttp-priority-pg_xhttp-out");
|
||||
assert(stable_priority && length(stable_priority.outbounds) == 1 && stable_priority.outbounds[0] == "plain-node", "Priority receives only compatible subscription leaves");
|
||||
|
||||
let xhttp_extended = cfg("subscription-xhttp-extended");
|
||||
assert(outbound(xhttp_extended, "xhttp-node") != null, "extended sing-box keeps XHTTP subscription leaf");
|
||||
assert(outbound(xhttp_extended, "depends-on-xhttp").detour == "xhttp-node", "extended sing-box keeps XHTTP detour chain");
|
||||
assert(outbound(xhttp_extended, "recursive-dependent").detour == "depends-on-xhttp", "extended sing-box keeps recursive detour chain");
|
||||
|
||||
let proxy_cache = json(fs.readfile(dir + "/subscription-metadata.json.section-cache/proxy.json"));
|
||||
let test_cache = json(fs.readfile(dir + "/subscription-metadata.json.section-cache/test.json"));
|
||||
let proxy_share_link_cached = false;
|
||||
|
||||
Reference in New Issue
Block a user