Skip to content

Commit 4e916a2

Browse files
author
Marko Petzold
committed
new kill session by authid
1 parent de5b002 commit 4e916a2

2 files changed

Lines changed: 78 additions & 0 deletions

File tree

router/killbyauthid_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package router //nolint:testpackage
2+
3+
import (
4+
"testing"
5+
"testing/synctest"
6+
"time"
7+
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/gammazero/nexus/v3/wamp"
11+
)
12+
13+
// TestKillSessionsByAuthidGoAPI covers the Go-level Router.KillSessionsByAuthid
14+
// added for embedders (the cluster mesh) to enforce single-authority
15+
// identities: it closes matching sessions except the excluded one, returns the
16+
// kill count, and reports -1 for an unknown realm.
17+
func TestKillSessionsByAuthidGoAPI(t *testing.T) {
18+
synctest.Test(t, func(t *testing.T) {
19+
r := newTestRouter(t)
20+
defer r.Close()
21+
22+
// testClient gives every session authid "user1".
23+
keep := testClient(t, r)
24+
victim1 := testClient(t, r)
25+
victim2 := testClient(t, r)
26+
27+
// Unknown realm → -1, nothing killed.
28+
require.Equal(t, -1, r.KillSessionsByAuthid("no.such.realm", "user1", 0))
29+
30+
// Kill all "user1" sessions except `keep`.
31+
killed := r.KillSessionsByAuthid(testRealm, "user1", keep.ID)
32+
require.Equal(t, 2, killed, "expected both non-excluded sessions killed")
33+
34+
for i, v := range []*wamp.Session{victim1, victim2} {
35+
msg, err := wamp.RecvTimeout(v, time.Second)
36+
require.NoErrorf(t, err, "victim %d should receive GOODBYE", i+1)
37+
g, ok := msg.(*wamp.Goodbye)
38+
require.Truef(t, ok, "victim %d expected GOODBYE", i+1)
39+
require.Equal(t, wamp.CloseNormal, g.Reason)
40+
}
41+
42+
// The excluded session is untouched.
43+
_, err := wamp.RecvTimeout(keep, time.Millisecond)
44+
require.Error(t, err, "excluded session must not be killed")
45+
46+
// A non-matching authid kills nothing.
47+
require.Equal(t, 0, r.KillSessionsByAuthid(testRealm, "nobody", 0))
48+
})
49+
}

router/router.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ type Router interface {
5454
// is used as a pluggable library sometimes it is usefully to expose WAMP
5555
// features current version provides.
5656
RouterFeatures() *wamp.Dict
57+
58+
// KillSessionsByAuthid closes every session on the named realm whose
59+
// authid equals the given value, except the session identified by
60+
// exclude (pass 0 to exclude none). It returns the number of sessions
61+
// closed, or -1 if the realm is not served by this router. This is the
62+
// Go-level equivalent of the wamp.session.kill_by_authid meta procedure,
63+
// exposed so an embedder (e.g. the cluster mesh) can enforce
64+
// single-authority identities without standing up a meta caller.
65+
KillSessionsByAuthid(realm wamp.URI, authid string, exclude wamp.ID) int
5766
}
5867

5968
// router is the default WAMP router implementation.
@@ -74,6 +83,26 @@ type router struct {
7483
memStatsStopped chan struct{}
7584
}
7685

86+
// KillSessionsByAuthid closes every session on realmURI whose authid matches,
87+
// except exclude (0 = none). Returns the kill count, or -1 if the realm is not
88+
// served here. The realm is located under the router actor; the kill itself
89+
// runs on the realm actor (killSessionsByDetail self-dispatches), so this never
90+
// holds both actors at once.
91+
func (r *router) KillSessionsByAuthid(realmURI wamp.URI, authid string, exclude wamp.ID) int {
92+
var realm *realm
93+
sync := make(chan struct{})
94+
r.actionChan <- func() {
95+
realm = r.realms[realmURI]
96+
close(sync)
97+
}
98+
<-sync
99+
if realm == nil {
100+
return -1
101+
}
102+
return realm.killSessionsByDetail("authid", authid, wamp.CloseNormal,
103+
"superseded by a newer session with the same authid", exclude)
104+
}
105+
77106
// NewRouter creates a WAMP router instance.
78107
func NewRouter(config *Config, logger stdlog.StdLog) (Router, error) {
79108
// If logger not provided, create one.

0 commit comments

Comments
 (0)