commit fcd6b14

delthas  ·  2025-03-17 22:45:42 +0000 UTC
parent aee34a4
Defer unimportant caps until CAP LS response

This keeps our conn-reg 0-RTT but introduces a theoretical race
condition on these CAPs (e.g. if we receive a SETNAME right after
conn-reg).

This helps us go below 20 messages for our conn-reg batch, which
is the default limit on solanum and the one used by Libera.

Fixes: https://todo.sr.ht/~delthas/senpai/200
1 files changed,  +49, -28
+49, -28
  1@@ -50,28 +50,29 @@ func (auth *SASLPlain) Respond(challenge string) (res string, err error) {
  2 }
  3 
  4 // SupportedCapabilities is the set of capabilities supported by this library.
  5-var SupportedCapabilities = map[string]struct{}{
  6-	"away-notify":      {},
  7-	"batch":            {},
  8-	"cap-notify":       {},
  9-	"echo-message":     {},
 10-	"extended-monitor": {},
 11-	"invite-notify":    {},
 12-	"labeled-response": {},
 13-	"message-tags":     {},
 14-	"multi-prefix":     {},
 15-	"server-time":      {},
 16-	"sasl":             {},
 17-	"setname":          {},
 18-	"standard-replies": {},
 19-
 20-	"draft/chathistory":               {},
 21-	"draft/event-playback":            {},
 22-	"draft/metadata-2":                {},
 23-	"draft/read-marker":               {},
 24-	"soju.im/bouncer-networks-notify": {},
 25-	"soju.im/bouncer-networks":        {},
 26-	"soju.im/search":                  {},
 27+// Value is false if the cap is deferred (to work around some daemons agfressive rate pre-conn-reg backlog limiting)
 28+var SupportedCapabilities = map[string]bool{
 29+	"away-notify":      false,
 30+	"batch":            true,
 31+	"cap-notify":       true,
 32+	"echo-message":     true,
 33+	"extended-monitor": false,
 34+	"invite-notify":    false,
 35+	"labeled-response": true,
 36+	"message-tags":     true,
 37+	"multi-prefix":     true,
 38+	"sasl":             true,
 39+	"server-time":      true,
 40+	"setname":          false,
 41+	"standard-replies": true,
 42+
 43+	"draft/chathistory":               true,
 44+	"draft/event-playback":            true,
 45+	"draft/metadata-2":                true,
 46+	"draft/read-marker":               true,
 47+	"soju.im/bouncer-networks-notify": true,
 48+	"soju.im/bouncer-networks":        true,
 49+	"soju.im/search":                  false,
 50 }
 51 
 52 // Values taken by the "@+typing=" client tag.  TypingUnspec means the value or
 53@@ -199,8 +200,10 @@ func NewSession(out chan<- Message, params SessionParams) *Session {
 54 	}
 55 
 56 	s.out <- NewMessage("CAP", "LS", "302")
 57-	for capability := range SupportedCapabilities {
 58-		s.out <- NewMessage("CAP", "REQ", capability)
 59+	for capability, immediate := range SupportedCapabilities {
 60+		if immediate || s.netID != "" {
 61+			s.out <- NewMessage("CAP", "REQ", capability)
 62+		}
 63 	}
 64 	s.out <- NewMessage("NICK", s.nick)
 65 	s.out <- NewMessage("USER", s.user, "0", "*", s.real)
 66@@ -877,9 +880,18 @@ func (s *Session) handleMessageRegistered(msg Message, playback bool) (Event, er
 67 		// do nothing
 68 	case "CAP":
 69 		var subcommand, caps string
 70-		if err := msg.ParseParams(nil, &subcommand, &caps); err != nil {
 71+		if err := msg.ParseParams(nil, &subcommand); err != nil {
 72 			return nil, err
 73 		}
 74+		if len(msg.Params) > 3 && msg.Params[2] == "*" {
 75+			if err := msg.ParseParams(nil, nil, nil, &caps); err != nil {
 76+				return nil, err
 77+			}
 78+		} else {
 79+			if err := msg.ParseParams(nil, nil, &caps); err != nil {
 80+				return nil, err
 81+			}
 82+		}
 83 
 84 		switch subcommand {
 85 		case "ACK":
 86@@ -912,16 +924,25 @@ func (s *Session) handleMessageRegistered(msg Message, playback bool) (Event, er
 87 			}
 88 		case "NAK":
 89 			// do nothing
 90-		case "NEW":
 91+		case "LS", "NEW":
 92+			var reqs []string
 93 			for _, c := range ParseCaps(caps) {
 94 				s.availableCaps[c.Name] = c.Value
 95-				if _, ok := SupportedCapabilities[c.Name]; !ok {
 96+				immediate, ok := SupportedCapabilities[c.Name]
 97+				if !ok {
 98+					continue
 99+				}
100+				if subcommand == "LS" && (immediate || s.netID != "") {
101+					// Already sent CAP, ignore
102 					continue
103 				}
104 				if _, ok := s.enabledCaps[c.Name]; ok {
105 					continue
106 				}
107-				s.out <- NewMessage("CAP", "REQ", c.Name)
108+				reqs = append(reqs, c.Name)
109+			}
110+			if len(reqs) > 0 {
111+				s.out <- NewMessage("CAP", "REQ", strings.Join(reqs, " "))
112 			}
113 		case "DEL":
114 			for _, c := range ParseCaps(caps) {