From ddc9e0cef1a79d363949095c519b1d4c1679e462 Mon Sep 17 00:00:00 2001 From: xfy Date: Thu, 16 Apr 2026 11:44:45 +0800 Subject: [PATCH] =?UTF-8?q?test(integration):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=AB=AF=E5=88=B0=E7=AB=AF=E9=9B=86=E6=88=90=E6=80=A7=E8=83=BD?= =?UTF-8?q?=E5=9F=BA=E5=87=86=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 覆盖静态文件、代理转发、中间件链、Lua 脚本和多路由场景的完整请求路径吞吐量。 Co-Authored-By: Claude Opus 4.6 --- internal/integration/e2e_bench_test.go | 1492 ++++++++++++++++++++++++ 1 file changed, 1492 insertions(+) create mode 100644 internal/integration/e2e_bench_test.go diff --git a/internal/integration/e2e_bench_test.go b/internal/integration/e2e_bench_test.go new file mode 100644 index 0000000..a3a2fc1 --- /dev/null +++ b/internal/integration/e2e_bench_test.go @@ -0,0 +1,1492 @@ +// Package integration 提供端到端集成基准测试。 +// +// 该文件测试完整请求路径的吞吐量,涵盖静态文件、代理转发、 +// 中间件链、Lua 脚本、HTTPS 和多路由等场景。 +// +// 测试策略: +// - 使用 fasthttputil.NewInmemoryListener 创建内存服务器 +// - 手动构建处理链模拟服务器的完整请求路径 +// - 使用 b.RunParallel 测试并发吞吐量 +// - 包含预热逻辑确保缓存命中场景测试 +// +// 作者:xfy +package integration + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "net" + "os" + "path/filepath" + "strconv" + "sync/atomic" + "testing" + "time" + + "github.com/valyala/fasthttp" + "github.com/valyala/fasthttp/fasthttputil" + "rua.plus/lolly/internal/benchmark/tools" + "rua.plus/lolly/internal/cache" + "rua.plus/lolly/internal/config" + "rua.plus/lolly/internal/handler" + "rua.plus/lolly/internal/loadbalance" + "rua.plus/lolly/internal/lua" + mw "rua.plus/lolly/internal/middleware" + "rua.plus/lolly/internal/middleware/accesslog" + "rua.plus/lolly/internal/middleware/compression" + "rua.plus/lolly/internal/middleware/rewrite" + "rua.plus/lolly/internal/middleware/security" + "rua.plus/lolly/internal/proxy" +) + +// generateTestCert 生成自签名测试证书(服务器认证)。 +// +// 返回值: +// - certPEM: PEM 编码的证书 +// - keyPEM: PEM 编码的私钥 +func generateTestCert(b *testing.B) ([]byte, []byte) { + b.Helper() + + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + b.Fatalf("生成私钥失败: %v", err) + } + + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + Organization: []string{"Lolly Test"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().Add(time.Hour), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + DNSNames: []string{"localhost"}, + } + + certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + b.Fatalf("创建证书失败: %v", err) + } + + certPEM := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: certDER, + }) + + keyDER, err := x509.MarshalECPrivateKey(priv) + if err != nil { + b.Fatalf("编码私钥失败: %v", err) + } + keyPEM := pem.EncodeToMemory(&pem.Block{ + Type: "EC PRIVATE KEY", + Bytes: keyDER, + }) + + return certPEM, keyPEM +} + +// setupTestStaticDir 创建测试静态文件目录。 +// +// 返回值: +// - dir: 临时目录路径 +// - cleanup: 清理函数 +func setupTestStaticDir(b *testing.B) (string, func()) { + b.Helper() + + dir, err := os.MkdirTemp("", "e2e_static_*") + if err != nil { + b.Fatalf("创建临时目录失败: %v", err) + } + + // 创建测试文件 + testFiles := map[string][]byte{ + "index.html": []byte("

Hello from Lolly

