168 lines
4.6 KiB
Go
168 lines
4.6 KiB
Go
|
package socketio
|
||
|
|
||
|
import "sync"
|
||
|
|
||
|
// EachFunc typed for each callback function
|
||
|
type EachFunc func(Conn)
|
||
|
|
||
|
// Broadcast is the adaptor to handle broadcasts & rooms for socket.io server API
|
||
|
type Broadcast interface {
|
||
|
Join(room string, connection Conn) // Join causes the connection to join a room
|
||
|
Leave(room string, connection Conn) // Leave causes the connection to leave a room
|
||
|
LeaveAll(connection Conn) // LeaveAll causes given connection to leave all rooms
|
||
|
Clear(room string) // Clear causes removal of all connections from the room
|
||
|
Send(room, event string, args ...interface{}) // Send will send an event with args to the room
|
||
|
SendAll(event string, args ...interface{}) // SendAll will send an event with args to all the rooms
|
||
|
ForEach(room string, f EachFunc) // ForEach sends data by DataFunc, if room does not exits sends nothing
|
||
|
Len(room string) int // Len gives number of connections in the room
|
||
|
Rooms(connection Conn) []string // Gives list of all the rooms if no connection given, else list of all the rooms the connection joined
|
||
|
AllRooms() []string // Gives list of all the rooms the connection joined
|
||
|
}
|
||
|
|
||
|
// broadcast gives Join, Leave & BroadcastTO server API support to socket.io along with room management
|
||
|
// map of rooms where each room contains a map of connection id to connections in that room
|
||
|
type broadcast struct {
|
||
|
rooms map[string]map[string]Conn
|
||
|
|
||
|
lock sync.RWMutex
|
||
|
}
|
||
|
|
||
|
// newBroadcast creates a new broadcast adapter
|
||
|
func newBroadcast() *broadcast {
|
||
|
return &broadcast{
|
||
|
rooms: make(map[string]map[string]Conn),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Join joins the given connection to the broadcast room
|
||
|
func (bc *broadcast) Join(room string, connection Conn) {
|
||
|
bc.lock.Lock()
|
||
|
defer bc.lock.Unlock()
|
||
|
|
||
|
if _, ok := bc.rooms[room]; !ok {
|
||
|
bc.rooms[room] = make(map[string]Conn)
|
||
|
}
|
||
|
|
||
|
bc.rooms[room][connection.ID()] = connection
|
||
|
}
|
||
|
|
||
|
// Leave leaves the given connection from given room (if exist)
|
||
|
func (bc *broadcast) Leave(room string, connection Conn) {
|
||
|
bc.lock.Lock()
|
||
|
defer bc.lock.Unlock()
|
||
|
|
||
|
if connections, ok := bc.rooms[room]; ok {
|
||
|
delete(connections, connection.ID())
|
||
|
|
||
|
if len(connections) == 0 {
|
||
|
delete(bc.rooms, room)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// LeaveAll leaves the given connection from all rooms
|
||
|
func (bc *broadcast) LeaveAll(connection Conn) {
|
||
|
bc.lock.Lock()
|
||
|
defer bc.lock.Unlock()
|
||
|
|
||
|
for room, connections := range bc.rooms {
|
||
|
delete(connections, connection.ID())
|
||
|
|
||
|
if len(connections) == 0 {
|
||
|
delete(bc.rooms, room)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Clear clears the room
|
||
|
func (bc *broadcast) Clear(room string) {
|
||
|
bc.lock.Lock()
|
||
|
defer bc.lock.Unlock()
|
||
|
|
||
|
delete(bc.rooms, room)
|
||
|
}
|
||
|
|
||
|
// Send sends given event & args to all the connections in the specified room
|
||
|
func (bc *broadcast) Send(room, event string, args ...interface{}) {
|
||
|
bc.lock.RLock()
|
||
|
defer bc.lock.RUnlock()
|
||
|
|
||
|
for _, connection := range bc.rooms[room] {
|
||
|
connection.Emit(event, args...)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// SendAll sends given event & args to all the connections to all the rooms
|
||
|
func (bc *broadcast) SendAll(event string, args ...interface{}) {
|
||
|
bc.lock.RLock()
|
||
|
defer bc.lock.RUnlock()
|
||
|
|
||
|
for _, connections := range bc.rooms {
|
||
|
for _, connection := range connections {
|
||
|
connection.Emit(event, args...)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ForEach sends data returned by DataFunc, if room does not exits sends nothing
|
||
|
func (bc *broadcast) ForEach(room string, f EachFunc) {
|
||
|
bc.lock.RLock()
|
||
|
defer bc.lock.RUnlock()
|
||
|
|
||
|
occupants, ok := bc.rooms[room]
|
||
|
if !ok {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
for _, connection := range occupants {
|
||
|
f(connection)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Len gives number of connections in the room
|
||
|
func (bc *broadcast) Len(room string) int {
|
||
|
bc.lock.RLock()
|
||
|
defer bc.lock.RUnlock()
|
||
|
|
||
|
return len(bc.rooms[room])
|
||
|
}
|
||
|
|
||
|
// Rooms gives the list of all the rooms available for broadcast in case of
|
||
|
// no connection is given, in case of a connection is given, it gives
|
||
|
// list of all the rooms the connection is joined to
|
||
|
func (bc *broadcast) Rooms(connection Conn) []string {
|
||
|
if connection == nil {
|
||
|
return bc.AllRooms()
|
||
|
}
|
||
|
|
||
|
bc.lock.RLock()
|
||
|
defer bc.lock.RUnlock()
|
||
|
|
||
|
return bc.getRoomsByConn(connection)
|
||
|
}
|
||
|
|
||
|
// AllRooms gives list of all rooms available for broadcast
|
||
|
func (bc *broadcast) AllRooms() []string {
|
||
|
bc.lock.RLock()
|
||
|
defer bc.lock.RUnlock()
|
||
|
|
||
|
rooms := make([]string, 0, len(bc.rooms))
|
||
|
for room := range bc.rooms {
|
||
|
rooms = append(rooms, room)
|
||
|
}
|
||
|
|
||
|
return rooms
|
||
|
}
|
||
|
|
||
|
func (bc *broadcast) getRoomsByConn(connection Conn) []string {
|
||
|
var rooms []string
|
||
|
|
||
|
for room, connections := range bc.rooms {
|
||
|
if _, ok := connections[connection.ID()]; ok {
|
||
|
rooms = append(rooms, room)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return rooms
|
||
|
}
|