|
@@ -1,10 +1,9 @@
|
|
|
package handlers
|
|
package handlers
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
|
|
+ "encoding/json"
|
|
|
"log"
|
|
"log"
|
|
|
"net/http"
|
|
"net/http"
|
|
|
- "sync"
|
|
|
|
|
- "time"
|
|
|
|
|
|
|
|
|
|
"github.com/gorilla/websocket"
|
|
"github.com/gorilla/websocket"
|
|
|
)
|
|
)
|
|
@@ -15,24 +14,86 @@ type VisitorDot struct {
|
|
|
Lng float64 `json:"lng"`
|
|
Lng float64 `json:"lng"`
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+type visitorsPayload struct {
|
|
|
|
|
+ Visitors []VisitorDot `json:"visitors"`
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
type WSHub struct {
|
|
type WSHub struct {
|
|
|
- mu sync.RWMutex
|
|
|
|
|
- clients map[*websocket.Conn]VisitorDot
|
|
|
|
|
|
|
+ clients map[*Client]bool
|
|
|
|
|
+ register chan *Client
|
|
|
|
|
+ unregister chan *Client
|
|
|
|
|
+ broadcast chan []byte
|
|
|
allowedOrigins []string
|
|
allowedOrigins []string
|
|
|
isProd bool
|
|
isProd bool
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func NewWSHub(allowedOrigins []string, isProd bool) *WSHub {
|
|
func NewWSHub(allowedOrigins []string, isProd bool) *WSHub {
|
|
|
- if isProd && len(allowedOrigins) == 0 {
|
|
|
|
|
- log.Println("WARN: WebSocket AllowedOrigins is empty in production — connections will be rejected")
|
|
|
|
|
- }
|
|
|
|
|
return &WSHub{
|
|
return &WSHub{
|
|
|
- clients: make(map[*websocket.Conn]VisitorDot),
|
|
|
|
|
|
|
+ clients: make(map[*Client]bool),
|
|
|
|
|
+ register: make(chan *Client),
|
|
|
|
|
+ unregister: make(chan *Client),
|
|
|
|
|
+ broadcast: make(chan []byte, 256),
|
|
|
allowedOrigins: allowedOrigins,
|
|
allowedOrigins: allowedOrigins,
|
|
|
isProd: isProd,
|
|
isProd: isProd,
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+func (h *WSHub) Run() {
|
|
|
|
|
+ for {
|
|
|
|
|
+ select {
|
|
|
|
|
+ case client := <-h.register:
|
|
|
|
|
+ h.clients[client] = true
|
|
|
|
|
+ h.broadcastVisitors()
|
|
|
|
|
+
|
|
|
|
|
+ case client := <-h.unregister:
|
|
|
|
|
+ if _, ok := h.clients[client]; ok {
|
|
|
|
|
+ delete(h.clients, client)
|
|
|
|
|
+ close(client.send)
|
|
|
|
|
+ h.broadcastVisitors()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ case message := <-h.broadcast:
|
|
|
|
|
+ for client := range h.clients {
|
|
|
|
|
+ select {
|
|
|
|
|
+ case client.send <- message:
|
|
|
|
|
+ default:
|
|
|
|
|
+ close(client.send)
|
|
|
|
|
+ delete(h.clients, client)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (h *WSHub) broadcastVisitors() {
|
|
|
|
|
+ visitors := make([]VisitorDot, 0, len(h.clients))
|
|
|
|
|
+ for c := range h.clients {
|
|
|
|
|
+ visitors = append(visitors, VisitorDot{
|
|
|
|
|
+ UserID: c.userID,
|
|
|
|
|
+ Lat: c.lat,
|
|
|
|
|
+ Lng: c.lng,
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ data, err := json.Marshal(visitorsPayload{Visitors: visitors})
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ msg, err := json.Marshal(wsMessage{Type: "visitors", Data: data})
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for client := range h.clients {
|
|
|
|
|
+ select {
|
|
|
|
|
+ case client.send <- msg:
|
|
|
|
|
+ default:
|
|
|
|
|
+ close(client.send)
|
|
|
|
|
+ delete(h.clients, client)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func (h *WSHub) HandleWS(w http.ResponseWriter, r *http.Request) {
|
|
func (h *WSHub) HandleWS(w http.ResponseWriter, r *http.Request) {
|
|
|
upgrader := websocket.Upgrader{
|
|
upgrader := websocket.Upgrader{
|
|
|
CheckOrigin: func(r *http.Request) bool {
|
|
CheckOrigin: func(r *http.Request) bool {
|
|
@@ -55,45 +116,14 @@ func (h *WSHub) HandleWS(w http.ResponseWriter, r *http.Request) {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- dot := VisitorDot{}
|
|
|
|
|
-
|
|
|
|
|
- h.mu.Lock()
|
|
|
|
|
- h.clients[conn] = dot
|
|
|
|
|
- h.mu.Unlock()
|
|
|
|
|
-
|
|
|
|
|
- for {
|
|
|
|
|
- var msg VisitorDot
|
|
|
|
|
- if err := conn.ReadJSON(&msg); err != nil {
|
|
|
|
|
- break
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- h.mu.Lock()
|
|
|
|
|
- h.clients[conn] = msg
|
|
|
|
|
- visitors := make([]VisitorDot, 0, len(h.clients))
|
|
|
|
|
- for _, v := range h.clients {
|
|
|
|
|
- visitors = append(visitors, v)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- var failed []*websocket.Conn
|
|
|
|
|
- for c := range h.clients {
|
|
|
|
|
- if err := c.SetWriteDeadline(time.Now().Add(10 * time.Second)); err != nil {
|
|
|
|
|
- failed = append(failed, c)
|
|
|
|
|
- continue
|
|
|
|
|
- }
|
|
|
|
|
- if err := c.WriteJSON(map[string]interface{}{"visitors": visitors}); err != nil {
|
|
|
|
|
- failed = append(failed, c)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- for _, c := range failed {
|
|
|
|
|
- c.WriteMessage(websocket.CloseMessage, []byte{})
|
|
|
|
|
- c.Close()
|
|
|
|
|
- delete(h.clients, c)
|
|
|
|
|
- }
|
|
|
|
|
- h.mu.Unlock()
|
|
|
|
|
|
|
+ client := &Client{
|
|
|
|
|
+ hub: h,
|
|
|
|
|
+ conn: conn,
|
|
|
|
|
+ send: make(chan []byte, 256),
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- h.mu.Lock()
|
|
|
|
|
- delete(h.clients, conn)
|
|
|
|
|
- h.mu.Unlock()
|
|
|
|
|
- conn.Close()
|
|
|
|
|
|
|
+ h.register <- client
|
|
|
|
|
+
|
|
|
|
|
+ go client.writePump()
|
|
|
|
|
+ go client.readPump()
|
|
|
}
|
|
}
|