commit 60f9e65

Hubert Hirtz  ·  2021-09-19 15:15:53 +0000 UTC
parent 185ea09
Eager registration

Decreases the number of roundtrips necessary for registration.

A typical registration might look like this:

- C: CAP LS; NICK; USER
- S: CAP LS
- C: CAP REQ
- S: CAP ACK
- C: AUTHENTICATE <mesh>
- S: AUTHENTICATE +
- C: AUTHENTICATE <user/pass payload>
- S: RPL_SASLSUCCESS
- C: CAP END
- S: welcome batch

By sending client messages eagerly, despite the number of message not
decreasing (it's increasing when the client supports more caps than the
server), the number of roundtrips is decreased to 1 (not counting TCP
and TLS negotiation).
1 files changed,  +25, -62
+25, -62
  1@@ -115,8 +115,7 @@ type Session struct {
  2 	host   string
  3 	auth   SASLClient
  4 
  5-	availableCaps map[string]string
  6-	enabledCaps   map[string]struct{}
  7+	enabledCaps map[string]struct{}
  8 
  9 	// ISUPPORT features
 10 	casemap       func(string) string
 11@@ -144,7 +143,6 @@ func NewSession(out chan<- Message, params SessionParams) *Session {
 12 		user:            params.Username,
 13 		real:            params.RealName,
 14 		auth:            params.Auth,
 15-		availableCaps:   map[string]string{},
 16 		enabledCaps:     map[string]struct{}{},
 17 		casemap:         CasemapRFC1459,
 18 		chantypes:       "#&",
 19@@ -159,9 +157,21 @@ func NewSession(out chan<- Message, params SessionParams) *Session {
 20 		pendingChannels: map[string]time.Time{},
 21 	}
 22 
 23-	s.out <- NewMessage("CAP", "LS", "302")
 24+	s.out <- NewMessage("CAP", "LS", "302") // needed to advertise 302 support
 25+	for capability := range SupportedCapabilities {
 26+		s.out <- NewMessage("CAP", "REQ", capability)
 27+	}
 28 	s.out <- NewMessage("NICK", s.nick)
 29 	s.out <- NewMessage("USER", s.user, "0", "*", s.real)
 30+	if s.auth != nil {
 31+		s.out <- NewMessage("AUTHENTICATE", s.auth.Handshake())
 32+		resp, err := s.auth.Respond("+")
 33+		if err != nil {
 34+			panic(err)
 35+		}
 36+		s.out <- NewMessage("AUTHENTICATE", resp)
 37+	}
 38+	s.out <- NewMessage("CAP", "END")
 39 
 40 	return s
 41 }
 42@@ -466,53 +476,18 @@ func (s *Session) handleUnregistered(msg Message) Event {
 43 	switch msg.Command {
 44 	case "AUTHENTICATE":
 45 		if s.auth != nil {
 46-			res, err := s.auth.Respond(msg.Params[0])
 47-			if err != nil {
 48-				s.out <- NewMessage("AUTHENTICATE", "*")
 49+			if msg.Params[0] == "+" {
 50+				// Server has processed the "AUTHENTICATE <mechanism>" message
 51 			} else {
 52-				s.out <- NewMessage("AUTHENTICATE", res)
 53+				// Unexpected AUTHENTICATE message from server, abort authentication.
 54+				s.out <- NewMessage("AUTHENTICATE", "*")
 55 			}
 56 		}
 57 	case rplLoggedin:
 58-		s.out <- NewMessage("CAP", "END")
 59 		s.acct = msg.Params[2]
 60 		s.host = ParsePrefix(msg.Params[1]).Host
 61 	case errNicklocked, errSaslfail, errSasltoolong, errSaslaborted, errSaslalready, rplSaslmechs:
 62-		s.out <- NewMessage("CAP", "END")
 63-	case "CAP":
 64-		switch msg.Params[1] {
 65-		case "LS":
 66-			var willContinue bool
 67-			var ls string
 68-
 69-			if msg.Params[2] == "*" {
 70-				willContinue = true
 71-				ls = msg.Params[3]
 72-			} else {
 73-				willContinue = false
 74-				ls = msg.Params[2]
 75-			}
 76-
 77-			for _, c := range ParseCaps(ls) {
 78-				s.availableCaps[c.Name] = c.Value
 79-			}
 80-
 81-			if !willContinue {
 82-				for c := range s.availableCaps {
 83-					if _, ok := SupportedCapabilities[c]; !ok {
 84-						continue
 85-					}
 86-					s.out <- NewMessage("CAP", "REQ", c)
 87-				}
 88-
 89-				_, ok := s.availableCaps["sasl"]
 90-				if s.auth == nil || !ok {
 91-					s.out <- NewMessage("CAP", "END")
 92-				}
 93-			}
 94-		default:
 95-			return s.handleRegistered(msg)
 96-		}
 97+		// Auth failed, let registration end anyway.
 98 	case errNicknameinuse:
 99 		s.out <- NewMessage("NICK", msg.Params[1]+"_")
100 	case rplSaslsuccess:
101@@ -563,38 +538,26 @@ func (s *Session) handleRegistered(msg Message) Event {
102 					delete(s.enabledCaps, c.Name)
103 				}
104 
105-				if s.auth != nil && c.Name == "sasl" {
106-					h := s.auth.Handshake()
107-					s.out <- NewMessage("AUTHENTICATE", h)
108-				} else if len(s.channels) != 0 && c.Name == "multi-prefix" {
109+				if c.Name == "multi-prefix" {
110 					// TODO merge NAMES commands
111 					for channel := range s.channels {
112 						s.out <- NewMessage("NAMES", channel)
113 					}
114 				}
115 			}
116-		case "NAK":
117-			// do nothing
118 		case "NEW":
119 			for _, c := range ParseCaps(msg.Params[2]) {
120-				s.availableCaps[c.Name] = c.Value
121-				_, ok := SupportedCapabilities[c.Name]
122-				if !ok {
123-					continue
124+				if _, ok := SupportedCapabilities[c.Name]; ok {
125+					s.out <- NewMessage("CAP", "REQ", c.Name)
126 				}
127-				s.out <- NewMessage("CAP", "REQ", c.Name)
128-			}
129-
130-			_, ok := s.availableCaps["sasl"]
131-			if s.acct == "" && ok {
132-				// TODO authenticate
133+				// TODO authenticate if necessary
134 			}
135 		case "DEL":
136 			for _, c := range ParseCaps(msg.Params[2]) {
137-				delete(s.availableCaps, c.Name)
138 				delete(s.enabledCaps, c.Name)
139 			}
140 		}
141+		// do nothing on LS and NAK
142 	case "JOIN":
143 		nickCf := s.Casemap(msg.Prefix.Name)
144 		channelCf := s.Casemap(msg.Params[0])
145@@ -747,7 +710,7 @@ func (s *Session) handleRegistered(msg Message) Event {
146 		if c, ok := s.channels[channelCf]; ok {
147 			return ModeChangeEvent{
148 				Channel: c.Name,
149-				Mode: strings.Join(msg.Params[1:], " "),
150+				Mode:    strings.Join(msg.Params[1:], " "),
151 			}
152 		}
153 	case "PRIVMSG", "NOTICE":