lolly/internal/proxy/headers.go
xfy 326eedc729 perf(proxy,logging,compression): 使用零拷贝字节路径减少内存分配
- proxy: headersPool sync.Pool 复用 header map,容量 20
- proxy: buildCacheKeyHash 使用池化 map 替代 make(map[string]string)
- proxy: ServeHTTP 目标 URI 构造使用 []byte append + SetRequestURIBytes
- headers: X-Forwarded-For 构造使用 []byte append + SetBytesKV
- logging: Str() 改为 Bytes() 零拷贝日志字段
- compression: Process() 直接操作 []byte,使用 bytes.Contains/Equal/HasPrefix
- compression: isCompressible() 签名从 string 改为 []byte

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 13:50:15 +08:00

119 lines
2.9 KiB
Go

// Package proxy 反向代理包,为 Lolly HTTP 服务器提供反向代理功能。
//
// 该文件提供统一的 X-Forwarded 头设置逻辑。
package proxy
import (
"strings"
"github.com/valyala/fasthttp"
"rua.plus/lolly/internal/netutil"
)
// 协议常量
const (
protoHTTP = "http"
protoHTTPS = "https"
)
// ForwardedHeaders 包含 X-Forwarded 系列头信息。
type ForwardedHeaders struct {
ClientIP string // 客户端 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 头
func SetForwardedHeaders(headers *fasthttp.RequestHeader, fh ForwardedHeaders, appendXFF 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
if fh.Host != "" {
headers.Set("X-Forwarded-Host", fh.Host)
}
// 设置 X-Forwarded-Proto
if fh.Proto != "" {
headers.Set("X-Forwarded-Proto", fh.Proto)
}
}
// WriteForwardedHeaders 将 X-Forwarded 头写入到 strings.Builder。
// 用于 WebSocket 升级请求构建。
//
// 参数:
// - builder: strings.Builder 实例
// - fh: ForwardedHeaders 结构体
func WriteForwardedHeaders(builder *strings.Builder, fh ForwardedHeaders) {
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 fh.Host != "" {
builder.WriteString("X-Forwarded-Host: ")
builder.WriteString(fh.Host)
builder.WriteString("\r\n")
}
if fh.Proto != "" {
builder.WriteString("X-Forwarded-Proto: ")
builder.WriteString(fh.Proto)
builder.WriteString("\r\n")
}
}