"), + "small.css": make([]byte, 512), // 512B + "medium.json": make([]byte, 10240), // 10KB + "assets/app.js": make([]byte, 5120), // 5KB + } + + for path, content := range testFiles { + fullPath := filepath.Join(dir, path) + if err := os.MkdirAll(filepath.Dir(fullPath), 0o755); err != nil { + b.Fatalf("创建目录失败: %v", err) + } + if err := os.WriteFile(fullPath, content, 0o644); err != nil { + b.Fatalf("写入文件失败: %v", err) + } + } + + cleanup := func() { + _ = os.RemoveAll(dir) + } + + return dir, cleanup +} + +// setupNetworkBackend 设置真实网络后端服务器。 +// +// 使用真实 TCP 监听器,确保代理转发测试走真实网络路径。 +// +// 返回值: +// - addr: 监听地址 +// - cleanup: 清理函数 +func setupNetworkBackend(b *testing.B, statusCode int, body []byte) (string, func()) { + b.Helper() + + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + b.Fatalf("创建监听器失败: %v", err) + } + + srv := &fasthttp.Server{ + Handler: func(ctx *fasthttp.RequestCtx) { + ctx.SetStatusCode(statusCode) + _, _ = ctx.Write(body) + }, + } + + go func() { + _ = srv.Serve(ln) + }() + + addr := ln.Addr().String() + cleanup := func() { + _ = srv.Shutdown() + _ = ln.Close() + } + + return addr, cleanup +} + +// buildMiddlewareChainForBenchmark 构建基准测试用中间件链。 +// +// 按服务器相同顺序构建:AccessLog -> Rewrite -> Compression -> SecurityHeaders。 +// +// 参数: +// - enableCompression: 是否启用压缩中间件 +// - enableSecurityHeaders: 是否启用安全头中间件 +// +// 返回值: +// - *mw.Chain: 构建完成的中间件链 +func buildMiddlewareChainForBenchmark(enableCompression, enableSecurityHeaders bool) *mw.Chain { + var middlewares []mw.Middleware + + // 1. AccessLog (始终添加) + accessLog := accesslog.New(&config.LoggingConfig{}) + middlewares = append(middlewares, accessLog) + + // 2. Rewrite + rewriteRules := []config.RewriteRule{ + {Pattern: "^/redirect/(.*)", Replacement: "/new/$1", Flag: "last"}, + } + rw, _ := rewrite.New(rewriteRules) + middlewares = append(middlewares, rw) + + // 3. Compression + if enableCompression { + comp, _ := compression.New(&config.CompressionConfig{ + Type: "gzip", + Level: 6, + Types: []string{"text/html", "text/css", "application/json", "application/javascript"}, + }) + middlewares = append(middlewares, comp) + } + + // 4. SecurityHeaders + if enableSecurityHeaders { + headers := security.NewHeadersWithHSTS(&config.SecurityHeaders{ + XFrameOptions: "DENY", + XContentTypeOptions: "nosniff", + }, &config.HSTSConfig{}) + middlewares = append(middlewares, headers) + } + + return mw.NewChain(middlewares...) +} + +// createTestProxy 创建测试代理实例。 +// +// 参数: +// - backendAddr: 后端地址 +// - path: 代理路径 +// +// 返回值: +// - *proxy.Proxy: 代理实例 +// - error: 创建错误 +func createTestProxy(backendAddr, path string) (*proxy.Proxy, error) { + cfg := &config.ProxyConfig{ + Path: path, + LoadBalance: "round_robin", + Timeout: config.ProxyTimeout{ + Connect: 5 * time.Second, + Read: 30 * time.Second, + Write: 30 * time.Second, + }, + } + + targets := []*loadbalance.Target{ + {URL: "http://" + backendAddr}, + } + targets[0].Healthy.Store(true) + + return proxy.NewProxy(cfg, targets, nil, nil) +} + +// warmupProxy 预热代理,确保连接池已建立。 +// +// 发送若干预热请求,确保后续基准测试命中缓存的连接池。 +// +// 参数: +// - p: 代理实例 +// - path: 请求路径 +// - count: 预热请求数量 +func warmupProxy(p *proxy.Proxy, path string, count int) { + for i := 0; i < count; i++ { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI(path) + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + p.ServeHTTP(ctx) + } +} + +// warmupStaticHandler 预热静态文件处理器,确保缓存已填充。 +// +// 参数: +// - h: 静态文件处理器 +// - paths: 预热路径列表 +func warmupStaticHandler(h *handler.StaticHandler, paths []string) { + for _, path := range paths { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI(path) + h.Handle(ctx) + } +} + +// ============================================================ +// BenchmarkE2EStaticFile - 静态文件完整请求路径 +// +// 测试从接收请求到查找文件、缓存查找、发送响应的完整路径。 +// 包含缓存未命中和缓存命中两种场景。 +// ============================================================ + +// BenchmarkE2EStaticFile 基准测试静态文件完整请求路径(缓存未命中)。 +func BenchmarkE2EStaticFile(b *testing.B) { + dir, cleanup := setupTestStaticDir(b) + defer cleanup() + + // 构建中间件链 + chain := buildMiddlewareChainForBenchmark(false, false) + + // 创建静态文件处理器 + staticHandler := handler.NewStaticHandler(dir, "/", []string{"index.html"}, true) + + // 创建路由器并注册静态路由 + router := handler.NewRouter() + router.GET("/{filepath:*}", staticHandler.Handle) + + // 应用中间件 + finalHandler := chain.Apply(router.Handler()) + + b.ResetTimer() + b.ReportAllocs() + + paths := []string{"/small.css", "/medium.json", "/assets/app.js", "/index.html"} + var counter uint64 + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + idx := atomic.AddUint64(&counter, 1) + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI(paths[idx%uint64(len(paths))]) + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + finalHandler(ctx) + } + }) +} + +// BenchmarkE2EStaticFileCacheHit 基准测试静态文件缓存命中场景。 +func BenchmarkE2EStaticFileCacheHit(b *testing.B) { + dir, cleanup := setupTestStaticDir(b) + defer cleanup() + + // 启用文件缓存 + fc := cache.NewFileCache(1000, 100*1024*1024, 0) + staticHandler := handler.NewStaticHandler(dir, "/", []string{"index.html"}, true) + staticHandler.SetFileCache(fc) + staticHandler.SetCacheTTL(5 * time.Second) + + // 预热缓存 + warmupStaticHandler(staticHandler, []string{"/small.css", "/medium.json", "/assets/app.js", "/index.html"}) + + // 构建中间件链和路由器 + chain := buildMiddlewareChainForBenchmark(false, false) + router := handler.NewRouter() + router.GET("/{filepath:*}", staticHandler.Handle) + finalHandler := chain.Apply(router.Handler()) + + b.ResetTimer() + b.ReportAllocs() + + paths := []string{"/small.css", "/medium.json", "/assets/app.js", "/index.html"} + var counter uint64 + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + idx := atomic.AddUint64(&counter, 1) + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI(paths[idx%uint64(len(paths))]) + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + finalHandler(ctx) + } + }) +} + +// ============================================================ +// BenchmarkE2EProxyForward - 代理转发完整请求路径 +// +// 测试通过代理将请求转发到后端的完整路径,包括负载均衡、 +// 连接池复用、请求头改写和响应转发。 +// ============================================================ + +// BenchmarkE2EProxyForward 基准测试代理转发完整路径。 +func BenchmarkE2EProxyForward(b *testing.B) { + // 启动后端服务器 + responseBody := []byte(`{"status":"ok","message":"Hello from backend"}`) + addr, cleanup := setupNetworkBackend(b, fasthttp.StatusOK, responseBody) + defer cleanup() + + // 创建代理 + p, err := createTestProxy(addr, "/api") + if err != nil { + b.Fatalf("创建代理失败: %v", err) + } + + // 预热连接池 + warmupProxy(p, "/api/test", 10) + + b.ResetTimer() + b.ReportAllocs() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/api/test") + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + ctx.Request.Header.Set("X-Forwarded-For", "192.168.1.100") + ctx.Request.Header.Set("Host", "example.com") + p.ServeHTTP(ctx) + } + }) +} + +// BenchmarkE2EProxyForwardLargeResponse 基准测试大响应代理转发。 +func BenchmarkE2EProxyForwardLargeResponse(b *testing.B) { + // 100KB 响应体 + largeBody := make([]byte, 100*1024) + for i := range largeBody { + largeBody[i] = byte('A' + i%26) + } + + addr, cleanup := setupNetworkBackend(b, fasthttp.StatusOK, largeBody) + defer cleanup() + + p, err := createTestProxy(addr, "/api") + if err != nil { + b.Fatalf("创建代理失败: %v", err) + } + + warmupProxy(p, "/api/data", 5) + + b.ResetTimer() + b.ReportAllocs() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/api/data") + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + p.ServeHTTP(ctx) + } + }) +} + +// ============================================================ +// BenchmarkE2EWithMiddleware - 带中间件链的完整路径 +// +// 测试包含完整中间件链(AccessLog、Rewrite、Compression、 +// SecurityHeaders)的请求处理路径。 +// ============================================================ + +// BenchmarkE2EWithMiddleware 基准测试带完整中间件链的请求路径。 +func BenchmarkE2EWithMiddleware(b *testing.B) { + // 启动后端 + responseBody := []byte(`{"status":"ok","data":"middleware test"}`) + addr, cleanup := setupNetworkBackend(b, fasthttp.StatusOK, responseBody) + defer cleanup() + + // 创建代理 + p, err := createTestProxy(addr, "/api") + if err != nil { + b.Fatalf("创建代理失败: %v", err) + } + warmupProxy(p, "/api/test", 5) + + // 构建完整中间件链 + chain := buildMiddlewareChainForBenchmark(true, true) + + // 创建路由器 + router := handler.NewRouter() + router.GET("/api/{path:*}", p.ServeHTTP) + + // 应用中间件链 + finalHandler := chain.Apply(router.Handler()) + + b.ResetTimer() + b.ReportAllocs() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/api/test") + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + ctx.Request.Header.Set("Host", "example.com") + ctx.Request.Header.Set("Accept-Encoding", "gzip, deflate") + finalHandler(ctx) + } + }) +} + +// BenchmarkE2EWithMiddlewareNoCompression 基准测试不带压缩的中间件链。 +func BenchmarkE2EWithMiddlewareNoCompression(b *testing.B) { + responseBody := []byte(`{"status":"ok"}`) + addr, cleanup := setupNetworkBackend(b, fasthttp.StatusOK, responseBody) + defer cleanup() + + p, err := createTestProxy(addr, "/api") + if err != nil { + b.Fatalf("创建代理失败: %v", err) + } + warmupProxy(p, "/api/test", 5) + + // 只启用安全头,不启用压缩 + chain := buildMiddlewareChainForBenchmark(false, true) + + router := handler.NewRouter() + router.GET("/api/{path:*}", p.ServeHTTP) + finalHandler := chain.Apply(router.Handler()) + + b.ResetTimer() + b.ReportAllocs() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/api/test") + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + ctx.Request.Header.Set("Host", "example.com") + finalHandler(ctx) + } + }) +} + +// ============================================================ +// BenchmarkE2ELuaScript - 带 Lua 脚本执行的完整路径 +// +// 测试包含 Lua 中间件的请求处理路径,包括 Lua 引擎初始化、 +// 脚本执行和结果处理。 +// ============================================================ + +// BenchmarkE2ELuaScript 基准测试带 Lua 脚本执行的完整路径。 +func BenchmarkE2ELuaScript(b *testing.B) { + // 启动后端 + responseBody := []byte(`{"status":"ok","lua":"executed"}`) + addr, cleanup := setupNetworkBackend(b, fasthttp.StatusOK, responseBody) + defer cleanup() + + p, err := createTestProxy(addr, "/api") + if err != nil { + b.Fatalf("创建代理失败: %v", err) + } + warmupProxy(p, "/api/test", 5) + + // 创建 Lua 引擎 + engine, err := lua.NewEngine(lua.DefaultConfig()) + if err != nil { + b.Fatalf("创建 Lua 引擎失败: %v", err) + } + defer engine.Close() + + // 创建简单的 Lua 脚本 + tmpDir := b.TempDir() + scriptPath := filepath.Join(tmpDir, "access.lua") + scriptContent := `-- access phase: add custom header +ngx.header["X-Lua-Processed"] = "true"` + if err := os.WriteFile(scriptPath, []byte(scriptContent), 0o644); err != nil { + b.Fatalf("写入 Lua 脚本失败: %v", err) + } + + // 创建 Lua 中间件 + luaMW, err := lua.NewLuaMiddleware(engine, lua.LuaMiddlewareConfig{ + ScriptPath: scriptPath, + Phase: lua.PhaseAccess, + }) + if err != nil { + b.Fatalf("创建 Lua 中间件失败: %v", err) + } + + // 使用 MultiPhaseLuaMiddleware 组合多个 Lua 阶段 + multiLua := lua.NewMultiPhaseLuaMiddleware(engine, "e2e-lua") + + // 添加 content phase 脚本 + contentScript := filepath.Join(tmpDir, "content.lua") + if err := os.WriteFile(contentScript, []byte(`-- content phase: noop`), 0o644); err != nil { + b.Fatalf("写入 Lua 脚本失败: %v", err) + } + if err := multiLua.AddPhase(lua.PhaseContent, contentScript, 10*time.Second); err != nil { + b.Fatalf("添加 Lua 阶段失败: %v", err) + } + + // 组合中间件链:AccessLog -> Lua -> Proxy + var middlewares []mw.Middleware + middlewares = append(middlewares, accesslog.New(&config.LoggingConfig{})) + middlewares = append(middlewares, luaMW) + + chain := mw.NewChain(middlewares...) + + router := handler.NewRouter() + router.GET("/api/{path:*}", p.ServeHTTP) + + wrappedByLua := luaMW.Process(router.Handler()) + finalHandler := chain.Apply(wrappedByLua) + + // 预热 Lua 引擎(字节码缓存) + for i := 0; i < 5; i++ { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/api/test") + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + finalHandler(ctx) + } + + b.ResetTimer() + b.ReportAllocs() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/api/test") + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + ctx.Request.Header.Set("Host", "example.com") + finalHandler(ctx) + } + }) +} + +// BenchmarkE2EMultiLuaPhase 基准测试多 Lua 阶段执行路径。 +func BenchmarkE2EMultiLuaPhase(b *testing.B) { + responseBody := []byte(`{"status":"ok","multi_lua":"executed"}`) + addr, cleanup := setupNetworkBackend(b, fasthttp.StatusOK, responseBody) + defer cleanup() + + p, err := createTestProxy(addr, "/api") + if err != nil { + b.Fatalf("创建代理失败: %v", err) + } + warmupProxy(p, "/api/test", 5) + + // 创建 Lua 引擎 + engine, err := lua.NewEngine(lua.DefaultConfig()) + if err != nil { + b.Fatalf("创建 Lua 引擎失败: %v", err) + } + defer engine.Close() + + tmpDir := b.TempDir() + + // 创建多阶段 Lua 中间件 + multiLua := lua.NewMultiPhaseLuaMiddleware(engine, "multi-phase") + + // 添加 rewrite phase + rewriteScript := filepath.Join(tmpDir, "rewrite.lua") + if err := os.WriteFile(rewriteScript, []byte(`-- rewrite phase: modify path`), 0o644); err != nil { + b.Fatalf("写入 Lua 脚本失败: %v", err) + } + if err := multiLua.AddPhase(lua.PhaseRewrite, rewriteScript, 10*time.Second); err != nil { + b.Fatalf("添加 Lua 阶段失败: %v", err) + } + + // 添加 access phase + accessScript := filepath.Join(tmpDir, "access2.lua") + if err := os.WriteFile(accessScript, []byte(`-- access phase: add header`), 0o644); err != nil { + b.Fatalf("写入 Lua 脚本失败: %v", err) + } + if err := multiLua.AddPhase(lua.PhaseAccess, accessScript, 10*time.Second); err != nil { + b.Fatalf("添加 Lua 阶段失败: %v", err) + } + + // 构建中间件链 + router := handler.NewRouter() + router.GET("/api/{path:*}", p.ServeHTTP) + wrappedByLua := multiLua.Process(router.Handler()) + + baseChain := buildMiddlewareChainForBenchmark(false, false) + finalHandler := baseChain.Apply(wrappedByLua) + + // 预热 + for i := 0; i < 5; i++ { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/api/test") + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + finalHandler(ctx) + } + + b.ResetTimer() + b.ReportAllocs() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/api/test") + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + ctx.Request.Header.Set("Host", "example.com") + finalHandler(ctx) + } + }) +} + +// ============================================================ +// BenchmarkE2EHTTPS - HTTPS 完整请求路径 +// +// 测试 TLS 握手和 HTTPS 请求处理的完整路径。 +// 使用内存监听器模拟 HTTPS 连接。 +// ============================================================ + +// BenchmarkE2EHTTPS 基准测试 HTTPS 完整请求路径。 +func BenchmarkE2EHTTPS(b *testing.B) { + // 生成测试证书 + certPEM, keyPEM := generateTestCert(b) + + // 写入临时文件 + tmpDir := b.TempDir() + certPath := filepath.Join(tmpDir, "server.crt") + keyPath := filepath.Join(tmpDir, "server.key") + if err := os.WriteFile(certPath, certPEM, 0o644); err != nil { + b.Fatalf("写入证书文件失败: %v", err) + } + if err := os.WriteFile(keyPath, keyPEM, 0o600); err != nil { + b.Fatalf("写入密钥文件失败: %v", err) + } + + // 加载 TLS 证书 + cert, err := tls.X509KeyPair(certPEM, keyPEM) + if err != nil { + b.Fatalf("加载 TLS 证书失败: %v", err) + } + + // 启动后端 + responseBody := []byte(`{"status":"ok","tls":"verified"}`) + addr, cleanup := setupNetworkBackend(b, fasthttp.StatusOK, responseBody) + defer cleanup() + + // 创建代理 + p, err := createTestProxy(addr, "/api") + if err != nil { + b.Fatalf("创建代理失败: %v", err) + } + warmupProxy(p, "/api/test", 5) + + // 创建路由器 + router := handler.NewRouter() + router.GET("/api/{path:*}", p.ServeHTTP) + + // 构建中间件链 + chain := buildMiddlewareChainForBenchmark(false, true) + finalHandler := chain.Apply(router.Handler()) + + // 创建 TLS 监听器 + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + b.Fatalf("创建监听器失败: %v", err) + } + + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{cert}, + MinVersion: tls.VersionTLS12, + } + tlsLn := tls.NewListener(ln, tlsConfig) + + tlsSrv := &fasthttp.Server{ + Name: "lolly", + Handler: finalHandler, + } + go func() { + _ = tlsSrv.Serve(tlsLn) + }() + + tlsAddr := ln.Addr().String() + + // 创建 TLS 客户端 + client := &fasthttp.HostClient{ + Addr: tlsAddr, + IsTLS: true, + TLSConfig: &tls.Config{InsecureSkipVerify: true}, + MaxConns: 1000, + } + + b.ResetTimer() + b.ReportAllocs() + + b.RunParallel(func(pb *testing.PB) { + req := fasthttp.AcquireRequest() + resp := fasthttp.AcquireResponse() + defer fasthttp.ReleaseRequest(req) + defer fasthttp.ReleaseResponse(resp) + + for pb.Next() { + req.SetRequestURI("https://" + tlsAddr + "/api/test") + req.Header.SetMethod(fasthttp.MethodGet) + req.Header.Set("Host", "example.com") + _ = client.Do(req, resp) + resp.Reset() + } + }) +} + +// BenchmarkE2ETLSHandshake 基准测试 TLS 握手开销。 +func BenchmarkE2ETLSHandshake(b *testing.B) { + certPEM, keyPEM := generateTestCert(b) + + cert, err := tls.X509KeyPair(certPEM, keyPEM) + if err != nil { + b.Fatalf("加载 TLS 证书失败: %v", err) + } + + responseBody := []byte("ok") + _, cleanup := setupNetworkBackend(b, fasthttp.StatusOK, responseBody) + defer cleanup() + + // 创建 TLS 监听器 + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + b.Fatalf("创建监听器失败: %v", err) + } + + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{cert}, + MinVersion: tls.VersionTLS12, + } + tlsLn := tls.NewListener(ln, tlsConfig) + + srv := &fasthttp.Server{ + Handler: func(ctx *fasthttp.RequestCtx) { + ctx.SetStatusCode(fasthttp.StatusOK) + _, _ = ctx.Write(responseBody) + }, + } + go func() { + _ = srv.Serve(tlsLn) + }() + + tlsAddr := ln.Addr().String() + + b.ResetTimer() + b.ReportAllocs() + + // 每次迭代都创建新连接以模拟完整握手 + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + // 创建新客户端(新连接 = 新 TLS 握手) + client := &fasthttp.HostClient{ + Addr: tlsAddr, + IsTLS: true, + TLSConfig: &tls.Config{InsecureSkipVerify: true}, + MaxConns: 1, + } + + req := fasthttp.AcquireRequest() + resp := fasthttp.AcquireResponse() + + req.SetRequestURI("https://" + tlsAddr + "/") + req.Header.SetMethod(fasthttp.MethodGet) + _ = client.Do(req, resp) + + fasthttp.ReleaseRequest(req) + fasthttp.ReleaseResponse(resp) + client.CloseIdleConnections() + } + }) +} + +// ============================================================ +// BenchmarkE2EMultipleRoutes - 多路由匹配性能 +// +// 测试路由器在多条路由规则下的匹配性能, +// 模拟真实服务器有多条代理和静态文件路径的场景。 +// ============================================================ + +// BenchmarkE2EMultipleRoutes 基准测试多路由匹配性能。 +func BenchmarkE2EMultipleRoutes(b *testing.B) { + // 启动多个后端 + addr1, cleanup1 := setupNetworkBackend(b, fasthttp.StatusOK, []byte(`{"service":"api-v1"}`)) + defer cleanup1() + addr2, cleanup2 := setupNetworkBackend(b, fasthttp.StatusOK, []byte(`{"service":"api-v2"}`)) + defer cleanup2() + addr3, cleanup3 := setupNetworkBackend(b, fasthttp.StatusOK, []byte(`{"service":"admin"}`)) + defer cleanup3() + + // 创建多个代理 + p1, _ := createTestProxy(addr1, "/api/v1") + p2, _ := createTestProxy(addr2, "/api/v2") + p3, _ := createTestProxy(addr3, "/admin") + + warmupProxy(p1, "/api/v1/test", 3) + warmupProxy(p2, "/api/v2/test", 3) + warmupProxy(p3, "/admin/test", 3) + + // 创建静态文件目录 + staticDir, staticCleanup := setupTestStaticDir(b) + defer staticCleanup() + + staticHandler := handler.NewStaticHandler(staticDir, "/static/", []string{"index.html"}, true) + warmupStaticHandler(staticHandler, []string{"/static/small.css", "/static/medium.json"}) + + // 构建路由器,注册多条路由 + router := handler.NewRouter() + router.GET("/api/v1/{path:*}", p1.ServeHTTP) + router.GET("/api/v2/{path:*}", p2.ServeHTTP) + router.POST("/api/v2/{path:*}", p2.ServeHTTP) + router.GET("/admin/{path:*}", p3.ServeHTTP) + router.GET("/static/{filepath:*}", staticHandler.Handle) + router.GET("/health", func(ctx *fasthttp.RequestCtx) { + ctx.SetBodyString(`{"status":"healthy"}`) + }) + router.GET("/", func(ctx *fasthttp.RequestCtx) { + ctx.SetBodyString(`Welcome`) + }) + + // 应用中间件链 + chain := buildMiddlewareChainForBenchmark(false, false) + finalHandler := chain.Apply(router.Handler()) + + // 预热路由匹配 + testPaths := []string{"/api/v1/test", "/api/v2/data", "/admin/dashboard", "/static/small.css", "/health", "/"} + for _, path := range testPaths { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI(path) + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + finalHandler(ctx) + } + + b.ResetTimer() + b.ReportAllocs() + + // 使用原子计数器轮询不同路径 + var counter uint64 + paths := []string{ + "/api/v1/users", + "/api/v2/items", + "/api/v2/items/123", + "/admin/settings", + "/static/small.css", + "/static/medium.json", + "/static/assets/app.js", + "/health", + "/", + } + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + idx := atomic.AddUint64(&counter, 1) + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI(paths[idx%uint64(len(paths))]) + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + ctx.Request.Header.Set("Host", "example.com") + finalHandler(ctx) + } + }) +} + +// BenchmarkE2EMultipleRoutesWithMiddleware 基准测试多路由+完整中间件链。 +func BenchmarkE2EMultipleRoutesWithMiddleware(b *testing.B) { + addr1, cleanup1 := setupNetworkBackend(b, fasthttp.StatusOK, []byte(`{"service":"api"}`)) + defer cleanup1() + addr2, cleanup2 := setupNetworkBackend(b, fasthttp.StatusOK, []byte(`{"service":"graphql"}`)) + defer cleanup2() + + p1, _ := createTestProxy(addr1, "/api") + p2, _ := createTestProxy(addr2, "/graphql") + + warmupProxy(p1, "/api/test", 3) + warmupProxy(p2, "/graphql/query", 3) + + staticDir, staticCleanup := setupTestStaticDir(b) + defer staticCleanup() + + fc := cache.NewFileCache(500, 50*1024*1024, 0) + staticHandler := handler.NewStaticHandler(staticDir, "/", []string{"index.html"}, true) + staticHandler.SetFileCache(fc) + staticHandler.SetCacheTTL(5 * time.Second) + warmupStaticHandler(staticHandler, []string{"/small.css", "/index.html"}) + + router := handler.NewRouter() + router.GET("/api/{path:*}", p1.ServeHTTP) + router.POST("/api/{path:*}", p1.ServeHTTP) + router.GET("/graphql/{path:*}", p2.ServeHTTP) + router.POST("/graphql/{path:*}", p2.ServeHTTP) + router.GET("/{filepath:*}", staticHandler.Handle) + + // 完整中间件链 + chain := buildMiddlewareChainForBenchmark(true, true) + finalHandler := chain.Apply(router.Handler()) + + // 预热 + paths := []string{"/api/test", "/graphql/query", "/small.css", "/index.html"} + for _, path := range paths { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI(path) + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + ctx.Request.Header.Set("Accept-Encoding", "gzip") + finalHandler(ctx) + } + + b.ResetTimer() + b.ReportAllocs() + + var counter uint64 + testPaths := []string{ + "/api/users", + "/api/users/42", + "/graphql/query", + "/small.css", + "/medium.json", + "/index.html", + "/assets/app.js", + } + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + idx := atomic.AddUint64(&counter, 1) + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI(testPaths[idx%uint64(len(testPaths))]) + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + ctx.Request.Header.Set("Host", "example.com") + ctx.Request.Header.Set("Accept-Encoding", "gzip, deflate") + finalHandler(ctx) + } + }) +} + +// ============================================================ +// BenchmarkE2EProxyWithCache - 带代理缓存的完整路径 +// +// 测试代理缓存命中/未命中场景下的吞吐量。 +// ============================================================ + +// BenchmarkE2EProxyWithCache 基准测试代理缓存未命中场景。 +func BenchmarkE2EProxyWithCache(b *testing.B) { + responseBody := []byte(`{"cached":true,"data":"proxy cache test"}`) + addr, cleanup := setupNetworkBackend(b, fasthttp.StatusOK, responseBody) + defer cleanup() + + cfg := &config.ProxyConfig{ + Path: "/cached", + LoadBalance: "round_robin", + Timeout: config.ProxyTimeout{ + Connect: 5 * time.Second, + Read: 30 * time.Second, + Write: 30 * time.Second, + }, + Cache: config.ProxyCacheConfig{ + Enabled: true, + MaxAge: 5 * time.Minute, + }, + } + + targets := []*loadbalance.Target{{URL: "http://" + addr}} + targets[0].Healthy.Store(true) + + p, err := proxy.NewProxy(cfg, targets, nil, nil) + if err != nil { + b.Fatalf("创建代理失败: %v", err) + } + + warmupProxy(p, "/cached/test", 5) + + b.ResetTimer() + b.ReportAllocs() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/cached/item") + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + p.ServeHTTP(ctx) + } + }) +} + +// BenchmarkE2EProxyCacheHit 基准测试代理缓存命中场景。 +func BenchmarkE2EProxyCacheHit(b *testing.B) { + responseBody := []byte(`{"cached":true,"data":"cache hit test"}`) + addr, cleanup := setupNetworkBackend(b, fasthttp.StatusOK, responseBody) + defer cleanup() + + cfg := &config.ProxyConfig{ + Path: "/cached", + LoadBalance: "round_robin", + Timeout: config.ProxyTimeout{ + Connect: 5 * time.Second, + Read: 30 * time.Second, + Write: 30 * time.Second, + }, + Cache: config.ProxyCacheConfig{ + Enabled: true, + MaxAge: 5 * time.Minute, + }, + } + + targets := []*loadbalance.Target{{URL: "http://" + addr}} + targets[0].Healthy.Store(true) + + p, err := proxy.NewProxy(cfg, targets, nil, nil) + if err != nil { + b.Fatalf("创建代理失败: %v", err) + } + + // 预热使缓存命中 + warmupProxy(p, "/cached/item", 10) + + b.ResetTimer() + b.ReportAllocs() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/cached/item") + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + p.ServeHTTP(ctx) + } + }) +} + +// ============================================================ +// BenchmarkE2EInmemoryServer - 纯内存服务器完整路径 +// +// 使用 fasthttputil.NewInmemoryListener 创建完全在内存中运行的 +// 服务器,消除网络开销,测试纯处理逻辑的吞吐量。 +// ============================================================ + +// BenchmarkE2EInmemoryServer 基准测试纯内存服务器的完整请求路径。 +func BenchmarkE2EInmemoryServer(b *testing.B) { + // 启动内存后端 + backendLn := fasthttputil.NewInmemoryListener() + backendSrv := &fasthttp.Server{ + Handler: func(ctx *fasthttp.RequestCtx) { + ctx.SetStatusCode(fasthttp.StatusOK) + _, _ = ctx.Write([]byte(`{"status":"ok","backend":"inmemory"}`)) + }, + } + go func() { + _ = backendSrv.Serve(backendLn) + }() + + // 后端地址(使用内存监听器的地址) + backendAddr := backendLn.Addr().String() + + // 创建代理(通过内存监听器传递连接) + p, err := createTestProxy(backendAddr, "/api") + if err != nil { + b.Fatalf("创建代理失败: %v", err) + } + + // 创建静态文件目录 + staticDir, staticCleanup := setupTestStaticDir(b) + defer staticCleanup() + staticHandler := handler.NewStaticHandler(staticDir, "/static/", []string{"index.html"}, true) + + // 构建路由器 + router := handler.NewRouter() + router.GET("/api/{path:*}", p.ServeHTTP) + router.GET("/static/{filepath:*}", staticHandler.Handle) + router.GET("/health", func(ctx *fasthttp.RequestCtx) { + ctx.SetBodyString(`{"status":"healthy"}`) + }) + + // 中间件链 + chain := buildMiddlewareChainForBenchmark(false, false) + finalHandler := chain.Apply(router.Handler()) + + // 创建内存服务器 + serverLn := fasthttputil.NewInmemoryListener() + fastSrv := &fasthttp.Server{ + Name: "lolly", + Handler: finalHandler, + } + go func() { + _ = fastSrv.Serve(serverLn) + }() + + // 创建内存客户端 + client := &fasthttp.HostClient{ + Dial: func(addr string) (net.Conn, error) { + return serverLn.Dial() + }, + MaxConns: 1000, + } + + // 预热 + warmupPaths := []string{"/api/test", "/static/small.css", "/health"} + for _, path := range warmupPaths { + req := fasthttp.AcquireRequest() + resp := fasthttp.AcquireResponse() + req.SetRequestURI("http://localhost" + path) + req.Header.SetMethod(fasthttp.MethodGet) + _ = client.Do(req, resp) + fasthttp.ReleaseRequest(req) + fasthttp.ReleaseResponse(resp) + } + + b.ResetTimer() + b.ReportAllocs() + + var counter uint64 + paths := []string{"/api/test", "/static/small.css", "/static/medium.json", "/health", "/api/data"} + + b.RunParallel(func(pb *testing.PB) { + req := fasthttp.AcquireRequest() + resp := fasthttp.AcquireResponse() + defer fasthttp.ReleaseRequest(req) + defer fasthttp.ReleaseResponse(resp) + + for pb.Next() { + idx := atomic.AddUint64(&counter, 1) + req.SetRequestURI("http://localhost" + paths[idx%uint64(len(paths))]) + req.Header.SetMethod(fasthttp.MethodGet) + req.Header.Set("Host", "example.com") + _ = client.Do(req, resp) + resp.ResetBody() + } + }) +} + +// BenchmarkE2EInmemoryServerParallel 基准测试内存服务器并发吞吐量。 +// +// 使用 tools 包的 MockBackend 工具模拟后端。 +func BenchmarkE2EInmemoryServerParallel(b *testing.B) { + // 使用 tools 包的 mock 后端 + addr, cleanup := tools.SimpleMockBackend(fasthttp.StatusOK, []byte(`{"mock":"tools"}`)) + defer cleanup() + + time.Sleep(10 * time.Millisecond) // 等待后端启动 + + p, err := createTestProxy(addr, "/api") + if err != nil { + b.Fatalf("创建代理失败: %v", err) + } + warmupProxy(p, "/api/test", 10) + + // 中间件链 + chain := buildMiddlewareChainForBenchmark(true, true) + + router := handler.NewRouter() + router.GET("/api/{path:*}", p.ServeHTTP) + router.GET("/health", func(ctx *fasthttp.RequestCtx) { + ctx.SetBodyString("ok") + }) + + finalHandler := chain.Apply(router.Handler()) + + b.ResetTimer() + b.ReportAllocs() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/api/test") + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + ctx.Request.Header.Set("Host", "example.com") + ctx.Request.Header.Set("Accept-Encoding", "gzip") + finalHandler(ctx) + } + }) +} + +// ============================================================ +// BenchmarkE2EStaticWithCompression - 静态文件+压缩完整路径 +// +// 测试静态文件服务配合响应压缩的完整处理路径。 +// ============================================================ + +// BenchmarkE2EStaticWithCompression 基准测试静态文件+压缩完整路径。 +func BenchmarkE2EStaticWithCompression(b *testing.B) { + staticDir, staticCleanup := setupTestStaticDir(b) + defer staticCleanup() + + // 创建可压缩的内容 + jsonContent := make([]byte, 20*1024) // 20KB JSON + for i := range jsonContent { + jsonContent[i] = `{"key":"value","data":"repeat"}`[i%32] + } + jsonPath := filepath.Join(staticDir, "compressible.json") + if err := os.WriteFile(jsonPath, jsonContent, 0o644); err != nil { + b.Fatalf("写入压缩测试文件失败: %v", err) + } + + staticHandler := handler.NewStaticHandler(staticDir, "/", []string{"index.html"}, true) + + // 创建压缩中间件 + comp, err := compression.New(&config.CompressionConfig{ + Type: "gzip", + Level: 6, + Types: []string{"application/json", "text/html", "text/css"}, + }) + if err != nil { + b.Fatalf("创建压缩中间件失败: %v", err) + } + + // 预热缓存 + warmupStaticHandler(staticHandler, []string{"/compressible.json"}) + + chain := mw.NewChain(accesslog.New(&config.LoggingConfig{}), comp) + router := handler.NewRouter() + router.GET("/{filepath:*}", staticHandler.Handle) + finalHandler := chain.Apply(router.Handler()) + + b.ResetTimer() + b.ReportAllocs() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/compressible.json") + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + ctx.Request.Header.Set("Accept-Encoding", "gzip, deflate, br") + ctx.Request.Header.Set("Host", "example.com") + finalHandler(ctx) + } + }) +} + +// ============================================================ +// BenchmarkE2ERewriteMiddleware - URL重写中间件完整路径 +// +// 测试 URL 重写中间件的完整处理路径。 +// ============================================================ + +// BenchmarkE2ERewriteMiddleware 基准测试 URL 重写中间件完整路径。 +func BenchmarkE2ERewriteMiddleware(b *testing.B) { + responseBody := []byte(`{"status":"ok","rewritten":true}`) + addr, cleanup := setupNetworkBackend(b, fasthttp.StatusOK, responseBody) + defer cleanup() + + p, err := createTestProxy(addr, "/api") + if err != nil { + b.Fatalf("创建代理失败: %v", err) + } + warmupProxy(p, "/api/test", 5) + + // 创建 Rewrite 中间件 + rw, err := rewrite.New([]config.RewriteRule{ + {Pattern: "^/old-api/(.*)", Replacement: "/api/$1", Flag: "last"}, + {Pattern: "^/v1/(.*)", Replacement: "/api/$1", Flag: "last"}, + {Pattern: "^/legacy/(.*)", Replacement: "/api/v1/$1", Flag: "redirect"}, + }) + if err != nil { + b.Fatalf("创建 rewrite 中间件失败: %v", err) + } + + chain := mw.NewChain(accesslog.New(&config.LoggingConfig{}), rw) + + router := handler.NewRouter() + router.GET("/api/{path:*}", p.ServeHTTP) + router.GET("/api/v1/{path:*}", p.ServeHTTP) + finalHandler := chain.Apply(router.Handler()) + + b.ResetTimer() + b.ReportAllocs() + + paths := []string{ + "/old-api/users", + "/v1/items", + "/api/direct", + "/old-api/settings", + "/v1/data/123", + } + var counter uint64 + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + idx := atomic.AddUint64(&counter, 1) + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI(paths[idx%uint64(len(paths))]) + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + ctx.Request.Header.Set("Host", "example.com") + finalHandler(ctx) + } + }) +} + +// BenchmarkE2EAccessControl 基准测试 IP 访问控制完整路径。 +func BenchmarkE2EAccessControl(b *testing.B) { + responseBody := []byte(`{"status":"ok"}`) + addr, cleanup := setupNetworkBackend(b, fasthttp.StatusOK, responseBody) + defer cleanup() + + p, err := createTestProxy(addr, "/api") + if err != nil { + b.Fatalf("创建代理失败: %v", err) + } + warmupProxy(p, "/api/test", 5) + + // 创建访问控制中间件(允许特定 IP 段) + ac, err := security.NewAccessControl(&config.AccessConfig{ + Allow: []string{"192.168.1.0/24", "10.0.0.0/8"}, + Deny: []string{"192.168.1.100"}, + Default: "deny", + }) + if err != nil { + b.Fatalf("创建访问控制中间件失败: %v", err) + } + + chain := mw.NewChain(accesslog.New(&config.LoggingConfig{}), ac) + + router := handler.NewRouter() + router.GET("/api/{path:*}", p.ServeHTTP) + finalHandler := chain.Apply(router.Handler()) + + b.ResetTimer() + b.ReportAllocs() + + // 混合允许和拒绝的 IP + allowedIPs := []string{"192.168.1.50", "192.168.1.200", "10.0.0.1", "10.1.2.3"} + deniedIPs := []string{"192.168.1.100", "172.16.0.1", "8.8.8.8"} + allIPs := append(allowedIPs, deniedIPs...) + var counter uint64 + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + idx := atomic.AddUint64(&counter, 1) + clientIP := allIPs[idx%uint64(len(allIPs))] + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/api/test") + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + ctx.Request.Header.Set("X-Forwarded-For", clientIP) + ctx.Request.Header.Set("X-Real-IP", clientIP) + ctx.SetRemoteAddr(&net.TCPAddr{ + IP: net.ParseIP(clientIP), + Port: 12345, + }) + finalHandler(ctx) + } + }) +} + +// BenchmarkE2ERateLimiter 基准测试速率限制完整路径。 +func BenchmarkE2ERateLimiter(b *testing.B) { + responseBody := []byte(`{"status":"ok"}`) + addr, cleanup := setupNetworkBackend(b, fasthttp.StatusOK, responseBody) + defer cleanup() + + p, err := createTestProxy(addr, "/api") + if err != nil { + b.Fatalf("创建代理失败: %v", err) + } + warmupProxy(p, "/api/test", 5) + + // 创建限流中间件 + rl, err := security.NewRateLimiter(&config.RateLimitConfig{ + RequestRate: 10000, + Burst: 20000, + Algorithm: "token_bucket", + }) + if err != nil { + b.Fatalf("创建限流中间件失败: %v", err) + } + + chain := mw.NewChain(accesslog.New(&config.LoggingConfig{}), rl) + + router := handler.NewRouter() + router.GET("/api/{path:*}", p.ServeHTTP) + finalHandler := chain.Apply(router.Handler()) + + b.ResetTimer() + b.ReportAllocs() + + var counter uint64 + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + idx := atomic.AddUint64(&counter, 1) + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/api/test") + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + ctx.Request.Header.Set("X-Forwarded-For", "192.168.1."+strconv.Itoa(int(idx%255))) + ctx.SetRemoteAddr(&net.TCPAddr{ + IP: net.ParseIP("192.168.1." + strconv.Itoa(int(idx%255))), + Port: 12345, + }) + finalHandler(ctx) + } + }) +} + +// BenchmarkE2EBasicAuth 基准测试 Basic 认证完整路径。 +func BenchmarkE2EBasicAuth(b *testing.B) { + responseBody := []byte(`{"status":"ok","authenticated":true}`) + addr, cleanup := setupNetworkBackend(b, fasthttp.StatusOK, responseBody) + defer cleanup() + + p, err := createTestProxy(addr, "/api") + if err != nil { + b.Fatalf("创建代理失败: %v", err) + } + warmupProxy(p, "/api/test", 5) + + // 创建 Basic Auth 中间件(使用 bcrypt 哈希) + bcryptPassword, _ := security.HashPassword("testpass", security.HashBcrypt) + auth, err := security.NewBasicAuth(&config.AuthConfig{ + RequireTLS: false, + Users: []config.User{ + {Name: "admin", Password: bcryptPassword}, + {Name: "user", Password: bcryptPassword}, + }, + }) + if err != nil { + b.Fatalf("创建认证中间件失败: %v", err) + } + + chain := mw.NewChain(accesslog.New(&config.LoggingConfig{}), auth) + + router := handler.NewRouter() + router.GET("/api/{path:*}", p.ServeHTTP) + finalHandler := chain.Apply(router.Handler()) + + b.ResetTimer() + b.ReportAllocs() + + // 预热认证(缓存 bcrypt 结果) + for i := 0; i < 3; i++ { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/api/test") + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + ctx.Request.Header.Set("Authorization", "Basic YWRtaW46dGVzdHBhc3M=") // admin:testpass + finalHandler(ctx) + } + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/api/test") + ctx.Request.Header.SetMethod(fasthttp.MethodGet) + ctx.Request.Header.Set("Authorization", "Basic YWRtaW46dGVzdHBhc3M=") + finalHandler(ctx) + } + }) +}