Repository Wiki
BeyondDimension/SteamTools

反向代理子系统(ReverseProxy 进程)

BD.WTTS.Client.Plugins.Accelerator.ReverseProxy 是 Watt Toolkit(Steam++)加速器的一个独立子进程,以 ASP.NET Core(Kestrel)承载本地 HTTP(S) 反向代理、正向代理、隧道转发与流量分析能力,并通过 dotnetCampus.Ipc IPC 框架向主进程暴露控制面接口。其设计大量参考并移植了 FastGithub 2.1.4 的实现(源码中多处保留出处注释)。

目的与范围(Purpose and Scope)

本页覆盖反向代理子进程端的完整机制:

  • 进程入口 Program.cs 与基于 IPCSubProcessService.MainAsync 的多进程宿主启动流程;
  • 进程内 DI 服务注册体系(AddReverseProxyService / AddReverseProxyServer / AddDomainResolve / AddFlowAnalyze 等);
  • Kestrel 中间件管线(HttpProxyMiddleware、TunnelMiddleware、HttpReverseProxyMiddleware、HttpProxyPacMiddleware、HttpLocalRequestMiddleware、RequestLoggingMiddleware)及其挂载扩展方法;
  • 证书子系统(CertGenerator、CertificateManagerImpl、LazyCertificateManager);
  • 域名解析(DomainResolver)、流量分析(FlowAnalyzer / FlowAnalyzeDuplexPipe)与配置抽象(IReverseProxyConfig / ReverseProxyConfig)。

以下内容不在本页范围内,属于兄弟页面:

  • 主进程侧如何拉起/管理该子进程、IPC 客户端封装(属于加速器多进程架构页面);
  • 加速脚本规则与兑换码等业务加速配置;
  • 主进程 UI 与加速开关状态机。

概述(Overview)

Watt Toolkit 的"网络加速"需要在本机监听端口、改写 HTTPS 流量(MITM)并管理根证书,这些操作存在崩溃风险且涉及管理员权限。将其放进独立子进程可以:

  1. 故障隔离:代理引擎崩溃不影响主 UI 进程;
  2. 权限最小化:仅该进程需要提权(如写入证书存储);
  3. 生命周期解耦:加速开启/关闭即子进程启动/退出,网络栈随进程销毁彻底释放。

进程启动后通过 IPC 暴露两个"联合点"(IpcJoint):LazyReverseProxyServiceImpl(反向代理服务,主进程远程调用)与 LazyCertificateManager(证书管理器)。子进程内部由 YARP(YarpReverseProxyServiceImpl)实现数据面,围绕它注册域名解析、HttpClient 工厂、Cookie 客户端、证书服务与流量分析等支撑组件。

关键概念:

概念含义
控制面(IPC)主进程通过 IPCSubProcessService + CreateIpcJoint 远程调用 IReverseProxyService / ICertificateManager
数据面(Kestrel)本地监听端口,按中间件链处理正向代理 / 反向代理 / 隧道 / PAC / 本地请求
MITM 证书由 CertGenerator 动态签发站点证书,根证书由 CertificateManagerImpl 安装/管理
流量分析FlowAnalyzer 通过包装 DuplexPipe 统计上下行字节数(FlowStatistics)

架构

Loading diagram...

说明:

  • 入口层:Program.cs 是 top-level program,先做系统关机早退检查,再进入 IPCSubProcessService.MainAsync 多进程宿主。
  • IPC 层:两个 CreateIpcJoint 调用把懒加载单例注册为 IPC 服务端点,主进程由此远程控制反向代理与证书。懒加载(Lazy* 前缀)意味着对象只在第一次被远程调用时才真正构造。
  • 核心层:YarpReverseProxyServiceImpl 同时实现 IReverseProxyService(行为)与 IReverseProxySettings(设置),以单一实例注册到两种接口;ReverseProxyConfig 把它包装为 IReverseProxyConfig 供 Kestrel/配置绑定使用。
  • 数据面:中间件均为单例注册(AddReverseProxyServer),Kestrel 按 ApplicationBuilderExtensions 提供的扩展方法挂载。
  • 支撑层:DomainResolver 自定义域名 → IP 解析(绕过系统 DNS),FlowAnalyzer 挂在管道上做字节级统计。

