@@ -8,31 +8,78 @@ import (
8
8
9
9
type Stream struct {
10
10
clients list.List
11
+ listLock sync.RWMutex
11
12
broadcast chan * Event
12
13
shutdownWait sync.WaitGroup
13
14
clientConnectHook func (* http.Request , * Client )
14
15
}
15
16
17
+ type registeredClient struct {
18
+ c * Client
19
+ topics map [string ]bool
20
+ }
21
+
16
22
func New () * Stream {
17
23
s := & Stream {}
18
24
go s .run ()
19
25
return s
20
26
}
21
27
28
+ // Register adds a client to the stream to receive all broadcast
29
+ // messages. Has no effect if the client is already registered.
22
30
func (s * Stream ) Register (c * Client ) {
23
31
32
+ // see if the client has been registered
33
+ if cli := s .getClient (c ); cli != nil {
34
+ return
35
+ }
36
+
37
+ // append new client
38
+ s .addClient (c )
24
39
}
25
40
41
+ // Broadcast sends the event to all clients registered on this stream.
26
42
func (s * Stream ) Broadcast (e * Event ) {
27
43
44
+ for element := s .clients .Front (); element != nil ; element .Next () {
45
+ cli := element .Value .(* registeredClient )
46
+ cli .c .Send (e )
47
+ }
28
48
}
29
49
50
+ // Subscribe add the client to the list of clients receiving publications
51
+ // to this topic. Subscribe will also Register an unregistered
52
+ // client.
30
53
func (s * Stream ) Subscribe (topic string , c * Client ) {
31
54
55
+ // see if the client is registered
56
+ cli := s .getClient (c )
57
+
58
+ // register if not
59
+ if cli == nil {
60
+ cli = s .addClient (c )
61
+ }
62
+
63
+ cli .topics [topic ] = true
32
64
}
33
65
34
- func (s * Stream ) Publish (topic string , e * Event ) {
66
+ // Unsubscribe removes clients from the topic, but not from broadcasts.
67
+ func (s * Stream ) Unsubscribe (topic string , c * Client ) {
68
+ cli := s .getClient (c )
69
+ if cli == nil {
70
+ return
71
+ }
72
+ cli .topics [topic ] = false
73
+ }
35
74
75
+ // Publish sends the event to clients that have subscribed to the given topic.
76
+ func (s * Stream ) Publish (topic string , e * Event ) {
77
+ for element := s .clients .Front (); element != nil ; element .Next () {
78
+ cli := element .Value .(* registeredClient )
79
+ if cli .topics [topic ] {
80
+ cli .c .Send (e )
81
+ }
82
+ }
36
83
}
37
84
38
85
func (s * Stream ) Shutdown () {
@@ -81,3 +128,30 @@ func (s *Stream) run() {
81
128
func (s * Stream ) sendAll (ev * Event ) {
82
129
83
130
}
131
+
132
+ func (s * Stream ) getClient (c * Client ) * registeredClient {
133
+ if s .clients .Len () > 0 {
134
+ // ensure client is not already registered
135
+ s .listLock .RLock ()
136
+
137
+ listItem := s .clients .Front ()
138
+ if regCli := listItem .Value .(* registeredClient ); regCli .c == c {
139
+ // client found
140
+ s .listLock .RUnlock ()
141
+ return regCli
142
+ }
143
+ }
144
+
145
+ return nil
146
+ }
147
+
148
+ func (s * Stream ) addClient (c * Client ) * registeredClient {
149
+ s .listLock .Lock ()
150
+
151
+ s .clients .PushBack (& registeredClient {
152
+ c : c ,
153
+ topics : make (map [string ]bool ),
154
+ })
155
+
156
+ s .listLock .Unlock ()
157
+ }
0 commit comments