Skip to content

Commit 1c38b0b

Browse files
committed
Merge branch 'channels'
2 parents e83263f + b99294e commit 1c38b0b

File tree

11 files changed

+598
-329
lines changed

11 files changed

+598
-329
lines changed

chat.go

Lines changed: 0 additions & 85 deletions
This file was deleted.

chat/client.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package chat
2+
3+
import (
4+
"encoding/json"
5+
"io"
6+
"net/http"
7+
"time"
8+
9+
"github.com/Sirupsen/logrus"
10+
"github.com/gorilla/websocket"
11+
"github.com/pkg/errors"
12+
"github.com/rmattam/go-websocket-chat-demo/redis"
13+
)
14+
15+
var (
16+
upgrader = websocket.Upgrader{
17+
ReadBufferSize: 1024,
18+
WriteBufferSize: 1024,
19+
}
20+
waitTimeout = time.Minute * 10
21+
log = logrus.WithField("cmd", "go-websocket-chat-demo-chat")
22+
)
23+
24+
//Hub struct
25+
type Hub struct {
26+
channel string
27+
28+
receive redis.Receiver
29+
write redis.Writer
30+
}
31+
32+
// message sent to us by the javascript client
33+
type message struct {
34+
Handle string `json:"handle"`
35+
Text string `json:"text"`
36+
}
37+
38+
// SubsribeRedis : Initialize the redis routines required for pub sub.
39+
func (c *Hub) SubsribeRedis(redisURL string, channel string) {
40+
redisPool, err := redis.NewRedisPoolFromURL(redisURL)
41+
if err != nil {
42+
log.WithField("url", redisURL).Fatal("Unable to create Redis pool")
43+
}
44+
c.channel = channel
45+
c.receive = redis.NewReceiver(redisPool, c.channel, ValidateRedisMessage)
46+
c.write = redis.NewWriter(redisPool, c.channel)
47+
go c.receive.Setup(redisURL)
48+
go c.write.Setup(redisURL)
49+
}
50+
51+
//ValidateRedisMessage validates incoming redis messages on the chat channel.
52+
func ValidateRedisMessage(data []byte) error {
53+
_, e := validateMessage(data)
54+
return e
55+
}
56+
57+
// validateMessage so that we know it's valid JSON and contains a Handle and
58+
// Text
59+
func validateMessage(data []byte) (message, error) {
60+
var msg message
61+
62+
if err := json.Unmarshal(data, &msg); err != nil {
63+
return msg, errors.Wrap(err, "Unmarshaling message")
64+
}
65+
66+
if msg.Handle == "" && msg.Text == "" {
67+
return msg, errors.New("Message has no Handle or Text")
68+
}
69+
70+
return msg, nil
71+
}
72+
73+
// HandleWebsocket connection.
74+
func HandleWebsocket(c *Hub, w http.ResponseWriter, r *http.Request) {
75+
if r.Method != "GET" {
76+
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
77+
return
78+
}
79+
80+
ws, err := upgrader.Upgrade(w, r, nil)
81+
defer func() {
82+
ws.Close()
83+
}()
84+
85+
if err != nil {
86+
m := "Unable to upgrade to websockets"
87+
log.WithField("err", err).Println(m)
88+
http.Error(w, m, http.StatusBadRequest)
89+
return
90+
}
91+
92+
c.receive.Register(ws)
93+
94+
for {
95+
mt, data, err := ws.ReadMessage()
96+
l := log.WithFields(logrus.Fields{"mt": mt, "err": err})
97+
if err != nil {
98+
if websocket.IsCloseError(err, websocket.CloseGoingAway) || err == io.EOF {
99+
l.Info("Websocket closed!")
100+
break
101+
}
102+
l.WithField("data", data).Error("Error reading websocket message")
103+
}
104+
switch mt {
105+
case websocket.TextMessage:
106+
msg, err := validateMessage(data)
107+
if err != nil {
108+
l.WithFields(logrus.Fields{"data": data, "msg": msg, "err": err}).Error("Invalid Message")
109+
break
110+
}
111+
l.WithField("msg", msg).Info("message from client")
112+
c.write.Publish(data)
113+
default:
114+
l.WithField("data", data).Warning("Unknown Message!")
115+
}
116+
}
117+
118+
c.receive.DeRegister(ws)
119+
ws.WriteMessage(websocket.CloseMessage, []byte{})
120+
}

main.go

Lines changed: 3 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,12 @@ package main
33
import (
44
"net/http"
55
"os"
6-
"time"
76

87
"github.com/Sirupsen/logrus"
9-
"github.com/heroku/x/redis"
108
)
119

1210
var (
13-
waitTimeout = time.Minute * 10
14-
log = logrus.WithField("cmd", "go-websocket-chat-demo")
15-
rr redisReceiver
16-
rw redisWriter
11+
log = logrus.WithField("cmd", "go-websocket-chat-demo")
1712
)
1813