进程启动流程(Core Flow)

Loading diagram...

逐步解读(对照真实代码):

  1. 关机早退:if (OSShuttingDownHelper.IsSystemShuttingDown()) return 0; 避免在系统关机瞬间残留子进程。
  2. 宿主进入:IPCSubProcessService.MainAsync(moduleName, pluginName, ConfigureServices, onStarted, args) 以 AssemblyInfo.Accelerator 作为模块名/插件名建立 IPC 管道。
  3. onStarted 回调:宿主就绪后调用第四个参数,在其中 CreateIpcJoint 注册两个远程端点并初始化 AppCenter。
  4. 异常兜底:catch 块打印异常并 Console.ReadLine()(便于人工观察崩溃现场),返回 500 作为子进程退出码。
  5. DEBUG 辅助:DEBUG 构建下用 SetConsoleTitle 打印进程 Id 与提权状态(IsProcessElevated_DEBUG_Only 用 WindowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator) 判断)。

用法示例(Usage Examples)

进程入口:IPC 宿主与联合点注册

csharp
1var exitCode = await IPCSubProcessService.MainAsync(moduleName, pluginName, ConfigureServices, static ipcProvider => 2{ 3 VisualStudioAppCenterSDK.Init(); 4 5 // 添加反向代理服务(供主进程的 IPC 远程访问) 6 ipcProvider.CreateIpcJoint(LazyReverseProxyServiceImpl.Instance); 7 ipcProvider.CreateIpcJoint(LazyCertificateManager.Instance); 8}, args);

Source: Program.cs

此段是整个子进程的控制面契约:主进程仅能看到 LazyReverseProxyServiceImpl 与 LazyCertificateManager 暴露的接口成员,数据面(Kestrel 监听、YARP 路由)完全封装在子进程内部。

子进程服务注册:日志、仓储与安全存储

csharp
1static void ConfigureServices(IServiceCollection services) 2{ 3 services.AddLogging(l => 4 { 5 l.ClearProviders(); 6 l.AddNLog(LogManager.Configuration!); // 添加 NLog 日志 7#if DEBUG 8 l.AddConsole(); 9#endif 10 l.AddProvider(new LogConsoleService.Utf8StringLoggerProvider(moduleName)); 11 }); 12 13 // 设置仓储层数据库文件存放路径 14 Repository.DataBaseDirectory = IOPath.AppDataDirectory; 15 services.TryAddSingleton<ISecureStorage, RepositorySecureStorage>(); 16 17 services.AddHttpClient(); 18 services.AddCommonHttpClientFactory(); 19 services.AddSingleton<IHttpPlatformHelperService, HttpPlatformHelperConsoleService>(); 20 21 services.AddDnsAnalysisService(); 22 // 添加反向代理服务(子进程实现) 23 services.AddReverseProxyService(); 24 services.AddSingleton<ICertificateManager, CertificateManagerImpl>(); 25}

Source: Program.cs

要点:

  • Repository.DataBaseDirectory = IOPath.AppDataDirectory 把仓储数据库指向应用数据目录,子进程拥有独立持久化。
  • RepositorySecureStorage 用本地数据库实现 ISecureStorage,键经 Hashs.String.SHA256 散列后存 KeyValuePair 表——代理所需的敏感数据(如上游账号态)不依赖主进程的安全存储。
  • HttpPlatformHelperConsoleService 固定了一个 Chrome/Edge UA,供子进程内 HttpClient 使用。

YARP 反向代理服务注册与配置绑定

csharp
1public static IServiceCollection AddReverseProxyService(this IServiceCollection services) 2{ 3 services.AddSingleton<YarpReverseProxyServiceImpl>(); 4 services.AddSingleton<IReverseProxySettings>(s => s.GetRequiredService<YarpReverseProxyServiceImpl>()); 5 services.AddSingleton<IReverseProxyService>(s => s.GetRequiredService<YarpReverseProxyServiceImpl>()); 6 return services; 7} 8 9internal static IServiceCollection AddConfiguration(this IServiceCollection services, YarpReverseProxyServiceImpl reverseProxyService) 10{ 11 TypeConverterBinder.Bind(IPAddress2.ParseNullable, val => val?.ToString()); 12 TypeConverterBinder.Bind(IPEndPoint.Parse, val => val?.ToString()); 13 14 // reverseProxyService 不能直接添加进服务集合,因 host 会释放 service 15 services.AddSingleton<IReverseProxyConfig>(new ReverseProxyConfig(reverseProxyService)); 16 return services; 17}

Source: ServiceCollectionExtensions.cs

设计意图:

  • 单实例多接口:YarpReverseProxyServiceImpl 只实例化一次,同时以 IReverseProxySettings(读)与 IReverseProxyService(读写)暴露,避免两份状态。
  • 生命周期规避:注释明确说明 ReverseProxyConfig 必须以 new 直接注册——若经工厂解析,Host 关闭时会 dispose 掉 reverseProxyService,导致 IPC 会话结束后对象失效。这是子进程宿主模型下常见的陷阱处理。
  • TypeConverterBinder:为 IPAddress? / IPEndPoint 注册字符串转换器,使 Kestrel 配置绑定支持 "0.0.0.0:80" 这类端点写法。

中间件挂载扩展(数据面管线)

csharp
1internal static IApplicationBuilder UseHttpReverseProxy(this IApplicationBuilder app) 2{ 3 var middleware = app.ApplicationServices.GetRequiredService<HttpReverseProxyMiddleware>(); 4 return app.Use(next => context => middleware.InvokeAsync(context, next)); 5} 6 7internal static IApplicationBuilder DisableRequestLogging(this IApplicationBuilder app) 8{ 9 return app.Use(next => context => 10 { 11 var loggingFeature = context.Features.Get<IRequestLoggingFeature>(); 12 if (loggingFeature != null) 13 { 14 loggingFeature.Enable = false; 15 } 16 return next(context); 17 }); 18}

Source: ApplicationBuilderExtensions.cs

Use* 扩展从 DI 解析单例中间件再包一层委托,而不是用常规的 UseMiddleware<T>(每请求构造实例)。对高 QPS 的代理链路而言,单例中间件省去每请求的对象分配;代价是中间件实现必须自身保证线程安全(无实例级可变状态)。DisableRequestLogging 通过 feature 开关按上下文关闭日志(例如健康检查或本地回环请求),而不是全局卸下中间件。

数据面组件注册全景

csharp
1internal static IServiceCollection AddReverseProxyServer(this IServiceCollection services) 2{ 3 services.AddCookieHttpClient(); 4 5 return services 6 .AddMemoryCache() 7 .AddHttpForwarder() 8 .AddSingleton<CertService>() 9 // tcp 10 .AddSingleton<HttpProxyMiddleware>() 11 .AddSingleton<TunnelMiddleware>() 12 //http 13 .AddSingleton<HttpLocalRequestMiddleware>() 14 .AddSingleton<HttpProxyPacMiddleware>() 15 .AddSingleton<RequestLoggingMiddleware>() 16 .AddSingleton<HttpReverseProxyMiddleware>(); 17}

Source: ServiceCollectionExtensions.cs

AddHttpForwarder()(来自 Microsoft.AspNetCore.HttpForwarder/YARP)提供流式转发原语;AddMemoryCache 支撑 DNS/证书结果缓存;注释掉的四行 ICaCertInstaller 表明各平台根证书安装逻辑(Windows/macOS/Linux 系)在整合时被有意移出/替换——实际由 CertificateManagerImpl 承担。源码注释同时保留了 FastGithub 对应文件的出处链接,便于对照移植差异。

配置选项(Configuration Options)

选项/注册类型/签名默认说明
AddReverseProxyService()IServiceCollection 扩展—注册 YarpReverseProxyServiceImpl 并映射到 IReverseProxyService / IReverseProxySettings
AddReverseProxyServer()IServiceCollection 扩展—注册 Kestrel 数据面全部中间件(正向代理/隧道/本地请求/PAC/日志/反向代理)+ CertService + AddHttpForwarder() + AddMemoryCache()
AddReverseProxyHttpClient()IServiceCollection 扩展—注册 ReverseProxyHttpClientFactory(上游连接工厂,供 YARP 指定 SslSocketHandler 等)
AddDomainResolve()IServiceCollection 扩展—TryAddSingleton<IDomainResolver, DomainResolver>,自定义域名→IP 解析
AddFlowAnalyze()IServiceCollection 扩展—AddSingleton<IFlowAnalyzer, FlowAnalyzer>,流量字节统计
AddDnsAnalysisService()IServiceCollection 扩展—DNS 分析服务(见同名扩展文件)
AddCookieHttpClient()IServiceCollection 扩展Timeout = GeneralHttpClientFactory.DefaultTimeout注册名为 CookieHttpClient.HttpClientName 的 HttpClient,UseCookies = true 并共享静态 CookieHttpClient.CookieContainer
Repository.DataBaseDirectorystringIOPath.AppDataDirectory子进程仓储数据库存放目录
HttpPlatformHelperConsoleService.DefaultUserAgentstringChrome 90 / Edge 90 UA 字符串子进程内 HttpClient 的默认 UA

枚举(Enums/ 目录,界定代理行为域):

  • ExternalProxyType — 上游(外部)代理的类型枚举,用于区分转发到第三方代理时所需的连接处理。
  • FlowType — 流量方向枚举(上行/下行),与 FlowStatistics 统计配合。
  • Models/FlowStatistics.cs — 单位时间内的流量统计模型,由 FlowAnalyzeDuplexPipe 填充。

实现细节说明:YarpReverseProxyServiceImpl、各 Middleware 与 KestrelServerOptionsExtensions 的完整内部实现未在本次读取范围内展开,本节仅基于已验证的注册与扩展代码描述其装配关系。

API 参考(API Reference)

IPCSubProcessService.MainAsync(moduleName, pluginName, configureServices, onStarted, args): Task<int>

子进程宿主入口。建立 IPC 服务端管道、初始化 DI、启动宿主并阻塞直至退出。

Parameters:

  • moduleName (string): IPC 模块名,此处为 AssemblyInfo.Accelerator
  • pluginName (string): 插件标识名
  • configureServices (Action<IServiceCollection>): 宿主服务配置回调
  • onStarted (Action<IpcProvider>): 宿主就绪回调,用于 CreateIpcJoint 注册远程端点
  • args (string[]): 命令行参数

Returns: 进程退出码(int);未捕获异常时返回 500。

AddReverseProxyService(this IServiceCollection): IServiceCollection

注册 YARP 实现的反向代理服务;YarpReverseProxyServiceImpl 同一实例注册为两个接口。

UseHttpReverseProxy(this IApplicationBuilder): IApplicationBuilder

从 DI 解析单例 HttpReverseProxyMiddleware 并接入请求管线,执行 YARP 反向代理转发。

同族的挂载方法:UseHttpProxyPac(PAC 脚本下发)、UseHttpLocalRequest(本地请求直连处理)、UseRequestLogging(请求日志)、DisableRequestLogging(按上下文禁用日志)。

ISecureStorage(子进程内实现:RepositorySecureStorage)

键值字节安全存储,方法:GetBytesAsync、SetAsync、RemoveAsync、ContainsKeyAsync;键统一经 SHA256 散列后落库。

故障模式、边界与并发

故障模式

  • 系统关机竞态:入口第一件事即 IsSystemShuttingDown() 早退,避免 Windows 关机时残留代理进程占用端口/证书资源。
  • 未捕获异常:catch (Exception ex) 打印异常、Console.ReadLine() 挂起(让用户能看到崩溃原因,仅非 0 退出码时),最终 return 500 向主进程回报失败。
  • 宿主释放陷阱:AddConfiguration 中显式注释"reverseProxyService 不能直接添加进服务集合,因 host 会释放 service"——IReverseProxyConfig 以 new ReverseProxyConfig(reverseProxyService) 方式注册,绕开 Host 生命周期管理,防止 IPC 端点被提前 dispose。
  • IPC 懒加载:LazyReverseProxyServiceImpl / LazyCertificateManager 只有首次远程调用才构造真实实现,进程启动成本与故障面更小。

并发与资源

  • 单例中间件:全部中间件为 AddSingleton,跨请求共享;线程安全由实现内部保证(无实例级可变状态)。
  • 共享 Cookie 容器:CookieHttpClient.CookieContainer 为静态共享容器,多个逻辑客户端共用同一 Cookie 域——这意味着同一域名下的会话态在代理转发与本地请求之间是互通的,属于有意设计(维持登录态),但也要求使用方不要并发写入冲突 Cookie(源码未见加锁,依赖 HttpClient 内部线程安全模型)。
  • 独立数据库:子进程把 Repository.DataBaseDirectory 指向 IOPath.AppDataDirectory,与主进程的存储隔离,避免 SQLite 并发写冲突。

边界情况

  • 非 Windows 平台:提权检测(IsProcessElevated_DEBUG_Only)在非 Windows 上直接返回 false;#if WINDOWS 分支在 ServiceCollectionExtensions.cs 尾部存在条件编译代码。
  • DEBUG/RELEASE 差异:DEBUG 下额外输出控制台日志、控制台标题与 Console.ReadLine() 等待;RELEASE 下静默。

性能与运维要点(Professional Notes)

  • 流式转发:数据面基于 AddHttpForwarder() 的流式拷贝(YARP HttpForwarder),不做完整请求缓冲,内存占用与并发连接数线性度可控。
  • 流量分析零侵入:FlowAnalyzeDuplexPipe 通过装饰 IDuplexPipe 在流经时累计字节(FlowType 区分方向),把统计逻辑从中间件转发路径中剥离。
  • 缓存:AddMemoryCache() 用于域名解析与证书指纹等热点数据的进程内缓存;证书签发(CertGenerator)结果可缓存复用,避免每个新域名都走生成开销。
  • 可观测性:NLog(复用主配置 LogManager.Configuration)+ LogConsoleService.Utf8StringLoggerProvider(moduleName) 把日志回传主进程统一展示;RequestLoggingMiddleware 可经 IRequestLoggingFeature.Enable 按请求关闭。
  • 运维入口:所有对子进程的操作(启停代理、装/卸证书)都经 IPC 完成,运维侧只需管理"子进程进程级"存活与退出码。

扩展点

  • 新增中间件:仿照 ApplicationBuilderExtensions 的 Use* 模式——先在 AddReverseProxyServer 中 AddSingleton,再写 internal static IApplicationBuilder UseXxx(...) 包装挂载。
  • 新增上游代理类型:扩展 ExternalProxyType 并在 ReverseProxyHttpClientFactory 的 handler 选择中分支处理。
  • 替换证书策略:ICertificateManager 在 DI 中注册为 CertificateManagerImpl,可实现同接口替换安装/管理逻辑;LazyCertificateManager 是其 IPC 门面。
  • 替换域名解析:IDomainResolver 已用 TryAddSingleton 注册,后续注册可覆盖 DomainResolver。

测试

本仓库该插件目录下未发现针对反向代理子进程的独立单元测试工程(基于当前列出的文件清单)。Program.cs 中的 DEBUG 辅助函数(控制台标题打印进程 Id 与提权状态)承担了人工诊断场景下的可观测性角色。

Sources

(3 files)
src/BD.WTTS.Client.Plugins.Accelerator.ReverseProxy
src/BD.WTTS.Client.Plugins.Accelerator.ReverseProxy/Extensions