Body 重写
Chute 可以使用正则表达式或 JSONPath 表达式在 HTTP 请求和响应正文中搜索并替换内容。对于 HTTPS 流量,这需要启用 MitM 解密。
注意:明文 HTTP 请求只有经由 Chute 的 HTTP 代理到达时才会被处理;经 TUN 接口到达的明文 HTTP 会被原样转发。Chute Android 让所有流量都经过 TUN,除非在设置中打开了系统 HTTP 代理,它会把 Chute 的 HTTP 代理交给应用使用(Android 10 及以上;默认关闭)。
同样支持 Surge 的 http-request-jq 和 http-response-jq 程序——见 Surge 语法。
Body 重写规则定义在 [Body Rewrite] 段中。对于每个方向(请求 / 响应),一条报文只会应用第一条匹配的规则。如果同一条报文还匹配了要读取正文的 http-request 或 http-response 脚本,先应用规则,脚本看到的是改写后的正文;http-request-before-send 脚本在两者之后执行。
[Body Rewrite]
^https://api\.example\.com/response.* response regex "old-text" "new-text"
^https://api\.example\.com/request.* request regex "sensitive" "[redacted]"
^https://api\.example\.com/data.* jsonpath-response jsonpath $.ads null
规则格式
每条规则遵循以下通用格式:
<URL 正则> [方向] <模式> <匹配模式> <替换内容>
注意:URL 正则表达式会在请求完整 URL 的任意位置匹配,也会单独拿路径去匹配(与 URL 重写相同):
^https://example\.com已覆盖该主机下的所有路径和查询字符串,无需在结尾加.*,^/api则会匹配路径以/api开头的每一个请求。已解密请求的完整 URL 以https://开头,所以^http://模式永远匹配不到这类请求。模式会保留原本的大小写,因此\S、\D、\W、\B的含义不会被改变;匹配本身不区分大小写。
位于行首或紧跟在空格后面的 # 或 // 会开启一段注释,一直到行尾,除非它在双引号之内;; 从不算注释。见注释。
Surge 语法
也接受 Surge 自己的行格式:方向写在最前,省略 regex 关键字。
[Body Rewrite]
http-response ^https://api\.example\.com/feed "\"ads\":\s*\[.*?\]" "\"ads\":[]"
http-request ^https://api\.example\.com/submit "sensitive" "[redacted]"
Surge 形式的行可以写多组「模式 / 替换」,按从左到右的顺序依次应用,每一组作用在前一组的结果上;http-request-jq 与 http-response-jq 用一段 jq 程序代替「模式 / 替换」:<类型> <URL 模式> <jq 程序>,程序通常要加引号,因为里面有空格。在引号之外,紧跟在空格后面的 // 或 # 会开启注释,程序就在那里结束;在引号之内,// 是 jq 的替代运算符,所以用到它的程序要加引号。它把正文当作 JSON 来处理。正文不是 JSON、程序抛出错误、程序没有任何输出——这三种情况都原样保留正文;程序本身无效时会被报告并只跳过这一条规则,不会让整份配置失败。程序读不到文件也读不到环境:import 与 include 解析不到任何东西,$ENV 与 env 都是空的。
注意:Chute Android 用 jackson-jq 运行 jq 程序。它的输出上限为 4096 个结果或 4 MB——产生更多输出的程序会原样保留正文——并且缺少部分 jq 1.7 内置函数,其中包括
now、todateiso8601和fromdateiso8601以外的日期函数(strftime、strptime、mktime、gmtime、todate等)、@base32和@base32d、abs、toarray、trim、ltrim和rtrim、IN、INDEX和JOIN、tostream和fromstream。调用其中任何一个的程序会在运行时抛出错误,正文因此原样保留。
方向
| 关键字 | 说明 |
|---|---|
response |
应用于响应正文(省略时的默认值) |
request |
应用于请求正文 |
模式
| 模式 | 说明 |
|---|---|
regex |
正则表达式搜索替换 |
jsonpath-response / jsonpath-request / body-jsonpath-response / body-jsonpath-request |
基于 JSONPath 的修改 |
正则模式
对解码后的正文字符串执行标准的正则查找替换。使用 NSRegularExpression (ICU),不区分大小写匹配。替换内容支持捕获组引用($1、$2 等)。
<URL 正则> [response|request] regex <匹配模式> <替换内容>
示例 — 从 JSON 响应中移除广告:
[Body Rewrite]
^https://api\.example\.com/feed.* response regex "\"ads\":\s*\[.*?\]" "\"ads\":[]"
示例 — 清洗请求正文:
[Body Rewrite]
^https://api\.example\.com/submit.* request regex "\"password\":\s*\".*?\"" "\"password\":\"[FILTERED]\""
示例 — 使用捕获组重新格式化数据:
[Body Rewrite]
// 将 "last, first" 转换为 "first last"
^https://api\.example\.com/users.* response regex "\"name\":\s*\"(\w+),\s*(\w+)\"" "\"name\":\"$2 $1\""
示例 — 重写响应正文中的内嵌 URL:
[Body Rewrite]
^https://api\.example\.com.* response regex "https://old-cdn\.example\.com" "https://new-cdn.example.com"
包含空格的词必须用双引号括起来:
[Body Rewrite]
^https://example\.com.* response regex "old value with spaces" "new value"
要在带引号的词里包含字面的双引号,请使用反斜杠转义:\"。
JSONPath 模式
使用 JSONPath 表达式修改 JSON 正文。支持在特定路径读取、设置和删除值。
<URL 正则> jsonpath-response|jsonpath-request jsonpath <jsonpath-表达式> [值]
支持的 JSONPath 语法
| 表达式 | 说明 |
|---|---|
$.key |
访问对象属性 |
$.key.subkey |
访问嵌套属性 |
$[0] |
按索引访问数组元素 |
$.key[0].subkey |
混合对象和数组访问 |
$.items[*].name |
通配符:数组中的所有元素 |
$.*.value |
通配符:所有属性 |
.* 取对象的每一个成员和数组的每一个元素,所以 $.items.*.name 能取到每一项的名字。作为最后一步作用在数组上时,它什么也不改变;要替换元素本身,请用 [*]。
值类型
| 值 | 结果 |
|---|---|
string |
设置为字符串值——不属于下面任何一种形式的值都是字符串,所以 example.com、1.0.0-beta、12abc 仍是字符串 |
42 |
设置为整数——整个词是一个 JSON 数字,且没有小数部分和指数 |
3.14 |
设置为浮点数——带小数部分或指数的 JSON 数字(如 1e3),以及超出 64 位范围的整数 |
true |
设置为布尔值 true |
false |
设置为布尔值 false |
[…] 或 {…} |
不加引号时按 JSON 解析,设置为对应的数组或对象;它是最后一个字段,因此这一行余下的内容照原样读取,可以包含空格。加了引号,或者不是合法的 JSON,则仍是字符串。 |
null、nil 或省略 |
删除该路径 |
注意:给值加引号并不会强制其为字符串类型——引号在分词阶段会被剥离,类型根据剩余内容推断,因此
"42"会变成数字 42,"true"会变成布尔值 true,"null"会删除该路径;引号只对[…]和{…}有影响——加了引号,它们仍是字符串。关键字必须完全一致(区分大小写),所以True和NULL是字符串。只有整个词按 JSON 的写法恰好是一个数字时才算数字:+1、.5、1.、007仍是字符串,大到连浮点数也表示不了的数(如1e400)同样是字符串。没有任何写法能设置"42"或"true"这样的字符串——请改用regex规则。
示例 — 设置 JSON 字段:
[Body Rewrite]
^https://api\.example\.com/profile.* jsonpath-response jsonpath $.user.name "Anonymous"
示例 — 删除 JSON 字段:
[Body Rewrite]
^https://api\.example\.com/data.* jsonpath-response jsonpath $.tracking null
示例 — 通配符修改:
[Body Rewrite]
^https://api\.example\.com/list.* jsonpath-response jsonpath $.items[*].hidden true
实践示例
从 JSON 响应中剥离追踪参数
从所有 API 响应中移除 trackingId 字段。由于每个方向只会应用第一条匹配的规则,每个 URL 模式请只使用一条规则:
[Body Rewrite]
^https://api\.example\.com/.* jsonpath-response jsonpath $.trackingId null
向 HTML 响应注入脚本标签
在所有 HTML 页面的 </body> 之前追加自定义 <script> 标签:
[Body Rewrite]
^https://www\.example\.com/.* response regex "</body>" "<script>console.log('injected')</script></body>"
对请求日志中的敏感字段脱敏
在请求正文到达服务器之前替换掉其中的 API 密钥和令牌。由于每个方向只会应用第一条匹配的规则,请将两个字段合并到一条规则中:
[Body Rewrite]
^https://api\.example\.com/.* request regex "\"(apiKey|token)\":\s*\"[^\"]+\"" "\"$1\":\"[REDACTED]\""
规范化响应中的日期格式
将 ISO 日期替换为更短的格式:
[Body Rewrite]
^https://api\.example\.com/.* response regex "(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z" "$1/$2/$3 $4:$5"
禁用应用配置中的功能开关
强制将配置端点中的所有功能开关设为 false:
[Body Rewrite]
^https://api\.example\.com/config.* jsonpath-response jsonpath $.features[*].enabled false
重写缓存响应中的 CDN URL
将对旧 CDN 的所有引用替换为新的 CDN:
[Body Rewrite]
^https://www\.example\.com/.* response regex "https://old-cdn\.example\.com" "https://new-cdn.example.com"
处理管线
Body 重写自动处理以下内容:
- Content-Encoding:支持
gzip和deflate。跳过不支持的编码。 - Transfer-Encoding:在处理前对分块传输编码进行去块化。
- 解码:在应用重写规则之前解压缩正文。
- 重新编码:重新压缩正文并更新
Content-Length。移除Transfer-Encoding头部。 - Accept-Encoding:当某条响应规则匹配请求的 URL 时,该请求的
Accept-Encoding头部会被改写为gzip, deflate, identity,以确保响应保持在可解码的编码中。
在 HTTP/1.x(明文 HTTP 和解密后的 HTTP/1.1)上,重写处理的正文最大大小为 128KB;更大的正文将直接透传而不做修改。解密后的 HTTP/2 报文会被完整缓冲,无论大小都会被重写。
在 HTTP/1.x 上,响应正文要全部到齐后才会被重写。如果服务器先关闭了连接:既没有
Content-Length也不是分块编码的正文以连接关闭为结尾,此时已经完整,照常重写;按Content-Length或分块编码分帧、却被关闭截断的正文按收到的原样发给客户端,不做重写,Content-Length也不改,随后关闭连接,让客户端能察觉响应不完整。
注意事项
- 对于 HTTPS 流量,必须为匹配的主机名启用 MitM 解密。
- 正则匹配不区分大小写。替换模板支持 ICU 捕获组引用:
$0(完整匹配)、$1(第一组)、$2(第二组)等。 - URL 模式或正文的匹配模式不是有效的正则表达式时,正则或 JSONPath 行会成为配置错误,该规则不会被加载;jq 行则会被跳过并给出警告。在第一组之后的各组「模式 / 替换」中,无效的模式只会丢弃那一组,并在日志中给出警告。
- JSONPath 模式仅在正文是有效 JSON 时适用。
- Body 重写规则应用于解码后(UTF-8)的正文文本。
- 每个方向(请求 / 响应)上,一条报文只会应用第一条匹配的规则。如果需要多处修改,请定义一条合并的规则。
- 删除不存在的 JSONPath 时正文保持不变。设置值时,如果其父对象存在则会创建该键;如果中间路径缺失,则不会有任何变化。
本页为英文版的翻译。如内容有出入,以英文版为准。