Close web socket connection on delete user
This commit is contained in:
parent
6954fb5adf
commit
1262f43846
|
|
@ -57,8 +57,20 @@ func New(pingPeriod, pongTimeout time.Duration) *API {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifyDeleted closes existing connections with the given token.
|
// NotifyDeletedUser closes existing connections for the given user.
|
||||||
func (a *API) NotifyDeleted(userID uint, token string) {
|
func (a *API) NotifyDeletedUser(userID uint) {
|
||||||
|
a.lock.Lock()
|
||||||
|
defer a.lock.Unlock()
|
||||||
|
if clients, ok := a.clients[userID]; ok {
|
||||||
|
for _, client := range clients {
|
||||||
|
client.Close()
|
||||||
|
}
|
||||||
|
delete(a.clients, userID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifyDeletedClient closes existing connections with the given token.
|
||||||
|
func (a *API) NotifyDeletedClient(userID uint, token string) {
|
||||||
a.lock.Lock()
|
a.lock.Lock()
|
||||||
defer a.lock.Unlock()
|
defer a.lock.Unlock()
|
||||||
if clients, ok := a.clients[userID]; ok {
|
if clients, ok := a.clients[userID]; ok {
|
||||||
|
|
|
||||||
|
|
@ -187,7 +187,7 @@ func TestDeleteClientShouldCloseConnection(t *testing.T) {
|
||||||
api.Notify(1, &model.Message{Message: "msg"})
|
api.Notify(1, &model.Message{Message: "msg"})
|
||||||
user.expectMessage(&model.Message{Message: "msg"})
|
user.expectMessage(&model.Message{Message: "msg"})
|
||||||
|
|
||||||
api.NotifyDeleted(1, "customtoken")
|
api.NotifyDeletedClient(1, "customtoken")
|
||||||
|
|
||||||
api.Notify(1, &model.Message{Message: "msg"})
|
api.Notify(1, &model.Message{Message: "msg"})
|
||||||
user.expectNoMessage()
|
user.expectNoMessage()
|
||||||
|
|
@ -236,7 +236,7 @@ func TestDeleteMultipleClients(t *testing.T) {
|
||||||
expectNoMessage(userTwo...)
|
expectNoMessage(userTwo...)
|
||||||
expectNoMessage(userThree...)
|
expectNoMessage(userThree...)
|
||||||
|
|
||||||
api.NotifyDeleted(1, "1-2")
|
api.NotifyDeletedClient(1, "1-2")
|
||||||
|
|
||||||
api.Notify(1, &model.Message{ID: 2, Message: "there"})
|
api.Notify(1, &model.Message{ID: 2, Message: "there"})
|
||||||
expectMessage(&model.Message{ID: 2, Message: "there"}, userOneIPhone, userOneOther)
|
expectMessage(&model.Message{ID: 2, Message: "there"}, userOneIPhone, userOneOther)
|
||||||
|
|
@ -257,6 +257,69 @@ func TestDeleteMultipleClients(t *testing.T) {
|
||||||
api.Close()
|
api.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeleteUser(t *testing.T) {
|
||||||
|
mode.Set(mode.TestDev)
|
||||||
|
|
||||||
|
defer leaktest.Check(t)()
|
||||||
|
userIDs := []uint{1, 1, 1, 1, 2, 2, 3}
|
||||||
|
tokens := []string{"1-1", "1-2", "1-2", "1-3", "2-1", "2-2", "3"}
|
||||||
|
i := 0
|
||||||
|
server, api := bootTestServer(func(context *gin.Context) {
|
||||||
|
auth.RegisterAuthentication(context, nil, userIDs[i], tokens[i])
|
||||||
|
i++
|
||||||
|
})
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
wsURL := wsURL(server.URL)
|
||||||
|
|
||||||
|
userOneIPhone := testClient(t, wsURL)
|
||||||
|
defer userOneIPhone.conn.Close()
|
||||||
|
userOneAndroid := testClient(t, wsURL)
|
||||||
|
defer userOneAndroid.conn.Close()
|
||||||
|
userOneBrowser := testClient(t, wsURL)
|
||||||
|
defer userOneBrowser.conn.Close()
|
||||||
|
userOneOther := testClient(t, wsURL)
|
||||||
|
defer userOneOther.conn.Close()
|
||||||
|
userOne := []*testingClient{userOneAndroid, userOneBrowser, userOneIPhone, userOneOther}
|
||||||
|
|
||||||
|
userTwoBrowser := testClient(t, wsURL)
|
||||||
|
defer userTwoBrowser.conn.Close()
|
||||||
|
userTwoAndroid := testClient(t, wsURL)
|
||||||
|
defer userTwoAndroid.conn.Close()
|
||||||
|
userTwo := []*testingClient{userTwoAndroid, userTwoBrowser}
|
||||||
|
|
||||||
|
userThreeAndroid := testClient(t, wsURL)
|
||||||
|
defer userThreeAndroid.conn.Close()
|
||||||
|
userThree := []*testingClient{userThreeAndroid}
|
||||||
|
|
||||||
|
// the server may take some time to register the client
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
api.Notify(1, &model.Message{ID: 4, Message: "there"})
|
||||||
|
expectMessage(&model.Message{ID: 4, Message: "there"}, userOne...)
|
||||||
|
expectNoMessage(userTwo...)
|
||||||
|
expectNoMessage(userThree...)
|
||||||
|
|
||||||
|
api.NotifyDeletedUser(1)
|
||||||
|
|
||||||
|
api.Notify(1, &model.Message{ID: 2, Message: "there"})
|
||||||
|
expectNoMessage(userOne...)
|
||||||
|
expectNoMessage(userThree...)
|
||||||
|
expectNoMessage(userTwo...)
|
||||||
|
|
||||||
|
api.Notify(2, &model.Message{ID: 2, Message: "there"})
|
||||||
|
expectNoMessage(userOne...)
|
||||||
|
expectMessage(&model.Message{ID: 2, Message: "there"}, userTwo...)
|
||||||
|
expectNoMessage(userThree...)
|
||||||
|
|
||||||
|
api.Notify(3, &model.Message{ID: 5, Message: "there"})
|
||||||
|
expectNoMessage(userOne...)
|
||||||
|
expectNoMessage(userTwo...)
|
||||||
|
expectMessage(&model.Message{ID: 5, Message: "there"}, userThree...)
|
||||||
|
|
||||||
|
api.Close()
|
||||||
|
}
|
||||||
|
|
||||||
func TestMultipleClients(t *testing.T) {
|
func TestMultipleClients(t *testing.T) {
|
||||||
mode.Set(mode.TestDev)
|
mode.Set(mode.TestDev)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ type UserDatabase interface {
|
||||||
type UserAPI struct {
|
type UserAPI struct {
|
||||||
DB UserDatabase
|
DB UserDatabase
|
||||||
PasswordStrength int
|
PasswordStrength int
|
||||||
|
NotifyDeleted func(uint)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUsers returns all the users
|
// GetUsers returns all the users
|
||||||
|
|
@ -72,6 +73,7 @@ func (a *UserAPI) GetUserByID(ctx *gin.Context) {
|
||||||
func (a *UserAPI) DeleteUserByID(ctx *gin.Context) {
|
func (a *UserAPI) DeleteUserByID(ctx *gin.Context) {
|
||||||
withID(ctx, "id", func(id uint) {
|
withID(ctx, "id", func(id uint) {
|
||||||
if user := a.DB.GetUserByID(id); user != nil {
|
if user := a.DB.GetUserByID(id); user != nil {
|
||||||
|
a.NotifyDeleted(id)
|
||||||
a.DB.DeleteUserByID(id)
|
a.DB.DeleteUserByID(id)
|
||||||
} else {
|
} else {
|
||||||
ctx.AbortWithError(404, errors.New("user does not exist"))
|
ctx.AbortWithError(404, errors.New("user does not exist"))
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ type UserSuite struct {
|
||||||
a *UserAPI
|
a *UserAPI
|
||||||
ctx *gin.Context
|
ctx *gin.Context
|
||||||
recorder *httptest.ResponseRecorder
|
recorder *httptest.ResponseRecorder
|
||||||
|
notified bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *UserSuite) BeforeTest(suiteName, testName string) {
|
func (s *UserSuite) BeforeTest(suiteName, testName string) {
|
||||||
|
|
@ -31,7 +32,12 @@ func (s *UserSuite) BeforeTest(suiteName, testName string) {
|
||||||
s.recorder = httptest.NewRecorder()
|
s.recorder = httptest.NewRecorder()
|
||||||
s.ctx, _ = gin.CreateTestContext(s.recorder)
|
s.ctx, _ = gin.CreateTestContext(s.recorder)
|
||||||
s.db = test.NewDB(s.T())
|
s.db = test.NewDB(s.T())
|
||||||
s.a = &UserAPI{DB: s.db}
|
s.notified = false
|
||||||
|
s.a = &UserAPI{DB: s.db, NotifyDeleted: s.notify}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UserSuite) notify(uint) {
|
||||||
|
s.notified = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *UserSuite) AfterTest(suiteName, testName string) {
|
func (s *UserSuite) AfterTest(suiteName, testName string) {
|
||||||
|
|
@ -107,6 +113,8 @@ func (s *UserSuite) Test_DeleteUserByID_UnknownUser() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *UserSuite) Test_DeleteUserByID() {
|
func (s *UserSuite) Test_DeleteUserByID() {
|
||||||
|
assert.False(s.T(), s.notified)
|
||||||
|
|
||||||
s.db.User(2)
|
s.db.User(2)
|
||||||
|
|
||||||
s.ctx.Params = gin.Params{{Key: "id", Value: "2"}}
|
s.ctx.Params = gin.Params{{Key: "id", Value: "2"}}
|
||||||
|
|
@ -115,6 +123,7 @@ func (s *UserSuite) Test_DeleteUserByID() {
|
||||||
|
|
||||||
assert.Equal(s.T(), 200, s.recorder.Code)
|
assert.Equal(s.T(), 200, s.recorder.Code)
|
||||||
s.db.AssertUserNotExist(2)
|
s.db.AssertUserNotExist(2)
|
||||||
|
assert.True(s.T(), s.notified)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *UserSuite) Test_CreateUser() {
|
func (s *UserSuite) Test_CreateUser() {
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ func Create(db *database.GormDatabase, vInfo *model.VersionInfo, conf *config.Co
|
||||||
streamHandler := stream.New(200*time.Second, 15*time.Second)
|
streamHandler := stream.New(200*time.Second, 15*time.Second)
|
||||||
authentication := auth.Auth{DB: db}
|
authentication := auth.Auth{DB: db}
|
||||||
messageHandler := api.MessageAPI{Notifier: streamHandler, DB: db}
|
messageHandler := api.MessageAPI{Notifier: streamHandler, DB: db}
|
||||||
tokenHandler := api.TokenAPI{DB: db, ImageDir: conf.UploadedImagesDir, NotifyDeleted: streamHandler.NotifyDeleted}
|
tokenHandler := api.TokenAPI{DB: db, ImageDir: conf.UploadedImagesDir, NotifyDeleted: streamHandler.NotifyDeletedClient}
|
||||||
userHandler := api.UserAPI{DB: db, PasswordStrength: conf.PassStrength}
|
userHandler := api.UserAPI{DB: db, PasswordStrength: conf.PassStrength, NotifyDeleted: streamHandler.NotifyDeletedUser}
|
||||||
g := gin.New()
|
g := gin.New()
|
||||||
|
|
||||||
g.Use(gin.Logger(), gin.Recovery(), error.Handler(), location.Default())
|
g.Use(gin.Logger(), gin.Recovery(), error.Handler(), location.Default())
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue