@@ -704,34 +704,62 @@ func IsLocalhost(host string) bool {
704704 return ip .IsLoopback ()
705705}
706706
707- // HTTPFallback is an http.RoundTripper which allows fallback from https to http
708- // for registry endpoints with configurations for both http and TLS, such as
709- // defaulted localhost endpoints.
710- type HTTPFallback struct {
711- http.RoundTripper
707+ // NewHTTPFallback returns http.RoundTripper which allows fallback from https to
708+ // http for registry endpoints with configurations for both http and TLS,
709+ // such as defaulted localhost endpoints.
710+ func NewHTTPFallback (transport http.RoundTripper ) http.RoundTripper {
711+ return & httpFallback {
712+ super : transport ,
713+ }
712714}
713715
714- func (f HTTPFallback ) RoundTrip (r * http.Request ) (* http.Response , error ) {
715- resp , err := f .RoundTripper .RoundTrip (r )
716- var tlsErr tls.RecordHeaderError
717- if errors .As (err , & tlsErr ) && string (tlsErr .RecordHeader [:]) == "HTTP/" {
718- // server gave HTTP response to HTTPS client
719- plainHTTPUrl := * r .URL
720- plainHTTPUrl .Scheme = "http"
716+ type httpFallback struct {
717+ super http.RoundTripper
718+ host string
719+ }
720+
721+ func (f * httpFallback ) RoundTrip (r * http.Request ) (* http.Response , error ) {
722+ // only fall back if the same host had previously fell back
723+ if f .host != r .URL .Host {
724+ resp , err := f .super .RoundTrip (r )
725+ if ! isTLSError (err ) {
726+ return resp , err
727+ }
728+ }
729+
730+ plainHTTPUrl := * r .URL
731+ plainHTTPUrl .Scheme = "http"
721732
722- plainHTTPRequest := * r
723- plainHTTPRequest .URL = & plainHTTPUrl
733+ plainHTTPRequest := * r
734+ plainHTTPRequest .URL = & plainHTTPUrl
724735
736+ if f .host != r .URL .Host {
737+ f .host = r .URL .Host
738+
739+ // update body on the second attempt
725740 if r .Body != nil && r .GetBody != nil {
726741 body , err := r .GetBody ()
727742 if err != nil {
728743 return nil , err
729744 }
730745 plainHTTPRequest .Body = body
731746 }
747+ }
732748
733- return f .RoundTripper .RoundTrip (& plainHTTPRequest )
749+ return f .super .RoundTrip (& plainHTTPRequest )
750+ }
751+
752+ func isTLSError (err error ) bool {
753+ if err == nil {
754+ return false
755+ }
756+ var tlsErr tls.RecordHeaderError
757+ if errors .As (err , & tlsErr ) && string (tlsErr .RecordHeader [:]) == "HTTP/" {
758+ return true
759+ }
760+ if strings .Contains (err .Error (), "TLS handshake timeout" ) {
761+ return true
734762 }
735763
736- return resp , err
764+ return false
737765}
0 commit comments