Last active
September 11, 2022 05:37
-
-
Save percybolmer/30afe06964f954161c6eb74dc6077b0f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/json" | |
"log" | |
"github.com/gorilla/websocket" | |
) | |
// ClientList is a map used to help manage a map of clients | |
type ClientList map[*Client]bool | |
// Client is a websocket client, basically a frontend visitor | |
type Client struct { | |
// the websocket connection | |
connection *websocket.Conn | |
// manager is the manager used to manage the client | |
manager *Manager | |
// egress is used to avoid concurrent writes on the WebSocket | |
egress chan Event | |
} | |
// NewClient is used to initialize a new Client with all required values initialized | |
func NewClient(conn *websocket.Conn, manager *Manager) *Client { | |
return &Client{ | |
connection: conn, | |
manager: manager, | |
egress: make(chan Event), | |
} | |
} | |
// readMessages will start the client to read messages and handle them | |
// appropriatly. | |
// This is suppose to be ran as a goroutine | |
func (c *Client) readMessages() { | |
defer func() { | |
// Graceful Close the Connection once this | |
// function is done | |
c.manager.removeClient(c) | |
}() | |
// Loop Forever | |
for { | |
// ReadMessage is used to read the next message in queue | |
// in the connection | |
_, payload, err := c.connection.ReadMessage() | |
if err != nil { | |
// If Connection is closed, we will Recieve an error here | |
// We only want to log Strange errors, but simple Disconnection | |
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { | |
log.Printf("error reading message: %v", err) | |
} | |
break // Break the loop to close conn & Cleanup | |
} | |
// Marshal incoming data into a Event struct | |
var request Event | |
if err := json.Unmarshal(payload, &request); err != nil { | |
log.Printf("error marshalling message: %v", err) | |
break // Breaking the connection here might be harsh xD | |
} | |
// Route the Event | |
if err := c.manager.routeEvent(request, c); err != nil { | |
log.Println("Error handeling Message: ", err) | |
} | |
} | |
} | |
// writeMessages is a process that listens for new messages to output to the Client | |
func (c *Client) writeMessages() { | |
defer func() { | |
// Graceful close if this triggers a closing | |
c.manager.removeClient(c) | |
}() | |
for { | |
select { | |
case message, ok := <-c.egress: | |
// Ok will be false Incase the egress channel is closed | |
if !ok { | |
// Manager has closed this connection channel, so communicate that to frontend | |
if err := c.connection.WriteMessage(websocket.CloseMessage, nil); err != nil { | |
// Log that the connection is closed and the reason | |
log.Println("connection closed: ", err) | |
} | |
// Return to close the goroutine | |
return | |
} | |
data, err := json.Marshal(message) | |
if err != nil { | |
log.Println(err) | |
return // closes the connection, should we really | |
} | |
// Write a Regular text message to the connection | |
if err := c.connection.WriteMessage(websocket.TextMessage, data); err != nil { | |
log.Println(err) | |
} | |
log.Println("sent message") | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment