diff --git a/CHANGELOG.md b/CHANGELOG.md index 846c51e3..8a726209 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.21.1 / 2022-06-17 + +* [BUGFIX] Fix a data race in HTTP probes. #929 + ## 0.21.0 / 2022-05-30 This Prometheus release is built with go1.18, which contains two noticeable diff --git a/VERSION b/VERSION index 88541566..a67cebaf 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.21.0 +0.21.1 diff --git a/prober/http.go b/prober/http.go index 54f40998..99d38a2b 100644 --- a/prober/http.go +++ b/prober/http.go @@ -46,8 +46,6 @@ import ( "github.com/prometheus/blackbox_exporter/config" ) -var caser = cases.Title(language.Und) - func matchRegularExpressions(reader io.Reader, httpConfig config.HTTPProbe, logger log.Logger) bool { body, err := ioutil.ReadAll(reader) if err != nil { @@ -342,6 +340,16 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr return false } + // Do not move the following variable to global scope. The cases.Caser returned by + // calling cases.Title *cannot* be shared among goroutines. This might happen when + // Prometheus tries to scrape multiple targets at the same time. From the docs: + // + // A Caser may be stateful and should therefore not be shared between goroutines. + // + // Issue: https://github.com/prometheus/blackbox_exporter/issues/922 + + caser := cases.Title(language.Und) + httpClientConfig := module.HTTP.HTTPClientConfig if len(httpClientConfig.TLSConfig.ServerName) == 0 { // If there is no `server_name` in tls_config, use diff --git a/prober/http_test.go b/prober/http_test.go index dc5b65bb..cd277618 100644 --- a/prober/http_test.go +++ b/prober/http_test.go @@ -35,6 +35,8 @@ import ( "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" pconfig "github.com/prometheus/common/config" + "golang.org/x/text/cases" + "golang.org/x/text/language" "github.com/prometheus/blackbox_exporter/config" ) @@ -1068,6 +1070,7 @@ func TestHTTPHeaders(t *testing.T) { "Accept-Language": "en-US", } ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + caser := cases.Title(language.Und) for key, value := range headers { if caser.String(key) == "Host" { if r.Host != value {