mchess-server/connection/type.go

157 lines
3.5 KiB
Go

package connection
import (
"context"
"log"
"mchess_server/types"
"sync"
"github.com/google/uuid"
gorillaws "github.com/gorilla/websocket"
)
type Connection struct {
ID uuid.UUID
ws *gorillaws.Conn
ctx context.Context
rxBuffer *MessageBuffer
txBuffer *MessageBuffer
disconnectCallback func()
forColor types.ChessColor
/* this is a hack since a user using the same
browser might get the same object of type Connection in two instances of
the same browser. Then if Close() gets called for both of these instances
the first Close() will set ws=nil, and the seconds one will dereference this
nil pointer. The correct way to fix this, is to make sure, two instances
of the same browser do not get the same connection instance.
At the moment, the usage of localStorage 'identifies' an already connected
player and reuses the old Connection struct instance */
closingMutex sync.Mutex
}
func NewConnection(options ...func(*Connection)) *Connection {
connection := Connection{
ID: uuid.New(),
rxBuffer: newMessageBuffer(100),
txBuffer: newMessageBuffer(100),
}
for _, option := range options {
option(&connection)
}
return &connection
}
func WithWebsocket(ws *gorillaws.Conn) func(*Connection) {
return func(c *Connection) {
c.ws = ws
}
}
func WithContext(ctx context.Context) func(*Connection) {
return func(c *Connection) {
c.ctx = ctx
}
}
func WithDisconnectCallback(cb func()) func(*Connection) {
return func(c *Connection) {
if cb != nil {
c.disconnectCallback = cb
}
}
}
func (conn *Connection) SetForColor(color types.ChessColor) {
conn.forColor = color
}
func (conn *Connection) SetDisconnectCallback(cb func()) {
conn.disconnectCallback = cb
}
func (conn *Connection) HasWebsocketConnection() bool {
return conn.ws != nil
}
func (conn *Connection) readFromRxBuffer() {
for {
_, msg, err := conn.ws.ReadMessage()
if err != nil {
conn.logConnection("while reading from websocket: %w", "ERROR:", err.Error())
conn.Close("")
return
}
conn.rxBuffer.Insert(string(msg))
}
}
func (conn *Connection) writeTxBuffer() {
for {
msg := conn.txBuffer.Get()
if conn.ws == nil {
return
}
err := conn.ws.WriteMessage(gorillaws.TextMessage, []byte(msg))
if err != nil {
conn.logConnection("while writing to websocket: %w", "ERROR:", err.Error())
return
}
}
}
func (conn *Connection) SetWebsocketConnection(ws *gorillaws.Conn) {
if ws == nil {
conn.logConnection("ERROR: setting ws = null")
return
}
conn.ws = ws
go conn.readFromRxBuffer()
go conn.writeTxBuffer()
defer conn.logConnection("websocket connection set")
}
func (conn *Connection) Write(msg string) {
conn.logConnection("Writing message: ", string(msg))
conn.txBuffer.Insert(msg)
}
func (conn *Connection) Read() []byte {
msg := conn.rxBuffer.Get()
return []byte(msg)
}
func (conn *Connection) Close(msg string) {
/* This is a hack, do not try this at home.
See more details at the definition of Connection */
conn.closingMutex.Lock()
defer conn.closingMutex.Unlock()
if conn == nil || conn.ws == nil {
return
}
conn.logConnection("closing websocket connection")
conn.ws.WriteMessage(gorillaws.TextMessage, []byte(msg))
conn.ws.Close()
conn.ws = nil
conn.txBuffer.Insert("we do this to make txBuffer.Get() return")
}
func (con *Connection) logConnection(v ...string) {
a := ""
for _, s := range v {
a += s + ", "
}
log.Println(a, "on connection ", con.ID, ", for color ", con.forColor.String())
}