205 lines
5.1 KiB
Go
205 lines
5.1 KiB
Go
// 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
|
||
}
|
||
}
|
||
}
|