Commit Graph

2 Commits

Author SHA1 Message Date
6d38a164eb chore(module): Go module 路径随托管地迁移 gitcode → github
## 背景

仓库早已迁移到 GitHub(`github.com/JianFeeeee/HomeAgent`,CI/CD 都在那边跑),
gitcode 保留为**国内只读镜像**(`ci.yml` 的 sync-gitcode-main 快进推送)。
但 go.mod 的 module 路径一直是 `gitcode.com/JianFeeeee/...` —— 既与权威源
不符,`meta.SourceURL`(AGPL §13 的源码提供入口)也指向镜像而非实际运行的代码。

## 关键区分(这是本次改动安全的前提)

两个 module 是**不同**的,影响面差一个数量级:

| module | 谁依赖 | 改它的后果 |
|---|---|---|
| `.../HomeAgent`(主仓) | **只有主仓自己** | 内部重构,零外部影响 |
| `.../homeagent-sdk`(SDK 仓) | 主仓 49 文件 + 所有外部插件 | 需重编插件 |

★ 实测确认:**插件不 import 主仓 module**(`strings qq/plugin.bin | grep HomeAgent` = 0),
只依赖 SDK 仓。而插件与内核的边界是 **RPC 方法名字符串**
(`plugin.init` / `tool.invoke`…,见 proc/protocol.go),**与 Go module 名无关**。
⇒ module 改名**不改变任何 wire 协议**,现网已装的插件照常运行。

SDK 的 GitHub 仓名是 `homeagentsdk`(无连字符),module 路径必须与之精确匹配,
否则 `go get` 会因路径不符失败,故取 `github.com/JianFeeeee/homeagentsdk`。

## 改动

- `go.mod` / `go.sum`:两个 module 路径 + require/replace
- 277 个 Go 文件 + Makefile + 打包脚本的 import 与 ldflags 路径
- SDK 仓 `meta.CoreModule`、`tools/hmapdev` 的 module 声明与工程模板
- 对外文档/链接的仓库地址(README、PLUGIN_DEV、OVERVIEW 等)

## 明确**保留** gitcode 的地方(镜像机制,不能动)

- `GITCODE_TOKEN`、`GC_URL="…@gitcode.com"`(CI 同步 job)
- `gitcode.com/api/v5/...`(release 附件上传)
- git remote `gitcode`(镜像拉取/推送)
- `docs/zh/ci-cd-runbook.md`(整篇讲镜像运维)

## 验证

- `go build ./...` / `go vet ./...` 绿
- `go test ./... -count=1` → 48 包 ok / 0 FAIL
- hmapdev 工具链编译通过(`third_party/homeagent-sdk/tools/hmapdev`)
- 全仓残留检查:module 形态的 `gitcode.com/JianFeeeee` = 0
- 镜像机制抽验:GC_URL / @gitcode / api/v5 / remote 均完好

## 顺带修正

`hmapdev/templates.go` 的注释此前写「gitcode 的模块不在 proxy.golang.org 上」——
module 已迁 GitHub,该论断不再成立。改为如实说明**为何仍默认 replace 到本机 SDK**:
离线可构建、避免版本漂移、内网可用。理由与托管地无关,故 replace 保留。
2026-10-08 10:40:17 +08:00
90245fea8a fix(webui): 旧反代两条路径合并修复 —— 302变502/泄露内网URL/流式被缓冲
新反代(proxy.go)早已修掉这些,但**两条旧路径**没跟上,各自复制了一份
http.DefaultClient 实现:

  proxyToPluginmgr        (handler_settings.go)
  handleDeviceGatewayProxy(handler_device.go)

同一个 bug 修了两遍还漏了两处。抽成共用的 reverseToUpstream,不再分叉。

## Bug 1:跟随上游 3xx → 302 变 502 + 泄露内网 URL

http.DefaultClient 默认跟最多 10 跳。上游回 302 时反代跟过去,目标可能是
内网另一个服务或不可达,于是把「上游的 302」变成「本层的 502」,
错误里还带着内网地址:

  {"error":"device gateway unreachable: Get \"http://127.0.0.1:1/api/...\":
   dial tcp 127.0.0.1:1: connect: connection refused"}

外部用户看到 Bad Gateway + 他访问不了的内网地址:既无用又泄露拓扑。
→ 反代**不应有重定向策略**(那是客户端的事),原样透传 3xx。
→ 上游错误细节只写日志,对外统一「上游服务不可达」。

## Bug 2:不逐帧 flush,流式响应被缓冲到结束

原实现 io.Copy(w, resp.Body),ResponseWriter 自带缓冲 → 上游按帧下发的
SSE/长轮询内容全堆到上游结束才吐。裸 TCP 实测:上游每 80ms 一帧共 3 帧,
缓冲版本只产生 **1 次**读(集中在 161ms),客户端表现为「卡住不动然后
一次性全出来」。改用 flushCopy(4KB 块 + 块间 Flush)。

## Bug 3:不过滤逐跳头

Content-Length / Transfer-Encoding 描述的是**上游那条连接**的分帧方式,
照抄到本层连接会导致客户端按错误长度读;Keep-Alive/Connection 同理。
按 RFC 7230 §6.1 剔除。

## 附带修正

- 补 X-Forwarded-For / X-Real-IP / X-Forwarded-Host / X-Forwarded-Proto,
  且**只在尚未设置时补** —— 外层 nginx 已注入时覆盖会丢掉真实客户端 IP。
- 与 proxy.go 新建了带超时的 upstreamClient(DefaultClient 无超时,
  上游卡住会拖住 goroutine)。

## 判据(5 条)+ ★ 判据设计的两个坑

★ 这条判据我试错了三轮,过程留在测试注释里:

1. 「首帧早于末帧」→ **假绿**。Go 在响应结束后把缓冲一次吐出,首末帧仍有
   微秒级差,任何 `> 0` 都绿。
2. 用 http.Client 量「首帧延迟 < 上游总时长 70%」→ 仍是**假绿**。实测直连
   上游首帧 761ns、总时长 160ms:Go 的 HTTP **客户端**合并读,量到的
   「首帧」是客户端首次取到数据的时间,与服务端何时 flush 无关。
3. 当前(正确):**裸 TCP 直连被测服务**,看读到几次、分别在什么时刻。
   对照组实测 3 次读 / 0.24ms / 80ms / 161ms —— 正是上游节奏。
   第二个坑:不能按「累计 12 字节正文」判结束(首读含响应头 + 首帧正文,
   会在首读就以为读完,后两帧时序全丢)。改为读到 EOF。

变异验证 4 条(均按预期打红后还原):退回 DefaultClient → 跟随重定向判红;
错误信息带 err.Error() → 泄露判红;退回 io.Copy → 只读到 1 次判红;
不过滤逐跳头 → Keep-Alive 判红。

全量:35 包全绿。
2026-09-26 13:49:59 +08:00