github.com/spotify/syslog-redirector-golang@v0.0.0-20140320174030-4859f03d829a/misc/goplay/goplay.go (about)

     1  // Copyright 2010 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  	"bytes"
     9  	"flag"
    10  	"io/ioutil"
    11  	"log"
    12  	"net/http"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  	"regexp"
    17  	"strconv"
    18  	"text/template"
    19  )
    20  
    21  var (
    22  	httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on")
    23  	htmlOutput = flag.Bool("html", false, "render program output as HTML")
    24  )
    25  
    26  var (
    27  	// a source of numbers, for naming temporary files
    28  	uniq = make(chan int)
    29  )
    30  
    31  func main() {
    32  	flag.Parse()
    33  
    34  	// source of unique numbers
    35  	go func() {
    36  		for i := 0; ; i++ {
    37  			uniq <- i
    38  		}
    39  	}()
    40  
    41  	http.HandleFunc("/", FrontPage)
    42  	http.HandleFunc("/compile", Compile)
    43  	log.Fatal(http.ListenAndServe(*httpListen, nil))
    44  }
    45  
    46  // FrontPage is an HTTP handler that renders the goplay interface.
    47  // If a filename is supplied in the path component of the URI,
    48  // its contents will be put in the interface's text area.
    49  // Otherwise, the default "hello, world" program is displayed.
    50  func FrontPage(w http.ResponseWriter, req *http.Request) {
    51  	data, err := ioutil.ReadFile(req.URL.Path[1:])
    52  	if err != nil {
    53  		data = helloWorld
    54  	}
    55  	frontPage.Execute(w, data)
    56  }
    57  
    58  // Compile is an HTTP handler that reads Go source code from the request,
    59  // runs the program (returning any errors),
    60  // and sends the program's output as the HTTP response.
    61  func Compile(w http.ResponseWriter, req *http.Request) {
    62  	out, err := compile(req)
    63  	if err != nil {
    64  		error_(w, out, err)
    65  		return
    66  	}
    67  
    68  	// write the output of x as the http response
    69  	if *htmlOutput {
    70  		w.Write(out)
    71  	} else {
    72  		output.Execute(w, out)
    73  	}
    74  }
    75  
    76  var (
    77  	commentRe = regexp.MustCompile(`(?m)^#.*\n`)
    78  	tmpdir    string
    79  )
    80  
    81  func init() {
    82  	// find real temporary directory (for rewriting filename in output)
    83  	var err error
    84  	tmpdir, err = filepath.EvalSymlinks(os.TempDir())
    85  	if err != nil {
    86  		log.Fatal(err)
    87  	}
    88  }
    89  
    90  func compile(req *http.Request) (out []byte, err error) {
    91  	// x is the base name for .go, .6, executable files
    92  	x := filepath.Join(tmpdir, "compile"+strconv.Itoa(<-uniq))
    93  	src := x + ".go"
    94  
    95  	// rewrite filename in error output
    96  	defer func() {
    97  		if err != nil {
    98  			// drop messages from the go tool like '# _/compile0'
    99  			out = commentRe.ReplaceAll(out, nil)
   100  		}
   101  		out = bytes.Replace(out, []byte(src+":"), []byte("main.go:"), -1)
   102  	}()
   103  
   104  	// write body to x.go
   105  	body := new(bytes.Buffer)
   106  	if _, err = body.ReadFrom(req.Body); err != nil {
   107  		return
   108  	}
   109  	defer os.Remove(src)
   110  	if err = ioutil.WriteFile(src, body.Bytes(), 0666); err != nil {
   111  		return
   112  	}
   113  
   114  	// go run x.go
   115  	dir, file := filepath.Split(src)
   116  	out, err = run(dir, "go", "run", file)
   117  	if err != nil {
   118  		return
   119  	}
   120  	return out, nil
   121  }
   122  
   123  // error writes compile, link, or runtime errors to the HTTP connection.
   124  // The JavaScript interface uses the 404 status code to identify the error.
   125  func error_(w http.ResponseWriter, out []byte, err error) {
   126  	w.WriteHeader(404)
   127  	if out != nil {
   128  		output.Execute(w, out)
   129  	} else {
   130  		output.Execute(w, err.Error())
   131  	}
   132  }
   133  
   134  // run executes the specified command and returns its output and an error.
   135  func run(dir string, args ...string) ([]byte, error) {
   136  	var buf bytes.Buffer
   137  	cmd := exec.Command(args[0], args[1:]...)
   138  	cmd.Dir = dir
   139  	cmd.Stdout = &buf
   140  	cmd.Stderr = cmd.Stdout
   141  	err := cmd.Run()
   142  	return buf.Bytes(), err
   143  }
   144  
   145  var frontPage = template.Must(template.New("frontPage").Parse(frontPageText)) // HTML template
   146  var output = template.Must(template.New("output").Parse(outputText))          // HTML template
   147  
   148  var outputText = `<pre>{{printf "%s" . |html}}</pre>`
   149  
   150  var frontPageText = `<!doctype html>
   151  <html>
   152  <head>
   153  <style>
   154  pre, textarea {
   155  	font-family: Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
   156  	font-size: 100%;
   157  }
   158  .hints {
   159  	font-size: 0.8em;
   160  	text-align: right;
   161  }
   162  #edit, #output, #errors { width: 100%; text-align: left; }
   163  #edit { height: 500px; }
   164  #output { color: #00c; }
   165  #errors { color: #c00; }
   166  </style>
   167  <script>
   168  
   169  function insertTabs(n) {
   170  	// find the selection start and end
   171  	var cont  = document.getElementById("edit");
   172  	var start = cont.selectionStart;
   173  	var end   = cont.selectionEnd;
   174  	// split the textarea content into two, and insert n tabs
   175  	var v = cont.value;
   176  	var u = v.substr(0, start);
   177  	for (var i=0; i<n; i++) {
   178  		u += "\t";
   179  	}
   180  	u += v.substr(end);
   181  	// set revised content
   182  	cont.value = u;
   183  	// reset caret position after inserted tabs
   184  	cont.selectionStart = start+n;
   185  	cont.selectionEnd = start+n;
   186  }
   187  
   188  function autoindent(el) {
   189  	var curpos = el.selectionStart;
   190  	var tabs = 0;
   191  	while (curpos > 0) {
   192  		curpos--;
   193  		if (el.value[curpos] == "\t") {
   194  			tabs++;
   195  		} else if (tabs > 0 || el.value[curpos] == "\n") {
   196  			break;
   197  		}
   198  	}
   199  	setTimeout(function() {
   200  		insertTabs(tabs);
   201  	}, 1);
   202  }
   203  
   204  function preventDefault(e) {
   205  	if (e.preventDefault) {
   206  		e.preventDefault();
   207  	} else {
   208  		e.cancelBubble = true;
   209  	}
   210  }
   211  
   212  function keyHandler(event) {
   213  	var e = window.event || event;
   214  	if (e.keyCode == 9) { // tab
   215  		insertTabs(1);
   216  		preventDefault(e);
   217  		return false;
   218  	}
   219  	if (e.keyCode == 13) { // enter
   220  		if (e.shiftKey) { // +shift
   221  			compile(e.target);
   222  			preventDefault(e);
   223  			return false;
   224  		} else {
   225  			autoindent(e.target);
   226  		}
   227  	}
   228  	return true;
   229  }
   230  
   231  var xmlreq;
   232  
   233  function autocompile() {
   234  	if(!document.getElementById("autocompile").checked) {
   235  		return;
   236  	}
   237  	compile();
   238  }
   239  
   240  function compile() {
   241  	var prog = document.getElementById("edit").value;
   242  	var req = new XMLHttpRequest();
   243  	xmlreq = req;
   244  	req.onreadystatechange = compileUpdate;
   245  	req.open("POST", "/compile", true);
   246  	req.setRequestHeader("Content-Type", "text/plain; charset=utf-8");
   247  	req.send(prog);	
   248  }
   249  
   250  function compileUpdate() {
   251  	var req = xmlreq;
   252  	if(!req || req.readyState != 4) {
   253  		return;
   254  	}
   255  	if(req.status == 200) {
   256  		document.getElementById("output").innerHTML = req.responseText;
   257  		document.getElementById("errors").innerHTML = "";
   258  	} else {
   259  		document.getElementById("errors").innerHTML = req.responseText;
   260  		document.getElementById("output").innerHTML = "";
   261  	}
   262  }
   263  </script>
   264  </head>
   265  <body>
   266  <table width="100%"><tr><td width="60%" valign="top">
   267  <textarea autofocus="true" id="edit" spellcheck="false" onkeydown="keyHandler(event);" onkeyup="autocompile();">{{printf "%s" . |html}}</textarea>
   268  <div class="hints">
   269  (Shift-Enter to compile and run.)&nbsp;&nbsp;&nbsp;&nbsp;
   270  <input type="checkbox" id="autocompile" value="checked" /> Compile and run after each keystroke
   271  </div>
   272  <td width="3%">
   273  <td width="27%" align="right" valign="top">
   274  <div id="output"></div>
   275  </table>
   276  <div id="errors"></div>
   277  </body>
   278  </html>
   279  `
   280  
   281  var helloWorld = []byte(`package main
   282  
   283  import "fmt"
   284  
   285  func main() {
   286  	fmt.Println("hello, world")
   287  }
   288  `)