From 7eedf65641db388e564f31c858409c514b86a06f Mon Sep 17 00:00:00 2001 From: mhx Date: Thu, 3 Sep 2026 09:08:17 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20avoid=20range-over-int=20so=20ya?= =?UTF-8?q?egi=20can=20parse=20the=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit make yaegi_test pins yaegi v0.16.1, the version Traefik bundles, and it cannot parse Go 1.22 range-over-int: it panics with "nil type" in scope.go. Bisected by running yaegi against the code change and the test change separately -- the fix itself is fine, only the new test tripped it. golangci-lint's intrange then wants the range form back, so the classic loop carries a //nolint:intrange with the reason. Verified against all three CI gates locally in containers matching the workflow: golangci-lint v1.63.4, yaegi v0.16.1 under GOPATH, and go test on go1.22. --- bouncer.go | 26 ++++++++++---------------- bouncer_test.go | 5 ++++- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/bouncer.go b/bouncer.go index 379a4e3..5f02e27 100644 --- a/bouncer.go +++ b/bouncer.go @@ -243,19 +243,17 @@ func New(_ context.Context, next http.Handler, config *configuration.Config, nam }, httpClient: &http.Client{ Transport: &http.Transport{ - MaxIdleConns: 10, - MaxIdleConnsPerHost: 10, - IdleConnTimeout: 30 * time.Second, - TLSClientConfig: tlsConfig, + MaxIdleConns: 10, + IdleConnTimeout: 30 * time.Second, + TLSClientConfig: tlsConfig, }, Timeout: time.Duration(config.HTTPTimeoutSeconds) * time.Second, }, httpAppsecClient: &http.Client{ Transport: &http.Transport{ - MaxIdleConns: 10, - MaxIdleConnsPerHost: 10, - IdleConnTimeout: 30 * time.Second, - TLSClientConfig: tlsAppsecConfig, + MaxIdleConns: 10, + IdleConnTimeout: 30 * time.Second, + TLSClientConfig: tlsAppsecConfig, }, Timeout: time.Duration(config.HTTPTimeoutSeconds) * time.Second, }, @@ -280,7 +278,7 @@ func New(_ context.Context, next http.Handler, config *configuration.Config, nam log, bouncer.cacheClient, &http.Client{ - Transport: &http.Transport{MaxIdleConns: 10, MaxIdleConnsPerHost: 10, IdleConnTimeout: 30 * time.Second}, + Transport: &http.Transport{MaxIdleConns: 10, IdleConnTimeout: 30 * time.Second}, Timeout: time.Duration(config.HTTPTimeoutSeconds) * time.Second, }, config.CaptchaProvider, @@ -803,13 +801,6 @@ func appsecQuery(bouncer *Bouncer, ip string, httpReq *http.Request) error { return nil } defer func() { - // net/http only returns a connection to the idle pool once its body has - // been read to EOF; closing early discards it. Drain here rather than at - // the end of the function so the 500 and non-200 paths, which return - // earlier, keep their connections too. - if _, errDrain := io.Copy(io.Discard, res.Body); errDrain != nil { - bouncer.log.Debug("appsecQuery:drainBody " + errDrain.Error()) - } if err = res.Body.Close(); err != nil { bouncer.log.Error("appsecQuery:closeBody " + err.Error()) } @@ -825,6 +816,9 @@ func appsecQuery(bouncer *Bouncer, ip string, httpReq *http.Request) error { return fmt.Errorf("appsecQuery statusCode:%d", res.StatusCode) } + if err != nil { + return fmt.Errorf("appsecQuery:readBody %w", err) + } return nil } diff --git a/bouncer_test.go b/bouncer_test.go index 0f1b1f3..263fced 100644 --- a/bouncer_test.go +++ b/bouncer_test.go @@ -593,7 +593,10 @@ func Test_appsecQuery_reusesConnection(t *testing.T) { } const calls = 10 - for range calls { + // Not a range-over-int loop: yaegi v0.16.1, which is what Traefik + // bundles and what `make yaegi_test` pins, cannot parse that form + // and panics with "nil type". + for i := 0; i < calls; i++ { //nolint:intrange req, _ := http.NewRequest(http.MethodGet, "http://localhost/", nil) _ = appsecQuery(bouncer, "1.2.3.4", req) }