github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/integration/messagebus/glue/backend/sockets/websocket/socket.go (about) 1 /* 2 * Glue - Robust Go and Javascript Socket Library 3 * Copyright (C) 2015 Roland Singer <roland.singer[at]desertbit.com> 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 package websocket 20 21 import ( 22 "io" 23 "sync" 24 "time" 25 26 "github.com/mdaxf/iac/integration/messagebus/glue/backend/closer" 27 "github.com/mdaxf/iac/integration/messagebus/glue/backend/global" 28 "github.com/mdaxf/iac/integration/messagebus/glue/log" 29 30 "github.com/gorilla/websocket" 31 "github.com/sirupsen/logrus" 32 ) 33 34 //#################// 35 //### Constants ###// 36 //#################// 37 38 const ( 39 // Time allowed to write a message to the peer. 40 writeWait = 10 * time.Second 41 42 // Time allowed to read the next message from the peer. 43 readWait = 60 * time.Second 44 45 // Maximum message size allowed from peer. 46 maxMessageSize = 0 47 ) 48 49 //######################// 50 //### WebSocket type ###// 51 //######################// 52 53 type Socket struct { 54 ws *websocket.Conn 55 writeMutex sync.Mutex 56 57 closer *closer.Closer 58 59 writeChan chan string 60 readChan chan string 61 62 userAgent string 63 remoteAddrFunc func() string 64 } 65 66 // Create a new websocket value. 67 func newSocket(ws *websocket.Conn) *Socket { 68 w := &Socket{ 69 ws: ws, 70 writeChan: make(chan string, global.WriteChanSize), 71 readChan: make(chan string, global.ReadChanSize), 72 } 73 74 // Set the closer function. 75 w.closer = closer.New(func() { 76 // Send a close message to the client. 77 // Ignore errors. 78 w.write(websocket.CloseMessage, []byte{}) 79 80 // Close the socket. 81 w.ws.Close() 82 }) 83 84 return w 85 } 86 87 //############################################// 88 //### WebSocket - Interface implementation ###// 89 //############################################// 90 91 func (w *Socket) Type() global.SocketType { 92 return global.TypeWebSocket 93 } 94 95 func (w *Socket) RemoteAddr() string { 96 return w.remoteAddrFunc() 97 } 98 99 func (w *Socket) UserAgent() string { 100 return w.userAgent 101 } 102 103 func (w *Socket) Close() { 104 w.closer.Close() 105 } 106 107 func (w *Socket) IsClosed() bool { 108 return w.closer.IsClosed() 109 } 110 111 func (w *Socket) ClosedChan() <-chan struct{} { 112 return w.closer.IsClosedChan 113 } 114 115 func (w *Socket) WriteChan() chan string { 116 return w.writeChan 117 } 118 119 func (w *Socket) ReadChan() chan string { 120 return w.readChan 121 } 122 123 //###########################// 124 //### WebSocket - Private ###// 125 //###########################// 126 127 // readLoop reads messages from the websocket 128 func (w *Socket) readLoop() { 129 defer func() { 130 // Close the socket on defer. 131 w.Close() 132 }() 133 134 // Set the limits. 135 w.ws.SetReadLimit(maxMessageSize) 136 137 // Set the pong handler. 138 w.ws.SetPongHandler(func(string) error { 139 // Reset the read deadline. 140 w.ws.SetReadDeadline(time.Now().Add(readWait)) 141 return nil 142 }) 143 144 for { 145 // Reset the read deadline. 146 w.ws.SetReadDeadline(time.Now().Add(readWait)) 147 148 // Read from the websocket. 149 _, data, err := w.ws.ReadMessage() 150 if err != nil { 151 // Websocket close code. 152 wsCode := -1 // -1 for not set. 153 154 // Try to obtain the websocket close code if present. 155 // Assert to gorilla websocket CloseError type if possible. 156 if closeErr, ok := err.(*websocket.CloseError); ok { 157 wsCode = closeErr.Code 158 } 159 160 // Only log errors if this is not EOF and 161 // if the socket was not closed already. 162 // Also check the websocket close code. 163 if err != io.EOF && !w.IsClosed() && 164 wsCode != websocket.CloseNormalClosure && 165 wsCode != websocket.CloseGoingAway && 166 wsCode != websocket.CloseNoStatusReceived { 167 // Log 168 log.L.WithFields(logrus.Fields{ 169 "remoteAddress": w.RemoteAddr(), 170 "userAgent": w.UserAgent(), 171 }).Warningf("failed to read data from websocket: %v", err) 172 } 173 174 // Return and release this goroutine. 175 // This will close this socket connection. 176 return 177 } 178 179 // Write the received data to the read channel. 180 w.readChan <- string(data) 181 } 182 } 183 184 // write writes a message with the given message type and payload. 185 // This method is thread-safe. 186 func (w *Socket) write(mt int, payload []byte) error { 187 w.writeMutex.Lock() 188 defer w.writeMutex.Unlock() 189 190 w.ws.SetWriteDeadline(time.Now().Add(writeWait)) 191 return w.ws.WriteMessage(mt, payload) 192 } 193 194 func (w *Socket) writeLoop() { 195 for { 196 select { 197 case data := <-w.writeChan: 198 // Write the data to the websocket. 199 err := w.write(websocket.TextMessage, []byte(data)) 200 if err != nil { 201 log.L.WithFields(logrus.Fields{ 202 "remoteAddress": w.RemoteAddr(), 203 "userAgent": w.UserAgent(), 204 }).Warningf("failed to write to websocket: %v", err) 205 206 // Close the websocket on error. 207 w.Close() 208 return 209 } 210 211 case <-w.closer.IsClosedChan: 212 // Just release this loop. 213 return 214 } 215 } 216 }