diff --git a/bind.go b/bind.go index 83bd11a9..dd01d320 100644 --- a/bind.go +++ b/bind.go @@ -188,7 +188,7 @@ func bindData(destination any, data map[string][]string, tag string, dataFiles m typeField := typ.Field(i) structField := val.Field(i) if typeField.Anonymous { - if structField.Kind() == reflect.Ptr { + if structField.Kind() == reflect.Pointer { structField = structField.Elem() } } @@ -273,7 +273,7 @@ func bindData(destination any, data map[string][]string, tag string, dataFiles m sliceOf := structField.Type().Elem().Kind() numElems := len(inputValue) slice := reflect.MakeSlice(structField.Type(), numElems, numElems) - for j := 0; j < numElems; j++ { + for j := range numElems { if err := setWithProperType(sliceOf, inputValue[j], slice.Index(j)); err != nil { return err } @@ -297,7 +297,7 @@ func setWithProperType(valueKind reflect.Kind, val string, structField reflect.V } switch valueKind { - case reflect.Ptr: + case reflect.Pointer: return setWithProperType(structField.Elem().Kind(), val, structField.Elem()) case reflect.Int: return setIntField(val, 0, structField) @@ -334,7 +334,7 @@ func setWithProperType(valueKind reflect.Kind, val string, structField reflect.V } func unmarshalInputsToField(valueKind reflect.Kind, values []string, field reflect.Value) (bool, error) { - if valueKind == reflect.Ptr { + if valueKind == reflect.Pointer { if field.IsNil() { field.Set(reflect.New(field.Type().Elem())) } @@ -350,7 +350,7 @@ func unmarshalInputsToField(valueKind reflect.Kind, values []string, field refle } func unmarshalInputToField(valueKind reflect.Kind, val string, field reflect.Value, formatTag string) (bool, error) { - if valueKind == reflect.Ptr { + if valueKind == reflect.Pointer { if field.IsNil() { field.Set(reflect.New(field.Type().Elem())) } diff --git a/bind_test.go b/bind_test.go index 1d5f8ca4..b23c638f 100644 --- a/bind_test.go +++ b/bind_test.go @@ -641,7 +641,7 @@ func TestBindUnmarshalTypeError(t *testing.T) { func TestBindSetWithProperType(t *testing.T) { ts := new(bindTestStruct) - typ := reflect.TypeOf(ts).Elem() + typ := reflect.TypeFor[bindTestStruct]() val := reflect.ValueOf(ts).Elem() for i := 0; i < typ.NumField(); i++ { typeField := typ.Field(i) @@ -662,7 +662,7 @@ func TestBindSetWithProperType(t *testing.T) { Bar bytes.Buffer } v := &foo{} - typ = reflect.TypeOf(v).Elem() + typ = reflect.TypeFor[foo]() val = reflect.ValueOf(v).Elem() assert.Error(t, setWithProperType(typ.Field(0).Type.Kind(), "5", val.Field(0))) } diff --git a/context_test.go b/context_test.go index 21a7af09..9b820c6e 100644 --- a/context_test.go +++ b/context_test.go @@ -178,7 +178,7 @@ func TestContextStream(t *testing.T) { r, w := io.Pipe() go func() { defer w.Close() - for i := 0; i < 3; i++ { + for i := range 3 { fmt.Fprintf(w, "data: index %v\n\n", i) time.Sleep(5 * time.Millisecond) } diff --git a/echo_test.go b/echo_test.go index b5045e11..17d9b4a7 100644 --- a/echo_test.go +++ b/echo_test.go @@ -1109,7 +1109,7 @@ func (ce *customError) StatusCode() int { } func (ce *customError) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf(`{"x":"%v"}`, ce.Message)), nil + return fmt.Appendf(nil, `{"x":"%v"}`, ce.Message), nil } func (ce *customError) Error() string { diff --git a/middleware/basic_auth.go b/middleware/basic_auth.go index 890a47bd..8a9500a9 100644 --- a/middleware/basic_auth.go +++ b/middleware/basic_auth.go @@ -133,9 +133,9 @@ func (config BasicAuthConfig) ToMiddleware() (echo.MiddlewareFunc, error) { lastError = echo.ErrBadRequest.Wrap(errDecode) continue } - idx := bytes.IndexByte(b, ':') - if idx >= 0 { - valid, errValidate := config.Validator(c, string(b[:idx]), string(b[idx+1:])) + before, after, ok := bytes.Cut(b, []byte{':'}) + if ok { + valid, errValidate := config.Validator(c, string(before), string(after)) if errValidate != nil { lastError = errValidate } else if valid { diff --git a/middleware/body_dump_test.go b/middleware/body_dump_test.go index f493e75c..e5f64541 100644 --- a/middleware/body_dump_test.go +++ b/middleware/body_dump_test.go @@ -381,7 +381,7 @@ func TestBodyDump_ClientGetsFullResponse(t *testing.T) { h := func(c *echo.Context) error { // Write response in chunks to test incremental writes - for i := 0; i < 4; i++ { + for range 4 { c.Response().Write([]byte(strings.Repeat("DATA", 125))) } return nil diff --git a/middleware/proxy.go b/middleware/proxy.go index 1996032f..5bf296f6 100644 --- a/middleware/proxy.go +++ b/middleware/proxy.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "io" + "maps" "math/rand" "net" "net/http" @@ -330,9 +331,7 @@ func (config ProxyConfig) ToMiddleware() (echo.MiddlewareFunc, error) { if config.RegexRewrite == nil { config.RegexRewrite = make(map[*regexp.Regexp]string) } - for k, v := range rewriteRulesRegex(config.Rewrite) { - config.RegexRewrite[k] = v - } + maps.Copy(config.RegexRewrite, rewriteRulesRegex(config.Rewrite)) } return func(next echo.HandlerFunc) echo.HandlerFunc { diff --git a/middleware/proxy_test.go b/middleware/proxy_test.go index 420be324..3a1310ef 100644 --- a/middleware/proxy_test.go +++ b/middleware/proxy_test.go @@ -676,15 +676,13 @@ func TestProxyRetryWithBackendTimeout(t *testing.T) { )) var wg sync.WaitGroup - for i := 0; i < 20; i++ { - wg.Add(1) - go func() { - defer wg.Done() + for range 20 { + wg.Go(func() { req := httptest.NewRequest(http.MethodGet, "/", nil) rec := httptest.NewRecorder() e.ServeHTTP(rec, req) assert.Equal(t, 200, rec.Code) - }() + }) } wg.Wait() diff --git a/middleware/rate_limiter_test.go b/middleware/rate_limiter_test.go index c591d2b1..267e8d08 100644 --- a/middleware/rate_limiter_test.go +++ b/middleware/rate_limiter_test.go @@ -461,7 +461,7 @@ func TestRateLimiterMemoryStore_FractionalRateDefaultBurst(t *testing.T) { func generateAddressList(count int) []string { addrs := make([]string, count) - for i := 0; i < count; i++ { + for i := range count { addrs[i] = randomString(15) } return addrs @@ -477,7 +477,7 @@ func run(wg *sync.WaitGroup, store RateLimiterStore, addrs []string, max int, b func benchmarkStore(store RateLimiterStore, parallel int, max int, b *testing.B) { addrs := generateAddressList(max) wg := &sync.WaitGroup{} - for i := 0; i < parallel; i++ { + for range parallel { wg.Add(1) go run(wg, store, addrs, max, b) } @@ -553,11 +553,9 @@ func TestRateLimiterMemoryStore_ConcurrentAccess(t *testing.T) { var wg sync.WaitGroup var allowedCount, deniedCount int32 - for i := 0; i < goroutines; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < requestsPerGoroutine; j++ { + for range goroutines { + wg.Go(func() { + for range requestsPerGoroutine { allowed, err := store.Allow("test-user") assert.NoError(t, err) if allowed { @@ -567,7 +565,7 @@ func TestRateLimiterMemoryStore_ConcurrentAccess(t *testing.T) { } time.Sleep(time.Millisecond) } - }() + }) } wg.Wait() @@ -598,11 +596,11 @@ func TestRateLimiterMemoryStore_RaceDetection(t *testing.T) { var wg sync.WaitGroup identifiers := []string{"user1", "user2", "user3", "user4", "user5"} - for i := 0; i < goroutines; i++ { + for i := range goroutines { wg.Add(1) go func(routineID int) { defer wg.Done() - for j := 0; j < requestsPerGoroutine; j++ { + for range requestsPerGoroutine { identifier := identifiers[routineID%len(identifiers)] _, err := store.Allow(identifier) assert.NoError(t, err) diff --git a/middleware/request_logger_test.go b/middleware/request_logger_test.go index 939af2a9..2232c6f6 100644 --- a/middleware/request_logger_test.go +++ b/middleware/request_logger_test.go @@ -46,12 +46,12 @@ func TestRequestLoggerOK(t *testing.T) { rec := httptest.NewRecorder() e.ServeHTTP(rec, req) - logAttrs := map[string]interface{}{} + logAttrs := map[string]any{} assert.NoError(t, json.Unmarshal(buf.Bytes(), &logAttrs)) logAttrs["latency"] = 123 logAttrs["time"] = "x" - expect := map[string]interface{}{ + expect := map[string]any{ "level": "INFO", "msg": "REQUEST", "method": "POST", @@ -88,12 +88,12 @@ func TestRequestLoggerError(t *testing.T) { rec := httptest.NewRecorder() e.ServeHTTP(rec, req) - logAttrs := map[string]interface{}{} + logAttrs := map[string]any{} assert.NoError(t, json.Unmarshal(buf.Bytes(), &logAttrs)) logAttrs["latency"] = 123 logAttrs["time"] = "x" - expect := map[string]interface{}{ + expect := map[string]any{ "level": "ERROR", "msg": "REQUEST_ERROR", "method": "GET", diff --git a/middleware/rewrite.go b/middleware/rewrite.go index ea58091b..02907ca4 100644 --- a/middleware/rewrite.go +++ b/middleware/rewrite.go @@ -5,6 +5,7 @@ package middleware import ( "errors" + "maps" "regexp" "github.com/labstack/echo/v5" @@ -61,9 +62,7 @@ func (config RewriteConfig) ToMiddleware() (echo.MiddlewareFunc, error) { if config.RegexRules == nil { config.RegexRules = make(map[*regexp.Regexp]string) } - for k, v := range rewriteRulesRegex(config.Rules) { - config.RegexRules[k] = v - } + maps.Copy(config.RegexRules, rewriteRulesRegex(config.Rules)) return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c *echo.Context) (err error) { diff --git a/middleware/rewrite_test.go b/middleware/rewrite_test.go index f45b8d98..adcc8e9f 100644 --- a/middleware/rewrite_test.go +++ b/middleware/rewrite_test.go @@ -190,7 +190,7 @@ func TestRewriteWithConfigPreMiddleware_Issue1143(t *testing.T) { return c.String(http.StatusOK, "eng") }) - for i := 0; i < 100; i++ { + for range 100 { req := httptest.NewRequest(http.MethodGet, "/api/v1/mgmt/proj/test/agt", nil) rec := httptest.NewRecorder() e.ServeHTTP(rec, req) diff --git a/middleware/util_test.go b/middleware/util_test.go index 03943158..3610e8dd 100644 --- a/middleware/util_test.go +++ b/middleware/util_test.go @@ -82,7 +82,7 @@ func TestRandomStringBias(t *testing.T) { counts := make(map[rune]int) var count int64 - for i := 0; i < loop; i++ { + for range loop { s := randomString(slen) require.Equal(t, slen, len(s)) for _, b := range s { diff --git a/router_concurrent_test.go b/router_concurrent_test.go index d9225517..182489c9 100644 --- a/router_concurrent_test.go +++ b/router_concurrent_test.go @@ -54,13 +54,13 @@ func TestConcurrentRouter_ConcurrentReads(t *testing.T) { routeCallsPerGoroutine := 50 routesCallsPerGoroutine := 20 - for i := 0; i < numGoroutines; i++ { + for i := range numGoroutines { wg.Add(1) go func(goroutineID int) { defer wg.Done() // Call Route() 50 times - for j := 0; j < routeCallsPerGoroutine; j++ { + for j := range routeCallsPerGoroutine { path := testPaths[j%len(testPaths)] req := httptest.NewRequest(http.MethodGet, path, nil) rec := httptest.NewRecorder() @@ -73,7 +73,7 @@ func TestConcurrentRouter_ConcurrentReads(t *testing.T) { } // Call Routes() 20 times - for j := 0; j < routesCallsPerGoroutine; j++ { + for range routesCallsPerGoroutine { routes := router.Routes() if len(routes) == 5 { routesCallCount.Add(1) @@ -105,12 +105,12 @@ func TestConcurrentRouter_ConcurrentWrites(t *testing.T) { numGoroutines := 5 addsPerGoroutine := 10 - for i := 0; i < numGoroutines; i++ { + for i := range numGoroutines { wg.Add(1) go func(goroutineID int) { defer wg.Done() - for j := 0; j < addsPerGoroutine; j++ { + for j := range addsPerGoroutine { path := fmt.Sprintf("/route-g%d-n%d", goroutineID, j) _, err := router.Add(Route{ Method: http.MethodGet, @@ -155,11 +155,9 @@ func TestConcurrentRouter_ConcurrentReadWrite(t *testing.T) { var routesCallCount atomic.Int64 // Launch 4 reader goroutines: call Route() 100 times each - for i := 0; i < 4; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < 100; j++ { + for range 4 { + wg.Go(func() { + for j := range 100 { path := initialPaths[j%len(initialPaths)] req := httptest.NewRequest(http.MethodGet, path, nil) @@ -171,15 +169,15 @@ func TestConcurrentRouter_ConcurrentReadWrite(t *testing.T) { routeCallCount.Add(1) } } - }() + }) } // Launch 2 writer goroutines: call Add() 20 times each - for i := 0; i < 2; i++ { + for i := range 2 { wg.Add(1) go func(goroutineID int) { defer wg.Done() - for j := 0; j < 20; j++ { + for j := range 20 { path := fmt.Sprintf("/write-g%d-n%d", goroutineID, j) _, err := router.Add(Route{ Method: http.MethodGet, @@ -194,17 +192,15 @@ func TestConcurrentRouter_ConcurrentReadWrite(t *testing.T) { } // Launch 2 inspector goroutines: call Routes() 50 times each - for i := 0; i < 2; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for j := 0; j < 50; j++ { + for range 2 { + wg.Go(func() { + for range 50 { routes := router.Routes() if routes != nil { routesCallCount.Add(1) } } - }() + }) } wg.Wait() @@ -226,7 +222,7 @@ func TestConcurrentRouter_RoutesIterationDuringModification(t *testing.T) { router := NewConcurrentRouter(NewRouter(RouterConfig{})) // Add initial routes - for i := 0; i < 10; i++ { + for i := range 10 { _, err := router.Add(Route{ Method: http.MethodGet, Path: fmt.Sprintf("/initial-%d", i), @@ -242,11 +238,11 @@ func TestConcurrentRouter_RoutesIterationDuringModification(t *testing.T) { var addRemoveCount atomic.Int64 // Launch 3 goroutines that iterate over Routes() and access each element - for i := 0; i < 3; i++ { + for i := range 3 { wg.Add(1) go func(goroutineID int) { defer wg.Done() - for j := 0; j < 100; j++ { + for range 100 { routes := router.Routes() // Actually iterate and access the route data // This would cause a data race if Routes() returned a direct reference @@ -264,11 +260,11 @@ func TestConcurrentRouter_RoutesIterationDuringModification(t *testing.T) { } // Launch 2 goroutines that continuously Add routes - for i := 0; i < 2; i++ { + for i := range 2 { wg.Add(1) go func(goroutineID int) { defer wg.Done() - for j := 0; j < 30; j++ { + for j := range 30 { path := fmt.Sprintf("/add-g%d-n%d", goroutineID, j) _, err := router.Add(Route{ Method: http.MethodPost, @@ -319,11 +315,11 @@ func TestConcurrentRouter_ParametersNoRace(t *testing.T) { var addCount atomic.Int64 // Launch 3 goroutines that read Parameters repeatedly - for i := 0; i < 3; i++ { + for i := range 3 { wg.Add(1) go func(goroutineID int) { defer wg.Done() - for j := 0; j < 100; j++ { + for range 100 { routes := router.Routes() // Actually access the Parameters slice data // This would cause a data race if Parameters weren't deep-copied @@ -341,11 +337,11 @@ func TestConcurrentRouter_ParametersNoRace(t *testing.T) { } // Launch 2 goroutines that add routes with parameters concurrently - for i := 0; i < 2; i++ { + for i := range 2 { wg.Add(1) go func(goroutineID int) { defer wg.Done() - for j := 0; j < 20; j++ { + for j := range 20 { path := fmt.Sprintf("/api/:v%d/resource/:id", goroutineID*100+j) _, err := router.Add(Route{ Method: http.MethodPost, diff --git a/server.go b/server.go index d012be0d..7bbc4094 100644 --- a/server.go +++ b/server.go @@ -156,11 +156,9 @@ func (sc StartConfig) start(ctx stdContext.Context, h http.Handler) error { defer cancel() if sc.GracefulTimeout >= 0 { - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { gracefulShutdown(gCtx, &sc, &server, logger) - }() + }) } if err := server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) {