go.talks: add "inside present" talk

R=r
CC=golang-dev
https://golang.org/cl/6718050
diff --git a/2012/insidepresent.slide b/2012/insidepresent.slide
new file mode 100644
index 0000000..842923b
--- /dev/null
+++ b/2012/insidepresent.slide
@@ -0,0 +1,144 @@
+Inside the "present" tool
+
+Andrew Gerrand
+Google
+@enneff
+adg@golang.org
+http://golang.org
+
+
+* The Playground API
+
+The API used by the Playground (and the Tour) is a simple HTTP POST request
+that returns a JSON-encoded response.
+
+Request:
+
+	POST /compile HTTP/1.1
+	Host:play.golang.org
+	Content-Length:113
+	Content-Type:application/x-www-form-urlencoded; charset=UTF-8
+	
+	body=package+main%0A%0Aimport+%22fmt%22%0A%0Afunc+main()+%7B%0A%09fmt.Println(%22Hello%2C+playground%22)%0A%7D%0A
+
+Response body:
+
+	{"compile_errors":"","output":"Hello, playground\n"}
+
+
+* Playground drawbacks
+
+The compile service has no concept of time. (Necessary to limit resource use.)
+
+The API reflects this; output is sent in one blob, not streamed.
+
+Even when running locally, the API is bad for demonstrating code that uses time.
+
+Rob needed to use time in his _Go_Concurrency_Patterns_ talk.
+
+
+* Enter WebSockets
+
+WebSockets are a bi-directional communication channel between a JavaScript program running in a web browser and a web server. They are part of HTML 5. 
+
+The `websocket` pacakge in Go's `go.net` sub-repository provides a WebSocket client and server.
+
+I thought I could use WebSockets to stream program output to a running
+presentation.
+
+And thus the `present` tool was born.
+
+
+* Hello, WebSocket
+
+.code insidepresent/websocket.js
+.play insidepresent/websocket.go
+
+
+* Messages
+
+The client (browser) and server (present) communicate with JSON-encoded messages.
+
+.code insidepresent/socket.go /Message is/,/^}/
+
+Go's `encoding/json` format can convert these `Message` values to and from JSON.
+
+Go:
+
+	Message{Id: "0", Kind: "run", Body: `package main; func main() { print("hello"); }`}
+
+JSON:
+
+	{"Id":"0","Kind":"run","Body":"package main; func main() { print(\"hello\"); }"}
+
+
+* On the wire
+
+.play insidepresent/hello.go
+
+.html insidepresent/wire.html
+
+
+* Implementation
+
+* socketHandler (1/3)
+
+First, register the handler with the `net/http` package:
+
+	http.Handle("/socket", websocket.Handler(socketHandler))
+
+Implementation:
+
+.code insidepresent/socket.go /func socketHandler/,/errc/
+
+* socketHandler (2/3)
+
+.code insidepresent/socket.go /Decode messages/,/END/
+
+* socketHandler (3/3)
+
+.code insidepresent/socket-simple.go /Start and kill/,/^}/
+
+
+* Process
+
+.code insidepresent/socket.go /Process represents/,/^}/
+
+* StartProcess
+
+.code insidepresent/socket.go /StartProcess builds/,/^}/
+
+* Process.start (1/2)
+
+.code insidepresent/socket.go /start builds/,/END/
+
+* Process.start (2/2)
+
+.code insidepresent/socket.go /build x\.go/,/^}/
+
+* Process.cmd
+
+.code insidepresent/socket.go /cmd builds/,/^}/
+.code insidepresent/socket.go /messageWriter is/,/END/
+
+* Process.wait and Process.end
+
+.code insidepresent/socket.go /wait waits/,/^}/
+.code insidepresent/socket.go /end sends/,/^}/
+
+* Process.Kill
+
+.code insidepresent/socket.go /Kill stops/,/^}/
+
+
+* One more thing
+
+* Limiting output (1/2)
+
+.code insidepresent/socket.go /switch m\.Kind/,/^			}/
+
+* Limiting output (2/2)
+
+.code insidepresent/socket.go /limiter returns/,/^}/
+
+
diff --git a/2012/insidepresent/hello.go b/2012/insidepresent/hello.go
new file mode 100644
index 0000000..308f6d1
--- /dev/null
+++ b/2012/insidepresent/hello.go
@@ -0,0 +1,10 @@
+package main
+
+import ( "fmt"; "time" )
+
+func main() {
+	for {
+		fmt.Println("Hello, Gophers!")
+		time.Sleep(time.Second)
+	}
+}
diff --git a/2012/insidepresent/socket-simple.go b/2012/insidepresent/socket-simple.go
new file mode 100644
index 0000000..aebadf9
--- /dev/null
+++ b/2012/insidepresent/socket-simple.go
@@ -0,0 +1,226 @@
+// 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 (
+	"encoding/json"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"runtime"
+	"strconv"
+
+	"code.google.com/p/go.net/websocket"
+)
+
+const socketPresent = true
+
+func HandleSocket(path string) {
+	http.Handle(path, websocket.Handler(socketHandler))
+}
+
+const msgLimit = 1000 // max number of messages to send per session
+
+var uniq = make(chan int) // a source of numbers for naming temporary files
+
+func init() {
+	go func() {
+		for i := 0; ; i++ {
+			uniq <- i
+		}
+	}()
+}
+
+// Message is the wire format for the websocket connection to the browser.
+// It is used for both sending output messages and receiving commands, as
+// distinguished by the Kind field.
+type Message struct {
+	Id   string // client-provided unique id for the process
+	Kind string // in: "run", "kill" out: "stdout", "stderr", "end"
+	Body string
+}
+
+// socketHandler handles the websocket connection for a given present session.
+// It handles transcoding Messages to and from JSON format, and starting
+// and killing Processes.
+func socketHandler(c *websocket.Conn) {
+	in, out := make(chan *Message), make(chan *Message)
+	errc := make(chan error, 1)
+
+	// Decode messages from client and send to the in channel.
+	go func() {
+		dec := json.NewDecoder(c)
+		for {
+			var m Message
+			if err := dec.Decode(&m); err != nil {
+				errc <- err
+				return
+			}
+			in <- &m
+		}
+	}()
+
+	// Receive messages from the out channel and encode to the client.
+	go func() {
+		enc := json.NewEncoder(c)
+		for m := range out {
+			if err := enc.Encode(m); err != nil {
+				errc <- err
+				return
+			}
+		}
+	}()
+	// END OMIT
+
+	// Start and kill Processes and handle errors.
+	proc := make(map[string]*Process)
+	for {
+		select {
+		case m := <-in:
+			switch m.Kind {
+			case "run":
+				proc[m.Id].Kill()
+				proc[m.Id] = StartProcess(m.Id, m.Body, out)
+			case "kill":
+				proc[m.Id].Kill()
+			}
+		case err := <-errc:
+			// A encode or decode has failed; bail.
+			log.Println(err)
+			// Shut down any running processes.
+			for _, p := range proc {
+				p.Kill()
+			}
+			return
+		}
+	}
+}
+
+// Process represents a running process.
+type Process struct {
+	id   string
+	out  chan<- *Message
+	done chan struct{} // closed when wait completes
+	run  *exec.Cmd
+}
+
+// StartProcess builds and runs the given program, sending its output
+// and end event as Messages on the provided channel.
+func StartProcess(id, body string, out chan<- *Message) *Process {
+	p := &Process{
+		id:   id,
+		out:  out,
+		done: make(chan struct{}),
+	}
+	cmd, err := p.start(body)
+	if err != nil {
+		p.end(err)
+		return nil
+	}
+	p.run = cmd
+	go p.wait(cmd)
+	return p
+}
+
+// Kill stops the process if it is running and waits for it to exit.
+func (p *Process) Kill() {
+	if p == nil {
+		return
+	}
+	if p.run != nil {
+		p.run.Process.Kill()
+	}
+	<-p.done
+}
+
+// start builds and starts the given program, sending its output to p.out,
+// and returns the associated *exec.Cmd.
+func (p *Process) start(body string) (*exec.Cmd, error) {
+	// x is the base name for .go and executable files
+	x := filepath.Join(tmpdir, "compile"+strconv.Itoa(<-uniq))
+	src := x + ".go"
+	bin := x
+	if runtime.GOOS == "windows" {
+		bin += ".exe"
+	}
+
+	// write body to x.go
+	defer os.Remove(src)
+	if err := ioutil.WriteFile(src, []byte(body), 0666); err != nil {
+		return nil, err
+	}
+	// END OMIT
+
+	// build x.go, creating x
+	dir, file := filepath.Split(src)
+	err := p.cmd(dir, "go", "build", "-o", bin, file).Run()
+	defer os.Remove(bin)
+	if err != nil {
+		return nil, err
+	}
+
+	// run x
+	cmd := p.cmd("", bin)
+	if err = cmd.Start(); err != nil {
+		return nil, err
+	}
+	return cmd, nil
+}
+
+// wait waits for the running process to complete and returns its error state.
+func (p *Process) wait(cmd *exec.Cmd) {
+	defer close(p.done)
+	p.end(cmd.Wait())
+}
+
+// end sends an "end" message to the client, containing the process id and the
+// given error value.
+func (p *Process) end(err error) {
+	m := &Message{Id: p.id, Kind: "end"}
+	if err != nil {
+		m.Body = err.Error()
+	}
+	p.out <- m
+}
+
+// cmd builds an *exec.Cmd that writes its standard output and error to the
+// Process' output channel.
+func (p *Process) cmd(dir string, args ...string) *exec.Cmd {
+	cmd := exec.Command(args[0], args[1:]...)
+	cmd.Dir = dir
+	cmd.Stdout = &messageWriter{p.id, "stdout", p.out}
+	cmd.Stderr = &messageWriter{p.id, "stderr", p.out}
+	return cmd
+}
+
+// messageWriter is an io.Writer that converts all writes to Message sends on
+// the out channel with the specified id and kind.
+type messageWriter struct {
+	id, kind string
+	out      chan<- *Message
+}
+
+func (w *messageWriter) Write(b []byte) (n int, err error) {
+	w.out <- &Message{Id: w.id, Kind: w.kind, Body: string(b)}
+	return len(b), nil
+}
+
+// END OMIT
+
+var tmpdir string
+
+func init() {
+	// find real path to temporary directory
+	var err error
+	tmpdir, err = filepath.EvalSymlinks(os.TempDir())
+	if err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/2012/insidepresent/socket.go b/2012/insidepresent/socket.go
new file mode 100644
index 0000000..d3f54d6
--- /dev/null
+++ b/2012/insidepresent/socket.go
@@ -0,0 +1,249 @@
+// 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 (
+	"encoding/json"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"runtime"
+	"strconv"
+
+	"code.google.com/p/go.net/websocket"
+)
+
+const socketPresent = true
+
+func HandleSocket(path string) {
+	http.Handle(path, websocket.Handler(socketHandler))
+}
+
+const msgLimit = 1000 // max number of messages to send per session
+
+var uniq = make(chan int) // a source of numbers for naming temporary files
+
+func init() {
+	go func() {
+		for i := 0; ; i++ {
+			uniq <- i
+		}
+	}()
+}
+
+// Message is the wire format for the websocket connection to the browser.
+// It is used for both sending output messages and receiving commands, as
+// distinguished by the Kind field.
+type Message struct {
+	Id   string // client-provided unique id for the process
+	Kind string // in: "run", "kill" out: "stdout", "stderr", "end"
+	Body string
+}
+
+// socketHandler handles the websocket connection for a given present session.
+// It handles transcoding Messages to and from JSON format, and starting
+// and killing Processes.
+func socketHandler(c *websocket.Conn) {
+	in, out := make(chan *Message), make(chan *Message)
+	errc := make(chan error, 1)
+
+	// Decode messages from client and send to the in channel.
+	go func() {
+		dec := json.NewDecoder(c)
+		for {
+			var m Message
+			if err := dec.Decode(&m); err != nil {
+				errc <- err
+				return
+			}
+			in <- &m
+		}
+	}()
+
+	// Receive messages from the out channel and encode to the client.
+	go func() {
+		enc := json.NewEncoder(c)
+		for m := range out {
+			if err := enc.Encode(m); err != nil {
+				errc <- err
+				return
+			}
+		}
+	}()
+	// END OMIT
+
+	// Start and kill Processes and handle errors.
+	proc := make(map[string]*Process)
+	for {
+		select {
+		case m := <-in:
+			switch m.Kind {
+			case "run":
+				proc[m.Id].Kill()
+				lOut := limiter(in, out) // HL
+				proc[m.Id] = StartProcess(m.Id, m.Body, lOut) // HL
+			case "kill":
+				proc[m.Id].Kill()
+			}
+		case err := <-errc:
+			// A encode or decode has failed; bail.
+			log.Println(err)
+			// Shut down any running processes.
+			for _, p := range proc {
+				p.Kill()
+			}
+			return
+		}
+	}
+}
+
+// Process represents a running process.
+type Process struct {
+	id   string
+	out  chan<- *Message
+	done chan struct{} // closed when wait completes
+	run  *exec.Cmd
+}
+
+// StartProcess builds and runs the given program, sending its output
+// and end event as Messages on the provided channel.
+func StartProcess(id, body string, out chan<- *Message) *Process {
+	p := &Process{
+		id:   id,
+		out:  out,
+		done: make(chan struct{}),
+	}
+	if err := p.start(body); err != nil {
+		p.end(err)
+		return nil
+	}
+	go p.wait()
+	return p
+}
+
+// Kill stops the process if it is running and waits for it to exit.
+func (p *Process) Kill() {
+	if p == nil {
+		return
+	}
+	p.run.Process.Kill()
+	<-p.done
+}
+
+// start builds and starts the given program, sends its output to p.out,
+// and stores the running *exec.Cmd in the run field.
+func (p *Process) start(body string) error {
+	// x is the base name for .go and executable files
+	x := filepath.Join(tmpdir, "compile"+strconv.Itoa(<-uniq))
+	src := x + ".go"
+	bin := x
+	if runtime.GOOS == "windows" {
+		bin += ".exe"
+	}
+
+	// write body to x.go
+	defer os.Remove(src)
+	if err := ioutil.WriteFile(src, []byte(body), 0666); err != nil {
+		return err
+	}
+	// END OMIT
+
+	// build x.go, creating x
+	dir, file := filepath.Split(src)
+	err := p.cmd(dir, "go", "build", "-o", bin, file).Run()
+	defer os.Remove(bin)
+	if err != nil {
+		return err
+	}
+
+	// run x
+	cmd := p.cmd("", bin)
+	if err = cmd.Start(); err != nil {
+		return err
+	}
+
+	p.run = cmd
+	return nil
+}
+
+// wait waits for the running process to complete
+// and sends its error state to the client.
+func (p *Process) wait() {
+	defer close(p.done)
+	p.end(p.run.Wait())
+}
+
+// end sends an "end" message to the client, containing the process id and the
+// given error value.
+func (p *Process) end(err error) {
+	m := &Message{Id: p.id, Kind: "end"}
+	if err != nil {
+		m.Body = err.Error()
+	}
+	p.out <- m
+}
+
+// cmd builds an *exec.Cmd that writes its standard output and error to the
+// Process' output channel.
+func (p *Process) cmd(dir string, args ...string) *exec.Cmd {
+	cmd := exec.Command(args[0], args[1:]...)
+	cmd.Dir = dir
+	cmd.Stdout = &messageWriter{p.id, "stdout", p.out}
+	cmd.Stderr = &messageWriter{p.id, "stderr", p.out}
+	return cmd
+}
+
+// messageWriter is an io.Writer that converts all writes to Message sends on
+// the out channel with the specified id and kind.
+type messageWriter struct {
+	id, kind string
+	out      chan<- *Message
+}
+
+func (w *messageWriter) Write(b []byte) (n int, err error) {
+	w.out <- &Message{Id: w.id, Kind: w.kind, Body: string(b)}
+	return len(b), nil
+}
+// END OMIT
+
+// limiter returns a channel that wraps dest. Messages sent to the channel are
+// sent to dest. After msgLimit Messages have been passed on, a "kill" Message
+// is sent to the kill channel, and only "end" messages are passed.
+func limiter(kill chan<- *Message, dest chan<- *Message) chan<- *Message {
+	ch := make(chan *Message)
+	go func() {
+		n := 0
+		for m := range ch {
+			switch {
+			case n < msgLimit || m.Kind == "end":
+				dest <- m
+				if m.Kind == "end" {
+					return
+				}
+			case n == msgLimit:
+				// Process produced too much output. Kill it.
+				kill <- &Message{Id: m.Id, Kind: "kill"}
+			}
+			n++
+		}
+	}()
+	return ch
+}
+
+var tmpdir string
+
+func init() {
+	// find real path to temporary directory
+	var err error
+	tmpdir, err = filepath.EvalSymlinks(os.TempDir())
+	if err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/2012/insidepresent/websocket.go b/2012/insidepresent/websocket.go
new file mode 100644
index 0000000..6d1547d
--- /dev/null
+++ b/2012/insidepresent/websocket.go
@@ -0,0 +1,19 @@
+package main
+
+import (
+	"code.google.com/p/go.net/websocket"
+	"fmt"
+	"net/http"
+)
+
+func main() {
+	http.Handle("/", websocket.Handler(handler))
+	http.ListenAndServe("localhost:4000", nil)
+}
+
+func handler(c *websocket.Conn) {
+	var s string
+	fmt.Fscan(c, &s)
+	fmt.Println("Received:", s)
+	fmt.Fprint(c, "How do you do?")
+}
diff --git a/2012/insidepresent/websocket.js b/2012/insidepresent/websocket.js
new file mode 100644
index 0000000..f9809d6
--- /dev/null
+++ b/2012/insidepresent/websocket.js
@@ -0,0 +1,3 @@
+var sock = new WebSocket("ws://localhost:4000/");
+sock.onmessage = function(m) { console.log("Received:", m.data); }
+sock.send("Hello!\n")
diff --git a/2012/insidepresent/wire.html b/2012/insidepresent/wire.html
new file mode 100644
index 0000000..2a98a7d
--- /dev/null
+++ b/2012/insidepresent/wire.html
@@ -0,0 +1,15 @@
+<style>
+#wire {
+	border: 1px solid #E0E0E0;
+	background: #F0F0F0;
+	height: 300px;
+	font-family: 'Droid Sans Mono', 'Courier New', monospace;
+	font-size: 18px;
+	line-height: 24px;
+	overflow: auto;
+}
+#wire div { margin-bottom: 10px; }
+#wire .send { color: #900; }
+#wire .recv { color: #009; }
+</style>
+<div id="wire"></div>