diff --git a/internal/api/members/handler.go b/internal/api/members/handler.go index 29656ac7..558991d0 100644 --- a/internal/api/members/handler.go +++ b/internal/api/members/handler.go @@ -2,6 +2,7 @@ package members import ( "context" + "errors" "net/http" "github.com/go-chi/chi" @@ -68,14 +69,21 @@ func GetMember(r *http.Request) MemberData { func (h *MembersHandler) ExtractMember(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { memberId := chi.URLParam(r, "memberId") + profile, err := h.members.Select(memberId) if err != nil { - utils.HttpNotFound(w, err) - } else { - next.ServeHTTP(w, SetMember(r, MemberData{ - ID: memberId, - Profile: profile, - })) + if errors.Is(err, types.ErrMemberDoesNotExist) { + utils.HttpNotFound(w, err) + } else { + utils.HttpInternalServerError(w, err) + } + + return } + + next.ServeHTTP(w, SetMember(r, MemberData{ + ID: memberId, + Profile: profile, + })) }) } diff --git a/internal/capture/broadcast.go b/internal/capture/broadcast.go index 3322f28c..ab9d9dfa 100644 --- a/internal/capture/broadcast.go +++ b/internal/capture/broadcast.go @@ -1,7 +1,6 @@ package capture import ( - "fmt" "strings" "sync" @@ -9,6 +8,7 @@ import ( "github.com/rs/zerolog/log" "demodesk/neko/internal/capture/gst" + "demodesk/neko/internal/types" ) type BroacastManagerCtx struct { @@ -67,7 +67,7 @@ func (manager *BroacastManagerCtx) Url() string { func (manager *BroacastManagerCtx) createPipeline() error { if manager.pipeline != nil { - return fmt.Errorf("pipeline already exists") + return types.ErrCapturePipelineAlreadyExists } var err error diff --git a/internal/capture/screencast.go b/internal/capture/screencast.go index 28bb1e74..bec88894 100644 --- a/internal/capture/screencast.go +++ b/internal/capture/screencast.go @@ -1,7 +1,7 @@ package capture import ( - "fmt" + "errors" "sync" "sync/atomic" "time" @@ -93,12 +93,12 @@ func (manager *ScreencastManagerCtx) Image() ([]byte, error) { case sample := <-manager.sample: return sample.Data, nil case <-time.After(1 * time.Second): - return nil, fmt.Errorf("timeouted") + return nil, errors.New("timeouted") } } if manager.image.Data == nil { - return nil, fmt.Errorf("image sample not found") + return nil, errors.New("image sample not found") } return manager.image.Data, nil @@ -109,7 +109,7 @@ func (manager *ScreencastManagerCtx) start() error { defer manager.mu.Unlock() if !manager.enabled { - return fmt.Errorf("screenshot pipeline not enabled") + return errors.New("screenshot pipeline not enabled") } err := manager.createPipeline() @@ -131,7 +131,7 @@ func (manager *ScreencastManagerCtx) stop() { func (manager *ScreencastManagerCtx) createPipeline() error { if manager.pipeline != nil { - return fmt.Errorf("pipeline already exists") + return types.ErrCapturePipelineAlreadyExists } var err error diff --git a/internal/capture/stream.go b/internal/capture/stream.go index 7c3ec6ad..34f6a91f 100644 --- a/internal/capture/stream.go +++ b/internal/capture/stream.go @@ -1,7 +1,6 @@ package capture import ( - "fmt" "reflect" "sync" @@ -141,7 +140,7 @@ func (manager *StreamManagerCtx) Started() bool { func (manager *StreamManagerCtx) createPipeline() error { if manager.pipeline != nil { - return fmt.Errorf("pipeline already exists") + return types.ErrCapturePipelineAlreadyExists } var err error diff --git a/internal/desktop/filechooserdialog.go b/internal/desktop/filechooserdialog.go index 85fb22e1..5091e001 100644 --- a/internal/desktop/filechooserdialog.go +++ b/internal/desktop/filechooserdialog.go @@ -1,7 +1,7 @@ package desktop import ( - "fmt" + "errors" "os/exec" "time" ) @@ -46,7 +46,7 @@ func (manager *DesktopManagerCtx) HandleFileChooserDialog(uri string) error { // if last command didn't return error, consider dialog as still open if err2 == nil { - return fmt.Errorf("unable to select files in dialog") + return errors.New("unable to select files in dialog") } return nil diff --git a/internal/member/dummy/provider.go b/internal/member/dummy/provider.go index 8072e79b..5dafd59d 100644 --- a/internal/member/dummy/provider.go +++ b/internal/member/dummy/provider.go @@ -1,7 +1,7 @@ package dummy import ( - "fmt" + "errors" "demodesk/neko/internal/types" ) @@ -37,7 +37,7 @@ func (provider *MemberProviderCtx) Authenticate(username string, password string } func (provider *MemberProviderCtx) Insert(username string, password string, profile types.MemberProfile) (string, error) { - return "", fmt.Errorf("not implemented") + return "", errors.New("not implemented") } func (provider *MemberProviderCtx) Select(id string) (types.MemberProfile, error) { @@ -54,7 +54,7 @@ func (provider *MemberProviderCtx) UpdateProfile(id string, profile types.Member } func (provider *MemberProviderCtx) UpdatePassword(id string, password string) error { - return fmt.Errorf("not implemented") + return errors.New("not implemented") } func (provider *MemberProviderCtx) Delete(id string) error { diff --git a/internal/member/file/provider.go b/internal/member/file/provider.go index e3ec3476..39fbd74e 100644 --- a/internal/member/file/provider.go +++ b/internal/member/file/provider.go @@ -2,7 +2,6 @@ package file import ( "encoding/json" - "fmt" "io/ioutil" "os" @@ -38,7 +37,7 @@ func (provider *MemberProviderCtx) Authenticate(username string, password string // TODO: Use hash function. if entry.Password != password { - return "", types.MemberProfile{}, fmt.Errorf("invalid password") + return "", types.MemberProfile{}, types.ErrMemberInvalidPassword } return id, entry.Profile, nil @@ -55,7 +54,7 @@ func (provider *MemberProviderCtx) Insert(username string, password string, prof _, ok := entries[id] if ok { - return "", fmt.Errorf("member ID already exists") + return "", types.ErrMemberAlreadyExists } entries[id] = MemberEntry{ @@ -75,7 +74,7 @@ func (provider *MemberProviderCtx) UpdateProfile(id string, profile types.Member entry, ok := entries[id] if !ok { - return fmt.Errorf("member ID does not exist") + return types.ErrMemberDoesNotExist } entry.Profile = profile @@ -92,7 +91,7 @@ func (provider *MemberProviderCtx) UpdatePassword(id string, password string) er entry, ok := entries[id] if !ok { - return fmt.Errorf("member ID does not exist") + return types.ErrMemberDoesNotExist } // TODO: Use hash function. @@ -139,7 +138,7 @@ func (provider *MemberProviderCtx) Delete(id string) error { _, ok := entries[id] if !ok { - return fmt.Errorf("member ID does not exist") + return types.ErrMemberDoesNotExist } delete(entries, id) @@ -178,7 +177,7 @@ func (provider *MemberProviderCtx) getEntry(id string) (MemberEntry, error) { entry, ok := entries[id] if !ok { - return MemberEntry{}, fmt.Errorf("member ID does not exist") + return MemberEntry{}, types.ErrMemberDoesNotExist } return entry, nil diff --git a/internal/member/manager.go b/internal/member/manager.go index f52a417f..8696cf40 100644 --- a/internal/member/manager.go +++ b/internal/member/manager.go @@ -1,7 +1,7 @@ package member import ( - "fmt" + "errors" "sync" "github.com/rs/zerolog" @@ -41,7 +41,7 @@ type MemberManagerCtx struct { config *config.Member providerMu sync.Mutex provider types.MemberProvider - sessionMu sync.Mutex + loginMu sync.Mutex } func (manager *MemberManagerCtx) Connect() error { @@ -91,13 +91,10 @@ func (manager *MemberManagerCtx) UpdateProfile(id string, profile types.MemberPr defer manager.providerMu.Unlock() // update corresponding session, if exists - manager.sessionMu.Lock() - if _, ok := manager.sessions.Get(id); ok { - if err := manager.sessions.Update(id, profile); err != nil { - manager.logger.Err(err).Msg("error while updating session") - } + err := manager.sessions.Update(id, profile) + if err != nil && !errors.Is(err, types.ErrSessionNotFound) { + manager.logger.Err(err).Msg("error while updating session") } - manager.sessionMu.Unlock() return manager.provider.UpdateProfile(id, profile) } @@ -114,13 +111,10 @@ func (manager *MemberManagerCtx) Delete(id string) error { defer manager.providerMu.Unlock() // destroy corresponding session, if exists - manager.sessionMu.Lock() - if _, ok := manager.sessions.Get(id); ok { - if err := manager.sessions.Delete(id); err != nil { - manager.logger.Err(err).Msg("error while deleting session") - } + err := manager.sessions.Delete(id) + if err != nil && !errors.Is(err, types.ErrSessionNotFound) { + manager.logger.Err(err).Msg("error while deleting session") } - manager.sessionMu.Unlock() return manager.provider.Delete(id) } @@ -130,8 +124,8 @@ func (manager *MemberManagerCtx) Delete(id string) error { // func (manager *MemberManagerCtx) Login(username string, password string) (types.Session, string, error) { - manager.sessionMu.Lock() - defer manager.sessionMu.Unlock() + manager.loginMu.Lock() + defer manager.loginMu.Unlock() id, profile, err := manager.provider.Authenticate(username, password) if err != nil { @@ -141,7 +135,7 @@ func (manager *MemberManagerCtx) Login(username string, password string) (types. session, ok := manager.sessions.Get(id) if ok { if session.State().IsConnected { - return nil, "", fmt.Errorf("session is already connected") + return nil, "", types.ErrSessionAlreadyConnected } // TODO: Replace session. @@ -154,12 +148,8 @@ func (manager *MemberManagerCtx) Login(username string, password string) (types. } func (manager *MemberManagerCtx) Logout(id string) error { - manager.sessionMu.Lock() - defer manager.sessionMu.Unlock() - - if _, ok := manager.sessions.Get(id); !ok { - return fmt.Errorf("session not found") - } + manager.loginMu.Lock() + defer manager.loginMu.Unlock() return manager.sessions.Delete(id) } diff --git a/internal/member/object/provider.go b/internal/member/object/provider.go index 68855d65..4dbdb77e 100644 --- a/internal/member/object/provider.go +++ b/internal/member/object/provider.go @@ -1,8 +1,6 @@ package object import ( - "fmt" - "demodesk/neko/internal/types" ) @@ -60,12 +58,12 @@ func (provider *MemberProviderCtx) Authenticate(username string, password string entry, ok := provider.entries[id] if !ok { - return "", types.MemberProfile{}, fmt.Errorf("member ID does not exist") + return "", types.MemberProfile{}, types.ErrMemberDoesNotExist } // TODO: Use hash function. if entry.Password != password { - return "", types.MemberProfile{}, fmt.Errorf("invalid password") + return "", types.MemberProfile{}, types.ErrMemberInvalidPassword } return id, entry.Profile, nil @@ -77,7 +75,7 @@ func (provider *MemberProviderCtx) Insert(username string, password string, prof _, ok := provider.entries[id] if ok { - return "", fmt.Errorf("member ID already exists") + return "", types.ErrMemberAlreadyExists } provider.entries[id] = &MemberEntry{ @@ -92,7 +90,7 @@ func (provider *MemberProviderCtx) Insert(username string, password string, prof func (provider *MemberProviderCtx) UpdateProfile(id string, profile types.MemberProfile) error { entry, ok := provider.entries[id] if !ok { - return fmt.Errorf("member ID does not exist") + return types.ErrMemberDoesNotExist } entry.Profile = profile @@ -103,7 +101,7 @@ func (provider *MemberProviderCtx) UpdateProfile(id string, profile types.Member func (provider *MemberProviderCtx) UpdatePassword(id string, password string) error { entry, ok := provider.entries[id] if !ok { - return fmt.Errorf("member ID does not exist") + return types.ErrMemberDoesNotExist } // TODO: Use hash function. @@ -115,7 +113,7 @@ func (provider *MemberProviderCtx) UpdatePassword(id string, password string) er func (provider *MemberProviderCtx) Select(id string) (types.MemberProfile, error) { entry, ok := provider.entries[id] if !ok { - return types.MemberProfile{}, fmt.Errorf("member ID does not exist") + return types.MemberProfile{}, types.ErrMemberDoesNotExist } return entry.Profile, nil @@ -139,7 +137,7 @@ func (provider *MemberProviderCtx) SelectAll(limit int, offset int) (map[string] func (provider *MemberProviderCtx) Delete(id string) error { _, ok := provider.entries[id] if !ok { - return fmt.Errorf("member ID does not exist") + return types.ErrMemberDoesNotExist } delete(provider.entries, id) diff --git a/internal/session/auth.go b/internal/session/auth.go index b12c2637..06c40d6d 100644 --- a/internal/session/auth.go +++ b/internal/session/auth.go @@ -1,7 +1,7 @@ package session import ( - "fmt" + "errors" "net/http" "strings" "time" @@ -39,16 +39,16 @@ func (manager *SessionManagerCtx) CookieClearToken(w http.ResponseWriter, r *htt func (manager *SessionManagerCtx) Authenticate(r *http.Request) (types.Session, error) { token, ok := manager.getToken(r) if !ok { - return nil, fmt.Errorf("no authentication provided") + return nil, errors.New("no authentication provided") } session, ok := manager.GetByToken(token) if !ok { - return nil, fmt.Errorf("session not found") + return nil, types.ErrSessionNotFound } if !session.Profile().CanLogin { - return nil, fmt.Errorf("login disabled") + return nil, types.ErrSessionLoginDisabled } return session, nil diff --git a/internal/session/manager.go b/internal/session/manager.go index 1ec7f9f3..2e357e7e 100644 --- a/internal/session/manager.go +++ b/internal/session/manager.go @@ -1,7 +1,7 @@ package session import ( - "fmt" + "errors" "sync" "github.com/kataras/go-events" @@ -68,12 +68,12 @@ func (manager *SessionManagerCtx) Create(id string, profile types.MemberProfile) manager.sessionsMu.Lock() if _, ok := manager.sessions[id]; ok { manager.sessionsMu.Unlock() - return nil, "", fmt.Errorf("session id already exists") + return nil, "", types.ErrSessionAlreadyExists } if _, ok := manager.tokens[token]; ok { manager.sessionsMu.Unlock() - return nil, "", fmt.Errorf("session token already exists") + return nil, "", errors.New("session token already exists") } session := &SessionCtx{ @@ -98,7 +98,7 @@ func (manager *SessionManagerCtx) Update(id string, profile types.MemberProfile) session, ok := manager.sessions[id] if !ok { manager.sessionsMu.Unlock() - return fmt.Errorf("session id not found") + return types.ErrSessionNotFound } session.profile = profile @@ -114,7 +114,7 @@ func (manager *SessionManagerCtx) Delete(id string) error { session, ok := manager.sessions[id] if !ok { manager.sessionsMu.Unlock() - return fmt.Errorf("session id not found") + return types.ErrSessionNotFound } delete(manager.tokens, session.token) diff --git a/internal/types/capture.go b/internal/types/capture.go index f8ba68be..8901bdf6 100644 --- a/internal/types/capture.go +++ b/internal/types/capture.go @@ -2,6 +2,7 @@ package types import ( "context" + "errors" "fmt" "math" "strings" @@ -12,6 +13,10 @@ import ( "demodesk/neko/internal/types/codec" ) +var ( + ErrCapturePipelineAlreadyExists = errors.New("capture pipeline already exists") +) + type Sample media.Sample type BroadcastManager interface { diff --git a/internal/types/member.go b/internal/types/member.go index be7d1e38..17173754 100644 --- a/internal/types/member.go +++ b/internal/types/member.go @@ -1,5 +1,13 @@ package types +import "errors" + +var ( + ErrMemberAlreadyExists = errors.New("member already exists") + ErrMemberDoesNotExist = errors.New("member does not exist") + ErrMemberInvalidPassword = errors.New("invalid password") +) + type MemberProfile struct { Name string `json:"name"` IsAdmin bool `json:"is_admin"` diff --git a/internal/types/session.go b/internal/types/session.go index 6df70d87..d243ec51 100644 --- a/internal/types/session.go +++ b/internal/types/session.go @@ -1,6 +1,16 @@ package types -import "net/http" +import ( + "errors" + "net/http" +) + +var ( + ErrSessionNotFound = errors.New("session not found") + ErrSessionAlreadyExists = errors.New("session already exists") + ErrSessionAlreadyConnected = errors.New("session is already connected") + ErrSessionLoginDisabled = errors.New("session login disabled") +) type SessionState struct { IsConnected bool `json:"is_connected"` diff --git a/internal/types/webrtc.go b/internal/types/webrtc.go index 1639a635..4bf34915 100644 --- a/internal/types/webrtc.go +++ b/internal/types/webrtc.go @@ -1,6 +1,15 @@ package types -import "github.com/pion/webrtc/v3" +import ( + "errors" + + "github.com/pion/webrtc/v3" +) + +var ( + ErrWebRTCVideoNotFound = errors.New("webrtc video not found") + ErrWebRTCDataChannelNotFound = errors.New("webrtc data channel not found") +) type ICEServer struct { URLs []string `mapstructure:"urls" json:"urls"` diff --git a/internal/webrtc/manager.go b/internal/webrtc/manager.go index 1d1a8b19..0da67ed5 100644 --- a/internal/webrtc/manager.go +++ b/internal/webrtc/manager.go @@ -66,7 +66,7 @@ func (manager *WebRTCManagerCtx) Start() { } audioListener := func(sample types.Sample) { - if err := manager.audioTrack.WriteSample(media.Sample(sample)); err != nil && err != io.ErrClosedPipe { + if err := manager.audioTrack.WriteSample(media.Sample(sample)); err != nil { if errors.Is(err, io.ErrClosedPipe) { // The peerConnection has been closed. return @@ -166,7 +166,7 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID strin } videoListener := func(sample types.Sample) { - if err := videoTrack.WriteSample(media.Sample(sample)); err != nil && err != io.ErrClosedPipe { + if err := videoTrack.WriteSample(media.Sample(sample)); err != nil { if errors.Is(err, io.ErrClosedPipe) { // The peerConnection has been closed. return @@ -200,7 +200,7 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID strin changeVideo := func(videoID string) error { newVideoStream, ok := manager.capture.Video(videoID) if !ok { - return fmt.Errorf("video stream not found") + return types.ErrWebRTCVideoNotFound } // should be new stream started @@ -357,7 +357,7 @@ func (manager *WebRTCManagerCtx) mediaEngine(videoID string) (*webrtc.MediaEngin // all videos must have the same codec video, ok := manager.capture.Video(videoID) if !ok { - return nil, fmt.Errorf("selected video track not found") + return nil, types.ErrWebRTCVideoNotFound } videoCodec := video.Codec() diff --git a/internal/webrtc/send.go b/internal/webrtc/send.go index 31d07c1a..55733f42 100644 --- a/internal/webrtc/send.go +++ b/internal/webrtc/send.go @@ -3,7 +3,6 @@ package webrtc import ( "bytes" "encoding/binary" - "fmt" "demodesk/neko/internal/types" ) @@ -28,8 +27,11 @@ type PayloadCursorImage struct { } func (peer *WebRTCPeerCtx) SendCursorPosition(x, y int) error { + peer.mu.Lock() + defer peer.mu.Unlock() + if peer.dataChannel == nil { - return fmt.Errorf("no data channel") + return types.ErrWebRTCDataChannelNotFound } data := PayloadCursorPosition{ @@ -50,8 +52,11 @@ func (peer *WebRTCPeerCtx) SendCursorPosition(x, y int) error { } func (peer *WebRTCPeerCtx) SendCursorImage(cur *types.CursorImage, img []byte) error { + peer.mu.Lock() + defer peer.mu.Unlock() + if peer.dataChannel == nil { - return fmt.Errorf("no data channel") + return types.ErrWebRTCDataChannelNotFound } data := PayloadCursorImage{