handle-close.go raw
1 package app
2
3 import (
4 "errors"
5
6 "next.orly.dev/pkg/lol/chk"
7 "next.orly.dev/pkg/lol/log"
8 "next.orly.dev/pkg/nostr/encoders/envelopes/closeenvelope"
9 )
10
11 // HandleClose processes a CLOSE envelope by unmarshalling the request,
12 // validates the presence of an <id> field, and signals cancellation for
13 // the associated listener through the server's publisher mechanism.
14 func (l *Listener) HandleClose(req []byte) (err error) {
15 var rem []byte
16 env := closeenvelope.New()
17 if rem, err = env.Unmarshal(req); chk.E(err) {
18 return
19 }
20 if len(rem) > 0 {
21 log.T.F("extra '%s'", rem)
22 }
23 if len(env.ID) == 0 {
24 return errors.New("CLOSE has no <id>")
25 }
26
27 subID := string(env.ID)
28
29 // Cancel the subscription goroutine by calling its cancel function
30 l.subscriptionsMu.Lock()
31 if cancelFunc, exists := l.subscriptions[subID]; exists {
32 log.D.F("cancelling subscription %s for %s", subID, l.remote)
33 cancelFunc()
34 delete(l.subscriptions, subID)
35 } else {
36 log.D.F("subscription %s not found for %s (already closed?)", subID, l.remote)
37 }
38 l.subscriptionsMu.Unlock()
39
40 // Also remove from publisher's tracking
41 l.publishers.Receive(
42 &W{
43 Cancel: true,
44 remote: l.remote,
45 Conn: l.conn,
46 Id: subID,
47 },
48 )
49
50 log.D.F("CLOSE processed for subscription %s @ %s", subID, l.remote)
51 return
52 }
53