123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- package websocket
- import (
- "github.com/olahol/melody"
- "github.com/pkg/errors"
- "net/http"
- "sync"
- "time"
- )
- type HandleConnectFunc func(context map[string]any)
- type HandleDisconnectFunc func(context map[string]any)
- type HandleErrorFunc func(err error, context map[string]any)
- type HandleCloseFunc func(i int, s string, context map[string]any) error
- type HandlePongFunc func(context map[string]any)
- type HandleMessageFunc func(message []byte, context map[string]any)
- type BroadCastFilterCallback func(context map[string]any) bool
- var managerInstance *Manager
- func Init() {
- if managerInstance == nil {
- managerInstance = &Manager{
- melodyMapMutex: &sync.RWMutex{},
- melodyMap: make(map[string]*melody.Melody),
- }
- }
- }
- func Destroy() {
- if managerInstance != nil {
- managerInstance.melodyMapMutex.Lock()
- defer managerInstance.melodyMapMutex.Unlock()
- for _, melodyInstance := range managerInstance.melodyMap {
- err := melodyInstance.Close()
- if err != nil {
- panic(err)
- }
- }
- managerInstance.melodyMap = nil
- managerInstance = nil
- }
- managerInstance = nil
- }
- func GetInstance() *Manager {
- return managerInstance
- }
- type Manager struct {
- melodyMapMutex *sync.RWMutex
- melodyMap map[string]*melody.Melody
- }
- func (m *Manager) RegisterHub(groupID string, opts ...InitOption) {
- m.melodyMapMutex.Lock()
- defer m.melodyMapMutex.Unlock()
- _, ok := m.melodyMap[groupID]
- if ok {
- return
- }
- melodyInstance := melody.New()
- options := new(InitOptions)
- for _, opt := range opts {
- opt(options)
- }
- if options.writeWaitSec != 0 {
- melodyInstance.Config.WriteWait = time.Duration(options.writeWaitSec) * time.Second
- }
- if options.pongWaitSec != 0 {
- melodyInstance.Config.PongWait = time.Duration(options.pongWaitSec) * time.Second
- }
- if options.pingPeriodSec != 0 {
- melodyInstance.Config.PingPeriod = time.Duration(options.pingPeriodSec) * time.Second
- }
- if options.maxMessageSize != 0 {
- melodyInstance.Config.MaxMessageSize = options.maxMessageSize
- }
- if options.messageBufferSize != 0 {
- melodyInstance.Config.MessageBufferSize = options.messageBufferSize
- }
- melodyInstance.Config.ConcurrentMessageHandling = options.concurrentMessageHandling
- melodyInstance.Upgrader.CheckOrigin = func(r *http.Request) bool { return true }
- m.melodyMap[groupID] = melodyInstance
- }
- func (m *Manager) UnregisterHub(groupID string) {
- m.melodyMapMutex.Lock()
- defer m.melodyMapMutex.Unlock()
- melodyInstance, ok := m.melodyMap[groupID]
- if !ok {
- return
- }
- err := melodyInstance.Close()
- if err != nil {
- panic(err)
- }
- melodyInstance = nil
- delete(m.melodyMap, groupID)
- }
- func (m *Manager) HandleConnect(groupID string, handleConnectFunc HandleConnectFunc) {
- m.melodyMapMutex.RLock()
- defer m.melodyMapMutex.RUnlock()
- melodyInstance, ok := m.melodyMap[groupID]
- if !ok {
- return
- }
- melodyInstance.HandleConnect(func(session *melody.Session) {
- if handleConnectFunc != nil {
- handleConnectFunc(session.Keys)
- }
- })
- }
- func (m *Manager) HandleDisconnect(groupID string, handleDisconnectFunc HandleDisconnectFunc) {
- m.melodyMapMutex.Lock()
- defer m.melodyMapMutex.Unlock()
- melodyInstance, ok := m.melodyMap[groupID]
- if !ok {
- return
- }
- melodyInstance.HandleDisconnect(func(session *melody.Session) {
- if handleDisconnectFunc != nil {
- handleDisconnectFunc(session.Keys)
- }
- })
- }
- func (m *Manager) HandleError(groupID string, handleErrorFunc HandleErrorFunc) {
- m.melodyMapMutex.RLock()
- defer m.melodyMapMutex.RUnlock()
- melodyInstance, ok := m.melodyMap[groupID]
- if !ok {
- return
- }
- melodyInstance.HandleError(func(session *melody.Session, err error) {
- if handleErrorFunc != nil {
- handleErrorFunc(err, session.Keys)
- }
- })
- }
- func (m *Manager) HandleClose(groupID string, handleCloseFunc HandleCloseFunc) {
- m.melodyMapMutex.RLock()
- defer m.melodyMapMutex.RUnlock()
- melodyInstance, ok := m.melodyMap[groupID]
- if !ok {
- return
- }
- melodyInstance.HandleClose(func(session *melody.Session, i int, s string) error {
- if handleCloseFunc != nil {
- err := handleCloseFunc(i, s, session.Keys)
- if err != nil {
- return err
- }
- }
- return nil
- })
- }
- func (m *Manager) HandlePong(groupID string, handlePongFunc HandlePongFunc) {
- m.melodyMapMutex.RLock()
- defer m.melodyMapMutex.RUnlock()
- melodyInstance, ok := m.melodyMap[groupID]
- if !ok {
- return
- }
- melodyInstance.HandlePong(func(session *melody.Session) {
- if handlePongFunc != nil {
- handlePongFunc(session.Keys)
- }
- })
- }
- func (m *Manager) HandleRequest(groupID string, w http.ResponseWriter, r *http.Request, opts ...ConnectionOption) error {
- m.melodyMapMutex.RLock()
- defer m.melodyMapMutex.RUnlock()
- melodyInstance, ok := m.melodyMap[groupID]
- if !ok {
- return errors.New("groupID尚未注册")
- }
- sessionMap := make(map[string]any)
- for _, opt := range opts {
- opt(&sessionMap)
- }
- err := melodyInstance.HandleRequestWithKeys(w, r, sessionMap)
- if err != nil {
- return err
- }
- return nil
- }
- func (m *Manager) HandleMessage(groupID string, handleMessageFunc HandleMessageFunc) error {
- m.melodyMapMutex.RLock()
- defer m.melodyMapMutex.RUnlock()
- melodyInstance, ok := m.melodyMap[groupID]
- if !ok {
- return errors.New("groupID尚未注册")
- }
- melodyInstance.HandleMessage(func(session *melody.Session, bytes []byte) {
- if handleMessageFunc != nil {
- handleMessageFunc(bytes, session.Keys)
- }
- })
- return nil
- }
- func (m *Manager) BroadCast(groupID string, msg []byte) error {
- m.melodyMapMutex.RLock()
- defer m.melodyMapMutex.RUnlock()
- melodyInstance, ok := m.melodyMap[groupID]
- if !ok {
- return errors.New("groupID尚未注册")
- }
- return melodyInstance.Broadcast(msg)
- }
- func (m *Manager) BroadCastFilter(groupID string, msg []byte, filterCallback BroadCastFilterCallback) error {
- m.melodyMapMutex.RLock()
- defer m.melodyMapMutex.RUnlock()
- melodyInstance, ok := m.melodyMap[groupID]
- if !ok {
- return errors.New("groupID尚未注册")
- }
- return melodyInstance.BroadcastFilter(msg, func(session *melody.Session) bool {
- return filterCallback(session.Keys)
- })
- }
- type InitOption func(*InitOptions)
- type InitOptions struct {
- writeWaitSec int64
- pongWaitSec int64
- pingPeriodSec int64
- maxMessageSize int64
- messageBufferSize int
- concurrentMessageHandling bool
- }
- func InitWithWriteWaitSec(writeWaitSec int64) InitOption {
- return func(options *InitOptions) {
- options.writeWaitSec = writeWaitSec
- }
- }
- func InitWithPongWaitSec(pongWaitSec int64) InitOption {
- return func(options *InitOptions) {
- options.pongWaitSec = pongWaitSec
- }
- }
- func InitWithPingPeriodSec(pingPeriodSec int64) InitOption {
- return func(options *InitOptions) {
- options.pingPeriodSec = pingPeriodSec
- }
- }
- func InitWithMaxMessageSize(maxMessageSize int64) InitOption {
- return func(options *InitOptions) {
- options.maxMessageSize = maxMessageSize
- }
- }
- func InitWithMaxMessageBufferSize(messageBufferSize int) InitOption {
- return func(options *InitOptions) {
- options.messageBufferSize = messageBufferSize
- }
- }
- func InitWithConcurrentMessageHandling(concurrentMessageHandling bool) InitOption {
- return func(options *InitOptions) {
- options.concurrentMessageHandling = concurrentMessageHandling
- }
- }
- type ConnectionOption func(sessionMap *map[string]any)
- func WithConnectionContext(context map[string]any) ConnectionOption {
- return func(sessionMap *map[string]any) {
- *sessionMap = context
- }
- }
|