mirror of
https://github.com/skidoodle/spotify-ws
synced 2025-10-09 05:22:43 +02:00
83 lines
2.2 KiB
Go
83 lines
2.2 KiB
Go
package websocket
|
|
|
|
import (
|
|
"log/slog"
|
|
"sync"
|
|
"time"
|
|
|
|
"golang.org/x/net/websocket"
|
|
)
|
|
|
|
const (
|
|
writeWait = 10 * time.Second
|
|
pongWait = 60 * time.Second
|
|
)
|
|
|
|
// Client is a middleman between the websocket connection and the hub.
|
|
type Client struct {
|
|
hub *Hub
|
|
conn *websocket.Conn
|
|
send chan []byte
|
|
closeOnce sync.Once
|
|
}
|
|
|
|
// close is a thread-safe method to clean up the client's resources.
|
|
// It ensures that the unregister and connection close operations happen exactly once.
|
|
func (c *Client) close() {
|
|
c.closeOnce.Do(func() {
|
|
slog.Debug("closing client connection", "remoteAddr", c.conn.RemoteAddr())
|
|
c.hub.unregister <- c
|
|
if err := c.conn.Close(); err != nil {
|
|
// This error is expected if the other end has already hung up.
|
|
slog.Debug("error while closing client connection", "error", err, "remoteAddr", c.conn.RemoteAddr())
|
|
}
|
|
})
|
|
}
|
|
|
|
// readPump is responsible for detecting a dead connection via read deadlines.
|
|
func (c *Client) readPump() {
|
|
defer c.close()
|
|
|
|
if err := c.conn.SetReadDeadline(time.Now().Add(pongWait)); err != nil {
|
|
slog.Warn("failed to set initial read deadline", "error", err, "remoteAddr", c.conn.RemoteAddr())
|
|
return
|
|
}
|
|
|
|
var msg string
|
|
for {
|
|
if err := websocket.Message.Receive(c.conn, &msg); err != nil {
|
|
slog.Debug("client read error, triggering disconnect", "error", err, "remoteAddr", c.conn.RemoteAddr())
|
|
break
|
|
}
|
|
if err := c.conn.SetReadDeadline(time.Now().Add(pongWait)); err != nil {
|
|
slog.Warn("failed to reset read deadline", "error", err, "remoteAddr", c.conn.RemoteAddr())
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// writePump pumps messages from the hub to the websocket connection.
|
|
func (c *Client) writePump() {
|
|
defer c.close()
|
|
|
|
for {
|
|
select {
|
|
case message, ok := <-c.send:
|
|
if !ok {
|
|
slog.Debug("hub closed channel, closing connection", "remoteAddr", c.conn.RemoteAddr())
|
|
return
|
|
}
|
|
|
|
if err := c.conn.SetWriteDeadline(time.Now().Add(writeWait)); err != nil {
|
|
slog.Warn("failed to set write deadline", "error", err, "remoteAddr", c.conn.RemoteAddr())
|
|
return
|
|
}
|
|
|
|
if err := websocket.Message.Send(c.conn, string(message)); err != nil {
|
|
slog.Warn("client write error", "error", err, "remoteAddr", c.conn.RemoteAddr())
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|