Remove support for mixed multi-upstream LIST
Multi-upstream connections can still send LIST commands with a network suffix.
This commit is contained in:
parent
d870efa666
commit
0c360d24c5
@ -1837,48 +1837,34 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
|
|||||||
case "LIST":
|
case "LIST":
|
||||||
// TODO: support ELIST when supported by all upstreams
|
// TODO: support ELIST when supported by all upstreams
|
||||||
|
|
||||||
pl := pendingLIST{
|
network := dc.network
|
||||||
downstreamID: dc.id,
|
if network == nil && len(msg.Params) > 0 {
|
||||||
pendingCommands: make(map[int64]*irc.Message),
|
var err error
|
||||||
}
|
network, msg.Params[0], err = dc.unmarshalEntityNetwork(msg.Params[0])
|
||||||
var upstream *upstreamConn
|
|
||||||
var upstreamChannels map[int64][]string
|
|
||||||
if len(msg.Params) > 0 {
|
|
||||||
uc, upstreamMask, err := dc.unmarshalEntity(msg.Params[0])
|
|
||||||
if err == nil && upstreamMask == "*" { // LIST */network: send LIST only to one network
|
|
||||||
upstream = uc
|
|
||||||
} else {
|
|
||||||
upstreamChannels = make(map[int64][]string)
|
|
||||||
channels := strings.Split(msg.Params[0], ",")
|
|
||||||
for _, channel := range channels {
|
|
||||||
uc, upstreamChannel, err := dc.unmarshalEntity(channel)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
upstreamChannels[uc.network.ID] = append(upstreamChannels[uc.network.ID], upstreamChannel)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if network == nil {
|
||||||
|
dc.SendMessage(&irc.Message{
|
||||||
|
Prefix: dc.srv.prefix(),
|
||||||
|
Command: irc.RPL_LISTEND,
|
||||||
|
Params: []string{dc.nick, "LIST without a network suffix is not supported in multi-upstream mode"},
|
||||||
|
})
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
dc.user.pendingLISTs = append(dc.user.pendingLISTs, pl)
|
uc := network.conn
|
||||||
dc.forEachUpstream(func(uc *upstreamConn) {
|
if uc == nil {
|
||||||
if upstream != nil && upstream != uc {
|
dc.SendMessage(&irc.Message{
|
||||||
return
|
Prefix: dc.srv.prefix(),
|
||||||
}
|
Command: irc.RPL_LISTEND,
|
||||||
var params []string
|
Params: []string{dc.nick, "Disconnected from upstream server"},
|
||||||
if upstreamChannels != nil {
|
|
||||||
if channels, ok := upstreamChannels[uc.network.ID]; ok {
|
|
||||||
params = []string{strings.Join(channels, ",")}
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pl.pendingCommands[uc.network.ID] = &irc.Message{
|
|
||||||
Command: "LIST",
|
|
||||||
Params: params,
|
|
||||||
}
|
|
||||||
uc.trySendLIST(dc.id)
|
|
||||||
})
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
uc.enqueueLIST(dc, msg)
|
||||||
case "NAMES":
|
case "NAMES":
|
||||||
if len(msg.Params) == 0 {
|
if len(msg.Params) == 0 {
|
||||||
dc.SendMessage(&irc.Message{
|
dc.SendMessage(&irc.Message{
|
||||||
|
133
upstream.go
133
upstream.go
@ -72,6 +72,11 @@ func (uc *upstreamChannel) updateAutoDetach(dur time.Duration) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type pendingUpstreamCommand struct {
|
||||||
|
downstreamID uint64
|
||||||
|
cmd *irc.Message
|
||||||
|
}
|
||||||
|
|
||||||
type upstreamConn struct {
|
type upstreamConn struct {
|
||||||
conn
|
conn
|
||||||
|
|
||||||
@ -104,8 +109,10 @@ type upstreamConn struct {
|
|||||||
|
|
||||||
casemapIsSet bool
|
casemapIsSet bool
|
||||||
|
|
||||||
// set of LIST commands in progress, per downstream
|
// Queue of LIST commands in progress. The first entry has been sent to the
|
||||||
pendingLISTDownstreamSet map[uint64]struct{}
|
// server and is awaiting reply. The following entries have not been sent
|
||||||
|
// yet.
|
||||||
|
pendingLIST []pendingUpstreamCommand
|
||||||
|
|
||||||
gotMotd bool
|
gotMotd bool
|
||||||
}
|
}
|
||||||
@ -201,7 +208,6 @@ func connectToUpstream(network *network) (*upstreamConn, error) {
|
|||||||
availableChannelModes: stdChannelModes,
|
availableChannelModes: stdChannelModes,
|
||||||
availableMemberships: stdMemberships,
|
availableMemberships: stdMemberships,
|
||||||
isupport: make(map[string]*string),
|
isupport: make(map[string]*string),
|
||||||
pendingLISTDownstreamSet: make(map[uint64]struct{}),
|
|
||||||
}
|
}
|
||||||
return uc, nil
|
return uc, nil
|
||||||
}
|
}
|
||||||
@ -235,28 +241,9 @@ func (uc *upstreamConn) isOurNick(nick string) bool {
|
|||||||
return uc.nickCM == uc.network.casemap(nick)
|
return uc.nickCM == uc.network.casemap(nick)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uc *upstreamConn) getPendingLIST() *pendingLIST {
|
func (uc *upstreamConn) endPendingLISTs() {
|
||||||
for _, pl := range uc.user.pendingLISTs {
|
for _, pendingCmd := range uc.pendingLIST {
|
||||||
if _, ok := pl.pendingCommands[uc.network.ID]; !ok {
|
uc.forEachDownstreamByID(pendingCmd.downstreamID, func(dc *downstreamConn) {
|
||||||
continue
|
|
||||||
}
|
|
||||||
return &pl
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (uc *upstreamConn) endPendingLISTs(all bool) (found bool) {
|
|
||||||
found = false
|
|
||||||
for i := 0; i < len(uc.user.pendingLISTs); i++ {
|
|
||||||
pl := uc.user.pendingLISTs[i]
|
|
||||||
if _, ok := pl.pendingCommands[uc.network.ID]; !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
delete(pl.pendingCommands, uc.network.ID)
|
|
||||||
if len(pl.pendingCommands) == 0 {
|
|
||||||
uc.user.pendingLISTs = append(uc.user.pendingLISTs[:i], uc.user.pendingLISTs[i+1:]...)
|
|
||||||
i--
|
|
||||||
uc.forEachDownstreamByID(pl.downstreamID, func(dc *downstreamConn) {
|
|
||||||
dc.SendMessage(&irc.Message{
|
dc.SendMessage(&irc.Message{
|
||||||
Prefix: dc.srv.prefix(),
|
Prefix: dc.srv.prefix(),
|
||||||
Command: irc.RPL_LISTEND,
|
Command: irc.RPL_LISTEND,
|
||||||
@ -264,46 +251,55 @@ func (uc *upstreamConn) endPendingLISTs(all bool) (found bool) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
found = true
|
uc.pendingLIST = nil
|
||||||
if !all {
|
|
||||||
delete(uc.pendingLISTDownstreamSet, pl.downstreamID)
|
|
||||||
uc.user.forEachUpstream(func(uc *upstreamConn) {
|
|
||||||
uc.trySendLIST(pl.downstreamID)
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uc *upstreamConn) trySendLIST(downstreamID uint64) {
|
func (uc *upstreamConn) sendNextPendingLIST() {
|
||||||
if _, ok := uc.pendingLISTDownstreamSet[downstreamID]; ok {
|
if len(uc.pendingLIST) == 0 {
|
||||||
// a LIST command is already pending
|
|
||||||
// we will try again when that command is completed
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
uc.SendMessage(uc.pendingLIST[0].cmd)
|
||||||
|
}
|
||||||
|
|
||||||
for _, pl := range uc.user.pendingLISTs {
|
func (uc *upstreamConn) enqueueLIST(dc *downstreamConn, cmd *irc.Message) {
|
||||||
if pl.downstreamID != downstreamID {
|
uc.pendingLIST = append(uc.pendingLIST, pendingUpstreamCommand{
|
||||||
continue
|
downstreamID: dc.id,
|
||||||
}
|
cmd: cmd,
|
||||||
// this is the first pending LIST command list of the downstream
|
})
|
||||||
listCommand, ok := pl.pendingCommands[uc.network.ID]
|
|
||||||
if !ok {
|
|
||||||
// there is no command for this upstream in these LIST commands
|
|
||||||
// do not send anything
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// there is a command for this upstream in these LIST commands
|
|
||||||
// send it now
|
|
||||||
|
|
||||||
uc.SendMessageLabeled(downstreamID, listCommand)
|
if len(uc.pendingLIST) == 1 {
|
||||||
|
uc.sendNextPendingLIST()
|
||||||
uc.pendingLISTDownstreamSet[downstreamID] = struct{}{}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (uc *upstreamConn) currentPendingLIST() (*downstreamConn, *irc.Message) {
|
||||||
|
if len(uc.pendingLIST) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingCmd := uc.pendingLIST[0]
|
||||||
|
for _, dc := range uc.user.downstreamConns {
|
||||||
|
if dc.id == pendingCmd.downstreamID {
|
||||||
|
return dc, pendingCmd.cmd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, pendingCmd.cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *upstreamConn) dequeueLIST() (*downstreamConn, *irc.Message) {
|
||||||
|
dc, cmd := uc.currentPendingLIST()
|
||||||
|
|
||||||
|
if len(uc.pendingLIST) > 0 {
|
||||||
|
copy(uc.pendingLIST, uc.pendingLIST[1:])
|
||||||
|
uc.pendingLIST = uc.pendingLIST[:len(uc.pendingLIST)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
uc.sendNextPendingLIST()
|
||||||
|
|
||||||
|
return dc, cmd
|
||||||
|
}
|
||||||
|
|
||||||
func (uc *upstreamConn) parseMembershipPrefix(s string) (ms *memberships, nick string) {
|
func (uc *upstreamConn) parseMembershipPrefix(s string) (ms *memberships, nick string) {
|
||||||
memberships := make(memberships, 0, 4)
|
memberships := make(memberships, 0, 4)
|
||||||
i := 0
|
i := 0
|
||||||
@ -1099,23 +1095,31 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
pl := uc.getPendingLIST()
|
dc, cmd := uc.currentPendingLIST()
|
||||||
if pl == nil {
|
if cmd == nil {
|
||||||
return fmt.Errorf("unexpected RPL_LIST: no matching pending LIST")
|
return fmt.Errorf("unexpected RPL_LIST: no matching pending LIST")
|
||||||
|
} else if dc == nil {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
uc.forEachDownstreamByID(pl.downstreamID, func(dc *downstreamConn) {
|
|
||||||
dc.SendMessage(&irc.Message{
|
dc.SendMessage(&irc.Message{
|
||||||
Prefix: dc.srv.prefix(),
|
Prefix: dc.srv.prefix(),
|
||||||
Command: irc.RPL_LIST,
|
Command: irc.RPL_LIST,
|
||||||
Params: []string{dc.nick, dc.marshalEntity(uc.network, channel), clients, topic},
|
Params: []string{dc.nick, dc.marshalEntity(uc.network, channel), clients, topic},
|
||||||
})
|
})
|
||||||
})
|
|
||||||
case irc.RPL_LISTEND:
|
case irc.RPL_LISTEND:
|
||||||
ok := uc.endPendingLISTs(false)
|
dc, cmd := uc.dequeueLIST()
|
||||||
if !ok {
|
if cmd == nil {
|
||||||
return fmt.Errorf("unexpected RPL_LISTEND: no matching pending LIST")
|
return fmt.Errorf("unexpected RPL_LISTEND: no matching pending LIST")
|
||||||
|
} else if dc == nil {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dc.SendMessage(&irc.Message{
|
||||||
|
Prefix: dc.srv.prefix(),
|
||||||
|
Command: irc.RPL_LISTEND,
|
||||||
|
Params: []string{dc.nick, "End of /LIST"},
|
||||||
|
})
|
||||||
case irc.RPL_NAMREPLY:
|
case irc.RPL_NAMREPLY:
|
||||||
var name, statusStr, members string
|
var name, statusStr, members string
|
||||||
if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
|
if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
|
||||||
@ -1433,10 +1437,7 @@ func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if command == "LIST" {
|
if command == "LIST" {
|
||||||
ok := uc.endPendingLISTs(false)
|
uc.endPendingLISTs()
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("unexpected response for LIST: %q: no matching pending LIST", msg.Command)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
|
uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
|
||||||
|
11
user.go
11
user.go
@ -405,15 +405,6 @@ type user struct {
|
|||||||
networks []*network
|
networks []*network
|
||||||
downstreamConns []*downstreamConn
|
downstreamConns []*downstreamConn
|
||||||
msgStore messageStore
|
msgStore messageStore
|
||||||
|
|
||||||
// LIST commands in progress
|
|
||||||
pendingLISTs []pendingLIST
|
|
||||||
}
|
|
||||||
|
|
||||||
type pendingLIST struct {
|
|
||||||
downstreamID uint64
|
|
||||||
// list of per-upstream LIST commands not yet sent or completed
|
|
||||||
pendingCommands map[int64]*irc.Message
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUser(srv *Server, record *User) *user {
|
func newUser(srv *Server, record *User) *user {
|
||||||
@ -690,7 +681,7 @@ func (u *user) run() {
|
|||||||
func (u *user) handleUpstreamDisconnected(uc *upstreamConn) {
|
func (u *user) handleUpstreamDisconnected(uc *upstreamConn) {
|
||||||
uc.network.conn = nil
|
uc.network.conn = nil
|
||||||
|
|
||||||
uc.endPendingLISTs(true)
|
uc.endPendingLISTs()
|
||||||
|
|
||||||
for _, entry := range uc.channels.innerMap {
|
for _, entry := range uc.channels.innerMap {
|
||||||
uch := entry.value.(*upstreamChannel)
|
uch := entry.value.(*upstreamChannel)
|
||||||
|
Loading…
Reference in New Issue
Block a user