func runClientServerTests(t testing.TB, f func(*Client)) { var wg sync.WaitGroup ready1 := make(chan struct{}) ready2 := make(chan struct{}) uri := "tcp://127.0.0.1:1883" u, err := url.Parse(uri) require.NoError(t, err, "Error parsing URL") // Start listener wg.Add(1) go startService(t, u, &wg, ready1, ready2) <-ready1 c := connectToServer(t, uri) if c == nil { return } defer topics.Unregister(c.svc.sess.ID()) if f != nil { f(c) } c.Disconnect() close(ready2) wg.Wait() }
func startServiceN(t testing.TB, u *url.URL, wg *sync.WaitGroup, ready1, ready2 chan struct{}, cnt int) { defer wg.Done() topics.Unregister("mem") tp := topics.NewMemProvider() topics.Register("mem", tp) sessions.Unregister("mem") sp := sessions.NewMemProvider() sessions.Register("mem", sp) ln, err := net.Listen(u.Scheme, u.Host) require.NoError(t, err) defer ln.Close() close(ready1) svr := &Server{ Authenticator: authenticator, } for i := 0; i < cnt; i++ { conn, err := ln.Accept() require.NoError(t, err) _, err = svr.handleConnection(conn) if authenticator == "mockFailure" { require.Error(t, err) return } else { require.NoError(t, err) } } <-ready2 for _, svc := range svr.svcs { glog.Infof("Stopping service %d", svc.id) svc.stop() } }
// FIXME: The order of closing here causes panic sometimes. For example, if receiver // calls this, and closes the buffers, somehow it causes buffer.go:476 to panid. func (this *service) stop() { defer func() { // Let's recover from panic if r := recover(); r != nil { glog.Errorf("(%s) Recovering from panic: %v", this.cid(), r) } }() doit := atomic.CompareAndSwapInt64(&this.closed, 0, 1) if !doit { return } // Close quit channel, effectively telling all the goroutines it's time to quit if this.done != nil { glog.Debugf("(%s) closing this.done", this.cid()) close(this.done) } // Close the network connection if this.conn != nil { glog.Debugf("(%s) closing this.conn", this.cid()) this.conn.Close() } this.in.Close() this.out.Close() // Wait for all the goroutines to stop. this.wgStopped.Wait() glog.Debugf("(%s) Received %d bytes in %d messages.", this.cid(), this.inStat.bytes, this.inStat.msgs) glog.Debugf("(%s) Sent %d bytes in %d messages.", this.cid(), this.outStat.bytes, this.outStat.msgs) // Unsubscribe from all the topics for this client, only for the server side though if !this.client && this.sess != nil { topics, _, err := this.sess.Topics() if err != nil { glog.Errorf("(%s/%d): %v", this.cid(), this.id, err) } else { for _, t := range topics { if err := this.topicsMgr.Unsubscribe([]byte(t), &this.onpub); err != nil { glog.Errorf("(%s): Error unsubscribing topic %q: %v", this.cid(), t, err) } } } } // Publish will message if WillFlag is set. Server side only. if !this.client && this.sess.Cmsg.WillFlag() { glog.Infof("(%s) service/stop: connection unexpectedly closed. Sending Will.", this.cid()) this.onPublish(this.sess.Will) } // Remove the client topics manager if this.client { topics.Unregister(this.sess.ID()) } // Remove the session from session store if it's suppose to be clean session if this.sess.Cmsg.CleanSession() && this.sessMgr != nil { this.sessMgr.Del(this.sess.ID()) } this.conn = nil this.in = nil this.out = nil }
func TestServiceWillDelivery(t *testing.T) { var wg sync.WaitGroup ready1 := make(chan struct{}) ready2 := make(chan struct{}) ready3 := make(chan struct{}) subscribers := 3 uri := "tcp://127.0.0.1:1883" u, err := url.Parse(uri) require.NoError(t, err, "Error parsing URL") // Start listener wg.Add(1) go startServiceN(t, u, &wg, ready1, ready2, subscribers) <-ready1 c1 := connectToServer(t, uri) require.NotNil(t, c1) defer topics.Unregister(c1.svc.sess.ID()) c2 := connectToServer(t, uri) require.NotNil(t, c2) defer topics.Unregister(c2.svc.sess.ID()) c3 := connectToServer(t, uri) require.NotNil(t, c3) defer topics.Unregister(c3.svc.sess.ID()) sub := message.NewSubscribeMessage() sub.AddTopic([]byte("will"), 1) subdone := int64(0) willdone := int64(0) c2.Subscribe(sub, func(msg, ack message.Message, err error) error { subs := atomic.AddInt64(&subdone, 1) if subs == int64(subscribers-1) { c1.Disconnect() } return nil }, func(msg *message.PublishMessage) error { require.Equal(t, message.QosAtLeastOnce, msg.QoS()) require.Equal(t, []byte("send me home"), msg.Payload()) will := atomic.AddInt64(&willdone, 1) if will == int64(subscribers-1) { close(ready3) } return nil }) c3.Subscribe(sub, func(msg, ack message.Message, err error) error { subs := atomic.AddInt64(&subdone, 1) if subs == int64(subscribers-1) { c1.Disconnect() } return nil }, func(msg *message.PublishMessage) error { require.Equal(t, message.QosAtLeastOnce, msg.QoS()) require.Equal(t, []byte("send me home"), msg.Payload()) will := atomic.AddInt64(&willdone, 1) if will == int64(subscribers-1) { close(ready3) } return nil }) select { case <-ready3: case <-time.After(time.Millisecond * 100): require.FailNow(t, "Test timed out") } c2.Disconnect() close(ready2) wg.Wait() }