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