fix changes from debug, edit groups return, add new connections func

This commit is contained in:
2026-04-05 12:55:36 +02:00
parent 8f5e405532
commit 9d279c9680
5 changed files with 48 additions and 75 deletions
-35
View File
@@ -1,35 +0,0 @@
# Bug List
## Open
### Bug 1 — `WsSendToUser`: sends message to sender, not recipient
**File:** `wsServer.go:86`
`to *User` parameter is never used — message is sent back to `from` instead.
```go
sendMessageCloseIfTimeout(from, &msg) // should be `to`
```
### Bug 2 — `ServeWsConnection`: `ignoreCache` captured by value in defer
**File:** `wsServer.go:30`
`ignoreCache` is `false` at defer registration; setting it to `true` later has no effect.
The user is always evicted from cache on disconnect.
```go
defer closeConnection(&user, ignoreCache) // wrong
defer func() { closeConnection(&user, ignoreCache) }() // correct
```
### Bug 3 — Deadlock in `sendToAllMessageCloseIfTimeout`
**File:** `wsServer.go:72`, `cache.go:52`
`sendToAllMessageCloseIfTimeout` holds `mu.RLock()`, calls `sendMessageCloseIfTimeout`,
which on timeout calls `closeConnection``CacheDeleteUser``mu.Lock()`.
Upgrading RLock to write Lock deadlocks.
### Bug 4 — `closeConnection`: nil `WsConn` not guarded
**File:** `wsServer.go:179`
`user.WsConn.CloseNow()` panics if `WsConn` is nil (e.g. unauthenticated user).
`sendMessageCloseIfTimeout` guards against this but `closeConnection` does not.
### Bug 5 — `HttpHandleUserAcceptConnection`: potential nil dereference
**File:** `http.go:410`
`target.Connections[user.Id].IsAccepted = true` panics if the entry is missing
(DB inconsistency). The sender side is guarded but the recipient side is not.
+21 -14
View File
@@ -381,7 +381,7 @@ func HttpHandleUserAcceptConnection(response http.ResponseWriter, request *http.
} }
targetId, err := ConvertStringUint32(request.FormValue("connectedid")) targetId, err := ConvertStringUint32(request.FormValue("connectedid"))
if err != nil { if err != nil || user.Connections[targetId] == nil {
http.Error(response, "invalid recipient id", http.StatusBadRequest) http.Error(response, "invalid recipient id", http.StatusBadRequest)
return return
} }
@@ -400,7 +400,7 @@ func HttpHandleUserAcceptConnection(response http.ResponseWriter, request *http.
return return
} }
} }
if user.Connections[targetId] == nil { if target.Connections[user.Id] == nil {
http.Error(response, "invalid recipient id", http.StatusBadRequest) http.Error(response, "invalid recipient id", http.StatusBadRequest)
return return
} }
@@ -420,6 +420,23 @@ func HttpHandleUserAcceptConnection(response http.ResponseWriter, request *http.
response.WriteHeader(http.StatusAccepted) response.WriteHeader(http.StatusAccepted)
} }
func HttpHandleUserGetConnections(response http.ResponseWriter, request *http.Request) {
ctx := request.Context()
user, err := getUser(ctx, request.FormValue("token"))
if err != nil {
http.Error(response, "invalid token", http.StatusUnauthorized)
return
}
json, err := json2.Marshal(user.Connections)
if err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError)
return
}
response.WriteHeader(http.StatusAccepted)
response.Write(json)
}
func HttpHandleTokenNew(response http.ResponseWriter, request *http.Request) { func HttpHandleTokenNew(response http.ResponseWriter, request *http.Request) {
if !isMethodAllowed(&response, request) { if !isMethodAllowed(&response, request) {
return return
@@ -777,23 +794,13 @@ func HttpHandleGroupsGetWithoutMembers(response http.ResponseWriter, request *ht
return return
} }
groups := make([]GroupNoMembers, 0, len(user.Groups)) groups := make(map[uint32]*Group, len(user.Groups))
for groupId := range user.Groups { for groupId := range user.Groups {
group, err := getGroup(ctx, groupId) group, err := getGroup(ctx, groupId)
if err != nil { if err != nil {
continue continue
} }
groups[groupId] = group
groups = append(groups, GroupNoMembers{
Id: groupId,
Name: group.Name,
CreatedAt: group.CreatedAt,
CreatorId: group.CreatorId,
OwnerId: group.OwnerId,
Color: group.Color,
EnableUsersColors: group.EnableUserColors,
})
} }
json, err := json2.Marshal(groups) json, err := json2.Marshal(groups)
+4
View File
@@ -18,17 +18,21 @@ func main() {
DbInit(ctx) DbInit(ctx)
http.HandleFunc("/new/user", withCORS(HttpHandleUserNew)) http.HandleFunc("/new/user", withCORS(HttpHandleUserNew))
http.HandleFunc("/new/connection", withCORS(HttpHandleUserNewConnection))
http.HandleFunc("/new/token", withCORS(HttpHandleTokenNew)) http.HandleFunc("/new/token", withCORS(HttpHandleTokenNew))
http.HandleFunc("/new/group", withCORS(HttpHandeGroupCreate)) http.HandleFunc("/new/group", withCORS(HttpHandeGroupCreate))
http.HandleFunc("/mod/user/appearence", withCORS(HttpHandleUserModifyAppearance)) http.HandleFunc("/mod/user/appearence", withCORS(HttpHandleUserModifyAppearance))
http.HandleFunc("/mod/user/about", withCORS(HttpHandleUserModifyAbout)) http.HandleFunc("/mod/user/about", withCORS(HttpHandleUserModifyAbout))
http.HandleFunc("/mod/connection/accept", withCORS(HttpHandleUserAcceptConnection))
http.HandleFunc("/mod/connection/delete", withCORS(HttpHandleUserDeleteConnection))
http.HandleFunc("/mod/group/addusers", withCORS(HttpHandleGroupAddUser)) http.HandleFunc("/mod/group/addusers", withCORS(HttpHandleGroupAddUser))
http.HandleFunc("/mod/group/removeusers", withCORS(HttpHandleGroupRemoveUser)) http.HandleFunc("/mod/group/removeusers", withCORS(HttpHandleGroupRemoveUser))
http.HandleFunc("/mod/group/color", withCORS(HttpHandleGroupChangeColor)) http.HandleFunc("/mod/group/color", withCORS(HttpHandleGroupChangeColor))
http.HandleFunc("/mod/group/owner", withCORS(HttpHandleGroupChangeOwner)) http.HandleFunc("/mod/group/owner", withCORS(HttpHandleGroupChangeOwner))
http.HandleFunc("/get/groups", withCORS(HttpHandleGroupsGetWithoutMembers)) http.HandleFunc("/get/groups", withCORS(HttpHandleGroupsGetWithoutMembers))
http.HandleFunc("/get/connections", withCORS(HttpHandleUserGetConnections))
http.HandleFunc("/get/group/members", withCORS(HttpHandleGroupMembersGet)) http.HandleFunc("/get/group/members", withCORS(HttpHandleGroupMembersGet))
http.HandleFunc("/del/group", withCORS(HttpHandleGroupDelete)) http.HandleFunc("/del/group", withCORS(HttpHandleGroupDelete))
+12 -22
View File
@@ -19,29 +19,19 @@ type User struct {
} }
type Connection struct { type Connection struct {
CreatedAt time.Time CreatedAt time.Time `json:"createdAt"`
With uint32 With uint32 `json:"-"`
IsFromUser bool IsFromUser bool `json:"isFromUser"`
IsAccepted bool IsAccepted bool `json:"isAccepted"`
} }
type Group struct { type Group struct {
Name string Name string `json:"name"`
CreatedAt time.Time CreatedAt time.Time `json:"createdAt"`
Id uint32 Id uint32 `json:"-"`
CreatorId uint32 CreatorId uint32 `json:"creatorId"`
OwnerId uint32 OwnerId uint32 `json:"ownerId"`
Users map[uint32]struct{} Users map[uint32]struct{} `json:"-"`
Color [3]uint8 Color [3]uint8 `json:"color"`
EnableUserColors bool EnableUserColors bool `json:"enableUserColors"`
}
type GroupNoMembers struct {
Name string `json:"name"`
CreatedAt time.Time `json:"createdAt"`
Id uint32 `json:"id"`
CreatorId uint32 `json:"creatorId"`
OwnerId uint32 `json:"ownerId"`
Color [3]uint8 `json:"color"`
EnableUsersColors bool `json:"enableUsersColors"`
} }
+11 -4
View File
@@ -27,7 +27,7 @@ func ServeWsConnection(responseWriter http.ResponseWriter, request *http.Request
var isAuthenticated bool var isAuthenticated bool
var ignoreCache bool var ignoreCache bool
defer closeConnection(&user, ignoreCache) defer func() { closeConnection(&user, ignoreCache) }()
for { for {
var userMessage map[string]any var userMessage map[string]any
err = wsjson.Read(ctx, connection, &userMessage) err = wsjson.Read(ctx, connection, &userMessage)
@@ -71,8 +71,13 @@ func sendMessageCloseIfTimeout(user *User, message *map[string]any) {
func sendToAllMessageCloseIfTimeout(message *map[string]any) { func sendToAllMessageCloseIfTimeout(message *map[string]any) {
mu.RLock() mu.RLock()
defer mu.RUnlock() users := make([]*User, 0, len(CacheUsers))
for _, user := range CacheUsers { for _, user := range CacheUsers {
users = append(users, user)
}
mu.RUnlock()
for _, user := range users {
sendMessageCloseIfTimeout(user, message) sendMessageCloseIfTimeout(user, message)
} }
} }
@@ -83,7 +88,7 @@ func WsSendToUser(from *User, to *User, message string) {
"from": from.Id, "from": from.Id,
"content": message, "content": message,
} }
sendMessageCloseIfTimeout(from, &msg) sendMessageCloseIfTimeout(to, &msg)
} }
func WsSendToGroup(group *Group, sender *User, message string) error { func WsSendToGroup(group *Group, sender *User, message string) error {
@@ -176,5 +181,7 @@ func closeConnection(user *User, ignoreCache bool) {
if !ignoreCache { if !ignoreCache {
CacheDeleteUser(user.Id) CacheDeleteUser(user.Id)
} }
user.WsConn.CloseNow() if user.WsConn != nil {
user.WsConn.CloseNow()
}
} }