Inside the "present" tool Andrew Gerrand Google @enneff adg@golang.org https://go.dev * 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` package 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/,/^}/