This repository has been archived by the owner on May 1, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
/
sessionRegistry.go
151 lines (137 loc) · 4 KB
/
sessionRegistry.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package webwire
import (
"fmt"
"sync"
)
// sessionRegistry represents a thread safe registry
// of all currently active sessions
type sessionRegistry struct {
server *server
lock *sync.RWMutex
maxConns uint
registry map[string]map[*connection]struct{}
onSessionDestroy func(sessionKey string)
}
// newSessionRegistry returns a new instance of a session registry.
// maxConns defines the maximum number of concurrent connections
// for a single session while zero stands for unlimited
func newSessionRegistry(
maxConns uint,
onSessionDestroy func(sessionKey string),
) *sessionRegistry {
return &sessionRegistry{
lock: &sync.RWMutex{},
maxConns: maxConns,
registry: make(map[string]map[*connection]struct{}),
onSessionDestroy: onSessionDestroy,
}
}
// register registers a new connection for the given clients session.
// Returns an error if the given clients session already reached
// the maximum number of concurrent connections
func (asr *sessionRegistry) register(con *connection) error {
asr.lock.Lock()
if connSet, exists := asr.registry[con.session.Key]; exists {
// Ensure max connections isn't exceeded
if asr.maxConns > 0 && uint(len(connSet)+1) > asr.maxConns {
asr.lock.Unlock()
return fmt.Errorf(
"Max conns (%d) reached for session %s",
asr.maxConns,
con.session.Key,
)
}
// Overwrite the current entry incrementing the number of connections
connSet[con] = struct{}{}
asr.registry[con.session.Key] = connSet
asr.lock.Unlock()
return nil
}
newList := map[*connection]struct{}{
con: {},
}
asr.registry[con.session.Key] = newList
asr.lock.Unlock()
return nil
}
// deregister removes a connection from the list of connections of a session
// and returns the number of connections left.
// If there's only one connection left then the entire session will be removed
// from the register and 0 will be returned. If destroy is set to true then the
// session manager hook OnSessionClosed is executed destroying the session,
// otherwise the session will be deregistered but left untouched in the storage.
// If the given connection is not in the register -1 is returned
func (asr *sessionRegistry) deregister(
conn *connection,
destroy bool,
) int {
if conn.session == nil {
return -1
}
asr.lock.Lock()
if connSet, exists := asr.registry[conn.session.Key]; exists {
// If a single connection is left then remove or destroy the session
if len(connSet) < 2 {
delete(asr.registry, conn.session.Key)
asr.lock.Unlock()
// Destroy the session
if destroy {
// Recover potential user-space hook panics
// to avoid panicking the server
defer func() {
if recvErr := recover(); recvErr != nil {
asr.server.errorLog.Printf(
"session closure hook panic: %v",
recvErr,
)
}
}()
asr.onSessionDestroy(conn.session.Key)
}
return 0
}
// Find and remove the client from the connections list
delete(connSet, conn)
connSetLen := len(connSet)
asr.lock.Unlock()
return connSetLen
}
asr.lock.Unlock()
return -1
}
// activeSessionsNum returns the number of currently active sessions
func (asr *sessionRegistry) activeSessionsNum() int {
asr.lock.RLock()
registryLen := len(asr.registry)
asr.lock.RUnlock()
return registryLen
}
// sessionConnectionsNum implements the sessionRegistry interface
func (asr *sessionRegistry) sessionConnectionsNum(sessionKey string) int {
asr.lock.RLock()
if connSet, exists := asr.registry[sessionKey]; exists {
connSetLen := len(connSet)
asr.lock.RUnlock()
return connSetLen
}
asr.lock.RUnlock()
return -1
}
// sessionConnections implements the sessionRegistry interface
func (asr *sessionRegistry) sessionConnections(
sessionKey string,
) []Connection {
asr.lock.RLock()
if connSet, exists := asr.registry[sessionKey]; exists {
list := make([]Connection, len(connSet))
index := 0
for conn := range connSet {
list[index] = conn
index++
}
asr.lock.RUnlock()
return list
}
asr.lock.RUnlock()
return nil
}