--- /dev/null
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package playground registers HTTP handlers at "/compile" and "/share" that
+// proxy requests to the golang.org playground service.
+// This package may be used unaltered on App Engine Standard with Go 1.11+ runtime.
+package playground // import "golang.org/x/tools/playground"
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "os"
+ "strings"
+ "time"
+
+ "golang.org/x/tools/godoc/golangorgenv"
+)
+
+const baseURL = "https://play.golang.org"
+
+func init() {
+ http.HandleFunc("/compile", bounce)
+ http.HandleFunc("/share", bounce)
+}
+
+func bounce(w http.ResponseWriter, r *http.Request) {
+ b := new(bytes.Buffer)
+ if err := passThru(b, r); os.IsPermission(err) {
+ http.Error(w, "403 Forbidden", http.StatusForbidden)
+ log.Println(err)
+ return
+ } else if err != nil {
+ http.Error(w, "500 Internal Server Error", http.StatusInternalServerError)
+ log.Println(err)
+ return
+ }
+ io.Copy(w, b)
+}
+
+func passThru(w io.Writer, req *http.Request) error {
+ if req.URL.Path == "/share" && googleCN(req) {
+ return os.ErrPermission
+ }
+ defer req.Body.Close()
+ url := baseURL + req.URL.Path
+ ctx, cancel := context.WithTimeout(req.Context(), 60*time.Second)
+ defer cancel()
+ r, err := post(ctx, url, req.Header.Get("Content-Type"), req.Body)
+ if err != nil {
+ return fmt.Errorf("making POST request: %v", err)
+ }
+ defer r.Body.Close()
+ if _, err := io.Copy(w, r.Body); err != nil {
+ return fmt.Errorf("copying response Body: %v", err)
+ }
+ return nil
+}
+
+func post(ctx context.Context, url, contentType string, body io.Reader) (*http.Response, error) {
+ req, err := http.NewRequest(http.MethodPost, url, body)
+ if err != nil {
+ return nil, fmt.Errorf("http.NewRequest: %v", err)
+ }
+ req.Header.Set("Content-Type", contentType)
+ return http.DefaultClient.Do(req.WithContext(ctx))
+}
+
+// googleCN reports whether request r is considered
+// to be served from golang.google.cn.
+func googleCN(r *http.Request) bool {
+ if r.FormValue("googlecn") != "" {
+ return true
+ }
+ if strings.HasSuffix(r.Host, ".cn") {
+ return true
+ }
+ if !golangorgenv.CheckCountry() {
+ return false
+ }
+ switch r.Header.Get("X-Appengine-Country") {
+ case "", "ZZ", "CN":
+ return true
+ }
+ return false
+}