03 引擎反向代理与头清理


文档摘要

03 引擎反向代理与头清理 本节摘要:服务端作为「唯一中枢」,不仅要处理自己的业务,还要把对底层引擎的请求透明地代理过去。本节讲清这套反向代理:服务端如何识别「该代理给引擎」的请求、如何剥除敏感头、如何注入工作区凭据、如何清理响应。这套「透明反代」让引擎感觉不到中间层存在,同时让服务端保持对访问的控制。 一、为什么服务端要代理引擎 回忆第 2 章拓扑:所有对引擎的访问都经服务端代理,不直接连引擎。为什么? 统一鉴权:服务端代理时先鉴权(第 4 章),保证只有授权请求到引擎。 统一治理:服务端可以加额外逻辑(如 viewer 强制只读,第 4 章)。 隐藏引擎细节:客户端不用知道引擎的端口/凭据,只认服务端。 所以「访问引擎」=「访问服务端的某路径,服务端转给引擎」。这就是反向代理。

03 引擎反向代理与头清理

本节摘要:服务端作为「唯一中枢」,不仅要处理自己的业务,还要把对底层引擎的请求透明地代理过去。本节讲清这套反向代理:服务端如何识别「该代理给引擎」的请求、如何剥除敏感头、如何注入工作区凭据、如何清理响应。这套「透明反代」让引擎感觉不到中间层存在,同时让服务端保持对访问的控制。

一、为什么服务端要代理引擎

回忆第 2 章拓扑:所有对引擎的访问都经服务端代理,不直接连引擎。为什么?

  • 统一鉴权:服务端代理时先鉴权(第 4 章),保证只有授权请求到引擎。
  • 统一治理:服务端可以加额外逻辑(如 viewer 强制只读,第 4 章)。
  • 隐藏引擎细节:客户端不用知道引擎的端口/凭据,只认服务端。

所以「访问引擎」=「访问服务端的某路径,服务端转给引擎」。这就是反向代理。

二、识别「该代理」的请求

服务端怎么知道一个请求「该自己处理」还是「该代理给引擎」?靠路径模式。某些路径(如 /引擎/* 或工作区挂载点)被识别为「引擎相关」,走代理:

请求进来 │ ▼ 解析路径 │ ├─ 匹配「引擎挂载点」(如 /workspace/:id/引擎/*) │ └─ 走反向代理 │ └─ 其他路径 └─ 走服务端自己的路由匹配

这种「路径分流」让引擎请求和服务端请求在同一端口共存,各走各的。

三、剥除敏感头

代理时一个关键动作——剥除敏感头。客户端发给服务端的请求,可能带着「给服务端的」头(如授权头、主机头)。这些头不能原样转发给引擎——引擎可能误解,或泄露信息。

所以服务端代理时,会剥除这些:

  • 授权头(给服务端的,不是给引擎的)
  • 主机头(可能导致引擎路由错乱)
  • 来源头
  • 其他服务端专属头
客户端请求: { 头: { Authorization, Host, Origin, ... }, ... } │ ▼ 服务端代理:剥除敏感头 │ 转发给引擎: { 头: { (剥掉了 Authorization/Host/Origin) } }

四、注入工作区凭据

剥除后,服务端注入工作区自己的引擎凭据。因为引擎不认客户端给服务端的凭据,它认「工作区配置的引擎凭据」:

剥除后: { 头: {} } │ ▼ 服务端注入工作区凭据 │ ├─ x-opencode-directory: 工作区目录标识 └─ 工作区自带的 authHeader │ 转发给引擎: { 头: { x-opencode-directory, authHeader } }

这样引擎收到的请求,就像「工作区直接发的」——带着正确的目录标识和凭据。引擎完全感觉不到中间有个服务端。

💡 透明反代的精髓:剥除客户端头 + 注入工作区头,让引擎「以为」是工作区直接在调它。这就是「透明」——中间层存在,但对引擎不可见。

五、请求体的处理

代理时请求体也要正确处理:

  • GET/HEAD 请求:不带体,直接转发。
  • 其他(POST/PUT 等):把体缓存成 ArrayBuffer,正确转发。

有些特殊路径(如与会话命令相关)走「fire-and-forget」——发了不管结果(因为是单向通知)。

六、清理响应头

引擎返回响应后,服务端代理也要清理响应头——特别是「逐跳头(hop-by-hop)」:

引擎响应: { 头: { content-encoding, transfer-encoding, content-length, ... } } │ ▼ 服务端清理逐跳头 │ 返回客户端: { 头: { (清理了逐跳头) } }

逐跳头(如 content-encoding、transfer-encoding、content-length)是「单跳专用」的——客户端到服务端一跳、服务端到引擎一跳,各自处理。如果不清理,客户端可能误用引擎那跳的头,导致解码错误。

七、viewer 强制只读(与第 4 章呼应)

代理时还有一个与第 4 章权限的衔接点——viewer 强制只读。代理前,服务端检查令牌 scope:

代理请求前: │ ▼ 检查 scope │ ├─ viewer 且方法非 GET/HEAD ──► 拒绝(403,只读) │ └─ 其他 ──► 继续代理

这就是第 4 章说的「viewer 在反代层把关」——它不是前端隐藏按钮,而是在服务端代理引擎请求时强制只读。这是真安全的权限治理。

八、本节要点回顾

  1. 服务端代理引擎:所有引擎访问经服务端,统一鉴权/治理/隐藏细节。
  2. 路径分流:引擎路径走代理,其他走服务端自己的路由。
  3. 剥除敏感头:Authorization/Host/Origin 等不转发给引擎。
  4. 注入工作区凭据:x-opencode-directory + 工作区 authHeader,让引擎以为工作区直连。
  5. 请求体处理:GET/HEAD 不带体,其他缓存转发;特殊路径 fire-and-forget。
  6. 清理响应逐跳头:避免客户端误用引擎那跳的头。
  7. viewer 强制只读:代理前查 scope,viewer 非 GET/HEAD 拒绝——真安全。

引擎反代讲清了,最后一节讲「未命中」时怎么兜底——静态 UI 与跨域。


发布者: 作者: 灏天文库 转发
评论区 (0)
U