
mitmproxy 自定义 Options 开发指南类型化配置、configure 事件与 YAML 持久化全解析【免费下载链接】mitmproxyAn interactive TLS-capable intercepting HTTP proxy for penetration testers and software developers.项目地址: https://gitcode.com/GitHub_Trending/mi/mitmproxy在 mitmproxy 中驱动代理运行时行为的一切设置都集中存放在一个全局options 存储global options store中。它不仅是内置功能端口、模式、上游代理等的配置中枢也是第三方 addon 与主程序交互最标准、最强大的接口addon 只需声明一个带类型注解的选项就能自动获得命令行参数、交互式编辑器、YAML 配置文件、类型校验与回滚等一系列完整支持。本文围绕 docs/src/content/addons/options.md 展开结合源码OptManager、Loader、ConfigureHook等深入讲解如何为 addon 定义选项、响应配置变更并解释这套类型化配置系统在 optmanager.py 中是如何实现的。读完本文你将能写出声明即配置的生产级 addon并理解配置在命令行、交互界面与~/.mitmproxy/config.yaml之间的流转机制。Options 机制概览一个贯穿全工具链的配置中枢在 mitmproxy 的设计里options 是会决定 mitmproxy 及其 addon 行为的设置项。它们有三种修改途径且互相等价配置文件从 YAML 配置文件中读取命令行通过--set等标志在启动时覆盖交互式修改用户可在 mitmproxy / mitmweb 的界面里即时改动。所有选项都必须带类型注解。mitmproxy 对支持的类型有统一的序列化 / 反序列化能力并在交互式程序中提供标准化的类型编辑方式。一旦尝试赋入错误类型的值系统会报错。这意味着addon 选项只要声明了类型就自动获得整个 mitmproxy 工具链的完整支持——这是该设计最核心的收益。这套机制还与更上层的配置概念打通根据 docs/src/content/concepts/options.md 的说明所有 mitmproxy 工具共享位于~/.mitmproxy/config.yaml的 YAML 配置文件绝大多数命令行标志本质上是底层 option 的别名交互式工具中的改动也只是在修改运行时 options 存储中的值。因此 addon 自定义的选项也会出现在集中配置文件与选项编辑器中与内置选项地位完全对等。第一个示例用load事件声明一个布尔选项声明自定义选项的入口是load事件。它收到一个 addonmanager.py 中的Loader实例addon 在此完成选项与命令的注册。最直接的例子是仓库自带的 examples/addons/options-simple.py Add a new mitmproxy option. Usage: mitmproxy -s options-simple.py --set addheadertrue from mitmproxy import ctx class AddHeader: def __init__(self): self.num 0 def load(self, loader): loader.add_option( nameaddheader, typespecbool, defaultFalse, helpAdd a count header to responses, ) def response(self, flow): if ctx.options.addheader: self.num self.num 1 flow.response.headers[count] str(self.num) addons [AddHeader()]这里的关键点loader.add_option(name, typespec, default, help)注册了一个名为addheader、类型为bool、默认值为False的选项在response事件中通过 ctx.options.addheader 读取当前值——ctx是 mitmproxy 提供给 addon 的全局上下文单例其中的options对象即全局 options 存储因为访问的是当前值用户任何时候改动选项都会立即影响后续请求的处理。运行与验证用 console 模式加载脚本并启动代理 mitmproxy -s ./examples/addons/options-simple.py此时通过代理发起一个请求-I只取响应头 env http_proxyhttp://localhost:8080 curl -I http://google.com由于选项默认值为false响应头里不会出现count。现在在 mitmproxy 界面中按下O进入选项编辑器找到addheader——你会发现 mitmproxy 知道它是布尔类型并允许你在true/false之间切换。将它设为true后再发一次请求 env http_proxyhttp://localhost:8080 curl -I http://google.com HTTP/1.1 301 Moved Permanently Location: http://www.google.com/ Content-Length: 219 count: 1count: 1表明选项已被启用并成功为第一个响应注入了计数响应头。命令行覆盖--set标志同一个选项也可以在启动命令行中直接覆盖适用于所有工具mitmproxy -s ./examples/addons/options-simple.py --set addheadertrue注意--set的取值在命令行里是字符串true需要被正确转换回bool。这正是类型系统发挥作用的地方options 存储会按选项声明的类型把字符串解析为对应的 Python 值解析规则见后文底层实现一节的_parse_setval。响应配置变更configure事件与校验回滚有些场景下仅在某次事件里读一下选项值是不够的——我们希望在用户改动选项的瞬间就采取行动例如校验值的合法性并及时反馈。这就是configure事件的作用。根据 hooks.py 中的ConfigureHook的定义当配置发生变化时configure会被调用其updated参数是包含所有被改动选项名的集合set-like 对象。addon 可以判断某个选项是否在集合中再从ctx.options读取新值。同时要注意configure 会在启动阶段被调用一次updated 集合包含全部选项——因此第一个被触发的configure会用默认值或配置文件中已设置的值初始化 addon 状态。configure最常见的用途之一是校验选项如果在校验过程中抛出 exceptions.OptionsError则本次更新的所有改动都会被自动回滚并向用户显示错误。仓库示例 examples/addons/options-configure.py 完整演示了这一模式React to configuration changes. from typing import Optional from mitmproxy import ctx from mitmproxy import exceptions class AddHeader: def load(self, loader): loader.add_option( nameaddheader, typespecOptional[int], defaultNone, helpAdd a header to responses, ) def configure(self, updates): if addheader in updates: if ctx.options.addheader is not None and ctx.options.addheader 100: raise exceptions.OptionsError(addheader must be 100) def response(self, flow): if ctx.options.addheader is not None: flow.response.headers[addheader] str(ctx.options.addheader) addons [AddHeader()]这个例子与上一个有两处显著差异选项类型是typing.Optional[int]向 mitmproxy 表明None是该选项的合法取值——即未设置。相应地response里要判空后再使用。configure会被调用两次先用默认值None之后一旦用户修改就用新值再次调用。本例规定取值不得超过 100否则抛出OptionsError。触发错误回滚的验证用错误值加载脚本会看到如下错误输出 mitmdump -s ./examples/addons/options-configure.py --set addheader1000 Loading script: ./examples/addons/options-configure.py /Users/cortesi/mitmproxy/mitmproxy/venv/bin/mitmdump: addheader must be 100OptionsError抛出后本次设置addheader1000被整体回滚选项保持之前的合法值用户得到明确的错误提示。支持的选项类型根据文档选项支持以下类型并可通过相应注解扩展类型注解写法说明原始类型str、int、float、bool最基础的标量选项可选值typing.Optional[...]如Optional[int]允许取None即未设置值序列collections.abc.Sequence如Sequence[str]允许一次配置多个值这套类型不仅是声明还贯穿校验与 CLI 生成。从源码 typecheck.py 的check_option_type可以看出运行时校验对 Union / Optional、tuple、Sequence、IO、Any都有专门分支例如 Sequence 的元素会逐个递归校验float选项接受整数输入。而 typecheck.py 的typespec_to_str则负责把注解渲染成人类可读的描述文本如optional str、sequence of str用于帮助文本与配置转储。值得补充的是Loader.add_option的真实签名还接受一个可选参数choices一个字符串序列用于限定字符串选项的候选值。addon 注册选项时若同名选项已存在且签名name / typespec / default / help / choices完全一致则静默复用签名不同则打印 Over-riding existing option 警告并覆盖。命令行里三种类型的实际表现布尔值解析非常宽容见 optmanager.py 的_parse_setvaltrue/false显式赋值省略值时视为true--set addheader即开启特殊的toggle关键字会翻转当前值。整型/字符串选项按对应类型解析字符串不合法时抛出 Failed to parse option ... 的OptionsError普通str/int不允许缺省值Optional类型则可显式置空。Sequence[str]选项可以通过重复传参累积多个值。官方文档给出的例子是mitmweb --set ignore_hostsexample.com --set ignore_hostsexample.org从源码看make_parser见下节为Sequence[str]选项生成的是actionappend的命令行参数天然支持多次指定。底层实现Options 如何完成类型校验、回滚与配置加载要真正用好这套机制理解 optmanager.py 中的OptManager与_Option至关重要。_Option每个选项的最小单元每个注册的选项都被包装成一个 optmanager.py 中的_Option实例携带name、typespec、default、help、choices以及当前值value。它有如下设计细节构造与赋值时都会调用typecheck.check_option_type校验把非法值挡在门外当前值未显式设置时以unset哨兵标记读取时返回默认值读取永远返回深拷贝current()与default属性保证外部对返回值的修改不会意外污染存储中的选项状态reset()恢复默认值has_changed()判断是否偏离默认值。注册、更新与回滚OptManager.add_option 把_Option放入内部字典后立即通过changed信号广播updated{name}——这正是configure事件触发的源头AddonManager 将options.changed信号接到自己的_configure_all见 addonmanager.py。更新的主干是update_knownoptmanager.py批量赋新值后用with self.rollback(...)包裹并发起changed广播。rollback 上下文管理器 是整个机制的关键更新前深拷贝全部选项若广播期间任何 handler 抛出OptionsError则整体回滚到旧状态、触发errored信号并再次广播changed以便界面刷新为已回滚的值。这正是上一节校验失败即回滚的底层来源——configure中的异常经由changed信号传播被rollback捕获处理。subscribe(func, opts)提供了更精细的订阅只对指定选项列表的变化回调可用于对特定选项做轻量级监听。从命令行字符串到类型化值命令行标志与--set最终都会进入 OptManager.set 与_parse_setval前者把namevalue规格按选项分组后者按typespec把字符串转换为 Python 值bool 的toggle、int 的严格解析、Sequence 的取多值等规则即来自此函数。未知选项在deferFalse时直接抛出OptionsError开启 defer 时会被暂存待选项注册后再通过process_deferred()应用——这让配置文件中声明的选项可以出现在脚本选项之前。自动生成命令行参数OptManager.make_parser 把声明即得 CLI落实到了 argparse 层面遍历 options 自动为每个选项创建参数。细节包括布尔选项生成一对互斥参数--flag/--no-flagstore_true / store_false短选项自动挂在非默认值一侧字符串选项自动附带choices校验Sequence[str]选项使用actionappend帮助文本标注 May be passed multiple times.。内置的绝大多数命令行开关都因此是自动派生的这也是 concepts 文档声称几乎所有命令行标志都是底层 option 别名的直接原因。YAML 配置的加载与持久化配置存储层的加载与保存全部位于 optmanager.pyparse(text)用 ruamel.yaml 安全加载 YAML并针对语法错误给出带行号定位的可读报错load(opts, text)应用解析结果特殊处理了scripts键——把相对路径重写为相对于配置文件所在目录因此配置里写- scripts/addon.py不必依赖启动目录load_paths(*paths)依次加载多个配置文件后者覆盖前者不存在的路径直接忽略serialize/save做往返式round-trip持久化默认只写出相对默认值有变化的选项保留原文件中已有的注释与默认项并自动清理配置中已不存在的未知选项。各工具提供--options标志dump_defaultsoptmanager.py会向标准输出转储带注释的 YAML——每个选项含默认值、帮助文本以及由typespec_to_str生成的类型说明是核对当前环境全部可用选项与默认值的权威参考。实战建议与设计要点基于以上机制编写带选项的 addon 时可遵循以下建议尽早声明、类型明确在load事件里用loader.add_option注册所有选项typespec 严格写清bool、int、Optional[str]或Sequence[str]。类型会一路传染出 CLI 参数、编辑器和 YAML 校验。把校验放在configure而非事件内只有configure里抛出的OptionsError才能触发自动回滚和用户提示在事件里判错只能中断单个请求无法阻止状态进入非法值。记住 configure 的启动调用configure启动时会用全部选项调用一次可直接在此初始化派生状态若依赖多个选项的组合可在configure中判断多个 key 是否同时在updates里或在所有需要后手动兜底。善用 choices 与 Sequence有限候选用choices约束需要白名单、忽略列表等多值场景用Sequence[str]命令行重复传参即可累积。文档与发现面向使用者的选项说明写在help参数中即可——它会出现在选项编辑器、--help与--options转储中无需另写文档。延伸阅读配置文件的完整语义、内置选项总表与各工具编辑器的用法参见 docs/src/content/concepts/options.md选项存储与加载/序列化的实现细节mitmproxy/optmanager.pyload事件的 Loader 与configure的触发链mitmproxy/addonmanager.py、mitmproxy/hooks.py类型校验与类型到文本的转换mitmproxy/utils/typecheck.py两个可直接运行参考的完整 addonexamples/addons/options-simple.py、examples/addons/options-configure.py。【免费下载链接】mitmproxyAn interactive TLS-capable intercepting HTTP proxy for penetration testers and software developers.项目地址: https://gitcode.com/GitHub_Trending/mi/mitmproxy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考