Files
ShadowsocksX-NG/ShadowsocksX-NG2/Domain/SystemProxy/SystemProxyHelperEngine.swift
T
Qiu Yuzhou 856d6b4272 refactor(domain): 按主题重组 Domain/ 源码目录
Domain/ 根目录原有 62 个平铺源文件,无法从目录读出主题划分。

按「目录即主题」重组为 11 个目录(Rules/Custom 为嵌套子目录):Catalog /
Subscription / Rules / Runtime / Activation / Settings / SystemProxy /
Plugin / Credentials / LegacyImport / Diagnostics。纯移动:类型名、模块名
与 import 均未改动,Domain/ 根下不再有散落文件。目录口径取自
GLOSSARY.md 的章节划分(配置与订阅、代理规则、代理控制与网络、设置、
凭据与诊断),而非新造分类。

配套改动:

- project.yml:wrapper 与 helper 两个 target 按文件引用的 15 条显式路径
  随目录改名,并补注释固定这一约定——这 15 条指向不存在的文件时,
  xcodegen generate 以 "missing source directory" 直接失败(实测,不静默)。
- SkeletonTests 新增 DomainLayoutTests:断言 Domain/ 根下无平铺 .swift、
  树下只有 .swift。后者防的是与 App/ 同源的坑——Domain/ 整目录是 app 与
  Core 的 source path,任何非源码文件都会被 XcodeGen 收进 Resources 阶段
  并封进签名后的 bundle。
- README 工程结构说明同步;docs/design 与 docs/research 属历史文档不改。

验证:xcodegen generate 后 app 164 / Core 163 源文件数与整理前一致;Agent
恰好 9 个、Helper 恰好 6 个 Domain 文件(无缺失无多出);app 与 Core 各覆盖
全部 62 个。clean build 0 error;task test 1024 用例全通过(整理前 1022,
+2 为新增守卫);task format 未改写任何被移动文件(证明纯移动);task lint
与整理前同为 21 条内容型 warning;packaging-gate.sh ALL CHECKS PASSED。

布局守卫已做变异验证:临时投放松散 .swift 与非 .swift 文件时两个用例均
失败并点名违规文件,清理后复验为绿。
2026-10-05 20:13:35 +08:00

92 lines
2.9 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Foundation
/// 特权 helper 的请求引擎(issue #71):解码 closed typed 请求 → 注入的特权
/// 写入器 → 编码应答。引擎本身不含任何产品策略,只在 payload 无效时拒绝。
/// 所有连接共享一条串行队列:请求逐个执行,最后接受的请求决定设备级配置
/// (last-writer-wins)。
final class SystemProxyHelperEngine: NSObject, SystemProxyHelperControlling {
/// 注入的特权写入器:真正触到 SystemConfiguration 的唯一边界。
typealias Performing =
@Sendable (SystemProxyHelperEngine.Request) throws ->
SystemProxyHelperEngine.Outcome
enum Request: Equatable, Sendable {
case apply(SystemProxyConfiguration)
case clear
}
enum Outcome: Equatable, Sendable {
case applied(SystemProxyWriteOutcome)
case cleared
}
/// 跨全部 XPC 连接共享的串行执行队列。
private static let sharedQueue = DispatchQueue(
label: "\(SystemProxyHelperIdentity.machServiceName).work")
private let queue: DispatchQueue
private let perform: Performing
init(perform: @escaping Performing, queue: DispatchQueue? = nil) {
self.perform = perform
self.queue = queue ?? Self.sharedQueue
super.init()
}
func apply(_ payload: Data, withReply reply: @escaping (Data) -> Void) {
let reply = ReplyBox(reply)
queue.async { [perform, reply] in
let request = Result { try SystemProxyHelperWire.decodeConfiguration(payload) }
reply(Self.responseData(request: request.map { .apply($0) }, perform: perform))
}
}
func clear(withReply reply: @escaping (Data) -> Void) {
let reply = ReplyBox(reply)
queue.async { [perform, reply] in
reply(Self.responseData(request: .success(.clear), perform: perform))
}
}
// MARK: - 请求执行(纯逻辑,可测)
static func responseData(
request: Result<Request, Error>, perform: Performing
) -> Data {
SystemProxyHelperWire.encode(response(request: request, perform: perform))
}
static func response(
request: Result<Request, Error>, perform: Performing
) -> SystemProxyHelperResponse {
do {
let outcome = try perform(request.get())
switch outcome {
case .applied(let writeOutcome):
return .applied(writeOutcome)
case .cleared:
return .cleared
}
} catch let error as SystemProxyError {
return .failure(error)
} catch {
return .failure(.helperUnavailable(String(describing: error)))
}
}
}
/// XPC 应答闭包的 Sendable 桥。@objc 协议里 reply 参数无法标注 @Sendable,
/// 但 XPC 运行时生成的应答块线程安全且恰好调用一次,引擎在串行工作队列上
/// 调用它;装箱只为跨过 queue.async 的 @Sendable 边界。
private final class ReplyBox: @unchecked Sendable {
private let reply: (Data) -> Void
init(_ reply: @escaping (Data) -> Void) {
self.reply = reply
}
func callAsFunction(_ data: Data) {
reply(data)
}
}