1914
func main() {
@@ -26,44 +21,8 @@ func main() {
2621
if redisURL == "" {
2722
log.WithField("REDIS_URL", redisURL).Fatal("$REDIS_URL must be set")
2823
}
29-
redisPool, err := redis.NewRedisPoolFromURL(redisURL)
30-
if err != nil {
31-
log.WithField("url", redisURL).Fatal("Unable to create Redis pool")
32-
}
33-
34-
rr = newRedisReceiver(redisPool)
35-
rw = newRedisWriter(redisPool)
36-
37-
go func() {
38-
for {
39-
waited, err := redis.WaitForAvailability(redisURL, waitTimeout, rr.wait)
40-
if !waited || err != nil {
41-
log.WithFields(logrus.Fields{"waitTimeout": waitTimeout, "err": err}).Fatal("Redis not available by timeout!")
42-
}
43-
rr.broadcast(availableMessage)
44-
err = rr.run()
45-
if err == nil {
46-
break
47-
}
48-
log.Error(err)
49-
}
50-
}()
51-
52-
go func() {
53-
for {
54-
waited, err := redis.WaitForAvailability(redisURL, waitTimeout, nil)
55-
if !waited || err != nil {
56-
log.WithFields(logrus.Fields{"waitTimeout": waitTimeout, "err": err}).Fatal("Redis not available by timeout!")
57-
}
58-
err = rw.run()
59-
if err == nil {
60-
break
61-
}
62-
log.Error(err)
63-
}
64-
}()
6524

66-
http.Handle("/", http.FileServer(http.Dir("./public")))
67-
http.HandleFunc("/ws", handleWebsocket)
25+
router := NewRouter(redisURL)
26+
http.Handle("/", router)
6827
log.Println(http.ListenAndServe(":"+port, nil))
6928
}

public/css/application.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,8 @@
22
overflow: auto;
33
height: 500px;
44
}
5+
6+
#chat-text2 {
7+
overflow: auto;
8+
height: 500px;
9+
}

public/index.html

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,25 @@ <h1>Go Websocket Chat Demo</h1>
2424
<button class="btn btn-primary" type="submit">Send</button>
2525
</form>
2626
<div class="page-header">
27-
<h1>Chat Log</h1>
27+
<h1>Chat Log (Channel 1)</h1>
2828
</div>
2929
<div id="chat-text">
3030
</div>
31+
32+
<form id="input-form2" class="form-inline">
33+
<div class="form-group">
34+
<input id="input-handle2" type="text" class="form-control" placeholder="Enter handle" autofocus />
35+
</div>
36+
<div class="form-group">
37+
<input id="input-text2" type="text" class="form-control" placeholder="Enter chat text here!" autofocus />
38+
</div>
39+
<button class="btn btn-primary" type="submit">Send</button>
40+
</form>
41+
<div class="page-header">
42+
<h1>Chat Log (Channel 2)</h1>
43+
</div>
44+
<div id="chat-text2">
45+
</div>
3146
</div>
3247

3348
<a href="https://github.com/heroku-examples/go-websocket-chat-demo"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>

public/js/application.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
var box = new ReconnectingWebSocket(location.protocol.replace("http","ws") + "//" + location.host + "/ws");
1+
var box = new ReconnectingWebSocket(location.protocol.replace("http","ws") + "//" + location.host + "/ws/chat1");
22

33
box.onmessage = function(message) {
44
var data = JSON.parse(message.data);
@@ -21,3 +21,27 @@ $("#input-form").on("submit", function(event) {
2121
box.send(JSON.stringify({ handle: handle, text: text }));
2222
$("#input-text")[0].value = "";
2323
});
24+
25+
26+
var box2 = new ReconnectingWebSocket(location.protocol.replace("http","ws") + "//" + location.host + "/ws/chat2");
27+
box2.onmessage = function(message) {
28+
var data = JSON.parse(message.data);
29+
$("#chat-text2").append("<div class='panel panel-default'><div class='panel-heading'>" + $('<span/>').text(data.handle).html() + "</div><div class='panel-body'>" + $('<span/>').text(data.text).html() + "</div></div>");
30+
$("#chat-text2").stop().animate({
31+
scrollTop: $('#chat-text2')[0].scrollHeight
32+
}, 800);
33+
};
34+
35+
box2.onclose = function(){
36+
console.log('box2 closed');
37+
this.box2 = new WebSocket(box2.url);
38+
39+
};
40+
41+
$("#input-form2").on("submit", function(event) {
42+
event.preventDefault();
43+
var handle = $("#input-handle2")[0].value;
44+
var text = $("#input-text2")[0].value;
45+
box2.send(JSON.stringify({ handle: handle, text: text }));
46+
$("#input-text2")[0].value = "";
47+
});

0 commit comments

Comments
 (0)