From 37fd46feaea9f88591493cb90891998c53754473 Mon Sep 17 00:00:00 2001 From: Andrew Bonventre Date: Tue, 9 Oct 2018 17:35:48 -0400 Subject: [PATCH] cmd/present: merge appengine and non-appengine files Without changing the behavior of the present command for local usage (using the local socket for running examples, defaulting to the current directory for all content). Add flags and set them to the appropriate values if running on App Engine. Notably, since the Go files must be in the same directory as app.yaml, the content root must be ./content/ to avoid listing the present source files. It also defaults to running example snippets via the HTTPTransport (https://play.golang.org/compile) instead of locally when on App Engine. There are also some small cleanup code changes. Update golang/go#28080 Change-Id: I40bb7923107614f88d2bfdffd34a824d4bacb3a1 Reviewed-on: https://go-review.googlesource.com/c/140841 Reviewed-by: Brad Fitzpatrick Reviewed-by: Bryan C. Mills --- cmd/present/appengine.go | 22 --------------- cmd/present/dir.go | 25 +++++++++-------- cmd/present/doc.go | 37 +++++++++++-------------- cmd/present/{local.go => main.go} | 40 +++++++++++++++++++-------- cmd/present/play.go | 46 +++++++++++++++++++++++++++++++ cmd/present/play_http.go | 23 ---------------- cmd/present/play_socket.go | 36 ------------------------ 7 files changed, 105 insertions(+), 124 deletions(-) delete mode 100644 cmd/present/appengine.go rename cmd/present/{local.go => main.go} (66%) delete mode 100644 cmd/present/play_http.go delete mode 100644 cmd/present/play_socket.go diff --git a/cmd/present/appengine.go b/cmd/present/appengine.go deleted file mode 100644 index 1e39ce1d..00000000 --- a/cmd/present/appengine.go +++ /dev/null @@ -1,22 +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 main - -import ( - "mime" - - "golang.org/x/tools/present" -) - -func init() { - initTemplates("./present/") - present.PlayEnabled = true - initPlayground("./present/", nil) - - // App Engine has no /etc/mime.types - mime.AddExtensionType(".svg", "image/svg+xml") -} diff --git a/cmd/present/dir.go b/cmd/present/dir.go index 19aec87e..17736ec1 100644 --- a/cmd/present/dir.go +++ b/cmd/present/dir.go @@ -13,6 +13,7 @@ import ( "os" "path/filepath" "sort" + "strings" "golang.org/x/tools/present" ) @@ -21,19 +22,18 @@ func init() { http.HandleFunc("/", dirHandler) } -// dirHandler serves a directory listing for the requested path, rooted at basePath. +// dirHandler serves a directory listing for the requested path, rooted at *contentPath. func dirHandler(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/favicon.ico" { - http.Error(w, "not found", 404) + http.NotFound(w, r) return } - const base = "." - name := filepath.Join(base, r.URL.Path) + name := filepath.Join(*contentPath, r.URL.Path) if isDoc(name) { err := renderDoc(w, name) if err != nil { log.Println(err) - http.Error(w, err.Error(), 500) + http.Error(w, err.Error(), http.StatusInternalServerError) } return } @@ -43,12 +43,12 @@ func dirHandler(w http.ResponseWriter, r *http.Request) { addr = r.RemoteAddr } log.Printf("request from %s: %s", addr, err) - http.Error(w, err.Error(), 500) + http.Error(w, err.Error(), http.StatusInternalServerError) return } else if isDir { return } - http.FileServer(http.Dir(base)).ServeHTTP(w, r) + http.FileServer(http.Dir(*contentPath)).ServeHTTP(w, r) } func isDoc(path string) bool { @@ -138,7 +138,9 @@ func dirList(w io.Writer, name string) (isDir bool, err error) { if err != nil { return false, err } - d := &dirListData{Path: name} + strippedPath := strings.TrimPrefix(name, filepath.Clean(*contentPath)) + strippedPath = strings.TrimPrefix(strippedPath, "/") + d := &dirListData{Path: strippedPath} for _, fi := range fis { // skip the golang.org directory if name == "." && fi.Name() == "golang.org" { @@ -146,15 +148,16 @@ func dirList(w io.Writer, name string) (isDir bool, err error) { } e := dirEntry{ Name: fi.Name(), - Path: filepath.ToSlash(filepath.Join(name, fi.Name())), + Path: filepath.ToSlash(filepath.Join(strippedPath, fi.Name())), } if fi.IsDir() && showDir(e.Name) { d.Dirs = append(d.Dirs, e) continue } if isDoc(e.Name) { - if p, err := parse(e.Path, present.TitlesOnly); err != nil { - log.Println(err) + fn := filepath.ToSlash(filepath.Join(name, fi.Name())) + if p, err := parse(fn, present.TitlesOnly); err != nil { + log.Printf("parse(%q, present.TitlesOnly): %v", fn, err) } else { e.Title = p.Title } diff --git a/cmd/present/doc.go b/cmd/present/doc.go index fafe4c19..9ad136e7 100644 --- a/cmd/present/doc.go +++ b/cmd/present/doc.go @@ -8,43 +8,38 @@ presents slide and article files from the current directory. It may be run as a stand-alone command or an App Engine app. -Usage of present: - -base="": base path for slide template and static resources - -http="127.0.0.1:3999": HTTP service address (e.g., '127.0.0.1:3999') - -nacl=false: use Native Client environment playground (prevents non-Go code execution) - -notes=false: enable presenter notes (press 'N' from the browser to display them) - -orighost="": host component of web origin URL (e.g., 'localhost') - -play=true: enable playground (permit execution of arbitrary user code) - The setup of the Go version of NaCl is documented at: https://golang.org/wiki/NativeClient -To use with App Engine, copy the tools/cmd/present directory to the root of -your application and create an app.yaml file similar to this: +To use with App Engine, copy the files in the tools/cmd/present directory to the +root of your application and create an app.yaml file similar to this: - application: [application] - version: [version] - runtime: go - api_version: go1 + runtime: go111 handlers: - url: /favicon.ico - static_files: present/static/favicon.ico - upload: present/static/favicon.ico + static_files: static/favicon.ico + upload: static/favicon.ico - url: /static - static_dir: present/static - application_readable: true + static_dir: static - url: /.* - script: _go_app + script: auto # nobuild_files is a regexp that identifies which files to not build. It # is useful for embedding static assets like code snippets and preventing # them from producing build errors for your project. nobuild_files: [path regexp for talk materials] +When running on App Engine, content will be served from the ./content/ +subdirectory. + Present then can be tested in a local App Engine environment with - goapp serve + GAE_ENV=standard go run . + +And deployed using + + gcloud app deploy Input files are named foo.extension, where "extension" defines the format of the generated output. The supported formats are: @@ -54,4 +49,4 @@ the generated output. The supported formats are: The present file format is documented by the present package: http://godoc.org/golang.org/x/tools/present */ -package main // import "golang.org/x/tools/cmd/present" +package main diff --git a/cmd/present/local.go b/cmd/present/main.go similarity index 66% rename from cmd/present/local.go rename to cmd/present/main.go index 260cacbb..a400f1d9 100644 --- a/cmd/present/local.go +++ b/cmd/present/main.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build !appengine - package main import ( @@ -23,10 +21,12 @@ import ( const basePkg = "golang.org/x/tools/cmd/present" var ( - httpAddr = flag.String("http", "127.0.0.1:3999", "HTTP service address (e.g., '127.0.0.1:3999')") - originHost = flag.String("orighost", "", "host component of web origin URL (e.g., 'localhost')") - basePath = flag.String("base", "", "base path for slide template and static resources") - nativeClient = flag.Bool("nacl", false, "use Native Client environment playground (prevents non-Go code execution)") + httpAddr = flag.String("http", "127.0.0.1:3999", "HTTP service address (e.g., '127.0.0.1:3999')") + originHost = flag.String("orighost", "", "host component of web origin URL (e.g., 'localhost')") + basePath = flag.String("base", "", "base path for slide template and static resources") + contentPath = flag.String("content", ".", "base path for presentation content") + usePlayground = flag.Bool("use_playground", false, "if false, arbitrary code (Go, shell scripts, etc.) is run locally via WebSocket transport; otherwise it uses play.golang.org") + nativeClient = flag.Bool("nacl", false, "use Native Client environment playground (prevents non-Go code execution) when using local WebSocket transport") ) func main() { @@ -34,6 +34,23 @@ func main() { flag.BoolVar(&present.NotesEnabled, "notes", false, "enable presenter notes (press 'N' from the browser to display them)") flag.Parse() + if os.Getenv("GAE_ENV") == "standard" { + log.Print("Configuring for App Engine Standard") + port := os.Getenv("PORT") + if port == "" { + port = "8080" + } + *httpAddr = fmt.Sprintf("0.0.0.0:%s", port) + pwd, err := os.Getwd() + if err != nil { + fmt.Fprintf(os.Stderr, "Couldn't get pwd: %v\n", err) + os.Exit(1) + } + *basePath = pwd + *usePlayground = true + *contentPath = "./content/" + } + if *basePath == "" { p, err := build.Default.Import(basePkg, "", build.FindOnly) if err != nil { @@ -81,7 +98,7 @@ func main() { http.Handle("/static/", http.FileServer(http.Dir(*basePath))) if !ln.Addr().(*net.TCPAddr).IP.IsLoopback() && - present.PlayEnabled && !*nativeClient { + present.PlayEnabled && !*nativeClient && !*usePlayground { log.Print(localhostWarning) } @@ -121,11 +138,12 @@ You may use the -base flag to specify an alternate location. const localhostWarning = ` WARNING! WARNING! WARNING! -The present server appears to be listening on an address that is not localhost. -Anyone with access to this address and port will have access to this machine as -the user running present. +The present server appears to be listening on an address that is not localhost +and using socket transport for running Go code. Anyone with access to this address +and port will have access to this machine as the user running present. -To avoid this message, listen on localhost or run with -play=false. +To avoid this message, listen on localhost, run with -play=false, or run with +-play_socket=false. If you don't understand this message, hit Control-C to terminate this process. diff --git a/cmd/present/play.go b/cmd/present/play.go index 831b99f4..6650b943 100644 --- a/cmd/present/play.go +++ b/cmd/present/play.go @@ -9,10 +9,21 @@ import ( "fmt" "io/ioutil" "net/http" + "net/url" "path/filepath" + "runtime" "time" "golang.org/x/tools/godoc/static" + "golang.org/x/tools/playground/socket" + "golang.org/x/tools/present" + + // This will register handlers at /compile and /share that will proxy to the + // respective endpoints at play.golang.org. This allows the frontend to call + // these endpoints without needing cross-origin request sharing (CORS). + // Note that this is imported regardless of whether the endpoints are used or + // not (in the case of a local socket connection, they are not called). + _ "golang.org/x/tools/playground" ) var scripts = []string{"jquery.js", "jquery-ui.js", "playground.js", "play.js"} @@ -41,3 +52,38 @@ func playScript(root, transport string) { http.ServeContent(w, r, "", modTime, bytes.NewReader(b)) }) } + +func initPlayground(basepath string, origin *url.URL) { + if !present.PlayEnabled { + return + } + if *usePlayground { + playScript(basepath, "HTTPTransport") + return + } + + if *nativeClient { + // When specifying nativeClient, non-Go code cannot be executed + // because the NaCl setup doesn't support doing so. + socket.RunScripts = false + socket.Environ = func() []string { + if runtime.GOARCH == "amd64" { + return environ("GOOS=nacl", "GOARCH=amd64p32") + } + return environ("GOOS=nacl") + } + } + playScript(basepath, "SocketTransport") + http.Handle("/socket", socket.NewHandler(origin)) +} + +func playable(c present.Code) bool { + play := present.PlayEnabled && c.Play + + // Restrict playable files to only Go source files when using play.golang.org, + // since there is no method to execute shell scripts there. + if *usePlayground { + return play && c.Ext == ".go" + } + return play +} diff --git a/cmd/present/play_http.go b/cmd/present/play_http.go deleted file mode 100644 index 02e7314d..00000000 --- a/cmd/present/play_http.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015 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 appenginevm - -package main - -import ( - "net/url" - - "golang.org/x/tools/present" - - _ "golang.org/x/tools/playground" -) - -func initPlayground(basepath string, origin *url.URL) { - playScript(basepath, "HTTPTransport") -} - -func playable(c present.Code) bool { - return present.PlayEnabled && c.Play && c.Ext == ".go" -} diff --git a/cmd/present/play_socket.go b/cmd/present/play_socket.go deleted file mode 100644 index df63edab..00000000 --- a/cmd/present/play_socket.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2015 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,!appenginevm - -package main - -import ( - "net/http" - "net/url" - "runtime" - - "golang.org/x/tools/playground/socket" - "golang.org/x/tools/present" -) - -func initPlayground(basepath string, origin *url.URL) { - if present.PlayEnabled { - if *nativeClient { - socket.RunScripts = false - socket.Environ = func() []string { - if runtime.GOARCH == "amd64" { - return environ("GOOS=nacl", "GOARCH=amd64p32") - } - return environ("GOOS=nacl") - } - } - playScript(basepath, "SocketTransport") - http.Handle("/socket", socket.NewHandler(origin)) - } -} - -func playable(c present.Code) bool { - return present.PlayEnabled && c.Play -}