Skip to content

Commit 1bac031

Browse files
committed
First draft
1 parent da3f44f commit 1bac031

File tree

9 files changed

+503
-2
lines changed

9 files changed

+503
-2
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.idea/

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
# websocket-command-handler
2-
Standardized websocket command handling in Go
1+
# websocketserver
2+
Websocketserver helps to organize code for handling different formats of websocket messages sent by clients.

docker-compose.yml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
version: '3.7'
2+
3+
services:
4+
test:
5+
image: golang:1.16
6+
volumes:
7+
- .:/code
8+
working_dir: /code
9+
command: go test

examples/chat-client/main.go

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"github.com/gorilla/websocket"
7+
"github.com/ndthuan/websocketserver"
8+
"os"
9+
)
10+
11+
func main() {
12+
c, _, err := websocket.DefaultDialer.Dial("ws://localhost:8989/ws", nil)
13+
if err != nil {
14+
panic(err)
15+
}
16+
defer c.Close()
17+
18+
err = c.WriteJSON(websocketserver.Message{
19+
Type: "login",
20+
Payload: fmt.Sprintf("PID %d", os.Getpid()),
21+
})
22+
23+
if err != nil {
24+
panic(err)
25+
}
26+
27+
quit := make(chan int)
28+
29+
go func() {
30+
var msg websocketserver.Message
31+
32+
for {
33+
err := c.ReadJSON(&msg)
34+
35+
if err != nil {
36+
if _, ok := err.(*websocket.CloseError); ok {
37+
println("Server disconnected")
38+
}
39+
40+
quit <- 0
41+
break
42+
}
43+
44+
println(fmt.Sprintf("%v", msg))
45+
}
46+
}()
47+
48+
go func() {
49+
s := bufio.NewScanner(os.Stdin)
50+
51+
for {
52+
if !s.Scan() {
53+
break
54+
}
55+
56+
input := s.Text()
57+
58+
switch input {
59+
case "bye":
60+
c.WriteJSON(websocketserver.Message{
61+
Type: "logout",
62+
Payload: "Gotta go",
63+
})
64+
65+
quit <- 0
66+
break
67+
default:
68+
c.WriteJSON(websocketserver.Message{
69+
Type: "human-message",
70+
Payload: input,
71+
})
72+
}
73+
}
74+
}()
75+
76+
select {
77+
case <-quit:
78+
return
79+
}
80+
}

