github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/modules/transporter/mqtt/mqtt_subscriber.go (about) 1 package mqtt 2 3 import ( 4 "context" 5 "net/url" 6 "strconv" 7 "strings" 8 9 mqtt "github.com/eclipse/paho.mqtt.golang" 10 "github.com/golang/protobuf/proto" 11 "github.com/pkg/errors" 12 13 "github.com/machinefi/w3bstream/pkg/depends/conf/logger" 14 confmqtt "github.com/machinefi/w3bstream/pkg/depends/conf/mqtt" 15 "github.com/machinefi/w3bstream/pkg/depends/protocol/eventpb" 16 "github.com/machinefi/w3bstream/pkg/depends/x/contextx" 17 "github.com/machinefi/w3bstream/pkg/modules/transporter/proxy" 18 "github.com/machinefi/w3bstream/pkg/types" 19 ) 20 21 type subscriber struct { 22 cli *confmqtt.Client 23 topic string 24 } 25 26 func ParseInboundMessage(msg mqtt.Message) (*eventpb.Event, error) { 27 topic := msg.Topic() 28 29 parts := strings.Split(topic, "/") 30 31 if len(parts) == 1 { 32 ev := &eventpb.Event{} 33 err := proto.Unmarshal(msg.Payload(), ev) 34 return ev, err 35 } 36 37 if len(parts) != 4 && len(parts) != 5 { 38 return nil, ErrInvalidTopicParts 39 } 40 41 if parts[1] != "push" { 42 return nil, ErrNotInboundTopic 43 } 44 45 ev := &eventpb.Event{ 46 Header: &eventpb.Header{ 47 Token: parts[2], 48 EventType: parts[3], 49 }, 50 Payload: msg.Payload(), 51 } 52 if len(parts) == 5 { 53 values, err := url.ParseQuery(parts[4]) 54 if err != nil { 55 return nil, err 56 } 57 if v := values.Get("ts"); v != "" { 58 ev.Header.PubTime, _ = strconv.ParseInt(v, 10, 64) 59 } 60 if v := values.Get("id"); v != "" { 61 ev.Header.EventId = v 62 } 63 } 64 return ev, nil 65 } 66 67 func (s *subscriber) subscribing(ctx context.Context) error { 68 ctx = contextx.WithContextCompose( 69 types.WithProxyClientContext(types.MustProxyClientFromContext(ctx)), 70 )(context.Background()) 71 return s.cli.WithQoS(confmqtt.QOS__ONLY_ONCE).WithTopic(s.topic + "/#").Subscribe( 72 func(c mqtt.Client, msg mqtt.Message) { 73 ctx, l := logger.NewSpanContext(ctx, "transporter.mqtt.subscriber.handle") 74 defer l.End() 75 76 ev, err := ParseInboundMessage(msg) 77 if err != nil { 78 l.Error(err) 79 return 80 } 81 82 l = l.WithValues( 83 "topic", msg.Topic(), 84 "id", ev.Header.GetEventId(), 85 "type", ev.Header.GetEventType(), 86 "time", ev.Header.GetPubTime(), 87 "data", len(ev.Payload), 88 ) 89 90 _, err = proxy.Forward(ctx, s.topic, ev) 91 if err != nil { 92 l.Error(errors.Wrap(err, "forward")) 93 return 94 } 95 l.Info("event forwarded") 96 // rsp, err := json.Marshal(ret) 97 // if err != nil { 98 // l.Error(errors.Wrap(err, "marshal rsp")) 99 // return 100 // } 101 102 // topic := path.Join("ack", ret.(*event.EventRsp).PublisherKey) 103 // cli := s.cli.WithTopic(topic).WithQoS(confmqtt.QOS__ONCE) 104 // if err = cli.Publish(rsp); err != nil { 105 // l.Error(errors.Wrap(err, "publish rsp")) 106 // } 107 }, 108 ) 109 } 110 111 var ( 112 ErrInvalidTopicParts = errors.New("invalid topic parts") 113 ErrNotInboundTopic = errors.New("not inbound topic") 114 )