$ go build
x := 1
y := "Hello, world!"
z := map[string]int{"a": 1, "b": 2}
Old programs read like quiet conversations between a well-spoken research worker and a well-studied mechanical colleague, not as a debate with a compiler.
— Dick Gabriel
$ gofmt --comments=false *.go | wc -l
216
type history struct {
messages [][]byte
writer chan []byte
reader chan chan []byte
}
func (h *history) listen() {
for {
select {
case msg := <-h.writer:
h.messages = append(h.messages, msg)
case resp := <-h.reader:
for _, msg := range h.messages {
resp <- msg
}
close(resp)
}
}
}
func (h *history) dump() [][]byte {
resp := make(chan []byte)
h.reader <- resp
result := make([][]byte, 0)
for msg := range resp {
result = append(result, msg)
}
return result
}
type connection struct {
ws *websocket.Conn
send chan []byte
}
func (c *connection) readPump() {
defer func() {
wsHub.unregister <- c
c.ws.Close()
}()
c.ws.SetReadLimit(maxMessageSize)
c.ws.SetReadDeadline(time.Now().Add(readWait))
....
}
...
for {
op, r, err := c.ws.NextReader()
if err != nil {
break
}
...
}
...
switch op {
case websocket.OpPong:
c.ws.SetReadDeadline(time.Now().Add(readWait))
case websocket.OpText:
message, err := ioutil.ReadAll(r)
if err != nil {
break
}
wsHub.broadcast <- message
msgHistory.writer <- message
}
func (c *connection) write(opCode int, payload []byte) error {
c.ws.SetWriteDeadline(time.Now().Add(writeWait))
return c.ws.WriteMessage(opCode, payload)
}
func (c *connection) writePump() {
ticker := time.NewTicker(pingPeriod)
defer func() {
ticker.Stop()
c.ws.Close()
}()
for {
...
}
}
select {
case message, ok := <-c.send:
if !ok {
c.write(websocket.OpClose, []byte{})
return
}
if err := c.write(websocket.OpText, message); err != nil {
return
}
case <-ticker.C:
if err := c.write(websocket.OpPing, []byte{}); err != nil {
return
}
}
func serveWs(w http.ResponseWriter, r *http.Request) {
ws, err := websocket.Upgrade(w, r.Header, nil, 1024, 1024)
if _, ok := err.(websocket.HandshakeError); ok {
http.Error(w, "Not a websocket handshake", 400)
return
} else if err != nil {
log.Println(err)
return
}
c := &connection{send: make(chan []byte, 256), ws: ws}
wsHub.register <- c
go c.writePump()
c.readPump()
}
type hub struct {
connections map[*connection]bool
broadcast chan []byte
register chan *connection
unregister chan *connection
}
var wsHub = hub{
broadcast: make(chan []byte),
register: make(chan *connection),
unregister: make(chan *connection),
connections: make(map[*connection]bool),
}
func (h *hub) run() {
for {
select {
case c := <-h.register:
h.connections[c] = true
case c := <-h.unregister:
delete(h.connections, c)
close(c.send)
case m := <-h.broadcast:
for c := range h.connections {
select {
case c.send <- m:
default:
close(c.send)
delete(h.connections, c)
var addr = flag.String("addr", ":8080", "Address to listen on")
var file = flag.String("file", "growup.html", "Template to load")
var temp *template.Template
var msgHistory = &history{
messages: make([][]byte, 0),
writer: make(chan []byte),
reader: make(chan chan []byte),
}
type TemplateData struct {
Host string
History []string
}
func serve(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
msgs := msgHistory.dump()
tempMsgs := make([]string, len(msgs))
for _, msg := range msgs {
tempMsgs = append(tempMsgs, string(msg))
}
data := TemplateData{Host: r.Host, History: tempMsgs}
err := temp.Execute(w, data)
if err != nil {
log.Println("temp.Execute: ", err)
return
}
}
func main() {
flag.Parse()
temp = template.Must(template.ParseFiles(*file))
go wsHub.run()
go msgHistory.listen()
http.HandleFunc("/", serve)
http.HandleFunc("/ws", serveWs)
err := http.ListenAndServe(*addr, nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}