// Package adapter 提供 HTTP/2 和 HTTP/3 适配器的共享组件。 // // 该包提取了两个适配器中通用的功能,避免代码重复: // // - 共享的 bufferPool singleton(零拷贝优化) // - 统一的请求体处理阈值 // - 通用的上下文重置逻辑 // - 流式请求体读取 // // 关键设计决策: // // 1. bufferPool 使用 singleton 模式,ctxPool 保持独立 // 2. CommonAdapter 不包含 ConvertResponse(HTTP/2/HTTP/3 行为不同) // 3. 阈值常量统一,避免 HTTP/2 inline 和 HTTP/3 constant 不一致 // // 作者:xfy package adapter import ( "io" "net/http" "sync" "github.com/valyala/fasthttp" ) // DefaultBodyThreshold 是请求体大小阈值,超过此值使用流式处理。 // // 64KB 是经过测试的平衡点: // - 小于此值:直接读取到内存,避免 pool 开销 // - 大于此值:使用流式缓冲区,避免大内存分配 const DefaultBodyThreshold = 64 * 1024 // 64KB // bufferPoolInstance 是全局共享的缓冲区池 singleton。 // // 使用 singleton 模式避免多个适配器实例创建多个 pool, // 提高内存复用效率。该 pool 被 HTTP/2 和 HTTP/3 适配器共享。 var bufferPoolInstance = &sync.Pool{ New: func() any { buf := make([]byte, 4096) // 4KB 初始缓冲区 return &buf }, } // CommonAdapter 提供 HTTP/2 和 HTTP/3 适配器的共享基础结构。 // // 该结构体提取了两个适配器共用的字段和方法, // 但不包含 ConvertResponse(HTTP/2 和 HTTP/3 的响应转换逻辑不同)。 type CommonAdapter struct { // CtxPool 用于复用 fasthttp.RequestCtx 对象 // 每个协议适配器实例独立维护自己的 ctxPool CtxPool sync.Pool } // NewCommonAdapter 创建新的共享适配器实例。 // // 初始化 CommonAdapter,设置 ctxPool 的 New 函数。 // bufferPool 使用全局 singleton,不需要在实例中存储。 // // 返回值: // - *CommonAdapter: 初始化的共享适配器实例 func NewCommonAdapter() *CommonAdapter { return &CommonAdapter{ CtxPool: sync.Pool{ New: func() any { return &fasthttp.RequestCtx{} }, }, } } // ResetContext 重置 fasthttp.RequestCtx 状态。 // // 从 pool 获取的 ctx 可能带有之前请求的残留状态, // 必须在每次使用前调用此方法进行清理。 // // 参数: // - ctx: 需要重置的 fasthttp 请求上下文 func (a *CommonAdapter) ResetContext(ctx *fasthttp.RequestCtx) { // 禁用头部规范化以保持原始大小写 ctx.Request.Header.DisableNormalizing() // 重置请求和响应状态 ctx.Request.Reset() ctx.Response.Reset() // 清除用户自定义值 ctx.SetUserValueBytes(nil, nil) } // StreamRequestBody 流式读取 HTTP 请求体到 fasthttp。 // // 对于小于等于 DefaultBodyThreshold(64KB)的请求体,直接读取到内存; // 对于大于阈值的请求体,使用共享 bufferPool 进行流式处理,避免内存峰值。 // // 参数: // - r: 标准库的 HTTP 请求 // - ctx: fasthttp 请求上下文,用于存储读取的请求体 func (a *CommonAdapter) 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 <= DefaultBodyThreshold { body, err := io.ReadAll(r.Body) if err == nil { ctx.Request.SetBody(body) } return } // 大请求体:使用流式缓冲区(> 64KB 或未知长度) // 从全局 pool 获取缓冲区 bufPtr, ok := bufferPoolInstance.Get().(*[]byte) if !ok { // 如果类型断言失败,创建新的缓冲区(不应该发生) buf := make([]byte, 4096) bufPtr = &buf } defer bufferPoolInstance.Put(bufPtr) buf := *bufPtr var body []byte // 如果已知 ContentLength,预分配精确大小的缓冲区 if r.ContentLength > 0 { body = make([]byte, 0, r.ContentLength) } // 分块读取请求体 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) } } // GetContext 从 pool 获取一个 fasthttp.RequestCtx。 // // 使用 pool 复用 RequestCtx 对象,减少 GC 压力。 // 获取的 ctx 必须通过 ResetContext 重置后才能使用。 // // 返回值: // - *fasthttp.RequestCtx: fasthttp 请求上下文 // - bool: 如果为 false,表示类型断言失败,ctx 是新创建的 func (a *CommonAdapter) GetContext() (*fasthttp.RequestCtx, bool) { ctx, ok := a.CtxPool.Get().(*fasthttp.RequestCtx) if !ok { ctx = &fasthttp.RequestCtx{} } return ctx, ok } // PutContext 将 fasthttp.RequestCtx 放回 pool。 // // 在放回 pool 前应该调用 ResetContext 清理状态。 // // 参数: // - ctx: 要放回 pool 的 fasthttp 请求上下文 func (a *CommonAdapter) PutContext(ctx *fasthttp.RequestCtx) { a.CtxPool.Put(ctx) }