github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/cmd/present/main.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"embed"
     9  	"flag"
    10  	"fmt"
    11  	"io/fs"
    12  	"log"
    13  	"net"
    14  	"net/http"
    15  	"net/url"
    16  	"os"
    17  	"strings"
    18  
    19  	"golang.org/x/tools/present"
    20  )
    21  
    22  var (
    23  	httpAddr      = flag.String("http", "127.0.0.1:3999", "HTTP service address (e.g., '127.0.0.1:3999')")
    24  	originHost    = flag.String("orighost", "", "host component of web origin URL (e.g., 'localhost')")
    25  	basePath      = flag.String("base", "", "base path for slide template and static resources")
    26  	contentPath   = flag.String("content", ".", "base path for presentation content")
    27  	usePlayground = flag.Bool("use_playground", false, "run code snippets using play.golang.org; if false, run them locally and deliver results by WebSocket transport")
    28  )
    29  
    30  //go:embed static templates
    31  var embedFS embed.FS
    32  
    33  func main() {
    34  	flag.BoolVar(&present.PlayEnabled, "play", true, "enable playground (permit execution of arbitrary user code)")
    35  	flag.BoolVar(&present.NotesEnabled, "notes", false, "enable presenter notes (press 'N' from the browser to display them)")
    36  	flag.Parse()
    37  
    38  	if os.Getenv("GAE_ENV") == "standard" {
    39  		log.Print("Configuring for App Engine Standard")
    40  		port := os.Getenv("PORT")
    41  		if port == "" {
    42  			port = "8080"
    43  		}
    44  		*httpAddr = fmt.Sprintf("0.0.0.0:%s", port)
    45  		pwd, err := os.Getwd()
    46  		if err != nil {
    47  			log.Fatalf("Couldn't get pwd: %v\n", err)
    48  		}
    49  		*basePath = pwd
    50  		*usePlayground = true
    51  		*contentPath = "./content/"
    52  	}
    53  
    54  	var fsys fs.FS = embedFS
    55  	if *basePath != "" {
    56  		fsys = os.DirFS(*basePath)
    57  	}
    58  	err := initTemplates(fsys)
    59  	if err != nil {
    60  		log.Fatalf("Failed to parse templates: %v", err)
    61  	}
    62  
    63  	ln, err := net.Listen("tcp", *httpAddr)
    64  	if err != nil {
    65  		log.Fatal(err)
    66  	}
    67  	defer ln.Close()
    68  
    69  	_, port, err := net.SplitHostPort(ln.Addr().String())
    70  	if err != nil {
    71  		log.Fatal(err)
    72  	}
    73  
    74  	origin := &url.URL{Scheme: "http"}
    75  	if *originHost != "" {
    76  		if strings.HasPrefix(*originHost, "https://") {
    77  			*originHost = strings.TrimPrefix(*originHost, "https://")
    78  			origin.Scheme = "https"
    79  		}
    80  		*originHost = strings.TrimPrefix(*originHost, "http://")
    81  		origin.Host = net.JoinHostPort(*originHost, port)
    82  	} else if ln.Addr().(*net.TCPAddr).IP.IsUnspecified() {
    83  		name, _ := os.Hostname()
    84  		origin.Host = net.JoinHostPort(name, port)
    85  	} else {
    86  		reqHost, reqPort, err := net.SplitHostPort(*httpAddr)
    87  		if err != nil {
    88  			log.Fatal(err)
    89  		}
    90  		if reqPort == "0" {
    91  			origin.Host = net.JoinHostPort(reqHost, port)
    92  		} else {
    93  			origin.Host = *httpAddr
    94  		}
    95  	}
    96  
    97  	initPlayground(fsys, origin)
    98  	http.Handle("/static/", http.FileServer(http.FS(fsys)))
    99  
   100  	if !ln.Addr().(*net.TCPAddr).IP.IsLoopback() &&
   101  		present.PlayEnabled && !*usePlayground {
   102  		log.Print(localhostWarning)
   103  	}
   104  
   105  	log.Printf("Open your web browser and visit %s", origin.String())
   106  	if present.NotesEnabled {
   107  		log.Println("Notes are enabled, press 'N' from the browser to display them.")
   108  	}
   109  	log.Fatal(http.Serve(ln, nil))
   110  }
   111  
   112  func environ(vars ...string) []string {
   113  	env := os.Environ()
   114  	for _, r := range vars {
   115  		k := strings.SplitAfter(r, "=")[0]
   116  		var found bool
   117  		for i, v := range env {
   118  			if strings.HasPrefix(v, k) {
   119  				env[i] = r
   120  				found = true
   121  			}
   122  		}
   123  		if !found {
   124  			env = append(env, r)
   125  		}
   126  	}
   127  	return env
   128  }
   129  
   130  const basePathMessage = `
   131  By default, gopresent locates the slide template files and associated
   132  static content by looking for a %q package
   133  in your Go workspaces (GOPATH).
   134  
   135  You may use the -base flag to specify an alternate location.
   136  `
   137  
   138  const localhostWarning = `
   139  WARNING!  WARNING!  WARNING!
   140  
   141  The present server appears to be listening on an address that is not localhost
   142  and is configured to run code snippets locally. Anyone with access to this address
   143  and port will have access to this machine as the user running present.
   144  
   145  To avoid this message, listen on localhost, run with -play=false, or run with
   146  -play_socket=false.
   147  
   148  If you don't understand this message, hit Control-C to terminate this process.
   149  
   150  WARNING!  WARNING!  WARNING!
   151  `