package bench_test import ( "bytes" "io" "net" "net/http" "strconv" "strings" "sync" "testing" "time" web "git.sharkk.net/Go/Web" ) // startTestServer starts a server for benchmarking on a random port func startTestServer(b *testing.B) (string, web.Server) { s := web.NewServer() // Setup routes s.Get("/", func(ctx web.Context) error { return ctx.String("Hello, World!") }) s.Get("/json", func(ctx web.Context) error { ctx.Response().SetHeader("Content-Type", "application/json") return ctx.String(`{"message":"Hello, World!","code":200,"success":true}`) }) s.Post("/echo", func(ctx web.Context) error { body := ctx.Request().Body() return ctx.Bytes(body) }) s.Get("/users/:id/posts/:postId", func(ctx web.Context) error { userId := ctx.Request().Param("id") postId := ctx.Request().Param("postId") return ctx.String(userId + ":" + postId) }) s.Get("/middleware-test", func(ctx web.Context) error { return ctx.String("OK") }) s.Get("/headers", func(ctx web.Context) error { ctx.Response().SetHeader("X-Test-1", "Value1") ctx.Response().SetHeader("X-Test-2", "Value2") ctx.Response().SetHeader("X-Test-3", "Value3") ctx.Response().SetHeader("Content-Type", "text/plain") return ctx.String("Headers set") }) s.Post("/submit", func(ctx web.Context) error { return ctx.String("Received " + string(ctx.Request().Body())) }) // Add middleware for middleware test for i := 0; i < 5; i++ { s.Use(func(ctx web.Context) error { return ctx.Next() }) } // Find a free port addr, err := net.ResolveTCPAddr("tcp", "localhost:0") if err != nil { b.Fatalf("Failed to resolve TCP address: %v", err) } listener, err := net.ListenTCP("tcp", addr) if err != nil { b.Fatalf("Failed to listen on TCP: %v", err) } port := listener.Addr().(*net.TCPAddr).Port listener.Close() serverAddr := "localhost:" + strconv.Itoa(port) // Start the server in a goroutine var wg sync.WaitGroup wg.Add(1) go func() { wg.Done() s.Run(serverAddr) }() // Wait for server to start wg.Wait() time.Sleep(100 * time.Millisecond) return serverAddr, s } // BenchmarkRealStaticGetRequest measures performance of real HTTP GET request func BenchmarkRealStaticGetRequest(b *testing.B) { serverAddr, _ := startTestServer(b) url := "http://" + serverAddr + "/" b.ResetTimer() for i := 0; i < b.N; i++ { resp, err := http.Get(url) if err != nil { b.Fatalf("Request failed: %v", err) } body, err := io.ReadAll(resp.Body) if err != nil { b.Fatalf("Failed to read response: %v", err) } resp.Body.Close() if resp.StatusCode != 200 || string(body) != "Hello, World!" { b.Fatalf("Invalid response: status=%d, body=%s", resp.StatusCode, body) } } } // BenchmarkRealJSONResponse measures performance of real HTTP JSON response func BenchmarkRealJSONResponse(b *testing.B) { serverAddr, _ := startTestServer(b) url := "http://" + serverAddr + "/json" b.ResetTimer() for i := 0; i < b.N; i++ { resp, err := http.Get(url) if err != nil { b.Fatalf("Request failed: %v", err) } _, err = io.ReadAll(resp.Body) if err != nil { b.Fatalf("Failed to read response: %v", err) } resp.Body.Close() if resp.StatusCode != 200 { b.Fatalf("Invalid status: %d", resp.StatusCode) } } } // BenchmarkRealPostWithBody measures performance of real HTTP POST request with body func BenchmarkRealPostWithBody(b *testing.B) { serverAddr, _ := startTestServer(b) url := "http://" + serverAddr + "/echo" requestBody := strings.Repeat("Hello, World! ", 10) b.ResetTimer() for i := 0; i < b.N; i++ { resp, err := http.Post(url, "text/plain", strings.NewReader(requestBody)) if err != nil { b.Fatalf("Request failed: %v", err) } body, err := io.ReadAll(resp.Body) if err != nil { b.Fatalf("Failed to read response: %v", err) } resp.Body.Close() if resp.StatusCode != 200 || string(body) != requestBody { b.Fatalf("Invalid response\nstatus=%d\nbody=%s", resp.StatusCode, body) } } } // BenchmarkRealRouteParams measures performance of real HTTP route parameter handling func BenchmarkRealRouteParams(b *testing.B) { serverAddr, _ := startTestServer(b) url := "http://" + serverAddr + "/users/123/posts/456" b.ResetTimer() for i := 0; i < b.N; i++ { resp, err := http.Get(url) if err != nil { b.Fatalf("Request failed: %v", err) } body, err := io.ReadAll(resp.Body) if err != nil { b.Fatalf("Failed to read response: %v", err) } resp.Body.Close() if resp.StatusCode != 200 || string(body) != "123:456" { b.Fatalf("Invalid response: status=%d, body=%s", resp.StatusCode, body) } } } // BenchmarkRealHeaders measures real HTTP header handling performance func BenchmarkRealHeaders(b *testing.B) { serverAddr, _ := startTestServer(b) url := "http://" + serverAddr + "/headers" client := &http.Client{} b.ResetTimer() for i := 0; i < b.N; i++ { req, _ := http.NewRequest("GET", url, nil) req.Header.Add("User-Agent", "Benchmark") req.Header.Add("Accept", "*/*") req.Header.Add("Authorization", "Bearer token12345") resp, err := client.Do(req) if err != nil { b.Fatalf("Request failed: %v", err) } _, err = io.ReadAll(resp.Body) if err != nil { b.Fatalf("Failed to read response: %v", err) } resp.Body.Close() if resp.StatusCode != 200 || resp.Header.Get("X-Test-1") != "Value1" { b.Fatalf("Invalid response: status=%d", resp.StatusCode) } } } // BenchmarkRealParallelRequests measures real HTTP performance under concurrent load func BenchmarkRealParallelRequests(b *testing.B) { serverAddr, _ := startTestServer(b) baseURL := "http://" + serverAddr b.ResetTimer() b.RunParallel(func(pb *testing.PB) { client := &http.Client{} counter := 0 for pb.Next() { var resp *http.Response var err error switch counter % 3 { case 0: resp, err = client.Get(baseURL + "/") case 1: id := strconv.Itoa(counter & 0xff) resp, err = client.Get(baseURL + "/users/" + id + "/posts/456") case 2: data := "data" + strconv.Itoa(counter&0xff) resp, err = client.Post(baseURL+"/submit", "text/plain", bytes.NewBufferString(data)) } if err != nil { b.Fatalf("Request failed: %v", err) } _, _ = io.ReadAll(resp.Body) resp.Body.Close() if resp.StatusCode != 200 { b.Fatalf("Invalid response: status=%d", resp.StatusCode) } counter++ } }) } // startNetHTTPTestServer starts a standard net/http server for benchmarking func startNetHTTPTestServer(b *testing.B) (string, *http.Server) { mux := http.NewServeMux() // Setup routes mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.NotFound(w, r) return } w.Write([]byte("Hello, World!")) }) mux.HandleFunc("/json", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"message":"Hello, World!","code":200,"success":true}`)) }) mux.HandleFunc("/echo", func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } body, _ := io.ReadAll(r.Body) w.Write(body) }) mux.HandleFunc("/users/", func(w http.ResponseWriter, r *http.Request) { parts := strings.Split(r.URL.Path, "/") if len(parts) != 5 || parts[1] != "users" || parts[3] != "posts" || parts[0] != "" { http.NotFound(w, r) return } userId := parts[2] postId := parts[4] w.Write([]byte(userId + ":" + postId)) }) mux.HandleFunc("/middleware-test", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("OK")) }) mux.HandleFunc("/headers", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Test-1", "Value1") w.Header().Set("X-Test-2", "Value2") w.Header().Set("X-Test-3", "Value3") w.Header().Set("Content-Type", "text/plain") w.Write([]byte("Headers set")) }) mux.HandleFunc("/submit", func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } body, _ := io.ReadAll(r.Body) w.Write([]byte("Received " + string(body))) }) // Find a free port addr, err := net.ResolveTCPAddr("tcp", "localhost:0") if err != nil { b.Fatalf("Failed to resolve TCP address: %v", err) } listener, err := net.ListenTCP("tcp", addr) if err != nil { b.Fatalf("Failed to listen on TCP: %v", err) } port := listener.Addr().(*net.TCPAddr).Port serverAddr := "localhost:" + strconv.Itoa(port) // Start the server in a goroutine server := &http.Server{ Addr: serverAddr, Handler: mux, } var wg sync.WaitGroup wg.Add(1) go func() { wg.Done() server.Serve(listener) }() // Wait for server to start wg.Wait() time.Sleep(100 * time.Millisecond) return serverAddr, server } // BenchmarkNetHTTPStaticGetRequest measures performance of net/http static GET request func BenchmarkNetHTTPStaticGetRequest(b *testing.B) { serverAddr, _ := startNetHTTPTestServer(b) url := "http://" + serverAddr + "/" b.ResetTimer() for i := 0; i < b.N; i++ { resp, err := http.Get(url) if err != nil { b.Fatalf("Request failed: %v", err) } body, err := io.ReadAll(resp.Body) if err != nil { b.Fatalf("Failed to read response: %v", err) } resp.Body.Close() if resp.StatusCode != 200 || string(body) != "Hello, World!" { b.Fatalf("Invalid response: status=%d, body=%s", resp.StatusCode, body) } } } // BenchmarkNetHTTPJSONResponse measures performance of net/http JSON response func BenchmarkNetHTTPJSONResponse(b *testing.B) { serverAddr, _ := startNetHTTPTestServer(b) url := "http://" + serverAddr + "/json" b.ResetTimer() for i := 0; i < b.N; i++ { resp, err := http.Get(url) if err != nil { b.Fatalf("Request failed: %v", err) } _, err = io.ReadAll(resp.Body) if err != nil { b.Fatalf("Failed to read response: %v", err) } resp.Body.Close() if resp.StatusCode != 200 { b.Fatalf("Invalid status: %d", resp.StatusCode) } } } // BenchmarkNetHTTPPostWithBody measures performance of net/http POST request with body func BenchmarkNetHTTPPostWithBody(b *testing.B) { serverAddr, _ := startNetHTTPTestServer(b) url := "http://" + serverAddr + "/echo" requestBody := strings.Repeat("Hello, World! ", 10) b.ResetTimer() for i := 0; i < b.N; i++ { resp, err := http.Post(url, "text/plain", strings.NewReader(requestBody)) if err != nil { b.Fatalf("Request failed: %v", err) } body, err := io.ReadAll(resp.Body) if err != nil { b.Fatalf("Failed to read response: %v", err) } resp.Body.Close() if resp.StatusCode != 200 || string(body) != requestBody { b.Fatalf("Invalid response\nstatus=%d\nbody=%s", resp.StatusCode, body) } } } // BenchmarkNetHTTPRouteParams measures performance of net/http route parameter handling func BenchmarkNetHTTPRouteParams(b *testing.B) { serverAddr, _ := startNetHTTPTestServer(b) url := "http://" + serverAddr + "/users/123/posts/456" b.ResetTimer() for i := 0; i < b.N; i++ { resp, err := http.Get(url) if err != nil { b.Fatalf("Request failed: %v", err) } body, err := io.ReadAll(resp.Body) if err != nil { b.Fatalf("Failed to read response: %v", err) } resp.Body.Close() if resp.StatusCode != 200 || string(body) != "123:456" { b.Fatalf("Invalid response: status=%d, body=%s", resp.StatusCode, body) } } } // BenchmarkNetHTTPHeaders measures net/http header handling performance func BenchmarkNetHTTPHeaders(b *testing.B) { serverAddr, _ := startNetHTTPTestServer(b) url := "http://" + serverAddr + "/headers" client := &http.Client{} b.ResetTimer() for i := 0; i < b.N; i++ { req, _ := http.NewRequest("GET", url, nil) req.Header.Add("User-Agent", "Benchmark") req.Header.Add("Accept", "*/*") req.Header.Add("Authorization", "Bearer token12345") resp, err := client.Do(req) if err != nil { b.Fatalf("Request failed: %v", err) } _, err = io.ReadAll(resp.Body) if err != nil { b.Fatalf("Failed to read response: %v", err) } resp.Body.Close() if resp.StatusCode != 200 || resp.Header.Get("X-Test-1") != "Value1" { b.Fatalf("Invalid response: status=%d", resp.StatusCode) } } } // BenchmarkNetHTTPParallelRequests measures net/http performance under concurrent load func BenchmarkNetHTTPParallelRequests(b *testing.B) { serverAddr, _ := startNetHTTPTestServer(b) baseURL := "http://" + serverAddr b.ResetTimer() b.RunParallel(func(pb *testing.PB) { client := &http.Client{} counter := 0 for pb.Next() { var resp *http.Response var err error switch counter % 3 { case 0: resp, err = client.Get(baseURL + "/") case 1: id := strconv.Itoa(counter & 0xff) resp, err = client.Get(baseURL + "/users/" + id + "/posts/456") case 2: data := "data" + strconv.Itoa(counter&0xff) resp, err = client.Post(baseURL+"/submit", "text/plain", bytes.NewBufferString(data)) } if err != nil { b.Fatalf("Request failed: %v", err) } _, _ = io.ReadAll(resp.Body) resp.Body.Close() if resp.StatusCode != 200 { b.Fatalf("Invalid response: status=%d", resp.StatusCode) } counter++ } }) }