playground: allow explicit GOPROXY variable

Setting the PLAY_GOPROXY variable of the playground modifies where
the playground fetches module information from.  This allows users to
install the playground inside their company VPN and use private modules.

The variable isn't named "GOPROXY" to not confuse which context the URL
is used for.  The name PLAY_* is adopted from the existing env variable
ALLOW_PLAY_MODULE_DOWNLOADS.

Fixes golang/go#32740

Change-Id: Idcd0530c1c28342414e0bc5439fca6a2272e1eaa
Reviewed-on: https://go-review.googlesource.com/c/playground/+/184558
Reviewed-by: Andrew Bonventre <andybons@golang.org>
diff --git a/sandbox.go b/sandbox.go
index c4a865d..2050832 100644
--- a/sandbox.go
+++ b/sandbox.go
@@ -370,7 +370,7 @@
 			return nil, fmt.Errorf("error creating temp directory: %v", err)
 		}
 		defer os.RemoveAll(goPath)
-		cmd.Env = append(cmd.Env, "GO111MODULE=on", "GOPROXY=https://proxy.golang.org")
+		cmd.Env = append(cmd.Env, "GO111MODULE=on", "GOPROXY="+playgroundGoproxy())
 	} else {
 		goPath = os.Getenv("GOPATH")                 // contains old code.google.com/p/go-tour, etc
 		cmd.Env = append(cmd.Env, "GO111MODULE=off") // in case it becomes on by default later
@@ -451,13 +451,24 @@
 		// these packages to still run, so the Dockerfile adds these packages
 		// at this name in $GOPATH. Any snippets using this old name wouldn't
 		// have expected (or been able to use) third-party packages anyway,
-		// so disabling modules and proxy.golang.org fetches is acceptable.
+		// so disabling modules and proxy fetches is acceptable.
 		return false
 	}
 	v, _ := strconv.ParseBool(os.Getenv("ALLOW_PLAY_MODULE_DOWNLOADS"))
 	return v
 }
 
+// playgroundGoproxy returns the GOPROXY environment config the playground should use.
+// It is fetched from the environment variable PLAY_GOPROXY. A missing or empty
+// value for PLAY_GOPROXY returns the default value of https://proxy.golang.org.
+func playgroundGoproxy() string {
+	proxypath := os.Getenv("PLAY_GOPROXY")
+	if proxypath != "" {
+		return proxypath
+	}
+	return "https://proxy.golang.org"
+}
+
 func (s *server) healthCheck() error {
 	resp, err := compileAndRun(&request{Body: healthProg})
 	if err != nil {
diff --git a/server_test.go b/server_test.go
index 9cc19da..773a093 100644
--- a/server_test.go
+++ b/server_test.go
@@ -272,5 +272,36 @@
 			t.Errorf("%d. allow = %v; want %v; files:\n%s", i, got, tt.want, filesAsString(files))
 		}
 	}
+}
 
+func TestPlaygroundGoproxy(t *testing.T) {
+	const envKey = "PLAY_GOPROXY"
+	defer os.Setenv(envKey, os.Getenv(envKey))
+
+	tests := []struct {
+		name string
+		env  string
+		want string
+	}{
+		{name: "missing", env: "", want: "https://proxy.golang.org"},
+		{name: "set_to_default", env: "https://proxy.golang.org", want: "https://proxy.golang.org"},
+		{name: "changed", env: "https://company.intranet", want: "https://company.intranet"},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if tt.env != "" {
+				if err := os.Setenv(envKey, tt.env); err != nil {
+					t.Errorf("unable to set environment variable for test: %s", err)
+				}
+			} else {
+				if err := os.Unsetenv(envKey); err != nil {
+					t.Errorf("unable to unset environment variable for test: %s", err)
+				}
+			}
+			got := playgroundGoproxy()
+			if got != tt.want {
+				t.Errorf("playgroundGoproxy = %s; want %s; env: %s", got, tt.want, tt.env)
+			}
+		})
+	}
 }
diff --git a/vet.go b/vet.go
index 32edeca..a9cae12 100644
--- a/vet.go
+++ b/vet.go
@@ -60,7 +60,7 @@
 	if modules {
 		cmd.Env = append(cmd.Env,
 			"GO111MODULE=on",
-			"GOPROXY=https://proxy.golang.org",
+			"GOPROXY="+playgroundGoproxy(),
 		)
 	}
 	out, err := cmd.CombinedOutput()