-
Notifications
You must be signed in to change notification settings - Fork 10
/
main.go
133 lines (112 loc) · 2.86 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package main
import (
"flag"
"fmt"
"html/template"
"log"
"net/http"
"os"
"path"
"time"
"github.com/gorilla/websocket"
"github.com/tv42/birpc"
"github.com/tv42/birpc/wetsock"
"github.com/tv42/topic"
)
var (
host = flag.String("host", "0.0.0.0", "IP address to bind to")
port = flag.Int("port", 8000, "TCP port to listen on")
)
var html *template.Template = template.New("main")
func init() {
template.Must(html.New("chat.html").Parse(string(chat_html)))
template.Must(html.New("chat.css").Parse(string(chat_css)))
template.Must(html.New("chat.js").Parse(string(chat_js)))
}
func Usage() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
flag.PrintDefaults()
}
type Incoming struct {
From string
Message string
}
type Outgoing struct {
Time time.Time `json:"time"`
From string `json:"from"`
Message string `json:"message"`
}
func index(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
err := html.ExecuteTemplate(w, "chat.html", nil)
if err != nil {
log.Printf("Template error: %v", err)
}
}
type Chat struct {
broadcast *topic.Topic
registry *birpc.Registry
}
type nothing struct{}
func (c *Chat) Message(msg *Incoming, _ *nothing, ws *websocket.Conn) error {
log.Printf("recv from %v:%#v\n", ws.RemoteAddr(), msg)
c.broadcast.Broadcast <- Outgoing{
Time: time.Now(),
From: msg.From,
Message: msg.Message,
}
return nil
}
func main() {
prog := path.Base(os.Args[0])
log.SetFlags(0)
log.SetPrefix(prog + ": ")
flag.Usage = Usage
flag.Parse()
if flag.NArg() > 0 {
Usage()
os.Exit(1)
}
log.Printf("Serving at http://%s:%d/", *host, *port)
chat := Chat{}
chat.broadcast = topic.New()
chat.registry = birpc.NewRegistry()
chat.registry.RegisterService(&chat)
defer close(chat.broadcast.Broadcast)
upgrader := websocket.Upgrader{}
serve := func(w http.ResponseWriter, req *http.Request) {
ws, err := upgrader.Upgrade(w, req, nil)
if err != nil {
log.Println(err)
return
}
endpoint := wetsock.NewEndpoint(chat.registry, ws)
messages := make(chan interface{}, 10)
chat.broadcast.Register(messages)
go func() {
defer chat.broadcast.Unregister(messages)
for i := range messages {
msg := i.(Outgoing)
// Fire-and-forget.
// TODO use .Notify when it exists
_ = endpoint.Go("Chat.Message", msg, nil, nil)
}
// broadcast topic kicked us out for being too slow;
// probably a hung TCP connection. let client
// re-establish.
log.Printf("Kicking slow client: %v", ws.RemoteAddr())
ws.Close()
}()
if err := endpoint.Serve(); err != nil {
log.Printf("websocket error from %v: %v", ws.RemoteAddr(), err)
}
}
http.HandleFunc("/sock", serve)
http.Handle("/", http.HandlerFunc(index))
addr := fmt.Sprintf("%s:%d", *host, *port)
err := http.ListenAndServe(addr, nil)
if err != nil {
log.Fatal(err)
}
}