From 6a08e3108db3c45254bbacc63c70c6addc62e6cb Mon Sep 17 00:00:00 2001 From: Dmitri Shuralyov Date: Tue, 5 Mar 2019 16:57:57 -0500 Subject: [PATCH] playground: use stdlib instead of appengine packages With modern versions of App Engine, it's no longer needed to use the google.golang.org/appengine/... packages. Package log from standard library can be used instead of the google.golang.org/appengine/log package. Packages net/http and context from standard library can be used instead of google.golang.org/appengine/urlfetch. This simplifies the code and reduces the number of dependences. Start using the golangorgenv package from previous commit to make the decision of whether to enforce sharing restrictions, rather than relying on the appengine build tag. The appengine build tag is no longer set in App Engine Standard with Go 1.11+ runtime. An alternative solution would be detect App Engine by doing something like: // GAE_ENV environment variable is set to "standard" in App Engine environment, Go 1.11 runtime. // See https://cloud.google.com/appengine/docs/standard/go111/runtime#environment_variables. var onAppengine = os.Getenv("GAE_ENV") == "standard" But we choose to upgrade to explicit app-scoped environment variable configuration as part of this change. It provides better security properties, and the value of adding an intermediate transitional step is not high enough to justify doing it. When getting the value of "X-AppEngine-Country" header, use its canonical format "X-Appengine-Country" to avoid an allocation. This does not change behavior. Run go mod tidy (using Go 1.12). Updates golang/go#29981 Updates golang/go#30486 Change-Id: I82a59e0f28623e06762b7ebdf3930b5ee243acda Reviewed-on: https://go-review.googlesource.com/c/tools/+/160837 Reviewed-by: Michael Matloob Reviewed-by: Brad Fitzpatrick --- go.mod | 6 +-- go.sum | 8 ---- playground/appengine.go | 34 --------------- playground/common.go | 67 ------------------------------ playground/local.go | 32 -------------- playground/playground.go | 90 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 91 insertions(+), 146 deletions(-) delete mode 100644 playground/appengine.go delete mode 100644 playground/common.go delete mode 100644 playground/local.go create mode 100644 playground/playground.go diff --git a/go.mod b/go.mod index 2d6246ed7b..0803551be1 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,3 @@ module golang.org/x/tools -require ( - golang.org/x/net v0.0.0-20190213061140-3a22650c66bd - golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect - google.golang.org/appengine v1.4.0 -) +require golang.org/x/net v0.0.0-20190213061140-3a22650c66bd diff --git a/go.sum b/go.sum index 24edbf4948..9fb0388d64 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,2 @@ -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/playground/appengine.go b/playground/appengine.go deleted file mode 100644 index 08c61f3d19..0000000000 --- a/playground/appengine.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2012 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. - -// +build appengine - -package playground - -import ( - "context" - "io" - "net/http" - - "google.golang.org/appengine" - "google.golang.org/appengine/log" - "google.golang.org/appengine/urlfetch" -) - -func init() { - onAppengine = !appengine.IsDevAppServer() -} - -func contextFunc(r *http.Request) context.Context { - return appengine.NewContext(r) -} - -func post(ctx context.Context, url, contentType string, body io.Reader) (*http.Response, error) { - return urlfetch.Client(ctx).Post(url, contentType, body) -} - -func report(r *http.Request, err error) { - ctx := appengine.NewContext(r) - log.Errorf(ctx, "%v", err) -} diff --git a/playground/common.go b/playground/common.go deleted file mode 100644 index 564aff1743..0000000000 --- a/playground/common.go +++ /dev/null @@ -1,67 +0,0 @@ -// 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. -package playground // import "golang.org/x/tools/playground" - -import ( - "bytes" - "context" - "errors" - "fmt" - "io" - "net/http" - "time" -) - -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); err != nil { - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - report(r, err) - return - } - io.Copy(w, b) -} - -func passThru(w io.Writer, req *http.Request) error { - if req.URL.Path == "/share" && !allowShare(req) { - return errors.New("Forbidden") - } - defer req.Body.Close() - url := baseURL + req.URL.Path - ctx, cancel := context.WithTimeout(contextFunc(req), 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 -} - -var onAppengine = false // will be overridden by appengine.go - -func allowShare(r *http.Request) bool { - if !onAppengine { - return true - } - switch r.Header.Get("X-AppEngine-Country") { - case "", "ZZ", "CN": - return false - } - return true -} diff --git a/playground/local.go b/playground/local.go deleted file mode 100644 index 741ab6f9e7..0000000000 --- a/playground/local.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2012 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. - -// +build !appengine - -package playground - -import ( - "context" - "fmt" - "io" - "log" - "net/http" -) - -func post(ctx context.Context, url, contentType string, body io.Reader) (*http.Response, error) { - req, err := http.NewRequest("POST", 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)) -} - -func contextFunc(_ *http.Request) context.Context { - return context.Background() -} - -func report(r *http.Request, err error) { - log.Println(err) -} diff --git a/playground/playground.go b/playground/playground.go new file mode 100644 index 0000000000..14e9841831 --- /dev/null +++ b/playground/playground.go @@ -0,0 +1,90 @@ +// 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 +}