fix changes from debug, edit groups return, add new connections func
This commit is contained in:
-35
@@ -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.
|
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
+7
-17
@@ -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
|
|
||||||
CreatedAt time.Time
|
|
||||||
Id uint32
|
|
||||||
CreatorId uint32
|
|
||||||
OwnerId uint32
|
|
||||||
Users map[uint32]struct{}
|
|
||||||
Color [3]uint8
|
|
||||||
EnableUserColors bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type GroupNoMembers struct {
|
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
Id uint32 `json:"id"`
|
Id uint32 `json:"-"`
|
||||||
CreatorId uint32 `json:"creatorId"`
|
CreatorId uint32 `json:"creatorId"`
|
||||||
OwnerId uint32 `json:"ownerId"`
|
OwnerId uint32 `json:"ownerId"`
|
||||||
|
Users map[uint32]struct{} `json:"-"`
|
||||||
Color [3]uint8 `json:"color"`
|
Color [3]uint8 `json:"color"`
|
||||||
EnableUsersColors bool `json:"enableUsersColors"`
|
EnableUserColors bool `json:"enableUserColors"`
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-3
@@ -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)
|
||||||
}
|
}
|
||||||
|
if user.WsConn != nil {
|
||||||
user.WsConn.CloseNow()
|
user.WsConn.CloseNow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user