lolly/internal/http3/adapter.go
xfy 2458ac1ed1 docs: 为其余模块添加标准化 godoc 注释
为剩余模块添加完整文档注释:
- app: 应用生命周期管理
- cache: 文件缓存
- config: 配置加载器
- handler: 静态文件处理和错误页面
- http2/http3: HTTP/2 和 HTTP/3 适配器
- loadbalance: 负载均衡算法和均衡器
- middleware: bodylimit、compression、rewrite、security
- mimeutil: MIME 类型检测
- netutil: URL 处理工具
- resolver: DNS 解析器
- server: 服务器升级处理
- ssl: SSL/TLS 和 OCSP
- stream: 流处理
- testutil: 测试工具
- variable: 变量池和 SSL 变量

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 10:59:53 +08:00

253 lines
6.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package http3 提供 HTTP/3 请求适配层。
//
// 该文件实现 fasthttp.RequestHandler 与 http.Handler 之间的适配,
// 使 HTTP/3 服务器能够复用现有的 fasthttp 处理器。
//
// 主要特性:
//
// - 流式请求体处理:对于大请求体使用流式读取避免内存峰值
// - 阈值控制64KB 以下全量读取,以上使用流式处理
//
// 作者xfy
package http3
import (
"io"
"net"
"net/http"
"sync"
"github.com/valyala/fasthttp"
)
const (
// bodySizeThreshold 是请求体大小阈值,超过此值使用流式处理
bodySizeThreshold = 64 * 1024 // 64KB
)
// Adapter 将 fasthttp.RequestHandler 适配为 http.Handler。
//
// 由于 quic-go 使用标准库的 http.Handler 接口,
// 而 lolly 使用 fasthttp需要通过适配层进行转换。
type Adapter struct {
// ctxPool 用于复用 fasthttp.RequestCtx 对象
ctxPool sync.Pool
// bufferPool 用于复用字节缓冲区(流式处理优化)
bufferPool sync.Pool
}
// NewAdapter 创建 HTTP/3 适配器实例。
//
// 初始化用于将 fasthttp.RequestHandler 适配为标准库 http.Handler
// 的适配器。内部使用 sync.Pool 复用 RequestCtx 和缓冲区对象,
// 以降低内存分配开销。
//
// 返回值:
// - *Adapter: 初始化的 HTTP/3 适配器实例
func NewAdapter() *Adapter {
return &Adapter{
ctxPool: sync.Pool{
New: func() interface{} {
return &fasthttp.RequestCtx{}
},
},
bufferPool: sync.Pool{
New: func() interface{} {
buf := make([]byte, 4096) // 4KB 初始缓冲区
return &buf
},
},
}
}
// Wrap 包装 fasthttp handler 为 http.Handler。
//
// 将 http.Request 转换为 fasthttp.RequestCtx
// 调用 fasthttp handler然后将响应写回 http.ResponseWriter。
//
// 参数:
// - handler: fasthttp 请求处理器
//
// 返回值:
// - http.Handler: 标准库兼容的 HTTP 处理器
func (a *Adapter) Wrap(handler fasthttp.RequestHandler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 从池中获取 RequestCtx
ctx, ok := a.ctxPool.Get().(*fasthttp.RequestCtx)
if !ok {
// 如果类型断言失败,创建新的上下文(不应该发生,但为了安全)
ctx = &fasthttp.RequestCtx{}
}
defer a.ctxPool.Put(ctx)
// 重置 ctx 状态以避免污染
a.resetContext(ctx)
// 转换请求
a.convertRequest(r, ctx)
// 设置 ResponseWriter 用于后续写入
ctx.SetUserValue("http3_response_writer", w)
// 调用 fasthttp handler
handler(ctx)
// 转换响应
a.convertResponse(ctx, w)
})
}
// resetContext 重置 fasthttp.RequestCtx 状态。
//
// 参数:
// - ctx: 需要重置的上下文
func (a *Adapter) resetContext(ctx *fasthttp.RequestCtx) {
// 清空请求头
ctx.Request.Header.DisableNormalizing()
ctx.Request.Reset()
ctx.Response.Reset()
ctx.SetUserValueBytes(nil, nil)
}
// convertRequest 将 net/http.Request 转换为 fasthttp.RequestCtx。
//
// 参数:
// - r: 标准库 HTTP 请求
// - ctx: FastHTTP 请求上下文
func (a *Adapter) convertRequest(r *http.Request, ctx *fasthttp.RequestCtx) {
// 设置方法
ctx.Request.Header.SetMethod(r.Method)
// 设置 URI
uri := r.URL.Path
if r.URL.RawQuery != "" {
uri += "?" + r.URL.RawQuery
}
ctx.Request.SetRequestURI(uri)
// 设置 Host 头
ctx.Request.Header.SetHost(r.Host)
// 复制头部
for k, v := range r.Header {
for _, vv := range v {
ctx.Request.Header.Add(k, vv)
}
}
// 设置请求体(使用流式处理优化)
a.streamRequestBody(r, ctx)
// 设置远程地址
if r.RemoteAddr != "" {
if addr, err := net.ResolveTCPAddr("tcp", r.RemoteAddr); err == nil {
ctx.SetRemoteAddr(addr)
}
}
// 设置协议版本
ctx.Request.Header.SetProtocol("HTTP/3")
}
// convertResponse 将 fasthttp.RequestCtx 响应写入 http.ResponseWriter。
//
// 参数:
// - ctx: FastHTTP 请求上下文
// - w: 标准库 ResponseWriter
func (a *Adapter) convertResponse(ctx *fasthttp.RequestCtx, w http.ResponseWriter) {
// 设置状态码
statusCode := ctx.Response.StatusCode()
if statusCode == 0 {
statusCode = 200
}
// 复制响应头
for k, v := range ctx.Response.Header.All() {
w.Header().Add(string(k), string(v))
}
// 写入状态码
w.WriteHeader(statusCode)
// 写入响应体
body := ctx.Response.Body()
if len(body) > 0 {
_, _ = w.Write(body)
}
}
// streamRequestBody 流式读取请求体到 fasthttp。
//
// 对于小于等于 64KB 的请求体,直接读取到内存;
// 对于大于 64KB 的请求体,使用流式缓冲区避免内存峰值。
//
// 参数:
// - r: 标准库 HTTP 请求
// - ctx: FastHTTP 请求上下文
func (a *Adapter) streamRequestBody(r *http.Request, ctx *fasthttp.RequestCtx) {
if r.Body == nil || r.Body == http.NoBody {
return
}
defer func() {
_ = r.Body.Close()
}()
// 小请求体(<=64KB直接读取到内存
if r.ContentLength > 0 && r.ContentLength <= bodySizeThreshold {
body, err := io.ReadAll(r.Body)
if err == nil {
ctx.Request.SetBody(body)
}
return
}
// 大请求体(>64KB 或未知长度):使用流式缓冲区
// 如果已知 ContentLength预分配精确大小的缓冲区
var body []byte
if r.ContentLength > 0 {
body = make([]byte, 0, r.ContentLength)
}
// 从 pool 获取缓冲区进行分块读取
bufPtr, ok := a.bufferPool.Get().(*[]byte)
if !ok {
buf := make([]byte, 4096)
bufPtr = &buf
}
defer a.bufferPool.Put(bufPtr)
buf := *bufPtr
for {
n, err := r.Body.Read(buf)
if n > 0 {
body = append(body, buf[:n]...)
}
if err == io.EOF {
break
}
if err != nil {
break
}
}
if len(body) > 0 {
ctx.Request.SetBody(body)
}
}
// WrapHandler 包装特定的 fasthttp handler。
//
// 返回一个可以直接用于 http3.Server 的 http.Handler。
//
// 参数:
// - handler: fasthttp 请求处理器
//
// 返回值:
// - http.Handler: 标准库兼容的处理器
func (a *Adapter) WrapHandler(handler fasthttp.RequestHandler) http.Handler {
return a.Wrap(handler)
}