Skip to content

Commit b3b797e

Browse files
authored
resubscribe in OnConnectionUp (#49)
* resubscribe in OnConnectionUp * Protect MqttEngine.TopicData with a mutex
1 parent 58142ca commit b3b797e

2 files changed

Lines changed: 71 additions & 43 deletions

File tree

mqtt_utils.go

Lines changed: 69 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -124,23 +124,6 @@ func NewMqttEngine(creator, clientid string, pubsub uint8, statusch chan Compone
124124
// me.Cancel is used to tell the paho connection manager to stop
125125
ctx, me.Cancel = context.WithCancel(context.Background())
126126

127-
var subs []paho.SubscribeOptions
128-
if me.CanSubscribe {
129-
nolocal := false
130-
for topic, data := range me.TopicData {
131-
// if data.ValidatorKey != nil {
132-
if data.SubMode != "" {
133-
lg.Printf("MQTT Engine: subscribing to topic %s with mode %s, qos: %d", topic, data.SubMode, me.QoS)
134-
subs = append(subs, paho.SubscribeOptions{Topic: topic, QoS: byte(me.QoS), NoLocal: nolocal})
135-
}
136-
}
137-
138-
// log.Printf("MQTT Engine: there are %d topics to subscribe to", len(subs))
139-
// for _, v := range subs {
140-
// lg.Printf("MQTT Engine: subscribing to topic %s", v.Topic)
141-
// }
142-
}
143-
144127
apcConfig := autopaho.ClientConfig{
145128
ServerUrls: []*url.URL{serverURL},
146129
TlsCfg: &tls.Config{
@@ -153,6 +136,26 @@ func NewMqttEngine(creator, clientid string, pubsub uint8, statusch chan Compone
153136
SessionExpiryInterval: 60,
154137
OnConnectionUp: func(cm *autopaho.ConnectionManager, connAck *paho.Connack) {
155138
lg.Printf("MQTT Engine %s: MQTT connection up", me.Creator)
139+
140+
var subs []paho.SubscribeOptions
141+
if me.CanSubscribe {
142+
nolocal := false
143+
me.DataMu.Lock()
144+
for topic, data := range me.TopicData {
145+
// if data.ValidatorKey != nil {
146+
if data.SubMode != "" {
147+
lg.Printf("MQTT Engine: subscribing to topic %s with mode %s, qos: %d", topic, data.SubMode, me.QoS)
148+
subs = append(subs, paho.SubscribeOptions{Topic: topic, QoS: byte(me.QoS), NoLocal: nolocal})
149+
}
150+
}
151+
me.DataMu.Unlock()
152+
153+
// log.Printf("MQTT Engine: there are %d topics to subscribe to", len(subs))
154+
// for _, v := range subs {
155+
// lg.Printf("MQTT Engine: subscribing to topic %s", v.Topic)
156+
// }
157+
}
158+
156159
if subs != nil {
157160
lg.Printf("MQTT Engine %s: subscribing to topics: %v", me.Creator, subs)
158161
sa, err := cm.Subscribe(context.Background(), &paho.Subscribe{
@@ -309,6 +312,7 @@ func NewMqttEngine(creator, clientid string, pubsub uint8, statusch chan Compone
309312
}
310313

311314
var payload []byte
315+
me.DataMu.Lock()
312316
td := me.TopicData[outbox.Topic]
313317
// signingkey := me.SigningKeys[outbox.Topic]
314318
if td.Sign {
@@ -329,6 +333,7 @@ func NewMqttEngine(creator, clientid string, pubsub uint8, statusch chan Compone
329333
payload = buf.Bytes()
330334
lg.Printf("MQTT Engine %s: not signing raw message being sent to topic %s", me.Creator, outbox.Topic)
331335
}
336+
me.DataMu.Unlock()
332337

333338
mqttMsg := paho.Publish{
334339
Topic: outbox.Topic,
@@ -345,9 +350,11 @@ func NewMqttEngine(creator, clientid string, pubsub uint8, statusch chan Compone
345350
fmt.Printf("MQTT Engine %s: publish qos: %d, response: %+v\n", me.Creator, me.QoS, pubresponse)
346351
dump.P(pubresponse)
347352

353+
me.DataMu.Lock()
348354
td.PubMsgs++
349355
td.LatestPub = time.Now()
350356
me.TopicData[outbox.Topic] = td
357+
me.DataMu.Unlock()
351358
if GlobalCF.Debug {
352359
lg.Printf("MQTT Engine %s: sent message on topic %s: %s", me.Creator, outbox.Topic, string(payload))
353360
}
@@ -365,18 +372,21 @@ func NewMqttEngine(creator, clientid string, pubsub uint8, statusch chan Compone
365372
lg.Printf("MQTT Engine %s: error fetching topic data for topic %s: %v", me.Creator, inbox.Packet.Topic, err)
366373
continue
367374
}
375+
me.DataMu.Lock()
368376
td.SubMsgs++
369377
td.LatestSub = time.Now()
378+
validate := td.Validate
379+
subCh := td.SubscriberCh
370380
me.TopicData[td.Topic] = td
381+
me.DataMu.Unlock()
371382

372383
mpi := MqttPkgIn{
373384
TimeStamp: time.Now(),
374385
Topic: inbox.Packet.Topic,
375386
Payload: inbox.Packet.Payload,
376387
Validated: false,
377388
}
378-
379-
if td.Validate {
389+
if validate {
380390
payload, err := jws.Verify(inbox.Packet.Payload, jws.WithKeySet(me.Keystore))
381391
if err != nil {
382392
mpi.Error = true
@@ -392,9 +402,9 @@ func NewMqttEngine(creator, clientid string, pubsub uint8, statusch chan Compone
392402
} else {
393403
lg.Printf("MQTT Engine %s: unvalidated message: %s", me.Creator, inbox.Packet.Payload)
394404
}
395-
lg.Printf("MQTT Engine td.SubscriberCh: %+v", td.SubscriberCh)
396-
if td.SubscriberCh != nil {
397-
td.SubscriberCh <- mpi
405+
lg.Printf("MQTT Engine td.SubscriberCh: %+v", subCh)
406+
if subCh != nil {
407+
subCh <- mpi
398408
} else {
399409
lg.Printf("MQTT Engine %s: no subscriber channel for topic %s. Dropping message.", me.Creator, inbox.Packet.Topic)
400410
}
@@ -421,18 +431,19 @@ func NewMqttEngine(creator, clientid string, pubsub uint8, statusch chan Compone
421431
return &me, nil
422432
}
423433

424-
func (me *MqttEngine) PubToTopic(topic string, signingkey *ecdsa.PrivateKey, mode string, sign bool) (map[string]TopicData, error) {
434+
func (me *MqttEngine) PubToTopic(topic string, signingkey *ecdsa.PrivateKey, mode string, sign bool) error {
425435
if topic == "" {
426-
return me.TopicData, fmt.Errorf("PubToTopic: topic not specified")
436+
return fmt.Errorf("PubToTopic: topic not specified")
427437
}
428438
if signingkey == nil && sign {
429-
return me.TopicData, fmt.Errorf("PubToTopic: no signing key specified and signing requested")
439+
return fmt.Errorf("PubToTopic: no signing key specified and signing requested")
430440
}
431441

432442
if mode != "raw" && mode != "struct" {
433-
return me.TopicData, fmt.Errorf("PubToTopic: unknown mode: %s", mode)
443+
return fmt.Errorf("PubToTopic: unknown mode: %s", mode)
434444
}
435445

446+
me.DataMu.Lock()
436447
if _, exist := me.TopicData[topic]; !exist {
437448
me.TopicData[topic] = TopicData{}
438449
}
@@ -447,6 +458,8 @@ func (me *MqttEngine) PubToTopic(topic string, signingkey *ecdsa.PrivateKey, mod
447458
}
448459

449460
me.TopicData[topic] = tdata
461+
topicDataLen := len(me.TopicData)
462+
me.DataMu.Unlock()
450463

451464
log.Printf("MQTT Engine %s: added pub topic %s. Engine now has %d topics", me.Creator, topic, len(me.TopicData))
452465

@@ -460,34 +473,35 @@ func (me *MqttEngine) PubToTopic(topic string, signingkey *ecdsa.PrivateKey, mod
460473
},
461474
},
462475
}); err != nil {
463-
return me.TopicData, fmt.Errorf("AddTopic: failed to subscribe to topic %s: %v", topic, err)
476+
return fmt.Errorf("AddTopic: failed to subscribe to topic %s: %v", topic, err)
464477
}
465-
log.Printf("MQTT Engine %s: added topic %s to running MQTT Engine. Engine now has %d topics", me.Creator, topic, len(me.TopicData))
478+
log.Printf("MQTT Engine %s: added topic %s to running MQTT Engine. Engine now has %d topics", me.Creator, topic, topicDataLen)
466479
}
467480

468-
return me.TopicData, nil
481+
return nil
469482
}
470483

471484
func (me *MqttEngine) SubToTopic(topic string,
472485
subscriberCh chan MqttPkgIn, mode string, validate bool,
473-
) (map[string]TopicData, error) {
486+
) error {
474487
log.Printf("MQTT Engine: SubToTopic: topic %s, subscriberCh %v, mode %s, validate %t", topic, subscriberCh, mode, validate)
475488
if topic == "" {
476-
return me.TopicData, fmt.Errorf("SubToTopic: topic not specified")
489+
return fmt.Errorf("SubToTopic: topic not specified")
477490
}
478491

479492
if mode != "raw" && mode != "struct" {
480-
return me.TopicData, fmt.Errorf("SubToTopic: unknown mode: %s", mode)
493+
return fmt.Errorf("SubToTopic: unknown mode: %s", mode)
481494
}
482495

483496
if subscriberCh == nil {
484-
return me.TopicData, fmt.Errorf("SubToTopic: subscriber channel not specified")
497+
return fmt.Errorf("SubToTopic: subscriber channel not specified")
485498
}
486499

487500
if strings.HasSuffix(topic, "/#") {
488501
me.PrefixTopics[strings.TrimSuffix(topic, "#")] = true
489502
}
490503

504+
me.DataMu.Lock()
491505
if _, exist := me.TopicData[topic]; !exist {
492506
me.TopicData[topic] = TopicData{
493507
Topic: topic,
@@ -500,7 +514,9 @@ func (me *MqttEngine) SubToTopic(topic string,
500514
tdata.SubscriberCh = subscriberCh
501515

502516
me.TopicData[topic] = tdata
503-
log.Printf("MQTT Engine %s: added sub topic %s (validate %t, mode %s). Engine now has %d topics", me.Creator, topic, validate, mode, len(me.TopicData))
517+
topicDataLen := len(me.TopicData)
518+
me.DataMu.Unlock()
519+
log.Printf("MQTT Engine %s: added sub topic %s (validate %t, mode %s). Engine now has %d topics", me.Creator, topic, validate, mode, topicDataLen)
504520

505521
// does the MqttEngine already have a connection manager (i.e. is it already running)
506522
if me.ConnectionManager != nil {
@@ -512,7 +528,7 @@ func (me *MqttEngine) SubToTopic(topic string,
512528
},
513529
},
514530
}); err != nil {
515-
return me.TopicData, fmt.Errorf("SubToTopic: failed to subscribe to topic %s: %v", topic, err)
531+
return fmt.Errorf("SubToTopic: failed to subscribe to topic %s: %v", topic, err)
516532
}
517533
var topics, prefixTopics []string
518534
for t := range me.TopicData {
@@ -522,25 +538,29 @@ func (me *MqttEngine) SubToTopic(topic string,
522538
prefixTopics = append(prefixTopics, t)
523539
}
524540
log.Printf("MQTT Engine %s: added sub topic %s to running MQTT Engine.", me.Creator, topic)
525-
log.Printf("Engine now has %d topics: %v and %d prefix topics: %v", len(me.TopicData), topics, len(me.PrefixTopics), prefixTopics)
541+
log.Printf("Engine now has %d topics: %v and %d prefix topics: %v", topicDataLen, topics, len(me.PrefixTopics), prefixTopics)
526542
}
527543

544+
me.DataMu.Lock()
528545
log.Printf("MQTT Engine %s: TopicData for topic %s: %+v", me.Creator, topic, me.TopicData[topic])
546+
me.DataMu.Unlock()
529547

530-
return me.TopicData, nil
548+
return nil
531549
}
532550

533-
func (me *MqttEngine) RemoveTopic(topic string) (map[string]TopicData, error) {
551+
func (me *MqttEngine) RemoveTopic(topic string) error {
534552
if me.ConnectionManager != nil {
535553
if _, err := me.ConnectionManager.Unsubscribe(context.Background(), &paho.Unsubscribe{
536554
Topics: []string{topic},
537555
}); err != nil {
538-
return me.TopicData, fmt.Errorf("RemoveTopic: failed to unsubscribe from topic %s: %v", topic, err)
556+
return fmt.Errorf("RemoveTopic: failed to unsubscribe from topic %s: %v", topic, err)
539557
}
540558
}
559+
me.DataMu.Lock()
541560
delete(me.TopicData, topic)
542561
log.Printf("MQTT Engine: removed topic %s. Engine now has %d topics", topic, len(me.TopicData))
543-
return me.TopicData, nil
562+
me.DataMu.Unlock()
563+
return nil
544564
}
545565

546566
func (me *MqttEngine) StartEngine() (chan MqttEngineCmd, chan MqttPkgOut, chan MqttPkgIn, error) {
@@ -601,14 +621,20 @@ func (me *MqttEngine) SetupInterruptHandler() {
601621
}
602622

603623
func (me *MqttEngine) FetchTopicData(topic string) (TopicData, error) {
604-
if td, exist := me.TopicData[topic]; exist {
605-
log.Printf("MQTT Engine %s: topic %s: exact match found. TopicData: %+v", me.Creator, topic, td)
624+
me.DataMu.Lock()
625+
td, exist := me.TopicData[topic]
626+
me.DataMu.Unlock()
627+
if exist {
628+
log.Printf("MQTT Engine %s: topic %s: exact match found.", me.Creator, topic)
606629
return td, nil
607630
}
608631
for prefix := range me.PrefixTopics {
609632
if strings.HasPrefix(topic, prefix) {
633+
me.DataMu.Lock()
634+
tdPrefix := me.TopicData[topic]
610635
log.Printf("MQTT Engine %s: topic %s matches prefix %s. TopicData: %+v", me.Creator, topic, prefix, me.TopicData[prefix])
611-
return me.TopicData[prefix+"#"], nil
636+
me.DataMu.Unlock()
637+
return tdPrefix, nil
612638
}
613639
}
614640
return TopicData{}, fmt.Errorf("FetchTopicData: topic %s not found", topic)

structs.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"crypto/x509"
1212
"log"
1313
"net/http"
14+
"sync"
1415
"time"
1516

1617
"github.com/eclipse/paho.golang/autopaho"
@@ -296,6 +297,7 @@ type MqttEngine struct {
296297
CmdChan chan MqttEngineCmd
297298
PublishChan chan MqttPkgOut
298299
SubscribeChan chan MqttPkgIn
300+
DataMu sync.Mutex
299301
TopicData map[string]TopicData // map[topic]TopicData
300302
PrefixTopics map[string]bool // eg. "pubkey/up/" is a prefix topic if we subscribe to pubkey/up/#
301303
CanPublish bool // can publish to all topics

0 commit comments

Comments
 (0)