@@ -714,34 +714,62 @@ func IsLocalhost(host string) bool {
714714 return ip .IsLoopback ()
715715}
716716
717- // HTTPFallback is an http.RoundTripper which allows fallback from https to http
718- // for registry endpoints with configurations for both http and TLS, such as
719- // defaulted localhost endpoints.
720- type HTTPFallback struct {
721- http.RoundTripper
717+ // NewHTTPFallback returns http.RoundTripper which allows fallback from https to
718+ // http for registry endpoints with configurations for both http and TLS,
719+ // such as defaulted localhost endpoints.
720+ func NewHTTPFallback (transport http.RoundTripper ) http.RoundTripper {
721+ return & httpFallback {
722+ super : transport ,
723+ }
722724}
723725
724- func (f HTTPFallback ) RoundTrip (r * http.Request ) (* http.Response , error ) {
725- resp , err := f .RoundTripper .RoundTrip (r )
726- var tlsErr tls.RecordHeaderError
727- if errors .As (err , & tlsErr ) && string (tlsErr .RecordHeader [:]) == "HTTP/" {
728- // server gave HTTP response to HTTPS client
729- plainHTTPUrl := * r .URL
730- plainHTTPUrl .Scheme = "http"
726+ type httpFallback struct {
727+ super http.RoundTripper
728+ host string
729+ }
730+
731+ func (f * httpFallback ) RoundTrip (r * http.Request ) (* http.Response , error ) {
732+ // only fall back if the same host had previously fell back
733+ if f .host != r .URL .Host {
734+ resp , err := f .super .RoundTrip (r )
735+ if ! isTLSError (err ) {
736+ return resp , err
737+ }
738+ }
739+
740+ plainHTTPUrl := * r .URL
741+ plainHTTPUrl .Scheme = "http"
731742
732- plainHTTPRequest := * r
733- plainHTTPRequest .URL = & plainHTTPUrl
743+ plainHTTPRequest := * r
744+ plainHTTPRequest .URL = & plainHTTPUrl
734745
746+ if f .host != r .URL .Host {
747+ f .host = r .URL .Host
748+
749+ // update body on the second attempt
735750 if r .Body != nil && r .GetBody != nil {
736751 body , err := r .GetBody ()
737752 if err != nil {
738753 return nil , err
739754 }
740755 plainHTTPRequest .Body = body
741756 }
757+ }
742758
743- return f .RoundTripper .RoundTrip (& plainHTTPRequest )
759+ return f .super .RoundTrip (& plainHTTPRequest )
760+ }
761+
762+ func isTLSError (err error ) bool {
763+ if err == nil {
764+ return false
765+ }
766+ var tlsErr tls.RecordHeaderError
767+ if errors .As (err , & tlsErr ) && string (tlsErr .RecordHeader [:]) == "HTTP/" {
768+ return true
769+ }
770+ if strings .Contains (err .Error (), "TLS handshake timeout" ) {
771+ return true
744772 }
745773
746- return resp , err
774+ return false
747775}
0 commit comments