diff --git a/server/internal/http/legacy/handler.go b/server/internal/http/legacy/handler.go index fae80e8b..e1688e7d 100644 --- a/server/internal/http/legacy/handler.go +++ b/server/internal/http/legacy/handler.go @@ -1,11 +1,17 @@ package legacy import ( + "errors" "fmt" "log" "net/http" + oldEvent "github.com/demodesk/neko/internal/http/legacy/event" + oldMessage "github.com/demodesk/neko/internal/http/legacy/message" + "github.com/demodesk/neko/pkg/types" + "github.com/demodesk/neko/pkg/types/event" + "github.com/demodesk/neko/pkg/types/message" "github.com/demodesk/neko/pkg/utils" "github.com/gorilla/websocket" ) @@ -55,6 +61,11 @@ func (h *LegacyHandler) Route(r types.Router) { token, err := s.create(password) if err != nil { log.Printf("couldn't create a new session: %v", err) + s.toClient(&oldMessage.SystemMessage{ + Event: oldEvent.SYSTEM_DISCONNECT, + Title: "couldn't create a new session", + Message: err.Error(), + }) return nil } defer s.destroy() @@ -63,21 +74,31 @@ func (h *LegacyHandler) Route(r types.Router) { connBackend, _, err := DefaultDialer.Dial("ws://127.0.0.1:8080/api/ws?token="+token, nil) if err != nil { log.Printf("couldn't dial to the remote backend: %v", err) + s.toClient(&oldMessage.SystemMessage{ + Event: oldEvent.SYSTEM_DISCONNECT, + Title: "couldn't dial to the remote backend", + Message: err.Error(), + }) return nil } defer connBackend.Close() s.connBackend = connBackend // request signal - if err = connBackend.WriteMessage(websocket.TextMessage, []byte(`{"event":"signal/request", "payload":{}}`)); err != nil { + if err = s.toBackend(event.SIGNAL_REQUEST, message.SignalRequest{}); err != nil { log.Printf("couldn't request signal: %v", err) + s.toClient(&oldMessage.SystemMessage{ + Event: oldEvent.SYSTEM_DISCONNECT, + Title: "couldn't request signal", + Message: err.Error(), + }) return nil } // copy messages between the client and the backend errClient := make(chan error, 1) errBackend := make(chan error, 1) - replicateWebsocketConn := func(dst, src *websocket.Conn, errc chan error, rewriteTextMessage func([]byte, func([]byte) error) error) { + replicateWebsocketConn := func(dst, src *websocket.Conn, errc chan error, rewriteTextMessage func([]byte) error) { for { msgType, msg, err := src.ReadMessage() if err != nil { @@ -92,23 +113,24 @@ func (h *LegacyHandler) Route(r types.Router) { break } if msgType == websocket.TextMessage { - doBreak := false - err = rewriteTextMessage(msg, func(msg []byte) error { - err := dst.WriteMessage(msgType, msg) - if err != nil { - doBreak = true - } - return err - }) + err = rewriteTextMessage(msg) - if doBreak { - errc <- err - break + if err == nil { + continue } - if err != nil { - log.Printf("websocketproxy: Error when rewriting message: %v", err) + if errors.Is(err, ErrBackendRespone) { + s.toClient(&oldMessage.SystemMessage{ + Event: oldEvent.SYSTEM_ERROR, + Title: "backend response error", + Message: err.Error(), + }) continue + } else if errors.Is(err, ErrWebsocketSend) { + errc <- err + break + } else { + log.Printf("websocketproxy: %v", err) } } } diff --git a/server/internal/http/legacy/session.go b/server/internal/http/legacy/session.go index 461bf3c1..6df1661e 100644 --- a/server/internal/http/legacy/session.go +++ b/server/internal/http/legacy/session.go @@ -15,6 +15,11 @@ import ( "github.com/gorilla/websocket" ) +var ( + ErrWebsocketSend = fmt.Errorf("failed to send message to websocket") + ErrBackendRespone = fmt.Errorf("error response from backend") +) + type session struct { url string id string @@ -65,6 +70,14 @@ func (s *session) apiReq(method, path string, request, response any) error { if res.StatusCode < 200 || res.StatusCode >= 300 { body, _ := io.ReadAll(res.Body) + // try to unmarsal as json error message + var apiErr struct { + Message string `json:"message"` + } + if err := json.Unmarshal(body, &apiErr); err == nil { + return fmt.Errorf("%w: %s", ErrBackendRespone, apiErr.Message) + } + // return raw body if failed to unmarshal return fmt.Errorf("unexpected status code: %d, body: %s", res.StatusCode, body) } @@ -75,6 +88,44 @@ func (s *session) apiReq(method, path string, request, response any) error { return json.NewDecoder(res.Body).Decode(response) } +// send message to client (in old format) +func (s *session) toClient(payload any) error { + msg, err := json.Marshal(payload) + if err != nil { + return err + } + + err = s.connClient.WriteMessage(websocket.TextMessage, msg) + if err != nil { + return fmt.Errorf("%w: %s", ErrWebsocketSend, err) + } + + return nil +} + +// send message to backend (in new format) +func (s *session) toBackend(event string, payload any) error { + rawPayload, err := json.Marshal(payload) + if err != nil { + return err + } + + msg, err := json.Marshal(&types.WebSocketMessage{ + Event: event, + Payload: rawPayload, + }) + if err != nil { + return err + } + + err = s.connBackend.WriteMessage(websocket.TextMessage, msg) + if err != nil { + return fmt.Errorf("%w: %s", ErrWebsocketSend, err) + } + + return nil +} + func (s *session) create(password string) (string, error) { data := api.SessionDataPayload{} diff --git a/server/internal/http/legacy/wstobackend.go b/server/internal/http/legacy/wstobackend.go index 2a046d86..424cad2e 100644 --- a/server/internal/http/legacy/wstobackend.go +++ b/server/internal/http/legacy/wstobackend.go @@ -19,30 +19,13 @@ import ( "github.com/demodesk/neko/pkg/types/message" ) -func (s *session) wsToBackend(msg []byte, sendMsg func([]byte) error) error { +func (s *session) wsToBackend(msg []byte) error { header := oldMessage.Message{} err := json.Unmarshal(msg, &header) if err != nil { return err } - send := func(event string, payload any) error { - rawPayload, err := json.Marshal(payload) - if err != nil { - return err - } - - msg, err := json.Marshal(&types.WebSocketMessage{ - Event: event, - Payload: rawPayload, - }) - if err != nil { - return err - } - - return sendMsg(msg) - } - switch header.Event { // Signal Events case oldEvent.SIGNAL_OFFER: @@ -52,7 +35,7 @@ func (s *session) wsToBackend(msg []byte, sendMsg func([]byte) error) error { return err } - return send(event.SIGNAL_OFFER, &message.SignalDescription{ + return s.toBackend(event.SIGNAL_OFFER, &message.SignalDescription{ SDP: request.SDP, }) @@ -65,7 +48,7 @@ func (s *session) wsToBackend(msg []byte, sendMsg func([]byte) error) error { // TODO: Set Display Name here. - return send(event.SIGNAL_ANSWER, &message.SignalDescription{ + return s.toBackend(event.SIGNAL_ANSWER, &message.SignalDescription{ SDP: request.SDP, }) @@ -82,16 +65,16 @@ func (s *session) wsToBackend(msg []byte, sendMsg func([]byte) error) error { return err } - return send(event.SIGNAL_CANDIDATE, &message.SignalCandidate{ + return s.toBackend(event.SIGNAL_CANDIDATE, &message.SignalCandidate{ ICECandidateInit: candidate, }) // Control Events case oldEvent.CONTROL_RELEASE: - return send(event.CONTROL_RELEASE, nil) + return s.toBackend(event.CONTROL_RELEASE, nil) case oldEvent.CONTROL_REQUEST: - return send(event.CONTROL_REQUEST, nil) + return s.toBackend(event.CONTROL_REQUEST, nil) case oldEvent.CONTROL_GIVE: request := &oldMessage.Control{} @@ -109,7 +92,7 @@ func (s *session) wsToBackend(msg []byte, sendMsg func([]byte) error) error { return err } - return send(event.CLIPBOARD_SET, &message.ClipboardData{ + return s.toBackend(event.CLIPBOARD_SET, &message.ClipboardData{ Text: request.Text, }) @@ -121,7 +104,7 @@ func (s *session) wsToBackend(msg []byte, sendMsg func([]byte) error) error { } if request.Layout != nil { - err = send(event.KEYBOARD_MAP, &message.KeyboardMap{ + err = s.toBackend(event.KEYBOARD_MAP, &message.KeyboardMap{ KeyboardMap: types.KeyboardMap{ Layout: *request.Layout, }, @@ -133,7 +116,7 @@ func (s *session) wsToBackend(msg []byte, sendMsg func([]byte) error) error { } if request.CapsLock != nil || request.NumLock != nil || request.ScrollLock != nil { - err = send(event.KEYBOARD_MODIFIERS, &message.KeyboardModifiers{ + err = s.toBackend(event.KEYBOARD_MODIFIERS, &message.KeyboardModifiers{ KeyboardModifiers: types.KeyboardModifiers{ CapsLock: request.CapsLock, NumLock: request.NumLock, @@ -156,7 +139,7 @@ func (s *session) wsToBackend(msg []byte, sendMsg func([]byte) error) error { return err } - return send(chat.CHAT_MESSAGE, &chat.Content{ + return s.toBackend(chat.CHAT_MESSAGE, &chat.Content{ Text: request.Content, }) @@ -184,7 +167,7 @@ func (s *session) wsToBackend(msg []byte, sendMsg func([]byte) error) error { } // broadcast emote to other users - return send(event.SEND_BROADCAST, &message.SendBroadcast{ + return s.toBackend(event.SEND_BROADCAST, &message.SendBroadcast{ Sender: s.id, Subject: "emote", Body: request.Emote, @@ -192,7 +175,7 @@ func (s *session) wsToBackend(msg []byte, sendMsg func([]byte) error) error { // File Transfer Events case oldEvent.FILETRANSFER_REFRESH: - return send(filetransfer.FILETRANSFER_UPDATE, nil) + return s.toBackend(filetransfer.FILETRANSFER_UPDATE, nil) // Screen Events case oldEvent.SCREEN_RESOLUTION: @@ -210,7 +193,7 @@ func (s *session) wsToBackend(msg []byte, sendMsg func([]byte) error) error { return err } - return send(event.SCREEN_SET, &message.ScreenSize{ + return s.toBackend(event.SCREEN_SET, &message.ScreenSize{ ScreenSize: types.ScreenSize{ Width: request.Width, Height: request.Height, diff --git a/server/internal/http/legacy/wstoclient.go b/server/internal/http/legacy/wstoclient.go index bf3c5054..fa18b3d2 100644 --- a/server/internal/http/legacy/wstoclient.go +++ b/server/internal/http/legacy/wstoclient.go @@ -37,7 +37,7 @@ func sessionDataToMember(id string, session message.SessionData) (*oldTypes.Memb }, nil } -func (s *session) sendControlHost(request message.ControlHost, send func(payload any) error) error { +func (s *session) sendControlHost(request message.ControlHost) error { lastHostID := s.lastHostID if request.HasHost { @@ -45,19 +45,19 @@ func (s *session) sendControlHost(request message.ControlHost, send func(payload if request.ID == request.HostID { if request.ID == lastHostID || lastHostID == "" { - return send(&oldMessage.Control{ + return s.toClient(&oldMessage.Control{ Event: oldEvent.CONTROL_LOCKED, ID: request.HostID, }) } else { - return send(&oldMessage.AdminTarget{ + return s.toClient(&oldMessage.AdminTarget{ Event: oldEvent.ADMIN_CONTROL, ID: request.ID, Target: lastHostID, }) } } else { - return send(&oldMessage.ControlTarget{ + return s.toClient(&oldMessage.ControlTarget{ Event: oldEvent.CONTROL_GIVE, ID: request.HostID, Target: request.ID, @@ -69,12 +69,12 @@ func (s *session) sendControlHost(request message.ControlHost, send func(payload s.lastHostID = "" if request.ID == lastHostID { - return send(&oldMessage.Control{ + return s.toClient(&oldMessage.Control{ Event: oldEvent.CONTROL_RELEASE, ID: request.ID, }) } else { - return send(&oldMessage.Control{ + return s.toClient(&oldMessage.Control{ Event: oldEvent.ADMIN_RELEASE, ID: request.ID, }) @@ -84,22 +84,13 @@ func (s *session) sendControlHost(request message.ControlHost, send func(payload return nil } -func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { +func (s *session) wsToClient(msg []byte) error { data := types.WebSocketMessage{} err := json.Unmarshal(msg, &data) if err != nil { return err } - send := func(payload any) error { - msg, err := json.Marshal(payload) - if err != nil { - return err - } - - return sendMsg(msg) - } - switch data.Event { // System Events case event.SYSTEM_DISCONNECT: @@ -109,15 +100,11 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { return err } - return send(&oldMessage.SystemMessage{ + return s.toClient(&oldMessage.SystemMessage{ Event: oldEvent.SYSTEM_DISCONNECT, Message: request.Message, }) - // case: - // send(&oldMessage.SystemMessage{ - // Event: oldEvent.SYSTEM_ERROR, - // }) case event.SYSTEM_INIT: request := &message.SystemInit{} err := json.Unmarshal(data.Payload, request) @@ -143,7 +130,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { s.sessions[id] = member } - err = send(&oldMessage.MembersList{ + err = s.toClient(&oldMessage.MembersList{ Event: oldEvent.MEMBER_LIST, Members: membersList, }) @@ -155,7 +142,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { // ScreenSize // - err = send(&oldMessage.ScreenResolution{ + err = s.toClient(&oldMessage.ScreenResolution{ Event: oldEvent.SCREEN_RESOLUTION, Width: request.ScreenSize.Width, Height: request.ScreenSize.Height, @@ -172,7 +159,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { // ControlHost // - err = s.sendControlHost(request.ControlHost, send) + err = s.sendControlHost(request.ControlHost) if err != nil { return err } @@ -207,7 +194,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { s.lockedFileTransfer = true } - return send(&oldMessage.SystemInit{ + return s.toClient(&oldMessage.SystemInit{ Event: oldEvent.SYSTEM_INIT, ImplicitHosting: request.Settings.ImplicitHosting, Locks: locks, @@ -251,7 +238,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { } } - err = send(&oldMessage.ScreenConfigurations{ + err = s.toClient(&oldMessage.ScreenConfigurations{ Event: oldEvent.SCREEN_CONFIGURATIONS, Configurations: screenSizesList, }) @@ -263,7 +250,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { // BroadcastStatus // - return send(&oldMessage.BroadcastStatus{ + return s.toClient(&oldMessage.BroadcastStatus{ Event: oldEvent.BROADCAST_STATUS, URL: request.BroadcastStatus.URL, IsActive: request.BroadcastStatus.IsActive, @@ -302,7 +289,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { delete(s.sessions, request.ID) - return send(&oldMessage.MemberDisconnected{ + return s.toClient(&oldMessage.MemberDisconnected{ Event: oldEvent.MEMBER_DISCONNECTED, ID: request.ID, }) @@ -323,7 +310,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { s.sessions[request.ID] = nil // oldEvent.MEMBER_CONNECTED if not sent already - return send(&oldMessage.Member{ + return s.toClient(&oldMessage.Member{ Event: oldEvent.MEMBER_CONNECTED, Member: member, }) @@ -333,7 +320,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { delete(s.sessions, request.ID) // oldEvent.MEMBER_DISCONNECTED if nor sent already - return send(&oldMessage.MemberDisconnected{ + return s.toClient(&oldMessage.MemberDisconnected{ Event: oldEvent.MEMBER_DISCONNECTED, ID: request.ID, }) @@ -349,7 +336,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { return err } - return send(&oldMessage.SignalOffer{ + return s.toClient(&oldMessage.SignalOffer{ Event: oldEvent.SIGNAL_OFFER, SDP: request.SDP, }) @@ -361,7 +348,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { return err } - return send(&oldMessage.SignalAnswer{ + return s.toClient(&oldMessage.SignalAnswer{ Event: oldEvent.SIGNAL_ANSWER, DisplayName: s.profile.Name, // DisplayName SDP: request.SDP, @@ -379,7 +366,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { return err } - return send(&oldMessage.SignalCandidate{ + return s.toClient(&oldMessage.SignalCandidate{ Event: oldEvent.SIGNAL_CANDIDATE, Data: string(json), }) @@ -401,7 +388,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { }) } - return send(&oldMessage.SignalProvide{ + return s.toClient(&oldMessage.SignalProvide{ Event: oldEvent.SIGNAL_PROVIDE, ID: s.id, // SessionId SDP: request.SDP, @@ -417,7 +404,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { return err } - return send(&oldMessage.Clipboard{ + return s.toClient(&oldMessage.Clipboard{ Event: oldEvent.CONTROL_CLIPBOARD, Text: request.Text, }) @@ -429,7 +416,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { return err } - return s.sendControlHost(*request, send) + return s.sendControlHost(*request) case event.CONTROL_REQUEST: request := &message.SessionID{} @@ -440,13 +427,13 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { if s.id == request.ID { // if i am the one that is requesting, send CONTROL_REQUEST to me - return send(&oldMessage.Control{ + return s.toClient(&oldMessage.Control{ Event: oldEvent.CONTROL_REQUEST, ID: request.ID, }) } else { // if not, let me know someone else is requesting - return send(&oldMessage.Control{ + return s.toClient(&oldMessage.Control{ Event: oldEvent.CONTROL_REQUESTING, ID: request.ID, }) @@ -460,7 +447,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { return err } - return send(&oldMessage.ChatSend{ + return s.toClient(&oldMessage.ChatSend{ Event: oldEvent.CHAT_MESSAGE, ID: request.ID, Content: request.Content.Text, @@ -474,7 +461,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { } if request.Subject == "emote" { - return send(&oldMessage.EmoteSend{ + return s.toClient(&oldMessage.EmoteSend{ Event: oldEvent.CHAT_EMOTE, ID: request.Sender, Emote: request.Body.(string), @@ -507,7 +494,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { }) } - return send(&oldMessage.FileTransferList{ + return s.toClient(&oldMessage.FileTransferList{ Event: oldEvent.FILETRANSFER_LIST, Cwd: request.RootDir, Files: files, @@ -521,7 +508,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { return err } - return send(&oldMessage.ScreenResolution{ + return s.toClient(&oldMessage.ScreenResolution{ Event: oldEvent.SCREEN_RESOLUTION, ID: request.ID, Width: request.ScreenSize.Width, @@ -537,7 +524,7 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { return err } - return send(&oldMessage.BroadcastStatus{ + return s.toClient(&oldMessage.BroadcastStatus{ Event: oldEvent.BROADCAST_STATUS, URL: request.URL, IsActive: request.IsActive, @@ -555,12 +542,12 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { s.lockedControls = request.LockedControls if request.LockedControls { - err = send(&oldMessage.AdminLock{ + err = s.toClient(&oldMessage.AdminLock{ Event: oldEvent.ADMIN_LOCK, Resource: "control", }) } else { - err = send(&oldMessage.AdminLock{ + err = s.toClient(&oldMessage.AdminLock{ Event: oldEvent.ADMIN_UNLOCK, Resource: "control", }) @@ -575,12 +562,12 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { s.lockedLogins = request.LockedLogins if request.LockedLogins { - err = send(&oldMessage.AdminLock{ + err = s.toClient(&oldMessage.AdminLock{ Event: oldEvent.ADMIN_LOCK, Resource: "login", }) } else { - err = send(&oldMessage.AdminLock{ + err = s.toClient(&oldMessage.AdminLock{ Event: oldEvent.ADMIN_UNLOCK, Resource: "login", }) @@ -608,12 +595,12 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { s.lockedFileTransfer = !filetransferSettings.Enabled if !filetransferSettings.Enabled { - err = send(&oldMessage.AdminLock{ + err = s.toClient(&oldMessage.AdminLock{ Event: oldEvent.ADMIN_LOCK, Resource: "file_transfer", }) } else { - err = send(&oldMessage.AdminLock{ + err = s.toClient(&oldMessage.AdminLock{ Event: oldEvent.ADMIN_UNLOCK, Resource: "file_transfer", }) @@ -628,19 +615,19 @@ func (s *session) wsToClient(msg []byte, sendMsg func([]byte) error) error { /* case: - send(&oldMessage.AdminTarget{ + s.toClient(&oldMessage.AdminTarget{ Event: oldEvent.ADMIN_BAN, }) case: - send(&oldMessage.AdminTarget{ + s.toClient(&oldMessage.AdminTarget{ Event: oldEvent.ADMIN_KICK, }) case: - send(&oldMessage.AdminTarget{ + s.toClient(&oldMessage.AdminTarget{ Event: oldEvent.ADMIN_MUTE, }) case: - send(&oldMessage.AdminTarget{ + s.toClient(&oldMessage.AdminTarget{ Event: oldEvent.ADMIN_UNMUTE, }) */