go.talks: move websocket backend code to its own package
This makes it easier to include it in other programs, like the Tour.
R=golang-dev, campoy
CC=golang-dev
https://golang.org/cl/7297043
diff --git a/present/socket.go b/pkg/socket/socket.go
similarity index 80%
rename from present/socket.go
rename to pkg/socket/socket.go
index 40dba7e..96ede2a 100644
--- a/present/socket.go
+++ b/pkg/socket/socket.go
@@ -2,15 +2,19 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build !appengine
-
-package main
+// Package socket implements an WebSocket-based playground backend.
+// Clients connect to a websocket handler and send run/kill commands, and
+// the server sends the output and exit status of the running processes.
+// Multiple clients running multiple processes may be served concurrently.
+// The wire format is JSON and is described by the Message type.
+//
+// This will not run on App Engine as WebSockets are not supported there.
+package socket
import (
"encoding/json"
"io/ioutil"
"log"
- "net/http"
"os"
"os/exec"
"path/filepath"
@@ -20,14 +24,10 @@
"code.google.com/p/go.net/websocket"
)
-const msgLimit = 1000 // max number of messages to send per session
+// Handler implements a WebSocket handler for a client connection.
+var Handler = websocket.Handler(socketHandler)
-// HandleSocket registers the websocket handler with http.DefaultServeMux under
-// the given path.
-func HandleSocket(path string) {
- playScript("/static/socket.js")
- http.Handle(path, websocket.Handler(socketHandler))
-}
+const msgLimit = 1000 // max number of messages to send per session
// Message is the wire format for the websocket connection to the browser.
// It is used for both sending output messages and receiving commands, as
@@ -40,7 +40,7 @@
// socketHandler handles the websocket connection for a given present session.
// It handles transcoding Messages to and from JSON format, and starting
-// and killing Processes.
+// and killing processes.
func socketHandler(c *websocket.Conn) {
in, out := make(chan *Message), make(chan *Message)
errc := make(chan error, 1)
@@ -69,8 +69,8 @@
}
}()
- // Start and kill Processes and handle errors.
- proc := make(map[string]*Process)
+ // Start and kill processes and handle errors.
+ proc := make(map[string]*process)
for {
select {
case m := <-in:
@@ -78,7 +78,7 @@
case "run":
proc[m.Id].Kill()
lOut := limiter(in, out)
- proc[m.Id] = StartProcess(m.Id, m.Body, lOut)
+ proc[m.Id] = startProcess(m.Id, m.Body, lOut)
case "kill":
proc[m.Id].Kill()
}
@@ -94,18 +94,18 @@
}
}
-// Process represents a running process.
-type Process struct {
+// 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
+// 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{
+func startProcess(id, body string, out chan<- *Message) *process {
+ p := &process{
id: id,
out: out,
done: make(chan struct{}),
@@ -119,7 +119,7 @@
}
// Kill stops the process if it is running and waits for it to exit.
-func (p *Process) Kill() {
+func (p *process) Kill() {
if p == nil {
return
}
@@ -129,7 +129,7 @@
// start builds and starts the given program, sending its output to p.out,
// and stores the running *exec.Cmd in the run field.
-func (p *Process) start(body string) error {
+func (p *process) start(body string) error {
// We "go build" and then exec the binary so that the
// resultant *exec.Cmd is a handle to the user's program
// (rather than the go tool process).
@@ -167,14 +167,14 @@
// wait waits for the running process to complete
// and sends its error state to the client.
-func (p *Process) wait() {
+func (p *process) wait() {
p.end(p.run.Wait())
close(p.done) // unblock waiting Kill calls
}
// end sends an "end" message to the client, containing the process id and the
// given error value.
-func (p *Process) end(err error) {
+func (p *process) end(err error) {
m := &Message{Id: p.id, Kind: "end"}
if err != nil {
m.Body = err.Error()
@@ -183,8 +183,8 @@
}
// 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 {
+// 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}
@@ -219,7 +219,7 @@
return
}
case n == msgLimit:
- // Process produced too much output. Kill it.
+ // process produced too much output. Kill it.
kill <- &Message{Id: m.Id, Kind: "kill"}
}
n++
diff --git a/present/local.go b/present/local.go
new file mode 100644
index 0000000..e655352
--- /dev/null
+++ b/present/local.go
@@ -0,0 +1,20 @@
+// Copyright 2013 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 (
+ "net/http"
+
+ "code.google.com/p/go.talks/pkg/socket"
+)
+
+// HandleSocket registers the websocket handler with http.DefaultServeMux under
+// the given path.
+func HandleSocket(path string) {
+ playScript("/static/socket.js")
+ http.Handle(path, socket.Handler)
+}