From 2bf5cbff6571375f48058e92e6c58a336f29c668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Fri, 14 Jun 2024 17:38:13 +0200 Subject: [PATCH] util/resolver: Make httpFallback concurrent safe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The httpFallback mutates the `host` field, which needs a synchronization when the same httpFallback is used by multiple goroutines. This is happens with containerd push which uses `Dispatch` to walk the image recursively to push every blob. Signed-off-by: Paweł Gronowski (cherry picked from commit 02859508b5ab9f9867242cea9d479a76475e190b) --- util/resolver/resolver.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/util/resolver/resolver.go b/util/resolver/resolver.go index d6b7bc381eac..77250bf0b851 100644 --- a/util/resolver/resolver.go +++ b/util/resolver/resolver.go @@ -10,6 +10,7 @@ import ( "path/filepath" "runtime" "strings" + "sync" "syscall" "time" @@ -213,13 +214,18 @@ func newDefaultTransport() *http.Transport { } type httpFallback struct { - super http.RoundTripper - host string + super http.RoundTripper + host string + hostMut sync.Mutex } func (f *httpFallback) RoundTrip(r *http.Request) (*http.Response, error) { - // only fall back if the same host had previously fell back - if f.host != r.URL.Host { + f.hostMut.Lock() + // Skip the HTTPS call only if the same host had previously fell back + tryHTTPSFirst := f.host != r.URL.Host + f.hostMut.Unlock() + + if tryHTTPSFirst { resp, err := f.super.RoundTrip(r) if !isTLSError(err) && !isPortError(err, r.URL.Host) { return resp, err @@ -232,8 +238,13 @@ func (f *httpFallback) RoundTrip(r *http.Request) (*http.Response, error) { plainHTTPRequest := *r plainHTTPRequest.URL = &plainHTTPUrl - if f.host != r.URL.Host { + // We tried HTTPS first but it failed. + // Mark the host so we don't try HTTPS for this host next time + // and refresh the request body. + if tryHTTPSFirst { + f.hostMut.Lock() f.host = r.URL.Host + f.hostMut.Unlock() // update body on the second attempt if r.Body != nil && r.GetBody != nil {