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 -}