// Copyright 2010 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 main import ( "flag" "io" "io/ioutil" "log" "net/http" "os" "os/exec" "strconv" "text/template" ) var ( httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on") htmlOutput = flag.Bool("html", false, "render program output as HTML") ) var ( // a source of numbers, for naming temporary files uniq = make(chan int) ) func main() { flag.Parse() // source of unique numbers go func() { for i := 0; ; i++ { uniq <- i } }() // go to TempDir err := os.Chdir(os.TempDir()) if err != nil { log.Fatal(err) } http.HandleFunc("/", FrontPage) http.HandleFunc("/compile", Compile) log.Fatal(http.ListenAndServe(*httpListen, nil)) } // FrontPage is an HTTP handler that renders the goplay interface. // If a filename is supplied in the path component of the URI, // its contents will be put in the interface's text area. // Otherwise, the default "hello, world" program is displayed. func FrontPage(w http.ResponseWriter, req *http.Request) { data, err := ioutil.ReadFile(req.URL.Path[1:]) if err != nil { data = helloWorld } frontPage.Execute(w, data) } // Compile is an HTTP handler that reads Go source code from the request, // runs the program (returning any errors), // and sends the program's output as the HTTP response. func Compile(w http.ResponseWriter, req *http.Request) { // x is the base name for .go files x := "goplay" + strconv.Itoa(<-uniq) + ".go" // write request Body to x.go f, err := os.Create(x) if err != nil { error_(w, nil, err) return } defer os.Remove(x) defer f.Close() _, err = io.Copy(f, req.Body) if err != nil { error_(w, nil, err) return } f.Close() // run x out, err := run("go", "run", x) if err != nil { error_(w, out, err) return } // write the output of x as the http response if *htmlOutput { w.Write(out) } else { output.Execute(w, out) } } // error writes compile, link, or runtime errors to the HTTP connection. // The JavaScript interface uses the 404 status code to identify the error. func error_(w http.ResponseWriter, out []byte, err error) { w.WriteHeader(404) if out != nil { output.Execute(w, out) } else { output.Execute(w, err.Error()) } } // run executes the specified command and returns its output and an error. func run(cmd ...string) ([]byte, error) { return exec.Command(cmd[0], cmd[1:]...).CombinedOutput() } var frontPage = template.Must(template.New("frontPage").Parse(frontPageText)) // HTML template var output = template.Must(template.New("output").Parse(outputText)) // HTML template var outputText = `
{{printf "%s" . |html}}` var frontPageText = `
(Shift-Enter to compile and run.)
Compile and run after each keystroke
|