Files
screego/ws/rooms.go
Jannis Mattheis a0f3c37498 fix: race condition
In client.go

    if room, ok := message.(outgoing.Room); ok {
            c.info.RoomID = room.ID
    }

this part isn't thread safe. It could happen that user disconnected but
wasn't removed from a room, because the disconnecting go routine
couldn't see the roomID yet.
2024-10-11 15:11:07 +02:00

126 lines
2.9 KiB
Go

package ws
import (
"fmt"
"math/rand"
"net/http"
"net/url"
"time"
"github.com/gorilla/websocket"
"github.com/rs/xid"
"github.com/rs/zerolog/log"
"github.com/screego/server/auth"
"github.com/screego/server/config"
"github.com/screego/server/turn"
"github.com/screego/server/util"
)
func NewRooms(tServer turn.Server, users *auth.Users, conf config.Config) *Rooms {
return &Rooms{
Rooms: map[string]*Room{},
Incoming: make(chan ClientMessage),
connected: map[xid.ID]string{},
turnServer: tServer,
users: users,
config: conf,
r: rand.New(rand.NewSource(time.Now().Unix())),
upgrader: websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
origin := r.Header.Get("origin")
u, err := url.Parse(origin)
if err != nil {
return false
}
if u.Host == r.Host {
return true
}
return conf.CheckOrigin(origin)
},
},
}
}
type Rooms struct {
turnServer turn.Server
Rooms map[string]*Room
Incoming chan ClientMessage
upgrader websocket.Upgrader
users *auth.Users
config config.Config
r *rand.Rand
connected map[xid.ID]string
}
func (r *Rooms) CurrentRoom(info ClientInfo) (*Room, error) {
roomID, ok := r.connected[info.ID]
if !ok {
return nil, fmt.Errorf("not connected")
}
if roomID == "" {
return nil, fmt.Errorf("not in a room")
}
room, ok := r.Rooms[roomID]
if !ok {
return nil, fmt.Errorf("room with id %s does not exist", roomID)
}
return room, nil
}
func (r *Rooms) RandUserName() string {
return util.NewUserName(r.r)
}
func (r *Rooms) RandRoomName() string {
return util.NewRoomName(r.r)
}
func (r *Rooms) Upgrade(w http.ResponseWriter, req *http.Request) {
conn, err := r.upgrader.Upgrade(w, req, nil)
if err != nil {
log.Debug().Err(err).Msg("Websocket upgrade")
w.WriteHeader(400)
_, _ = w.Write([]byte(fmt.Sprintf("Upgrade failed %s", err)))
return
}
user, loggedIn := r.users.CurrentUser(req)
c := newClient(conn, req, r.Incoming, user, loggedIn, r.config.TrustProxyHeaders)
r.Incoming <- ClientMessage{Info: c.info, Incoming: Connected{}, SkipConnectedCheck: true}
go c.startReading(time.Second * 20)
go c.startWriteHandler(time.Second * 5)
}
func (r *Rooms) Start() {
for msg := range r.Incoming {
_, connected := r.connected[msg.Info.ID]
if !msg.SkipConnectedCheck && !connected {
log.Debug().Interface("event", fmt.Sprintf("%T", msg.Incoming)).Interface("payload", msg.Incoming).Msg("WebSocket Ignore")
continue
}
if err := msg.Incoming.Execute(r, msg.Info); err != nil {
dis := Disconnected{Code: websocket.CloseNormalClosure, Reason: err.Error()}
dis.executeNoError(r, msg.Info)
}
}
}
func (r *Rooms) closeRoom(roomID string) {
room, ok := r.Rooms[roomID]
if !ok {
return
}
usersLeftTotal.Add(float64(len(room.Users)))
for id := range room.Sessions {
room.closeSession(r, id)
}
delete(r.Rooms, roomID)
roomsClosedTotal.Inc()
}