-
Notifications
You must be signed in to change notification settings - Fork 44
Expand file tree
/
Copy pathmist.go
More file actions
142 lines (116 loc) · 3.37 KB
/
Copy pathmist.go
File metadata and controls
142 lines (116 loc) · 3.37 KB
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
134
135
136
137
138
139
140
141
142
// Package mist ...
package mist
import (
"fmt"
"strings"
"sync"
"github.com/jcelliott/lumber"
)
var (
mutex = &sync.RWMutex{}
subscribers = make(map[uint32]*Proxy)
uid uint32
)
type (
// A Message contains the tags used when subscribing, and the data that is being
// published through mist
Message struct {
Command string `json:"command"`
Tags []string `json:"tags,omitempty"`
Data string `json:"data,omitempty"`
Error string `json:"error,omitempty"`
}
// HandleFunc ...
HandleFunc func(*Proxy, Message) error
)
// Subscribers is listall related
func Subscribers() string {
subs := make(map[string]bool) // no duplicates
// get tags all clients subscribed to
for i := range subscribers {
s := subscribers[i].subscriptions.ToSlice()
for j := range s {
for k := range s[j] {
subs[s[j][k]] = true
}
}
}
// slice it
subSlice := []string{}
for k := range subs {
subSlice = append(subSlice, k)
}
return strings.Join(subSlice, " ")
}
// Who is who related
func Who() (int, int) {
// subs := make(map[string]bool) // no duplicates
subs := []string{}
// get tags all clients subscribed to
for i := range subscribers {
subs = append(subs, fmt.Sprint(subscribers[i].id))
}
return len(subs), int(uid)
}
// todo: delete these 2. limiting what is a subscriber makes this not needed
// if they subscribe to a thing on a reused connection, they wanted to get updates.. hopefully
// Publish publishes to ALL subscribers. Usefull in client applications
// who reuse the publish connection for subscribing (publishes to self)
func Publish(tags []string, data string) error {
lumber.Trace("Publishing...")
return publish(0, tags, data)
}
// publish publishes to all subscribers except the one who issued the publish
func publish(pid uint32, tags []string, data string) error {
if len(tags) == 0 {
return fmt.Errorf("Failed to publish. Missing tags")
}
// if there are no subscribers, the message goes nowhere
//
// this could be more optimized, but it might not be an issue unless thousands
// of clients are using mist.
go func() {
mutex.RLock()
for _, subscriber := range subscribers {
select {
case <-subscriber.done:
lumber.Trace("Subscriber done")
// do nothing?
default:
// dont send this message to the publisher who just sent it
if subscriber.id == pid {
lumber.Trace("Subscriber is publisher, skipping publish")
continue
}
// create message
msg := Message{Command: "publish", Tags: tags, Data: data}
// we don't want this operation blocking the range of other subscribers
// waiting to get messages
go func(p *Proxy, msg Message) {
p.check <- msg
lumber.Trace("Published message")
}(subscriber, msg)
}
}
mutex.RUnlock()
}()
return nil
}
// subscribe adds a proxy to the list of mist subscribers; we need this so that
// we can lock this process incase multiple proxies are subscribing at the same
// time
func subscribe(p *Proxy) {
lumber.Trace("Adding proxy to subscribers...")
mutex.Lock()
subscribers[p.id] = p
mutex.Unlock()
}
// unsubscribe removes a proxy from the list of mist subscribers; we need this
// so that we can lock this process incase multiple proxies are unsubscribing at
// the same time
func unsubscribe(pid uint32) {
lumber.Trace("Removing proxy from subscribers...")
mutex.Lock()
delete(subscribers, pid)
mutex.Unlock()
}