1// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2// See LICENSE.txt for license information. 3 4package app 5 6import ( 7 "net/http" 8 9 "github.com/mattermost/mattermost-server/v6/model" 10 "github.com/mattermost/mattermost-server/v6/shared/i18n" 11 "github.com/mattermost/mattermost-server/v6/shared/mlog" 12) 13 14type webSocketHandler interface { 15 ServeWebSocket(*WebConn, *model.WebSocketRequest) 16} 17 18type WebSocketRouter struct { 19 app *App 20 handlers map[string]webSocketHandler 21} 22 23func (wr *WebSocketRouter) Handle(action string, handler webSocketHandler) { 24 wr.handlers[action] = handler 25} 26 27func (wr *WebSocketRouter) ServeWebSocket(conn *WebConn, r *model.WebSocketRequest) { 28 if r.Action == "" { 29 err := model.NewAppError("ServeWebSocket", "api.web_socket_router.no_action.app_error", nil, "", http.StatusBadRequest) 30 returnWebSocketError(wr.app, conn, r, err) 31 return 32 } 33 34 if r.Seq <= 0 { 35 err := model.NewAppError("ServeWebSocket", "api.web_socket_router.bad_seq.app_error", nil, "", http.StatusBadRequest) 36 returnWebSocketError(wr.app, conn, r, err) 37 return 38 } 39 40 if r.Action == model.WebsocketAuthenticationChallenge { 41 if conn.GetSessionToken() != "" { 42 return 43 } 44 45 token, ok := r.Data["token"].(string) 46 if !ok { 47 conn.WebSocket.Close() 48 return 49 } 50 51 session, err := wr.app.GetSession(token) 52 if err != nil { 53 conn.WebSocket.Close() 54 return 55 } 56 conn.SetSession(session) 57 conn.SetSessionToken(session.Token) 58 conn.UserId = session.UserId 59 60 // TODO: Same logic to reconnect queue as api4/websocket.go 61 62 wr.app.HubRegister(conn) 63 64 wr.app.Srv().Go(func() { 65 wr.app.SetStatusOnline(session.UserId, false) 66 wr.app.UpdateLastActivityAtIfNeeded(*session) 67 }) 68 69 resp := model.NewWebSocketResponse(model.StatusOk, r.Seq, nil) 70 hub := wr.app.GetHubForUserId(conn.UserId) 71 if hub == nil { 72 return 73 } 74 hub.SendMessage(conn, resp) 75 76 return 77 } 78 79 if !conn.IsAuthenticated() { 80 err := model.NewAppError("ServeWebSocket", "api.web_socket_router.not_authenticated.app_error", nil, "", http.StatusUnauthorized) 81 returnWebSocketError(wr.app, conn, r, err) 82 return 83 } 84 85 handler, ok := wr.handlers[r.Action] 86 if !ok { 87 err := model.NewAppError("ServeWebSocket", "api.web_socket_router.bad_action.app_error", nil, "", http.StatusInternalServerError) 88 returnWebSocketError(wr.app, conn, r, err) 89 return 90 } 91 92 handler.ServeWebSocket(conn, r) 93} 94 95func returnWebSocketError(app *App, conn *WebConn, r *model.WebSocketRequest, err *model.AppError) { 96 logF := mlog.Error 97 if err.StatusCode >= http.StatusBadRequest && err.StatusCode < http.StatusInternalServerError { 98 logF = mlog.Debug 99 } 100 logF( 101 "websocket routing error.", 102 mlog.Int64("seq", r.Seq), 103 mlog.String("user_id", conn.UserId), 104 mlog.String("system_message", err.SystemMessage(i18n.T)), 105 mlog.Err(err), 106 ) 107 108 hub := app.GetHubForUserId(conn.UserId) 109 if hub == nil { 110 return 111 } 112 113 err.DetailedError = "" 114 errorResp := model.NewWebSocketError(r.Seq, err) 115 hub.SendMessage(conn, errorResp) 116} 117