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  }