Skip to content

feat(adblock): support domains-format blocklist sources (issue #213) - #222

Merged
flyhigher139 merged 3 commits into
masterfrom
feat/adblock-domains-format-213
Sep 24, 2026
Merged

flyhigher139 merged 3 commits into
masterfrom
feat/adblock-domains-format-213

Conversation

@flyhigher139

Copy link
Copy Markdown
Contributor

背景

Closes #213(跟踪 #130、#198)。

fetch_and_cache_source 用 mhost-hosts 的 Parser::parse 解析上游 blocklist,parse_line 要求首 token 必须是 IP —— 裸域名行被静默丢弃。anti-AD domains 版 / oisd 等「一行一个域名」的源会解析出 0 条规则且不报任何错,屏蔽完全不生效,属于静默失败。

方案

显式 format 字段,不做运行时嗅探:

  • 模型:AdBlockSource 新增 format: BlocklistFormat(hosts | domains),#[serde(default)] 默认 hosts,旧 adblock.json 零迁移;字段无条件序列化(issue AdBlock error banner false positive: last_error serialization mismatch (undefined vs null) #202 教训)。
  • 解析:domains 走轻量 reader(trim → 跳过空行/# 注释(含行内)→ 复用 validate_whitelist_domain 校验 + 小写化);hosts 路径顺带统计 ParseResult.errors(此前被静默丢弃)。
  • 反静默失败:
    • 0 规则 + 有错误行 → 硬失败:保留旧缓存,last_error 提示检查 format 设置;
    • 错误行 ≥10 且 >5% → 规则照常生效,last_error 写告警(UI badge 可见)。
  • 缓存格式不变(仍是 canonical 0.0.0.0 domain hosts 文本),DNS 引擎 / overlap 报告 / classify_rules 路径零改动;[Perf] ad-block cache 改为纯域名列表,跳过 hosts parser round-trip + ASCII lowercase 快路径 #198 的 cache 纯域名化重审留给后续。
  • UI:添加表单新增 Format 下拉(默认 hosts;批量添加共用同一选择),源卡片 meta 行显示 format,副标题文案更新。

验收对照(issue 四条)

  1. ✅ domains 格式源端到端解析:mock server 测试 fetch_and_cache_source_domains_format_parses_bare_domains(rule_count 正确、缓存为 canonical hosts 文本);dig 冒烟留手工验证
  2. ✅ hosts 格式回归:现有测试全绿
  3. ✅ 混合格式行为明确定义:严格按声明 format 解析,不匹配行计入 error_count,按阈值告警/硬失败,不允许静默丢规则
  4. ✅ error 行超阈值 → last_error + UI badge:fetch_and_cache_source_warns_when_error_lines_exceed_threshold

明确不做

  • 运行时格式嗅探(issue 明确反对);兜底提示体现在告警文案里
  • set_ad_block_source_format 编辑命令(无 edit-source IPC 先例,改格式 = 删除重加,可后续单独提)
  • oisd 深链 / DShield 等具体源接入

测试

  • cargo fmt --all -- --check ✓ / cargo clippy --all-targets --all-features -- -D warnings ✓
  • cargo test --all-features:mhost-core 51 ✓(+3:serde roundtrip、wire 格式、legacy 文档默认 Hosts)、主 crate 213 ✓(+8:domains reader 正/负路径、阈值边界、端到端、硬失败、告警)
  • pnpm test 336 ✓(+1:Format 下拉透传)、pnpm build ✓

mHost Developer added 3 commits September 24, 2026 08:49
Hosts-format parsing silently dropped bare-domain lines, so lists like
anti-AD's domains.txt fetched 'successfully' with 0 rules and blocking
never engaged — worse than an error because nothing surfaced it.

- AdBlockSource gains an explicit `format` field (hosts | domains),
  chosen at add time; no runtime sniffing. #[serde(default)] = Hosts,
  so legacy adblock.json needs no migration (always serialized per the
  issue #202 lesson).
- New parse_domains_blocklist reader: trim, skip blanks/# comments,
  lowercase + validate via the existing whitelist domain rules.
  The hosts path now also counts ParseResult.errors (previously
  discarded silently).
- Anti-silent-failure contract in fetch_and_cache_source:
  0 rules + malformed lines → hard failure (old cache kept, last_error
  points at the format setting); errors >= 10 and > 5% of kept rules →
  rules go live AND last_error carries an advisory warning.
- Cache stays canonical hosts text, so the DNS engine / overlap report
  paths are untouched.
- UI: Format dropdown on the add-source form (shared by bulk add),
  format shown in the source card meta line, copy updated.

Tests: 8 new backend tests (domains reader, threshold boundaries,
end-to-end domains fetch, loud 0-rule failure, warning path, serde
back-compat), 1 new frontend test, existing assertions updated.
)

CI clippy runs with --workspace, which compiles mhost-storage's lib
tests — the sample_source fixture there was missed in 3d52e43 (local
clippy without --workspace only covered the mhost package). Verified
locally with cargo clippy/test --workspace --all-targets --all-features.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 支持纯域名格式的 blocklist 源(一行一个域名)

1 participant