fix(channel): 渠道适配器与出站工具的上游响应体/带凭据 URL 不再进日志与错误(M41)
接 M38 的 18 条真泄漏里「渠道适配器 + 出站工具」这一族(#10 #11 #12 #13 #15 #16)。 只动日志与 error 文字:发送/接收、解析口径、重试次数一个都没改。 - httpclient.do:非 2xx 不再把 512B 响应体原文拼进 error,改 trace.UpstreamError (状态/体积/sha256 前缀)+ 信封 code/message(RedactText + 200 rune);error 里的 target 改 trace.HostOf,并剥掉 *url.Error 外壳(它带整条地址) - telegram doJSON:4xx 只解 error_code/description(清洗 + 截断),体其余字段不拼; 传输错误只留来源 —— Bot API 把 token 写在地址路径里 - weixin_ilink doJSON:4096B 体 ⇒ trace.UpstreamError,rawURL ⇒ mediafetch.HostOf; uploadOnce 剥掉预签名上传地址(encrypted_query_param),重试日志补来源主机 - qq_obv11 connectClient 成功 INFO:改走同包已有的 maskAccessTokenQuery (OneBot 惯例把 ?access_token= 写进 remote_url) - plugins/tools/io http 拦截日志:改记 trace.HostOf + 原因码,不记原 URL、也不记错误 原文(解析失败时它包着 *url.Error) 每条都有能变异打红的用例:QQ/iLink 预签名地址的哨兵、三处 4xx 体哨兵、 user:pass@ 与 query 凭据;八处变异逐条验证变红。
This commit is contained in:
1 parent
46a6da13e0
commit
0f9251b983
11 files changed
+726
-29
No files matched your search
@@ -31,6 +31,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/mediafetch"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -200,6 +201,9 @@ func (a *Adapter) uploadURLFromParam(param, fileKey string) string {
|
||||
// - `src/cdn/cdn-upload.ts:40-83`:4xx 立即放弃(附服务端 `x-error-message`),
|
||||
// 其余失败最多重试 3 次(无退避);
|
||||
// - `src/cdn/cdn-upload.ts:62-65`:200 但没有该响应头同样是失败(不得当成成功)。
|
||||
//
|
||||
// 日志只带**来源主机**:uploadURL 是服务端签发的预签名地址(`encrypted_query_param`
|
||||
// 就是下载凭据),整条写进日志等于把凭据写进日志。
|
||||
func (a *Adapter) uploadCiphertext(ctx context.Context, uploadURL string, data []byte) (string, error) {
|
||||
var lastErr error
|
||||
for attempt := 1; attempt <= uploadMaxAttempts; attempt++ {
|
||||
@@ -212,22 +216,28 @@ func (a *Adapter) uploadCiphertext(ctx context.Context, uploadURL string, data [
|
||||
return "", lastErr
|
||||
}
|
||||
a.log.Warn("cdn upload attempt failed, retrying",
|
||||
"attempt", attempt, "of", uploadMaxAttempts, "err", err)
|
||||
"attempt", attempt, "of", uploadMaxAttempts,
|
||||
"host", mediafetch.HostOf(uploadURL), "err", err)
|
||||
}
|
||||
return "", fmt.Errorf("weixin_ilink: upload media: %d 次尝试均失败: %w", uploadMaxAttempts, lastErr)
|
||||
}
|
||||
|
||||
// uploadOnce 做一次上传尝试,返回下载凭据。retryable 报告这次失败是否值得重试:
|
||||
// 4xx 是客户端错误(请求本身不对),重试只是徒劳;网络/5xx/缺头都可能瞬时。
|
||||
//
|
||||
// 错误里只留来源主机:上传地址是服务端签发的预签名 URL,而 `*url.Error` 会把整条
|
||||
// 地址(含 `encrypted_query_param`)拼进错误串 —— 这条错误还要经 send.go 的
|
||||
// `outbound media item skipped` 落日志,因此必须在**产生错误的那一步**剥掉。
|
||||
func (a *Adapter) uploadOnce(ctx context.Context, uploadURL string, data []byte) (string, bool, error) {
|
||||
host := mediafetch.HostOf(uploadURL)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, uploadURL, bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return "", false, fmt.Errorf("weixin_ilink: build upload: %w", err)
|
||||
return "", false, fmt.Errorf("weixin_ilink: build upload to %s: %w", host, urlFreeCause(err))
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/octet-stream")
|
||||
resp, err := a.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return "", true, fmt.Errorf("weixin_ilink: upload media: %w", err)
|
||||
return "", true, fmt.Errorf("weixin_ilink: upload media to %s: %w", host, urlFreeCause(err))
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
|
||||
Reference in new issue
Block a user