github.com/wgh-/mattermost-server@v4.8.0-rc2+incompatible/model/websocket_client.go (about) 1 // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package model 5 6 import ( 7 "encoding/json" 8 "net/http" 9 10 "github.com/gorilla/websocket" 11 ) 12 13 const ( 14 SOCKET_MAX_MESSAGE_SIZE_KB = 8 * 1024 // 8KB 15 ) 16 17 type WebSocketClient struct { 18 Url string // The location of the server like "ws://localhost:8065" 19 ApiUrl string // The api location of the server like "ws://localhost:8065/api/v3" 20 ConnectUrl string // The websocket URL to connect to like "ws://localhost:8065/api/v3/path/to/websocket" 21 Conn *websocket.Conn // The WebSocket connection 22 AuthToken string // The token used to open the WebSocket 23 Sequence int64 // The ever-incrementing sequence attached to each WebSocket action 24 EventChannel chan *WebSocketEvent 25 ResponseChannel chan *WebSocketResponse 26 ListenError *AppError 27 } 28 29 // NewWebSocketClient constructs a new WebSocket client with convienence 30 // methods for talking to the server. 31 func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) { 32 conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX_V3+"/users/websocket", nil) 33 if err != nil { 34 return nil, NewAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error(), http.StatusInternalServerError) 35 } 36 37 client := &WebSocketClient{ 38 url, 39 url + API_URL_SUFFIX_V3, 40 url + API_URL_SUFFIX_V3 + "/users/websocket", 41 conn, 42 authToken, 43 1, 44 make(chan *WebSocketEvent, 100), 45 make(chan *WebSocketResponse, 100), 46 nil, 47 } 48 49 client.SendMessage(WEBSOCKET_AUTHENTICATION_CHALLENGE, map[string]interface{}{"token": authToken}) 50 51 return client, nil 52 } 53 54 // NewWebSocketClient4 constructs a new WebSocket client with convienence 55 // methods for talking to the server. Uses the v4 endpoint. 56 func NewWebSocketClient4(url, authToken string) (*WebSocketClient, *AppError) { 57 conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX+"/websocket", nil) 58 if err != nil { 59 return nil, NewAppError("NewWebSocketClient4", "model.websocket_client.connect_fail.app_error", nil, err.Error(), http.StatusInternalServerError) 60 } 61 62 client := &WebSocketClient{ 63 url, 64 url + API_URL_SUFFIX, 65 url + API_URL_SUFFIX + "/websocket", 66 conn, 67 authToken, 68 1, 69 make(chan *WebSocketEvent, 100), 70 make(chan *WebSocketResponse, 100), 71 nil, 72 } 73 74 client.SendMessage(WEBSOCKET_AUTHENTICATION_CHALLENGE, map[string]interface{}{"token": authToken}) 75 76 return client, nil 77 } 78 79 func (wsc *WebSocketClient) Connect() *AppError { 80 var err error 81 wsc.Conn, _, err = websocket.DefaultDialer.Dial(wsc.ConnectUrl, nil) 82 if err != nil { 83 return NewAppError("Connect", "model.websocket_client.connect_fail.app_error", nil, err.Error(), http.StatusInternalServerError) 84 } 85 86 wsc.EventChannel = make(chan *WebSocketEvent, 100) 87 wsc.ResponseChannel = make(chan *WebSocketResponse, 100) 88 89 wsc.SendMessage(WEBSOCKET_AUTHENTICATION_CHALLENGE, map[string]interface{}{"token": wsc.AuthToken}) 90 91 return nil 92 } 93 94 func (wsc *WebSocketClient) Close() { 95 wsc.Conn.Close() 96 } 97 98 func (wsc *WebSocketClient) Listen() { 99 go func() { 100 defer func() { 101 wsc.Conn.Close() 102 close(wsc.EventChannel) 103 close(wsc.ResponseChannel) 104 }() 105 106 for { 107 var rawMsg json.RawMessage 108 var err error 109 if _, rawMsg, err = wsc.Conn.ReadMessage(); err != nil { 110 if !websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseNoStatusReceived) { 111 wsc.ListenError = NewAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error(), http.StatusInternalServerError) 112 } 113 114 return 115 } 116 117 var event WebSocketEvent 118 if err := json.Unmarshal(rawMsg, &event); err == nil && event.IsValid() { 119 wsc.EventChannel <- &event 120 continue 121 } 122 123 var response WebSocketResponse 124 if err := json.Unmarshal(rawMsg, &response); err == nil && response.IsValid() { 125 wsc.ResponseChannel <- &response 126 continue 127 } 128 129 } 130 }() 131 } 132 133 func (wsc *WebSocketClient) SendMessage(action string, data map[string]interface{}) { 134 req := &WebSocketRequest{} 135 req.Seq = wsc.Sequence 136 req.Action = action 137 req.Data = data 138 139 wsc.Sequence++ 140 141 wsc.Conn.WriteJSON(req) 142 } 143 144 // UserTyping will push a user_typing event out to all connected users 145 // who are in the specified channel 146 func (wsc *WebSocketClient) UserTyping(channelId, parentId string) { 147 data := map[string]interface{}{ 148 "channel_id": channelId, 149 "parent_id": parentId, 150 } 151 152 wsc.SendMessage("user_typing", data) 153 } 154 155 // GetStatuses will return a map of string statuses using user id as the key 156 func (wsc *WebSocketClient) GetStatuses() { 157 wsc.SendMessage("get_statuses", nil) 158 } 159 160 // GetStatusesByIds will fetch certain user statuses based on ids and return 161 // a map of string statuses using user id as the key 162 func (wsc *WebSocketClient) GetStatusesByIds(userIds []string) { 163 data := map[string]interface{}{ 164 "user_ids": userIds, 165 } 166 wsc.SendMessage("get_statuses_by_ids", data) 167 }