// Package http2 提供 HTTP/2 请求适配层。 // // 该文件实现 fasthttp.RequestHandler 与 http.Handler 之间的适配, // 使 HTTP/2 服务器能够复用现有的 fasthttp 处理器。 // // 主要特性: // // - 零拷贝头部转换:使用 sync.Pool 复用缓冲区 // - 流式请求体处理:避免大请求体内存复制 // - 低延迟:预估每请求 5-10µs 开销 // // 作者:xfy package http2 import ( "net" "net/http" "github.com/valyala/fasthttp" "rua.plus/lolly/internal/adapter" ) // FastHTTPHandlerAdapter 将 fasthttp.RequestHandler 适配为 http.Handler。 // // 由于 HTTP/2 服务器使用标准库的 http.Handler 接口, // 而 lolly 使用 fasthttp,需要通过适配层进行转换。 type FastHTTPHandlerAdapter struct { *adapter.CommonAdapter handler fasthttp.RequestHandler } // NewFastHTTPHandlerAdapter 创建新的 HTTP/2 适配器。 // // 参数: // - handler: fasthttp 请求处理器 // // 返回值: // - *FastHTTPHandlerAdapter: 适配器实例 func NewFastHTTPHandlerAdapter(handler fasthttp.RequestHandler) *FastHTTPHandlerAdapter { return &FastHTTPHandlerAdapter{ CommonAdapter: adapter.NewCommonAdapter(), handler: handler, } } // ServeHTTP 实现 http.Handler 接口。 // // 这是适配器的核心方法,将标准库 HTTP 请求转换为 fasthttp 请求, // 调用 fasthttp 处理器,然后将响应写回标准库 ResponseWriter。 // // 参数: // - w: 标准库 ResponseWriter // - r: 标准库 HTTP 请求 func (a *FastHTTPHandlerAdapter) ServeHTTP(w http.ResponseWriter, r *http.Request) { // 从池中获取 RequestCtx ctx, _ := a.GetContext() defer a.PutContext(ctx) // 重置 ctx 状态以避免污染 a.ResetContext(ctx) // 转换请求(零拷贝头部转换) a.convertRequest(r, ctx) // 流式处理请求体 a.StreamRequestBody(r, ctx) // 调用 fasthttp handler a.handler(ctx) // 转换响应 a.convertResponse(ctx, w) } // 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, }) } } } // 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 } } }