- Register requestid.New() as first middleware in buildMiddlewareChain (before AccessLog) so the ID is available for logging - Add SetRequestIDHeader() in proxy/headers.go to propagate X-Request-ID to upstream via proxy forwarding - Call SetRequestIDHeader in header_modifier.go after SetForwardedHeaders - Import requestid package in middleware_builder and proxy/headers
148 lines
4.4 KiB
Go
148 lines
4.4 KiB
Go
// Package proxy 反向代理包,为 Lolly HTTP 服务器提供反向代理功能。
|
||
//
|
||
// 该文件提供统一的 X-Forwarded 系列请求头设置逻辑,包括:
|
||
// - X-Forwarded-For: 客户端 IP 地址链
|
||
// - X-Real-IP: 客户端真实 IP 地址
|
||
// - X-Forwarded-Host: 原始请求 Host
|
||
// - X-Forwarded-Proto: 原始请求协议(http/https)
|
||
//
|
||
// 主要用途:
|
||
//
|
||
// 用于在代理转发时保留客户端原始请求信息,使后端服务能够获取
|
||
// 客户端的真实 IP、Host 和协议。
|
||
//
|
||
// 注意事项:
|
||
// - 所有函数均为非并发安全(无状态函数)
|
||
// - X-Forwarded-For 支持追加模式和覆盖模式
|
||
//
|
||
// 作者:xfy
|
||
package proxy
|
||
|
||
import (
|
||
"strings"
|
||
|
||
"github.com/valyala/fasthttp"
|
||
"rua.plus/lolly/internal/middleware/requestid"
|
||
"rua.plus/lolly/internal/netutil"
|
||
)
|
||
|
||
// 协议常量,用于标识请求使用的传输层协议。
|
||
const (
|
||
protoHTTP = "http" // 明文 HTTP 协议
|
||
protoHTTPS = "https" // TLS 加密的 HTTPS 协议
|
||
)
|
||
|
||
// ForwardedHeaders 包含 X-Forwarded 系列头信息。
|
||
//
|
||
// 用于在代理转发时保留客户端原始请求信息。
|
||
type ForwardedHeaders struct {
|
||
ClientIP string // 客户端 IP 地址,从连接信息或 X-Real-IP 头提取
|
||
Host string // 原始请求 Host 头,表示客户端访问的主机名
|
||
Proto string // 原始请求协议,http 或 https
|
||
}
|
||
|
||
// ExtractForwardedHeaders 从请求上下文中提取 X-Forwarded 头信息。
|
||
//
|
||
// 参数:
|
||
// - ctx: FastHTTP 请求上下文
|
||
//
|
||
// 返回值:
|
||
// - ForwardedHeaders: 提取的头信息
|
||
func ExtractForwardedHeaders(ctx *fasthttp.RequestCtx) ForwardedHeaders {
|
||
clientIP := netutil.ExtractClientIP(ctx)
|
||
host := string(ctx.Host())
|
||
|
||
proto := protoHTTP
|
||
if ctx.IsTLS() {
|
||
proto = protoHTTPS
|
||
}
|
||
|
||
return ForwardedHeaders{
|
||
ClientIP: clientIP,
|
||
Host: host,
|
||
Proto: proto,
|
||
}
|
||
}
|
||
|
||
// SetForwardedHeaders 设置 X-Forwarded 系列请求头。
|
||
//
|
||
// 参数:
|
||
// - headers: 目标请求头
|
||
// - fh: ForwardedHeaders 结构体
|
||
// - appendXFF: 是否追加到已有的 X-Forwarded-For 头
|
||
// - setHost: 是否设置 X-Forwarded-Host
|
||
// - setProto: 是否设置 X-Forwarded-Proto
|
||
func SetForwardedHeaders(headers *fasthttp.RequestHeader, fh ForwardedHeaders, appendXFF, setHost, setProto bool) {
|
||
// 设置 X-Real-IP
|
||
if fh.ClientIP != "" {
|
||
headers.Set("X-Real-IP", fh.ClientIP)
|
||
}
|
||
|
||
// 设置 X-Forwarded-For
|
||
if fh.ClientIP != "" {
|
||
if appendXFF {
|
||
existingXFF := headers.Peek("X-Forwarded-For")
|
||
if len(existingXFF) > 0 {
|
||
// SAFETY: Ephemeral — xffBuf is written to header immediately and not reused.
|
||
var xffBuf []byte
|
||
xffBuf = append(xffBuf, existingXFF...)
|
||
xffBuf = append(xffBuf, ", "...)
|
||
xffBuf = append(xffBuf, fh.ClientIP...)
|
||
headers.SetBytesKV([]byte("X-Forwarded-For"), xffBuf)
|
||
} else {
|
||
headers.SetBytesKV([]byte("X-Forwarded-For"), []byte(fh.ClientIP))
|
||
}
|
||
} else {
|
||
headers.SetBytesKV([]byte("X-Forwarded-For"), []byte(fh.ClientIP))
|
||
}
|
||
}
|
||
|
||
// 设置 X-Forwarded-Host(仅在 setHost 为 true 时)
|
||
if setHost && fh.Host != "" {
|
||
headers.Set("X-Forwarded-Host", fh.Host)
|
||
}
|
||
|
||
// 设置 X-Forwarded-Proto(仅在 setProto 为 true 时)
|
||
if setProto && fh.Proto != "" {
|
||
headers.Set("X-Forwarded-Proto", fh.Proto)
|
||
}
|
||
}
|
||
|
||
// SetRequestIDHeader propagates X-Request-ID from the request context to upstream headers.
|
||
func SetRequestIDHeader(headers *fasthttp.RequestHeader, ctx *fasthttp.RequestCtx) {
|
||
if id := requestid.GetRequestID(ctx); id != "" {
|
||
headers.Set("X-Request-ID", id)
|
||
}
|
||
}
|
||
|
||
// WriteForwardedHeaders 将 X-Forwarded 头写入到 strings.Builder。
|
||
// 用于 WebSocket 升级请求构建。
|
||
//
|
||
// 参数:
|
||
// - builder: strings.Builder 实例
|
||
// - fh: ForwardedHeaders 结构体
|
||
// - setHost: 是否设置 X-Forwarded-Host
|
||
// - setProto: 是否设置 X-Forwarded-Proto
|
||
func WriteForwardedHeaders(builder *strings.Builder, fh ForwardedHeaders, setHost, setProto bool) {
|
||
if fh.ClientIP != "" {
|
||
builder.WriteString("X-Forwarded-For: ")
|
||
builder.WriteString(fh.ClientIP)
|
||
builder.WriteString("\r\n")
|
||
builder.WriteString("X-Real-IP: ")
|
||
builder.WriteString(fh.ClientIP)
|
||
builder.WriteString("\r\n")
|
||
}
|
||
|
||
if setHost && fh.Host != "" {
|
||
builder.WriteString("X-Forwarded-Host: ")
|
||
builder.WriteString(fh.Host)
|
||
builder.WriteString("\r\n")
|
||
}
|
||
|
||
if setProto && fh.Proto != "" {
|
||
builder.WriteString("X-Forwarded-Proto: ")
|
||
builder.WriteString(fh.Proto)
|
||
builder.WriteString("\r\n")
|
||
}
|
||
}
|