2023-09-16 04:33:52 +02:00
|
|
|
package streaming
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/oklog/ulid/v2"
|
|
|
|
)
|
|
|
|
|
2023-10-15 17:08:55 +02:00
|
|
|
type SocketHolder struct {
|
2023-09-16 04:33:52 +02:00
|
|
|
// map of sockets to
|
|
|
|
sockets map[ulid.ULID]*userSockets
|
|
|
|
mu sync.Mutex
|
|
|
|
}
|
|
|
|
|
2023-10-15 21:39:50 +02:00
|
|
|
func NewSocketHolder() *SocketHolder {
|
|
|
|
return &SocketHolder{
|
|
|
|
sockets: make(map[ulid.ULID]*userSockets),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-15 17:08:55 +02:00
|
|
|
func (sh *SocketHolder) Send(acctID ulid.ULID, et EventType, data any) {
|
|
|
|
userSockets := sh.SocketsFor(acctID)
|
2023-09-16 04:33:52 +02:00
|
|
|
|
|
|
|
userSockets.mu.Lock()
|
2023-10-15 17:08:55 +02:00
|
|
|
sockets := make([]*Socket, len(userSockets.sockets))
|
2023-09-16 04:33:52 +02:00
|
|
|
copy(sockets, userSockets.sockets)
|
|
|
|
userSockets.mu.Unlock()
|
|
|
|
|
|
|
|
for _, s := range sockets {
|
2023-10-15 21:39:50 +02:00
|
|
|
if s.WillAcceptEvent(et) {
|
2023-09-16 04:33:52 +02:00
|
|
|
// the socket might block for a bit, so spin this off into a separate goroutine
|
2023-10-15 17:08:55 +02:00
|
|
|
go func(s *Socket) {
|
2023-09-16 04:33:52 +02:00
|
|
|
s.ch <- Event{Type: et, Data: data}
|
|
|
|
}(s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-15 17:08:55 +02:00
|
|
|
func (s *SocketHolder) SocketsFor(acct ulid.ULID) *userSockets {
|
2023-09-16 04:33:52 +02:00
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
|
|
us, ok := s.sockets[acct]
|
|
|
|
if !ok {
|
|
|
|
us = &userSockets{}
|
|
|
|
s.sockets[acct] = us
|
|
|
|
}
|
|
|
|
return us
|
|
|
|
}
|
|
|
|
|
|
|
|
const sessionCountLimit = 50 // no more than 50 concurrent sessions per user
|
|
|
|
|
|
|
|
type userSockets struct {
|
|
|
|
mu sync.Mutex
|
2023-10-15 17:08:55 +02:00
|
|
|
sockets []*Socket
|
2023-09-16 04:33:52 +02:00
|
|
|
}
|
|
|
|
|
2023-10-15 17:08:55 +02:00
|
|
|
func (s *userSockets) NewSocket(ctx context.Context, cancel context.CancelFunc) (*Socket, bool) {
|
2023-09-16 04:33:52 +02:00
|
|
|
s.mu.Lock()
|
2023-10-15 17:08:55 +02:00
|
|
|
defer s.mu.Unlock()
|
2023-09-16 04:33:52 +02:00
|
|
|
if len(s.sockets) >= sessionCountLimit {
|
|
|
|
return nil, false
|
|
|
|
}
|
2023-10-15 17:08:55 +02:00
|
|
|
socket := NewSocket(ctx, cancel)
|
2023-09-16 04:33:52 +02:00
|
|
|
s.sockets = append(s.sockets, socket)
|
|
|
|
return socket, true
|
|
|
|
}
|
|
|
|
|
2023-10-15 17:08:55 +02:00
|
|
|
type Socket struct {
|
2023-09-16 04:33:52 +02:00
|
|
|
ctx context.Context
|
|
|
|
cancel context.CancelFunc
|
|
|
|
|
|
|
|
ch chan Event
|
|
|
|
types map[EventType]struct{}
|
|
|
|
|
|
|
|
mu sync.RWMutex
|
|
|
|
}
|
|
|
|
|
2023-10-15 21:39:50 +02:00
|
|
|
func (s *Socket) WillAcceptEvent(mt EventType) bool {
|
2023-09-16 04:33:52 +02:00
|
|
|
if mt == EventTypeError {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
s.mu.RLock()
|
|
|
|
_, ok := s.types[mt]
|
|
|
|
s.mu.RUnlock()
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
2023-10-15 17:08:55 +02:00
|
|
|
func (s *Socket) SetEvent(mt EventType, add bool) {
|
2023-09-16 04:33:52 +02:00
|
|
|
s.mu.Lock()
|
|
|
|
if add {
|
|
|
|
s.types[mt] = struct{}{}
|
|
|
|
} else {
|
|
|
|
delete(s.types, mt)
|
|
|
|
}
|
|
|
|
s.mu.Unlock()
|
|
|
|
}
|
|
|
|
|
2023-10-15 17:08:55 +02:00
|
|
|
func (s *Socket) Cancel() {
|
|
|
|
s.cancel()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Socket) Done() <-chan struct{} {
|
|
|
|
return s.ctx.Done()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Socket) Chan() <-chan Event {
|
|
|
|
return s.ch
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewSocket(ctx context.Context, cancel context.CancelFunc) *Socket {
|
|
|
|
return &Socket{
|
2023-09-16 04:33:52 +02:00
|
|
|
ctx: ctx,
|
|
|
|
cancel: cancel,
|
|
|
|
ch: make(chan Event),
|
2023-10-15 21:39:50 +02:00
|
|
|
types: map[EventType]struct{}{
|
|
|
|
EventTypeEcho: {},
|
|
|
|
},
|
2023-09-16 04:33:52 +02:00
|
|
|
}
|
|
|
|
}
|