在kserve agent中用到了如下的knative库:
// cmd/agent/main.go
composedHandler = queue.ForwardedShimHandler(composedHandler)
在 Knative 的 HTTP 流量管理中,ForwardedShimHandler 用于将传统的 X-Forwarded-* 头转换为符合 RFC7239 标准的 Forwarded 头。其主要动机包括:
-
标准化请求头
RFC7239 定义了 Forwarded 头的格式,提供更结构化的信息(如 for、host、proto 等)。而许多环境中,代理层会设置 X-Forwarded-For、X-Forwarded-Proto 和 X-Forwarded-Host 等头。ForwardedShimHandler 的作用就是利用这些头的信息构造一个标准的 Forwarded 头,方便后续组件统一解析。 -
兼容性和安全性
如果请求中已经存在 Forwarded 头(可能由上游代理设置),Handler 不做任何处理,从而避免重复或冲突。同时,仅组合可信的 X-Forwarded-* 信息。
代码
下面是 ForwardedShimHandler 的核心代码片段:
func ForwardedShimHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer h.ServeHTTP(w, r)
// 如果已有 Forwarded 头,则不做处理
fwd := r.Header.Get("Forwarded")
if fwd != "" {
return
}
// 获取传统的 X-Forwarded-\* 头信息
xff := r.Header.Get("X-Forwarded-For")
xfp := r.Header.Get("X-Forwarded-Proto")
xfh := r.Header.Get("X-Forwarded-Host")
// 没有这些头信息则直接返回
if xff == "" && xfp == "" && xfh == "" {
return
}
// 根据获取的 X-Forwarded-\* 头信息生成 Forwarded 头
r.Header.Set("Forwarded", generateForwarded(xff, xfp, xfh))
})
}
分析过程:
-
检查和保护已有的 Forwarded 头
一开始通过r.Header.Get("Forwarded")
判断请求中是否已经存在标准 Forwarded 头。如存在,直接返回,不再更改,确保不覆盖已存在的值。 -
读取 X-Forwarded-* 头
分别获取 X-Forwarded-For、X-Forwarded-Proto 和 X-Forwarded-Host 头。如果这些头都没有设置,则说明没有相关信息可用,Handler 也不会做额外的处理。 -
构造标准 Forwarded 头
调用 generateForwarded 函数,将上面读取到的信息整合成标准格式,如:Forwarded: for=<client>;host=<host>;proto=<protocol>
在生成过程中还考虑了多个代理节点的情况,即 X-Forwarded-For 可能包含多个地址,通过逗号分隔,对每个节点均构造 “for” 域。
应用场景
在 Knative 的代理和负载均衡中,通过 ForwardedShimHandler 可以:
- 为下游组件提供标准化后的请求头,从而简化对请求来源、主机和协议的处理;
- 消除不同代理实现之间因头信息不一致带来的问题,提高请求转发的兼容性;
- 辅助日志、监控等功能更容易解析和展示客户端信息。
总体来看,ForwardedShimHandler 是 Knative 为兼容老式 X-Forwarded-* 头而新增的一个“shim”层,确保即便上游仅提供非标准头信息,也能生成正确的 Forwarded 头供后续使用。