diff --git a/internal/middleware/bodylimit/bodylimit_bench_test.go b/internal/middleware/bodylimit/bodylimit_bench_test.go new file mode 100644 index 0000000..647f6fc --- /dev/null +++ b/internal/middleware/bodylimit/bodylimit_bench_test.go @@ -0,0 +1,147 @@ +// Package bodylimit 提供请求体大小限制中间件的基准测试。 +// +// 作者:xfy +package bodylimit + +import ( + "bytes" + "testing" + + "github.com/valyala/fasthttp" +) + +// BenchmarkBodyLimitProcess 基准测试限制检查(无超限情况)。 +// +// 测试中间件处理正常请求(不触发限制)的性能。 +func BenchmarkBodyLimitProcess(b *testing.B) { + bl, err := New("1mb") + if err != nil { + b.Fatalf("创建中间件失败: %v", err) + } + + nextHandler := func(ctx *fasthttp.RequestCtx) { + ctx.SetStatusCode(fasthttp.StatusOK) + } + handler := bl.Process(nextHandler) + + // 准备请求上下文 + body := []byte("test body content") + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + ctx := &fasthttp.RequestCtx{} + ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetContentLength(len(body)) + ctx.Request.SetBodyStream(bytes.NewReader(body), len(body)) + + handler(ctx) + } +} + +// BenchmarkBodyLimitGetLimit 基准测试获取路径限制。 +// +// 测试 GetLimit 方法的性能,该方法在每次请求时都会被调用。 +func BenchmarkBodyLimitGetLimit(b *testing.B) { + bl, err := New("1mb") + if err != nil { + b.Fatalf("创建中间件失败: %v", err) + } + + // 添加多个路径配置 + paths := []string{ + "/api/v1/users", + "/api/v1/posts", + "/api/v2/upload", + "/admin/settings", + "/static/images", + } + limits := []string{"10kb", "100kb", "5mb", "50kb", "20mb"} + + for i, path := range paths { + if err := bl.AddPathLimit(path, limits[i]); err != nil { + b.Fatalf("添加路径限制失败: %v", err) + } + } + + testPaths := []string{ + "/api/v1/users/123", + "/api/v2/upload/file", + "/other/path", + "/admin/settings/general", + } + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + path := testPaths[i%len(testPaths)] + _ = bl.GetLimit(path) + } +} + +// BenchmarkBodyLimitPathMatching 基准测试多路径配置匹配。 +// +// 测试在有大量路径配置时 GetLimit 的性能(包括 RWMutex 锁开销)。 +func BenchmarkBodyLimitPathMatching(b *testing.B) { + bl, err := New("1mb") + if err != nil { + b.Fatalf("创建中间件失败: %v", err) + } + + // 添加大量路径配置 + for i := 0; i < 100; i++ { + path := "/api/v" + string(rune('0'+i%10)) + "/resource" + string(rune('0'+i%10)) + size := "1mb" + if i%3 == 0 { + size = "10mb" + } else if i%5 == 0 { + size = "100kb" + } + if err := bl.AddPathLimit(path, size); err != nil { + b.Fatalf("添加路径限制失败: %v", err) + } + } + + testPaths := []string{ + "/api/v1/resource1/123", + "/api/v5/resource5/upload", + "/other/path", + "/api/v9/resource9/data", + } + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + path := testPaths[i%len(testPaths)] + _ = bl.GetLimit(path) + } +} + +// BenchmarkParseSize 基准测试大小字符串解析。 +// +// 测试 ParseSize 函数解析各种大小字符串的性能。 +func BenchmarkParseSize(b *testing.B) { + sizes := []string{ + "1024", + "1kb", + "10kb", + "1mb", + "10mb", + "1.5mb", + "1gb", + } + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + sizeStr := sizes[i%len(sizes)] + _, err := ParseSize(sizeStr) + if err != nil { + b.Fatalf("ParseSize(%q) 失败: %v", sizeStr, err) + } + } +} diff --git a/internal/middleware/rewrite/rewrite_bench_test.go b/internal/middleware/rewrite/rewrite_bench_test.go new file mode 100644 index 0000000..7285415 --- /dev/null +++ b/internal/middleware/rewrite/rewrite_bench_test.go @@ -0,0 +1,254 @@ +// Package rewrite URL 重写中间件基准测试 +package rewrite + +import ( + "testing" + + "github.com/valyala/fasthttp" + "rua.plus/lolly/internal/config" +) + +// BenchmarkRewriteProcess 基准测试:正则匹配 + 替换 +// 测试单个重写规则的性能 +func BenchmarkRewriteProcess(b *testing.B) { + m, err := New([]config.RewriteRule{ + {Pattern: "^/api/v1/(.*)$", Replacement: "/api/v2/$1", Flag: "last"}, + }) + if err != nil { + b.Fatalf("New() error: %v", err) + } + + nextHandler := func(ctx *fasthttp.RequestCtx) { + ctx.WriteString("OK") + } + handler := m.Process(nextHandler) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/api/v1/users") + handler(ctx) + } +} + +// BenchmarkRewriteMultipleRules 基准测试:多规则匹配(最多 10 个) +// 测试规则列表的遍历和匹配性能 +func BenchmarkRewriteMultipleRules(b *testing.B) { + // 创建 10 个规则,只有最后一个匹配 + rules := []config.RewriteRule{ + {Pattern: "^/a/(.*)$", Replacement: "/x/$1", Flag: "last"}, + {Pattern: "^/b/(.*)$", Replacement: "/x/$1", Flag: "last"}, + {Pattern: "^/c/(.*)$", Replacement: "/x/$1", Flag: "last"}, + {Pattern: "^/d/(.*)$", Replacement: "/x/$1", Flag: "last"}, + {Pattern: "^/e/(.*)$", Replacement: "/x/$1", Flag: "last"}, + {Pattern: "^/f/(.*)$", Replacement: "/x/$1", Flag: "last"}, + {Pattern: "^/g/(.*)$", Replacement: "/x/$1", Flag: "last"}, + {Pattern: "^/h/(.*)$", Replacement: "/x/$1", Flag: "last"}, + {Pattern: "^/i/(.*)$", Replacement: "/x/$1", Flag: "last"}, + {Pattern: "^/j/(.*)$", Replacement: "/x/$1", Flag: "last"}, + } + + m, err := New(rules) + if err != nil { + b.Fatalf("New() error: %v", err) + } + + nextHandler := func(ctx *fasthttp.RequestCtx) { + ctx.WriteString("OK") + } + handler := m.Process(nextHandler) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/j/resource") + handler(ctx) + } +} + +// BenchmarkRewriteWithVariableExpand 基准测试:带变量展开 +// 测试变量展开对性能的影响 +func BenchmarkRewriteWithVariableExpand(b *testing.B) { + // 在替换字符串中使用变量 + m, err := New([]config.RewriteRule{ + {Pattern: "^/api/(.*)$", Replacement: "/proxy/${host}/$1", Flag: "last"}, + }) + if err != nil { + b.Fatalf("New() error: %v", err) + } + + nextHandler := func(ctx *fasthttp.RequestCtx) { + ctx.WriteString("OK") + } + handler := m.Process(nextHandler) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/api/data") + // 设置 Host 头以供变量展开使用 + ctx.Request.Header.Set("Host", "example.com") + handler(ctx) + } +} + +// BenchmarkRewriteFlagLast 基准测试:FlagLast 循环检测 +// 测试 FlagLast 重新扫描的性能 +func BenchmarkRewriteFlagLast(b *testing.B) { + // 创建两条规则形成链式重写 + // /v1/* -> /v2/* -> /v3/* + m, err := New([]config.RewriteRule{ + {Pattern: "^/v1/(.*)$", Replacement: "/v2/$1", Flag: "last"}, + {Pattern: "^/v2/(.*)$", Replacement: "/v3/$1", Flag: "last"}, + }) + if err != nil { + b.Fatalf("New() error: %v", err) + } + + nextHandler := func(ctx *fasthttp.RequestCtx) { + ctx.WriteString("OK") + } + handler := m.Process(nextHandler) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/v1/resource") + handler(ctx) + } +} + +// BenchmarkRewriteNoMatch 基准测试:无匹配情况 +// 测试当没有规则匹配时的性能 +func BenchmarkRewriteNoMatch(b *testing.B) { + m, err := New([]config.RewriteRule{ + {Pattern: "^/api/(.*)$", Replacement: "/proxy/$1", Flag: "last"}, + }) + if err != nil { + b.Fatalf("New() error: %v", err) + } + + nextHandler := func(ctx *fasthttp.RequestCtx) { + ctx.WriteString("OK") + } + handler := m.Process(nextHandler) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/static/file.txt") + handler(ctx) + } +} + +// BenchmarkRewriteComplexPattern 基准测试:复杂正则表达式 +// 测试复杂正则匹配的性能 +func BenchmarkRewriteComplexPattern(b *testing.B) { + // 使用更复杂的正则表达式 + m, err := New([]config.RewriteRule{ + {Pattern: "^/api/v\\d+/(users|posts|comments)/(\\d+)/(profile|settings)$", Replacement: "/internal/$1/$2/$3", Flag: "last"}, + }) + if err != nil { + b.Fatalf("New() error: %v", err) + } + + nextHandler := func(ctx *fasthttp.RequestCtx) { + ctx.WriteString("OK") + } + handler := m.Process(nextHandler) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/api/v1/users/123/profile") + handler(ctx) + } +} + +// BenchmarkRewriteRedirect 基准测试:重定向标志 +// 测试重定向响应的性能 +func BenchmarkRewriteRedirect(b *testing.B) { + m, err := New([]config.RewriteRule{ + {Pattern: "^/old/(.*)$", Replacement: "/new/$1", Flag: "redirect"}, + }) + if err != nil { + b.Fatalf("New() error: %v", err) + } + + nextHandler := func(ctx *fasthttp.RequestCtx) { + ctx.WriteString("OK") + } + handler := m.Process(nextHandler) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/old/page") + handler(ctx) + } +} + +// BenchmarkRewriteBreak 基准测试:Break 标志 +// 测试 Break 标志提前终止的性能 +func BenchmarkRewriteBreak(b *testing.B) { + m, err := New([]config.RewriteRule{ + {Pattern: "^/api/(.*)$", Replacement: "/internal/$1", Flag: "break"}, + {Pattern: "^/internal/(.*)$", Replacement: "/final/$1", Flag: "last"}, + }) + if err != nil { + b.Fatalf("New() error: %v", err) + } + + nextHandler := func(ctx *fasthttp.RequestCtx) { + ctx.WriteString("OK") + } + handler := m.Process(nextHandler) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/api/data") + handler(ctx) + } +} + +// BenchmarkRewriteMultipleCaptures 基准测试:多捕获组 +// 测试多个捕获组的替换性能 +func BenchmarkRewriteMultipleCaptures(b *testing.B) { + m, err := New([]config.RewriteRule{ + {Pattern: "^/(\\d{4})/(\\d{2})/(\\d{2})/(.*)$", Replacement: "/archive/$1-$2-$3/$4", Flag: "last"}, + }) + if err != nil { + b.Fatalf("New() error: %v", err) + } + + nextHandler := func(ctx *fasthttp.RequestCtx) { + ctx.WriteString("OK") + } + handler := m.Process(nextHandler) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/2024/03/15/article") + handler(ctx) + } +}