package relay // Pool manages connections to multiple relays. type Pool struct { conns map[string]*Conn } // NewPool creates a relay pool. func NewPool() (p *Pool) { return &Pool{ conns: map[string]*Conn{}, } } // Connect gets or creates a connection to a relay. // // An existing Conn is reused even when it is currently closed: Conn.maybeReconnect // redials the SAME object, and its flushSubs re-establishes every subscription // held in c.subs. Replacing it instead orphaned those subscriptions - the new // Conn started with an empty registry, nothing re-subscribed the entries the // consumer still had registered, and that relay went silently dead for them. func (p *Pool) Connect(url string) (c *Conn) { if existing, ok := p.conns[url]; ok { return existing } c = Dial(url) p.conns[url] = c return c } // Get returns an existing connection, or nil. func (p *Pool) Get(url string) (c *Conn) { got, ok := p.conns[url] if !ok || !got.IsOpen() { return nil } return got } // Disconnect closes and removes a connection. func (p *Pool) Disconnect(url string) { if c, ok2 := p.conns[url]; ok2 { c.Close() delete(p.conns, url) } } // CloseAll closes all connections. func (p *Pool) CloseAll() { for url, c := range p.conns { c.Close() delete(p.conns, url) } } // URLs returns all connected relay URLs. func (p *Pool) URLs() (ss []string) { var out []string for url, c := range p.conns { if c.IsOpen() { out = push(out, url) } } return out } // AllConns returns all connections regardless of state. func (p *Pool) AllConns() (cs []*Conn) { p.evictClosed() var out []*Conn for _, c := range p.conns { out = push(out, c) } return out } // evictClosed removes closed connections that hold no subscriptions. A closed // Conn that still holds them is mid-reconnect: it redials itself and flushes // them again, so dropping it here would lose them for good. func (p *Pool) evictClosed() { for url, c := range p.conns { if c.state == StateClosed && len(c.subs) == 0 { delete(p.conns, url) } } }