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  }