lolly/internal/http2/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

381 lines
9.5 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 http2 提供 HTTP/2 请求适配层。
//
// 该文件实现 fasthttp.RequestHandler 与 http.Handler 之间的适配,
// 使 HTTP/2 服务器能够复用现有的 fasthttp 处理器。
//
// 主要特性:
//
// - 零拷贝头部转换:使用 sync.Pool 复用缓冲区
// - 流式请求体处理:避免大请求体内存复制
// - 低延迟:预估每请求 5-10µs 开销
//
// 作者xfy
package http2
import (
"io"
"net"
"net/http"
"sync"
"time"
"github.com/valyala/fasthttp"
)
// FastHTTPHandlerAdapter 将 fasthttp.RequestHandler 适配为 http.Handler。
//
// 由于 HTTP/2 服务器使用标准库的 http.Handler 接口,
// 而 lolly 使用 fasthttp需要通过适配层进行转换。
type FastHTTPHandlerAdapter struct {
handler fasthttp.RequestHandler
// ctxPool 用于复用 fasthttp.RequestCtx 对象
ctxPool sync.Pool
// bufferPool 用于复用字节缓冲区(零拷贝优化)
bufferPool sync.Pool
// headerBufferPool 用于复用头部缓冲区
headerBufferPool sync.Pool
}
// NewFastHTTPHandlerAdapter 创建新的 HTTP/2 适配器。
//
// 参数:
// - handler: fasthttp 请求处理器
//
// 返回值:
// - *FastHTTPHandlerAdapter: 适配器实例
func NewFastHTTPHandlerAdapter(handler fasthttp.RequestHandler) *FastHTTPHandlerAdapter {
return &FastHTTPHandlerAdapter{
handler: handler,
ctxPool: sync.Pool{
New: func() interface{} {
return &fasthttp.RequestCtx{}
},
},
bufferPool: sync.Pool{
New: func() interface{} {
buf := make([]byte, 4096) // 4KB 初始缓冲区
return &buf
},
},
headerBufferPool: sync.Pool{
New: func() interface{} {
return &fasthttp.RequestHeader{}
},
},
}
}
// ServeHTTP 实现 http.Handler 接口。
//
// 这是适配器的核心方法,将标准库 HTTP 请求转换为 fasthttp 请求,
// 调用 fasthttp 处理器,然后将响应写回标准库 ResponseWriter。
//
// 参数:
// - w: 标准库 ResponseWriter
// - r: 标准库 HTTP 请求
func (a *FastHTTPHandlerAdapter) ServeHTTP(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)
// 流式处理请求体
a.streamRequestBody(r, ctx)
// 调用 fasthttp handler
a.handler(ctx)
// 转换响应
a.convertResponse(ctx, w)
}
// resetContext 重置 fasthttp.RequestCtx 状态。
//
// 参数:
// - ctx: 需要重置的上下文
func (a *FastHTTPHandlerAdapter) 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 *FastHTTPHandlerAdapter) 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)
// 设置协议版本为 HTTP/2
ctx.Request.Header.SetProtocol("HTTP/2.0")
// 设置 Host 头
ctx.Request.Header.SetHost(r.Host)
// 零拷贝头部转换
a.convertHeaders(r, ctx)
// 设置远程地址
a.setRemoteAddr(r, ctx)
// 设置 Content-Type
if ct := r.Header.Get("Content-Type"); ct != "" {
ctx.Request.Header.SetContentType(ct)
}
// 设置 Content-Length如果有
if r.ContentLength > 0 {
ctx.Request.Header.SetContentLength(int(r.ContentLength))
}
}
// convertHeaders 将 HTTP 请求头转换为 fasthttp 格式。
//
// 使用 HPACK 风格的零拷贝转换策略。
//
// 参数:
// - r: 标准库 HTTP 请求
// - ctx: FastHTTP 请求上下文
func (a *FastHTTPHandlerAdapter) convertHeaders(r *http.Request, ctx *fasthttp.RequestCtx) {
// 跳过已处理的头部
skipHeaders := map[string]bool{
"Host": true,
"Content-Type": true,
"Content-Length": true,
}
for k, v := range r.Header {
if skipHeaders[k] {
continue
}
// 复用缓冲区避免分配
for i, vv := range v {
if i == 0 {
ctx.Request.Header.Set(k, vv)
} else {
ctx.Request.Header.Add(k, vv)
}
}
}
}
// setRemoteAddr 设置远程客户端地址。
//
// 参数:
// - r: 标准库 HTTP 请求
// - ctx: FastHTTP 请求上下文
func (a *FastHTTPHandlerAdapter) setRemoteAddr(r *http.Request, ctx *fasthttp.RequestCtx) {
if r.RemoteAddr != "" {
// 尝试解析地址
if addr, err := net.ResolveTCPAddr("tcp", r.RemoteAddr); err == nil {
ctx.SetRemoteAddr(addr)
} else {
// 回退方案:使用字符串地址
ctx.SetRemoteAddr(&net.TCPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: 0,
})
}
}
}
// streamRequestBody 流式读取请求体到 fasthttp。
//
// 对于大请求体,使用流式处理避免内存峰值。
//
// 参数:
// - r: 标准库 HTTP 请求
// - ctx: FastHTTP 请求上下文
func (a *FastHTTPHandlerAdapter) streamRequestBody(r *http.Request, ctx *fasthttp.RequestCtx) {
if r.Body == nil || r.Body == http.NoBody {
return
}
defer func() {
_ = r.Body.Close()
}()
// 小请求体:直接读取到内存
if r.ContentLength > 0 && r.ContentLength <= 64*1024 {
body, err := io.ReadAll(r.Body)
if err == nil {
ctx.Request.SetBody(body)
}
return
}
// 大请求体:使用流式缓冲区
bufPtr, ok := a.bufferPool.Get().(*[]byte)
if !ok {
// 如果类型断言失败,创建新的缓冲区
buf := make([]byte, 4096)
bufPtr = &buf
}
defer a.bufferPool.Put(bufPtr)
buf := *bufPtr
var body []byte
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)
}
}
// convertResponse 将 fasthttp.RequestCtx 响应写入 http.ResponseWriter。
//
// 参数:
// - ctx: FastHTTP 请求上下文
// - w: 标准库 ResponseWriter
func (a *FastHTTPHandlerAdapter) convertResponse(ctx *fasthttp.RequestCtx, w http.ResponseWriter) {
// 设置状态码
statusCode := ctx.Response.StatusCode()
if statusCode == 0 {
statusCode = http.StatusOK
}
// 复制响应头
for key, value := range ctx.Response.Header.All() {
w.Header().Add(string(key), string(value))
}
// 确保 Content-Type 被设置
if ct := ctx.Response.Header.ContentType(); len(ct) > 0 {
w.Header().Set("Content-Type", string(ct))
}
// 确保 Content-Length 被设置(如果已知)
if cl := ctx.Response.Header.ContentLength(); cl > 0 {
w.Header().Set("Content-Length", string(fasthttp.AppendUint(nil, cl)))
}
// 写入状态码
w.WriteHeader(statusCode)
// 写入响应体
body := ctx.Response.Body()
if len(body) > 0 {
if _, err := w.Write(body); err != nil {
// 响应写入失败,无法向客户端返回错误,只能记录
return
}
}
}
// WrapHandler 创建一个适配器包装的 handler。
//
// 这是一个便捷函数,用于快速创建适配器实例。
//
// 参数:
// - handler: fasthttp 请求处理器
//
// 返回值:
// - http.Handler: 标准库兼容的处理器
func WrapHandler(handler fasthttp.RequestHandler) http.Handler {
return NewFastHTTPHandlerAdapter(handler)
}
// WrapHandlerFunc 创建一个适配器包装的 handler 函数。
//
// 这是一个便捷函数,允许直接使用函数而非创建 handler 实例。
//
// 参数:
// - fn: fasthttp handler 函数
//
// 返回值:
// - http.Handler: 标准库兼容的处理器
func WrapHandlerFunc(fn func(*fasthttp.RequestCtx)) http.Handler {
return NewFastHTTPHandlerAdapter(fn)
}
// AdapterConfig 提供适配器的配置选项。
type AdapterConfig struct {
// BufferSize 是缓冲区大小,默认为 4096 字节
BufferSize int
// MaxBodySize 是最大请求体大小,超过则使用流式处理
MaxBodySize int64
// Timeout 是请求处理超时时间
Timeout time.Duration
}
// DefaultAdapterConfig 返回适配器的默认配置。
//
// 返回包含默认缓冲区大小4096 字节、最大请求体大小64KB
// 和超时时间30 秒)的配置实例。
//
// 返回值:
// - *AdapterConfig: 初始化的适配器默认配置实例
func DefaultAdapterConfig() *AdapterConfig {
return &AdapterConfig{
BufferSize: 4096,
MaxBodySize: 64 * 1024, // 64KB
Timeout: 30 * time.Second,
}
}
// ConfigurableAdapter 是基于配置的可配置适配器。
type ConfigurableAdapter struct {
*FastHTTPHandlerAdapter
config *AdapterConfig
}
// NewConfigurableAdapter 创建可配置的 HTTP/2 适配器。
//
// 根据传入的配置参数创建适配器实例,如果配置为 nil 则使用默认配置。
// 适用于需要自定义缓冲区大小、请求体大小限制或超时时间的场景。
//
// 参数:
// - handler: fasthttp 请求处理器,用于处理 HTTP/2 请求
// - config: 适配器配置,为 nil 时使用默认配置
//
// 返回值:
// - *ConfigurableAdapter: 初始化的可配置适配器实例
func NewConfigurableAdapter(handler fasthttp.RequestHandler, config *AdapterConfig) *ConfigurableAdapter {
if config == nil {
config = DefaultAdapterConfig()
}
return &ConfigurableAdapter{
FastHTTPHandlerAdapter: NewFastHTTPHandlerAdapter(handler),
config: config,
}
}