examples/chat-server/main.go

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"github.com/gorilla/websocket"
6+
"github.com/labstack/echo/v4"
7+
"github.com/labstack/echo/v4/middleware"
8+
"github.com/ndthuan/websocketserver"
9+
"math/rand"
10+
"time"
11+
)
12+
13+
// This map is not thread safe, it's for demonstration purpose only. You should implement your own lock mechanism.
14+
var authenticatedConnections map[*websocket.Conn]string
15+
16+
func init() {
17+
authenticatedConnections = make(map[*websocket.Conn]string)
18+
}
19+
20+
func broadcast(msg string, excluded *websocket.Conn) {
21+
for conn := range authenticatedConnections {
22+
if conn == excluded {
23+
continue
24+
}
25+
_ = conn.WriteJSON(websocketserver.Message{
26+
Type: "system-broadcast",
27+
Payload: msg,
28+
})
29+
}
30+
}
31+
32+
func loginHandler() websocketserver.MessageHandler {
33+
return func(connection *websocket.Conn, listener *websocketserver.Server, message websocketserver.Message) error {
34+
username := message.Payload
35+
36+
authenticatedConnections[connection] = username
37+
38+
broadcast(fmt.Sprintf("Everyone, please welcome %s to our chat room!", username), connection)
39+
40+
return nil
41+
}
42+
}
43+
44+
func logoutHandler() websocketserver.MessageHandler {
45+
return func(connection *websocket.Conn, listener *websocketserver.Server, message websocketserver.Message) error {
46+
username, loggedIn := authenticatedConnections[connection]
47+
48+
if loggedIn {
49+
delete(authenticatedConnections, connection)
50+
broadcast(fmt.Sprintf("%s left the room", username), connection)
51+
_ = connection.WriteMessage(websocket.CloseMessage, []byte{})
52+
}
53+
54+
return nil
55+
}
56+
}
57+
58+
func disconnectedCallback() websocketserver.ConnectionCallback {
59+
return func(connection *websocket.Conn, listener *websocketserver.Server) error {
60+
loggedInUser, already := authenticatedConnections[connection]
61+
if already {
62+
broadcast(fmt.Sprintf("%s disconnected", loggedInUser), connection)
63+
delete(authenticatedConnections, connection)
64+
}
65+
return nil
66+
}
67+
}
68+
69+
func connectedCallback() websocketserver.ConnectionCallback {
70+
return func(connection *websocket.Conn, listener *websocketserver.Server) error {
71+
return connection.WriteJSON(websocketserver.Message{
72+
Type: "system-message",
73+
Payload: "Hi there, this is private message to you. Welcome!",
74+
})
75+
}
76+
}
77+
78+
func humanMessageHandler() websocketserver.MessageHandler {
79+
return func(connection *websocket.Conn, listener *websocketserver.Server, message websocketserver.Message) error {
80+
username, ok := authenticatedConnections[connection]
81+
82+
if ok {
83+
broadcast(fmt.Sprintf("%s: %s", username, message.Payload), connection)
84+
}
85+
86+
return nil
87+
}
88+
}
89+
90+
func standaloneRunner() websocketserver.StandaloneRunner {
91+
return func(server *websocketserver.Server) {
92+
for {
93+
time.Sleep(time.Duration(rand.Intn(10)) * time.Second)
94+
95+
server.Broadcast(func(conn *websocket.Conn) (*websocketserver.Message, bool) {
96+
if username, loggedIn := authenticatedConnections[conn]; loggedIn {
97+
now, _ := time.Now().MarshalText()
98+
99+
msg := &websocketserver.Message{
100+
Type: "system-broadcast",
101+
Payload: fmt.Sprintf("Hi %s, now is %s, how are you?", username, now),
102+
}
103+
104+
return msg, true
105+
}
106+
107+
return nil, false
108+
})
109+
}
110+
}
111+
}
112+
113+
func main() {
114+
wsServer := websocketserver.Server{
115+
Upgrader: websocket.Upgrader{},
116+
}
117+
118+
wsServer.OnConnected(connectedCallback())
119+
wsServer.OnDisconnected(disconnectedCallback())
120+
wsServer.SetStandaloneRunner(standaloneRunner())
121+
wsServer.On("login", loginHandler())
122+
wsServer.On("logout", logoutHandler())
123+
wsServer.On("human-message", humanMessageHandler())
124+
125+
e := echo.New()
126+
e.Use(middleware.Logger())
127+
e.GET("/ws", func(c echo.Context) error {
128+
return wsServer.Start(c.Response(), c.Request(), nil)
129+
})
130+
131+
e.Logger.Fatal(e.Start(":8989"))
132+
}

go.mod

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module github.com/ndthuan/websocketserver
2+
3+
go 1.15
4+
5+
require (
6+
github.com/gorilla/websocket v1.4.2
7+
github.com/labstack/echo/v4 v4.2.0
8+
github.com/stretchr/testify v1.7.0
9+
)

go.sum

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
2+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
4+
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
5+
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
6+
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
7+
github.com/labstack/echo v1.4.4 h1:1bEiBNeGSUKxcPDGfZ/7IgdhJJZx8wV/pICJh4W2NJI=
8+
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
9+
github.com/labstack/echo/v4 v4.2.0 h1:jkCSsjXmBmapVXF6U4BrSz/cgofWM0CU3Q74wQvXkIc=
10+
github.com/labstack/echo/v4 v4.2.0/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
11+
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
12+
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
13+
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
14+
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
15+
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
16+
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
17+
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
18+
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
19+
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
20+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
21+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
22+
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
23+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
24+
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
25+
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
26+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
27+
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
28+
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
29+
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
30+
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
31+
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
32+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
33+
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
34+
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
35+
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
36+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
37+
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
38+
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
39+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
40+
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
41+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
42+
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
43+
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
44+
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
45+
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
46+
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 h1:DvY3Zkh7KabQE/kfzMvYvKirSiguP9Q/veMtkYyf0o8=
47+
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
48+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
49+
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
50+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
51+
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
52+
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
53+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
54+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
55+
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
56+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
57+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)