-
Notifications
You must be signed in to change notification settings - Fork 0
/
receiver.go
136 lines (117 loc) · 2.81 KB
/
receiver.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
package outbound
import (
"sync"
"time"
"github.com/v2ray/v2ray-core/common/dice"
v2net "github.com/v2ray/v2ray-core/common/net"
proto "github.com/v2ray/v2ray-core/common/protocol"
)
type Receiver struct {
sync.RWMutex
Destination v2net.Destination
Accounts []*proto.User
}
func NewReceiver(dest v2net.Destination, users ...*proto.User) *Receiver {
return &Receiver{
Destination: dest,
Accounts: users,
}
}
func (this *Receiver) HasUser(user *proto.User) bool {
this.RLock()
defer this.RUnlock()
for _, u := range this.Accounts {
// TODO: handle AlterIds difference.
if u.ID.Equals(user.ID) {
return true
}
}
return false
}
func (this *Receiver) AddUser(user *proto.User) {
if this.HasUser(user) {
return
}
this.Lock()
this.Accounts = append(this.Accounts, user)
this.Unlock()
}
func (this *Receiver) PickUser() *proto.User {
return this.Accounts[dice.Roll(len(this.Accounts))]
}
type ExpiringReceiver struct {
*Receiver
until time.Time
}
func (this *ExpiringReceiver) Expired() bool {
return this.until.Before(time.Now())
}
type ReceiverManager struct {
receivers []*Receiver
detours []*ExpiringReceiver
detourAccess sync.RWMutex
}
func NewReceiverManager(receivers []*Receiver) *ReceiverManager {
return &ReceiverManager{
receivers: receivers,
detours: make([]*ExpiringReceiver, 0, 16),
}
}
func (this *ReceiverManager) AddDetour(rec *Receiver, availableMin byte) {
if availableMin < 2 {
return
}
this.detourAccess.RLock()
destExists := false
for _, r := range this.detours {
if r.Destination == rec.Destination {
destExists = true
// Destination exists, add new user if necessary
for _, u := range rec.Accounts {
r.AddUser(u)
}
break
}
}
this.detourAccess.RUnlock()
if !destExists {
expRec := &ExpiringReceiver{
Receiver: rec,
until: time.Now().Add(time.Duration(availableMin-1) * time.Minute),
}
this.detourAccess.Lock()
this.detours = append(this.detours, expRec)
this.detourAccess.Unlock()
}
}
func (this *ReceiverManager) pickDetour() *Receiver {
if len(this.detours) == 0 {
return nil
}
this.detourAccess.RLock()
idx := dice.Roll(len(this.detours))
rec := this.detours[idx]
this.detourAccess.RUnlock()
if rec.Expired() {
this.detourAccess.Lock()
detourLen := len(this.detours)
if detourLen > idx && this.detours[idx].Expired() {
this.detours[idx] = this.detours[detourLen-1]
this.detours = this.detours[:detourLen-1]
}
this.detourAccess.Unlock()
return nil
}
return rec.Receiver
}
func (this *ReceiverManager) pickStdReceiver() *Receiver {
return this.receivers[dice.Roll(len(this.receivers))]
}
func (this *ReceiverManager) PickReceiver() (v2net.Destination, *proto.User) {
rec := this.pickDetour()
if rec == nil {
rec = this.pickStdReceiver()
}
user := rec.PickUser()
return rec.Destination, user
}