157 lines
3.5 KiB
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())
|
|
}
|