Revert "cmd/go: add support for GOPROXY fallback on unexpected errors"
This reverts CL 223257.
Reason for revert: broke TestScript/mod_gonoproxy on the longtest builders.
Change-Id: I8637c52c5a7d5333a37ed1e9998c49786525ecb1
Reviewed-on: https://go-review.googlesource.com/c/go/+/225757
Reviewed-by: Michael Matloob <matloob@golang.org>
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/doc/go1.15.html b/doc/go1.15.html
index c59fc4f..aa951ee 100644
--- a/doc/go1.15.html
+++ b/doc/go1.15.html
@@ -43,18 +43,6 @@
<h3 id="go-command">Go command</h3>
-<p><!-- golang.org/issue/37367 -->
- The <code>GOPROXY</code> environment variable now supports skipping proxies
- that return errors. Proxy URLs may now be separated with either commas
- (<code>,</code>) or pipe characters (<code>|</code>). If a proxy URL is
- followed by a comma, the <code>go</code> command will only try the next proxy
- in the list after a 404 or 410 HTTP response. If a proxy URL is followed by a
- pipe character, the <code>go</code> command will try the next proxy in the
- list after any error. Note that the default value of <code>GOPROXY</code>
- remains <code>https://proxy.golang.org,direct</code>, which does not fall
- back to <code>direct</code> in case of errors.
-</p>
-
<p>
TODO
</p>
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index a20a92d..ef054c8 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -2694,15 +2694,15 @@
// Go module mirror run by Google and fall back to a direct connection
// if the proxy reports that it does not have the module (HTTP error 404 or 410).
// See https://proxy.golang.org/privacy for the service's privacy policy.
-//
-// If GOPROXY is set to the string "direct", downloads use a direct connection to
-// source control servers. Setting GOPROXY to "off" disallows downloading modules
-// from any source. Otherwise, GOPROXY is expected to be list of module proxy URLs
-// separated by either comma (,) or pipe (|) characters, which control error
-// fallback behavior. For each request, the go command tries each proxy in
-// sequence. If there is an error, the go command will try the next proxy in the
-// list if the error is a 404 or 410 HTTP response or if the current proxy is
-// followed by a pipe character, indicating it is safe to fall back on any error.
+// If GOPROXY is set to the string "direct", downloads use a direct connection
+// to source control servers. Setting GOPROXY to "off" disallows downloading
+// modules from any source. Otherwise, GOPROXY is expected to be a comma-separated
+// list of the URLs of module proxies, in which case the go command will fetch
+// modules from those proxies. For each request, the go command tries each proxy
+// in sequence, only moving to the next if the current proxy returns a 404 or 410
+// HTTP response. The string "direct" may appear in the proxy list,
+// to cause a direct connection to be attempted at that point in the search.
+// Any proxies listed after "direct" are never consulted.
//
// The GOPRIVATE and GONOPROXY environment variables allow bypassing
// the proxy for selected modules. See 'go help module-private' for details.
diff --git a/src/cmd/go/internal/modfetch/proxy.go b/src/cmd/go/internal/modfetch/proxy.go
index 73bf9e3..dcea71a 100644
--- a/src/cmd/go/internal/modfetch/proxy.go
+++ b/src/cmd/go/internal/modfetch/proxy.go
@@ -101,51 +101,27 @@
var proxyOnce struct {
sync.Once
- list []proxySpec
+ list []string
err error
}
-type proxySpec struct {
- // url is the proxy URL or one of "off", "direct", "noproxy".
- url string
-
- // fallBackOnError is true if a request should be attempted on the next proxy
- // in the list after any error from this proxy. If fallBackOnError is false,
- // the request will only be attempted on the next proxy if the error is
- // equivalent to os.ErrNotFound, which is true for 404 and 410 responses.
- fallBackOnError bool
-}
-
-func proxyList() ([]proxySpec, error) {
+func proxyURLs() ([]string, error) {
proxyOnce.Do(func() {
if cfg.GONOPROXY != "" && cfg.GOPROXY != "direct" {
- proxyOnce.list = append(proxyOnce.list, proxySpec{url: "noproxy"})
+ proxyOnce.list = append(proxyOnce.list, "noproxy")
}
-
- goproxy := cfg.GOPROXY
- for goproxy != "" {
- var url string
- fallBackOnError := false
- if i := strings.IndexAny(goproxy, ",|"); i >= 0 {
- url = goproxy[:i]
- fallBackOnError = goproxy[i] == '|'
- goproxy = goproxy[i+1:]
- } else {
- url = goproxy
- goproxy = ""
- }
-
- url = strings.TrimSpace(url)
- if url == "" {
+ for _, proxyURL := range strings.Split(cfg.GOPROXY, ",") {
+ proxyURL = strings.TrimSpace(proxyURL)
+ if proxyURL == "" {
continue
}
- if url == "off" {
+ if proxyURL == "off" {
// "off" always fails hard, so can stop walking list.
- proxyOnce.list = append(proxyOnce.list, proxySpec{url: "off"})
+ proxyOnce.list = append(proxyOnce.list, "off")
break
}
- if url == "direct" {
- proxyOnce.list = append(proxyOnce.list, proxySpec{url: "direct"})
+ if proxyURL == "direct" {
+ proxyOnce.list = append(proxyOnce.list, "direct")
// For now, "direct" is the end of the line. We may decide to add some
// sort of fallback behavior for them in the future, so ignore
// subsequent entries for forward-compatibility.
@@ -155,21 +131,18 @@
// Single-word tokens are reserved for built-in behaviors, and anything
// containing the string ":/" or matching an absolute file path must be a
// complete URL. For all other paths, implicitly add "https://".
- if strings.ContainsAny(url, ".:/") && !strings.Contains(url, ":/") && !filepath.IsAbs(url) && !path.IsAbs(url) {
- url = "https://" + url
+ if strings.ContainsAny(proxyURL, ".:/") && !strings.Contains(proxyURL, ":/") && !filepath.IsAbs(proxyURL) && !path.IsAbs(proxyURL) {
+ proxyURL = "https://" + proxyURL
}
// Check that newProxyRepo accepts the URL.
// It won't do anything with the path.
- if _, err := newProxyRepo(url, "golang.org/x/text"); err != nil {
+ _, err := newProxyRepo(proxyURL, "golang.org/x/text")
+ if err != nil {
proxyOnce.err = err
return
}
-
- proxyOnce.list = append(proxyOnce.list, proxySpec{
- url: url,
- fallBackOnError: fallBackOnError,
- })
+ proxyOnce.list = append(proxyOnce.list, proxyURL)
}
})
@@ -177,16 +150,15 @@
}
// TryProxies iterates f over each configured proxy (including "noproxy" and
-// "direct" if applicable) until f returns no error or until f returns an
-// error that is not equivalent to os.ErrNotExist on a proxy configured
-// not to fall back on errors.
+// "direct" if applicable) until f returns an error that is not
+// equivalent to os.ErrNotExist.
//
// TryProxies then returns that final error.
//
// If GOPROXY is set to "off", TryProxies invokes f once with the argument
// "off".
func TryProxies(f func(proxy string) error) error {
- proxies, err := proxyList()
+ proxies, err := proxyURLs()
if err != nil {
return err
}
@@ -194,39 +166,28 @@
return f("off")
}
- // We try to report the most helpful error to the user. "direct" and "noproxy"
- // errors are best, followed by proxy errors other than ErrNotExist, followed
- // by ErrNotExist. Note that errProxyOff, errNoproxy, and errUseProxy are
- // equivalent to ErrNotExist.
- const (
- notExistRank = iota
- proxyRank
- directRank
- )
- var bestErr error
- bestErrRank := notExistRank
+ var lastAttemptErr error
for _, proxy := range proxies {
- err := f(proxy.url)
- if err == nil {
- return nil
- }
- isNotExistErr := errors.Is(err, os.ErrNotExist)
-
- if (proxy.url == "direct" || proxy.url == "noproxy") && !isNotExistErr {
- bestErr = err
- bestErrRank = directRank
- } else if bestErrRank <= proxyRank && !isNotExistErr {
- bestErr = err
- bestErrRank = proxyRank
- } else if bestErrRank == notExistRank {
- bestErr = err
- }
-
- if !proxy.fallBackOnError && !isNotExistErr {
+ err = f(proxy)
+ if !errors.Is(err, os.ErrNotExist) {
+ lastAttemptErr = err
break
}
+
+ // The error indicates that the module does not exist.
+ // In general we prefer to report the last such error,
+ // because it indicates the error that occurs after all other
+ // options have been exhausted.
+ //
+ // However, for modules in the NOPROXY list, the most useful error occurs
+ // first (with proxy set to "noproxy"), and the subsequent errors are all
+ // errNoProxy (which is not particularly helpful). Do not overwrite a more
+ // useful error with errNoproxy.
+ if lastAttemptErr == nil || !errors.Is(err, errNoproxy) {
+ lastAttemptErr = err
+ }
}
- return bestErr
+ return lastAttemptErr
}
type proxyRepo struct {
diff --git a/src/cmd/go/internal/modfetch/sumdb.go b/src/cmd/go/internal/modfetch/sumdb.go
index ff81ef6..1ed71df 100644
--- a/src/cmd/go/internal/modfetch/sumdb.go
+++ b/src/cmd/go/internal/modfetch/sumdb.go
@@ -26,7 +26,6 @@
"cmd/go/internal/lockedfile"
"cmd/go/internal/str"
"cmd/go/internal/web"
-
"golang.org/x/mod/module"
"golang.org/x/mod/sumdb"
"golang.org/x/mod/sumdb/note"
@@ -147,50 +146,49 @@
}
// Try proxies in turn until we find out how to connect to this database.
- //
- // Before accessing any checksum database URL using a proxy, the proxy
- // client should first fetch <proxyURL>/sumdb/<sumdb-name>/supported.
- //
- // If that request returns a successful (HTTP 200) response, then the proxy
- // supports proxying checksum database requests. In that case, the client
- // should use the proxied access method only, never falling back to a direct
- // connection to the database.
- //
- // If the /sumdb/<sumdb-name>/supported check fails with a “not found” (HTTP
- // 404) or “gone” (HTTP 410) response, or if the proxy is configured to fall
- // back on errors, the client will try the next proxy. If there are no
- // proxies left or if the proxy is "direct" or "off", the client should
- // connect directly to that database.
- //
- // Any other response is treated as the database being unavailable.
- //
- // See https://golang.org/design/25530-sumdb#proxying-a-checksum-database.
- err := TryProxies(func(proxy string) error {
- switch proxy {
- case "noproxy":
- return errUseProxy
- case "direct", "off":
- return errProxyOff
- default:
- proxyURL, err := url.Parse(proxy)
- if err != nil {
- return err
- }
- if _, err := web.GetBytes(web.Join(proxyURL, "sumdb/"+c.name+"/supported")); err != nil {
- return err
- }
- // Success! This proxy will help us.
- c.base = web.Join(proxyURL, "sumdb/"+c.name)
- return nil
- }
- })
- if errors.Is(err, os.ErrNotExist) {
- // No proxies, or all proxies failed (with 404, 410, or were were allowed
- // to fall back), or we reached an explicit "direct" or "off".
- c.base = c.direct
- } else if err != nil {
+ urls, err := proxyURLs()
+ if err != nil {
c.baseErr = err
+ return
}
+ for _, proxyURL := range urls {
+ if proxyURL == "noproxy" {
+ continue
+ }
+ if proxyURL == "direct" || proxyURL == "off" {
+ break
+ }
+ proxy, err := url.Parse(proxyURL)
+ if err != nil {
+ c.baseErr = err
+ return
+ }
+ // Quoting https://golang.org/design/25530-sumdb#proxying-a-checksum-database:
+ //
+ // Before accessing any checksum database URL using a proxy,
+ // the proxy client should first fetch <proxyURL>/sumdb/<sumdb-name>/supported.
+ // If that request returns a successful (HTTP 200) response, then the proxy supports
+ // proxying checksum database requests. In that case, the client should use
+ // the proxied access method only, never falling back to a direct connection to the database.
+ // If the /sumdb/<sumdb-name>/supported check fails with a “not found” (HTTP 404)
+ // or “gone” (HTTP 410) response, the proxy is unwilling to proxy the checksum database,
+ // and the client should connect directly to the database.
+ // Any other response is treated as the database being unavailable.
+ _, err = web.GetBytes(web.Join(proxy, "sumdb/"+c.name+"/supported"))
+ if err == nil {
+ // Success! This proxy will help us.
+ c.base = web.Join(proxy, "sumdb/"+c.name)
+ return
+ }
+ // If the proxy serves a non-404/410, give up.
+ if !errors.Is(err, os.ErrNotExist) {
+ c.baseErr = err
+ return
+ }
+ }
+
+ // No proxies, or all proxies said 404, or we reached an explicit "direct".
+ c.base = c.direct
}
// ReadConfig reads the key from c.key
diff --git a/src/cmd/go/internal/modload/help.go b/src/cmd/go/internal/modload/help.go
index d80206b..bd19bb4 100644
--- a/src/cmd/go/internal/modload/help.go
+++ b/src/cmd/go/internal/modload/help.go
@@ -363,15 +363,15 @@
Go module mirror run by Google and fall back to a direct connection
if the proxy reports that it does not have the module (HTTP error 404 or 410).
See https://proxy.golang.org/privacy for the service's privacy policy.
-
-If GOPROXY is set to the string "direct", downloads use a direct connection to
-source control servers. Setting GOPROXY to "off" disallows downloading modules
-from any source. Otherwise, GOPROXY is expected to be list of module proxy URLs
-separated by either comma (,) or pipe (|) characters, which control error
-fallback behavior. For each request, the go command tries each proxy in
-sequence. If there is an error, the go command will try the next proxy in the
-list if the error is a 404 or 410 HTTP response or if the current proxy is
-followed by a pipe character, indicating it is safe to fall back on any error.
+If GOPROXY is set to the string "direct", downloads use a direct connection
+to source control servers. Setting GOPROXY to "off" disallows downloading
+modules from any source. Otherwise, GOPROXY is expected to be a comma-separated
+list of the URLs of module proxies, in which case the go command will fetch
+modules from those proxies. For each request, the go command tries each proxy
+in sequence, only moving to the next if the current proxy returns a 404 or 410
+HTTP response. The string "direct" may appear in the proxy list,
+to cause a direct connection to be attempted at that point in the search.
+Any proxies listed after "direct" are never consulted.
The GOPRIVATE and GONOPROXY environment variables allow bypassing
the proxy for selected modules. See 'go help module-private' for details.
diff --git a/src/cmd/go/testdata/script/mod_proxy_list.txt b/src/cmd/go/testdata/script/mod_proxy_list.txt
index 849cf2c..a486228 100644
--- a/src/cmd/go/testdata/script/mod_proxy_list.txt
+++ b/src/cmd/go/testdata/script/mod_proxy_list.txt
@@ -10,25 +10,17 @@
env GOPROXY=$proxy/404,$proxy/410,$proxy
go get rsc.io/quote@v1.1.0
-# get should not walk past other 4xx errors if proxies are separated with ','.
+# get should not walk past other 4xx errors.
env GOPROXY=$proxy/403,$proxy
! go get rsc.io/quote@v1.2.0
stderr 'reading.*/403/rsc.io/.*: 403 Forbidden'
-# get should not walk past non-4xx errors if proxies are separated with ','.
+# get should not walk past non-4xx errors.
env GOPROXY=$proxy/500,$proxy
! go get rsc.io/quote@v1.3.0
stderr 'reading.*/500/rsc.io/.*: 500 Internal Server Error'
-# get should walk past other 4xx errors if proxies are separated with '|'.
-env GOPROXY=$proxy/403|https://0.0.0.0|$proxy
-go get rsc.io/quote@v1.2.0
-
-# get should walk past non-4xx errors if proxies are separated with '|'.
-env GOPROXY=$proxy/500|https://0.0.0.0|$proxy
-go get rsc.io/quote@v1.3.0
-
-# get should return the final error if that's all we have.
+# get should return the final 404/410 if that's all we have.
env GOPROXY=$proxy/404,$proxy/410
! go get rsc.io/quote@v1.4.0
stderr 'reading.*/410/rsc.io/.*: 410 Gone'
diff --git a/src/cmd/go/testdata/script/mod_sumdb_proxy.txt b/src/cmd/go/testdata/script/mod_sumdb_proxy.txt
index 7bbc3f9..2816691 100644
--- a/src/cmd/go/testdata/script/mod_sumdb_proxy.txt
+++ b/src/cmd/go/testdata/script/mod_sumdb_proxy.txt
@@ -46,22 +46,5 @@
rm $GOPATH/pkg/mod/cache/download/sumdb
rm go.sum
-# the error from the last attempted proxy should be returned.
-cp go.mod.orig go.mod
-env GOSUMDB=$sumdb
-env GOPROXY=$proxy/sumdb-404,$proxy/sumdb-503
-! go get -d rsc.io/fortune@v1.0.0
-stderr '503 Service Unavailable'
-rm $GOPATH/pkg/mod/cache/download/sumdb
-rm go.sum
-
-# if proxies are separated with '|', fallback is allowed on any error.
-cp go.mod.orig go.mod
-env GOSUMDB=$sumdb
-env GOPROXY=$proxy/sumdb-503|https://0.0.0.0|$proxy
-go get -d rsc.io/fortune@v1.0.0
-rm $GOPATH/pkg/mod/cache/download/sumdb
-rm go.sum
-
-- go.mod.orig --
module m