github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/common/ws_user.go (about) 1 package common 2 3 import ( 4 "errors" 5 "sync" 6 "time" 7 8 "github.com/gorilla/websocket" 9 ) 10 11 var ErrNoneOnPage = errors.New("This user isn't on that page") 12 var ErrInvalidSocket = errors.New("That's not a valid WebSocket Connection") 13 14 type WSUser struct { 15 User *User 16 Sockets []*WSUserSocket 17 sync.Mutex 18 } 19 20 type WSUserSocket struct { 21 conn *websocket.Conn 22 Page string 23 } 24 25 func (u *WSUser) Ping() error { 26 var sockets []*WSUserSocket 27 var del int 28 func() { 29 u.Lock() 30 defer u.Unlock() 31 for i, s := range u.Sockets { 32 if s == nil || s.conn == nil { 33 del++ 34 u.Sockets[i] = u.Sockets[len(u.Sockets)-del] 35 continue 36 } 37 sockets = append(sockets, s) 38 } 39 }() 40 if del > 0 { 41 // TODO: Resize the capacity to release memory more eagerly? 42 u.Sockets = u.Sockets[:len(u.Sockets)-del] 43 } 44 45 for _, s := range sockets { 46 _ = s.conn.SetWriteDeadline(time.Now().Add(time.Minute)) 47 e := s.conn.WriteMessage(websocket.PingMessage, nil) 48 if e != nil { 49 s.conn.Close() 50 u.Lock() 51 s.conn = nil 52 u.Unlock() 53 } 54 } 55 56 return nil 57 } 58 59 func (u *WSUser) WriteAll(msg string) error { 60 msgbytes := []byte(msg) 61 for _, socket := range u.Sockets { 62 if socket == nil { 63 continue 64 } 65 w, e := socket.conn.NextWriter(websocket.TextMessage) 66 if e != nil { 67 return e 68 } 69 _, _ = w.Write(msgbytes) 70 w.Close() 71 } 72 return nil 73 } 74 75 func (u *WSUser) WriteToPage(msg, page string) error { 76 return u.WriteToPageBytes([]byte(msg), page) 77 } 78 79 // Inefficient as it looks for sockets for a page even if there are none 80 func (u *WSUser) WriteToPageBytes(msg []byte, page string) error { 81 var success bool 82 for _, socket := range u.Sockets { 83 if socket == nil { 84 continue 85 } 86 if socket.Page != page { 87 continue 88 } 89 w, e := socket.conn.NextWriter(websocket.TextMessage) 90 if e != nil { 91 continue // Skip dead sockets, a dedicated goroutine handles those 92 } 93 _, _ = w.Write(msg) 94 w.Close() 95 success = true 96 } 97 if !success { 98 return ErrNoneOnPage 99 } 100 return nil 101 } 102 103 // Inefficient as it looks for sockets for a page even if there are none 104 func (u *WSUser) WriteToPageBytesMulti(msgs [][]byte, page string) error { 105 var success bool 106 for _, socket := range u.Sockets { 107 if socket == nil { 108 continue 109 } 110 if socket.Page != page { 111 continue 112 } 113 w, e := socket.conn.NextWriter(websocket.TextMessage) 114 if e != nil { 115 continue // Skip dead sockets, a dedicated goroutine handles those 116 } 117 for _, msg := range msgs { 118 _, _ = w.Write(msg) 119 } 120 w.Close() 121 success = true 122 } 123 if !success { 124 return ErrNoneOnPage 125 } 126 return nil 127 } 128 129 func (u *WSUser) CountSockets() int { 130 u.Lock() 131 defer u.Unlock() 132 return len(u.Sockets) 133 } 134 135 func (u *WSUser) AddSocket(conn *websocket.Conn, page string) { 136 u.Lock() 137 // If the number of the sockets is small, then we can keep the size of the slice mostly static and just walk through it looking for empty slots 138 /*if len(u.Sockets) < 6 { 139 for i, socket := range u.Sockets { 140 if socket == nil { 141 u.Sockets[i] = &WSUserSocket{conn, page} 142 u.Unlock() 143 //fmt.Printf("%+v\n", u.Sockets) 144 return 145 } 146 } 147 }*/ 148 u.Sockets = append(u.Sockets, &WSUserSocket{conn, page}) 149 //fmt.Printf("%+v\n", u.Sockets) 150 u.Unlock() 151 } 152 153 func (u *WSUser) RemoveSocket(conn *websocket.Conn) { 154 var del int 155 u.Lock() 156 defer u.Unlock() 157 for i, socket := range u.Sockets { 158 if socket == nil || socket.conn == nil { 159 del++ 160 u.Sockets[i] = u.Sockets[len(u.Sockets)-del] 161 } else if socket.conn == conn { 162 del++ 163 u.Sockets[i] = u.Sockets[len(u.Sockets)-del] 164 //break 165 } 166 } 167 //Logf("%+v\n", u.Sockets) 168 //Log("del: ", del) 169 if del > 0 { 170 // TODO: Resize the capacity to release memory more eagerly? 171 u.Sockets = u.Sockets[:len(u.Sockets)-del] 172 } 173 //Logf("%+v\n", u.Sockets) 174 return 175 176 if len(u.Sockets) < 6 { 177 for i, socket := range u.Sockets { 178 if socket == nil { 179 continue 180 } 181 if socket.conn == conn { 182 u.Sockets[i] = nil 183 //fmt.Printf("%+v\n", wsUser.Sockets) 184 return 185 } 186 } 187 } 188 189 var key int 190 for i, socket := range u.Sockets { 191 if socket.conn == conn { 192 key = i 193 break 194 } 195 } 196 u.Sockets = append(u.Sockets[:key], u.Sockets[key+1:]...) 197 //fmt.Printf("%+v\n", u.Sockets) 198 } 199 200 func (u *WSUser) SetPageForSocket(conn *websocket.Conn, page string) error { 201 if conn == nil { 202 return ErrInvalidSocket 203 } 204 205 u.Lock() 206 for _, socket := range u.Sockets { 207 if socket == nil { 208 continue 209 } 210 if socket.conn == conn { 211 socket.Page = page 212 } 213 } 214 u.Unlock() 215 216 return nil 217 } 218 219 func (u *WSUser) InPage(page string) bool { 220 u.Lock() 221 defer u.Unlock() 222 for _, socket := range u.Sockets { 223 if socket == nil { 224 continue 225 } 226 if socket.Page == page { 227 return true 228 } 229 } 230 return false 231 } 232 233 func (u *WSUser) FinalizePage(page string, h func()) { 234 u.Lock() 235 defer u.Unlock() 236 for _, socket := range u.Sockets { 237 if socket == nil { 238 continue 239 } 240 if socket.Page == page { 241 return 242 } 243 } 244 h() 245 }