diff --git a/controller/login.go b/controller/login.go index d18a47b..094bbc8 100644 --- a/controller/login.go +++ b/controller/login.go @@ -22,12 +22,12 @@ func Login(w http.ResponseWriter, r *http.Request) { // login success user, err := opId.ValidateAndGetUser(config.STEAM_API_KEY) if err != nil { - utils.APIErrorRespond(w, utils.ErrorResponse{http.StatusInternalServerError, err.Error()}) + utils.APIErrorRespond(w, utils.NewAPIError(http.StatusInternalServerError, err.Error())) return } if err = CreateSteamUser(user); err != nil { - utils.APIErrorRespond(w, utils.ErrorResponse{http.StatusInternalServerError, err.Error()}) + utils.APIErrorRespond(w, utils.NewAPIError(http.StatusInternalServerError, err.Error())) return } diff --git a/controller/player.go b/controller/player.go index f71ac21..6c9f643 100644 --- a/controller/player.go +++ b/controller/player.go @@ -15,7 +15,7 @@ func GetPlayerStats(w http.ResponseWriter, r *http.Request) { player, err := model.GetPlayerStats(mux.Vars(r)["steamid"]) if err != nil { - utils.APIErrorRespond(w, utils.ErrorResponse{Code: http.StatusNotFound, ErrorMsg: err.Error()}) + utils.APIErrorRespond(w, utils.NewAPIError(http.StatusNotFound, err.Error())) return } diff --git a/controller/steam_user.go b/controller/steam_user.go index f5160e0..e517ccb 100644 --- a/controller/steam_user.go +++ b/controller/steam_user.go @@ -18,7 +18,7 @@ func GetSteamUser(w http.ResponseWriter, r *http.Request) { player, err := model.GetSteamUser(mux.Vars(r)["steamid"]) if err != nil { - utils.APIErrorRespond(w, utils.ErrorResponse{Code: http.StatusNotFound, ErrorMsg: err.Error()}) + utils.APIErrorRespond(w, utils.NewAPIError(http.StatusNotFound, err.Error())) return } @@ -39,13 +39,13 @@ func UpdateSteamUser(w http.ResponseWriter, r *http.Request) { exists, err := model.DoesSteamUserExist(steamID) if err != nil { - utils.APIErrorRespond(w, utils.ErrorResponse{Code: http.StatusInternalServerError, ErrorMsg: err.Error()}) + utils.APIErrorRespond(w, utils.NewAPIError(http.StatusInternalServerError, err.Error())) return } updatedUser, err := steam_go.GetPlayerSummaries(steamID, config.STEAM_API_KEY) if err != nil { - utils.APIErrorRespond(w, utils.ErrorResponse{Code: http.StatusInternalServerError, ErrorMsg: err.Error()}) + utils.APIErrorRespond(w, utils.NewAPIError(http.StatusInternalServerError, err.Error())) return } @@ -58,7 +58,7 @@ func UpdateSteamUser(w http.ResponseWriter, r *http.Request) { } if result != nil { - utils.APIErrorRespond(w, utils.ErrorResponse{Code: http.StatusInternalServerError, ErrorMsg: err.Error()}) + utils.APIErrorRespond(w, utils.NewAPIError(http.StatusInternalServerError, err.Error())) return } diff --git a/main.go b/main.go index b997896..9df240e 100644 --- a/main.go +++ b/main.go @@ -38,11 +38,11 @@ func setupRouter() { // player stats router.HandleFunc("/api/playerstats/{steamid}", controller.GetPlayerStats).Methods("GET") - // create rooms and run them (TODO: database integration) + // create rooms and run them (TODO: database rooms integration) for _, uid := range []uint{1, 2, 3, 4, 5} { newRoom := websocket.NewRoom(uid) roomRoute := fmt.Sprintf("/chat/%d", uid) - router.HandleFunc(roomRoute, newRoom.ServeHTTP) + router.HandleFunc(roomRoute, newRoom.HandleWebsocket) go newRoom.Run() } } diff --git a/utils/utils.go b/utils/utils.go index a1bece0..bc20706 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -14,6 +14,13 @@ type ErrorResponse struct { ErrorMsg string } +func NewAPIError(c int, m string) ErrorResponse { + return ErrorResponse{ + Code: c, + ErrorMsg: m, + } +} + // Error response in JSON format func APIErrorRespond(w http.ResponseWriter, res ErrorResponse) { w.Header().Set("Content-Type", "application/json") diff --git a/websocket/client.go b/websocket/client.go index b350ffa..7f1e7f6 100644 --- a/websocket/client.go +++ b/websocket/client.go @@ -4,8 +4,17 @@ import "github.com/gorilla/websocket" type Client struct { socket *websocket.Conn - send chan []byte room *Room + send chan []byte +} + +// Create new client +func newClient(s *websocket.Conn, r *Room) *Client { + return &Client{ + socket: s, + room: r, + send: make(chan []byte, messageBufferSize), + } } // Read client messages (from frontend) diff --git a/websocket/room.go b/websocket/room.go index 57f5566..2f9bd9c 100644 --- a/websocket/room.go +++ b/websocket/room.go @@ -7,63 +7,37 @@ import ( "github.com/gorilla/websocket" ) -const ( - socketBufferSize = 1024 - messageBufferSize = 256 -) - // Room represents a single chat room type Room struct { id uint + clients map[*Client]bool forward chan []byte join chan *Client leave chan *Client - clients map[*Client]bool } +const ( + socketBufferSize = 1024 + messageBufferSize = 256 +) + var upgrader = &websocket.Upgrader{ ReadBufferSize: socketBufferSize, WriteBufferSize: socketBufferSize, CheckOrigin: func(r *http.Request) bool { + // TODO: change origin to frontend return true }, } -func (room *Room) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // create socket to client - socket, err := upgrader.Upgrade(w, r, nil) - if err != nil { - return - } - - // create new user - client := &Client{ - socket: socket, - send: make(chan []byte, messageBufferSize), - room: room, - } - - // join the room - room.join <- client - - // executed at end of this function - defer func() { - room.leave <- client - }() - - // run write and read function in 2 separate goroutines - go client.write() - client.read() -} - // Create a new chat room -func NewRoom(uid uint) *Room { +func NewRoom(id uint) *Room { return &Room{ + id: id, + clients: make(map[*Client]bool), forward: make(chan []byte), join: make(chan *Client), leave: make(chan *Client), - clients: make(map[*Client]bool), - id: uid, } } @@ -72,23 +46,12 @@ func (r *Room) Run() { log.Printf("running chat room %d", r.id) for { select { - case Client := <-r.join: - r.joinRoom(Client) - case Client := <-r.leave: - r.leaveRoom(Client) + case client := <-r.join: + r.joinRoom(client) + case client := <-r.leave: + r.leaveRoom(client) case msg := <-r.forward: - data := FromJSON(msg) - log.Printf("Client '%v' writing message to room %v, message: %v", data.Sender, r.id, data.Message) - - // broadcast message to all - for client := range r.clients { - select { - case client.send <- msg: - default: - delete(r.clients, client) - close(client.send) - } - } + r.printToChatAll(msg) } } } @@ -105,3 +68,20 @@ func (r *Room) leaveRoom(c *Client) { delete(r.clients, c) close(c.send) } + +// Print message to all in the current room +func (r *Room) printToChatAll(msg []byte) { + data := FromJSON(msg) + log.Printf("[room %v] %v: %v", r.id, data.Sender, data.Message) + + for client := range r.clients { + select { + case client.send <- msg: + log.Println("client.send <- msg") + default: + // not sure if this is possible + delete(r.clients, client) + close(client.send) + } + } +} diff --git a/websocket/server.go b/websocket/server.go index 708bc8c..f20d0e2 100644 --- a/websocket/server.go +++ b/websocket/server.go @@ -1 +1,27 @@ package websocket + +import "net/http" + +// /api/rooms/{room_id} - create a client websocket, associating it with a room +func (room *Room) HandleWebsocket(w http.ResponseWriter, r *http.Request) { + // create client socket + socket, err := upgrader.Upgrade(w, r, nil) + if err != nil { + return + } + + // create new client associated to a room + client := newClient(socket, room) + + // join the room + room.join <- client + + // executed at end of this function (after goroutines stop) + defer func() { + room.leave <- client + }() + + // run write and read in 2 separate goroutines + go client.write() + client.read() +}