github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/communication/nats/receiver.go (about) 1 /* 2 * Copyright (C) 2017 The "MysteriumNetwork/node" Authors. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package nats 19 20 import ( 21 "sync" 22 23 "github.com/mysteriumnetwork/node/communication" 24 "github.com/nats-io/nats.go" 25 "github.com/pkg/errors" 26 "github.com/rs/zerolog/log" 27 ) 28 29 // NewReceiver constructs new Receiver's instance which works through NATS connection. 30 // Codec packs/unpacks messages to byte payloads. 31 // Topic (optional) if need to send messages prefixed topic. 32 func NewReceiver(connection Connection, codec communication.Codec, topic string) *receiverNATS { 33 return &receiverNATS{ 34 connection: connection, 35 codec: codec, 36 subs: make(map[string]*nats.Subscription), 37 } 38 } 39 40 type receiverNATS struct { 41 connection Connection 42 codec communication.Codec 43 44 mu sync.Mutex 45 subs map[string]*nats.Subscription 46 } 47 48 func (receiver *receiverNATS) Receive(consumer communication.MessageConsumer) error { 49 messageEndpoint, err := consumer.GetMessageEndpoint() 50 if err != nil { 51 return err 52 } 53 messageTopic := string(messageEndpoint) 54 55 messageHandler := func(msg *nats.Msg) { 56 log.WithLevel(levelFor(messageTopic)).Msgf("Message %q received: %s", messageTopic, msg.Data) 57 messagePtr := consumer.NewMessage() 58 err := receiver.codec.Unpack(msg.Data, messagePtr) 59 if err != nil { 60 err = errors.Wrapf(err, "failed to unpack message %q", messageTopic) 61 log.Error().Err(err).Msg("") 62 return 63 } 64 65 err = consumer.Consume(messagePtr) 66 if err != nil { 67 err = errors.Wrapf(err, "failed to process message %q", messageTopic) 68 log.Error().Err(err).Msg("") 69 return 70 } 71 } 72 73 receiver.mu.Lock() 74 defer receiver.mu.Unlock() 75 76 subscription, err := receiver.connection.Subscribe(messageTopic, messageHandler) 77 if err != nil { 78 err = errors.Wrapf(err, "failed subscribe message '%s'", messageTopic) 79 return err 80 } 81 receiver.subs[messageTopic] = subscription 82 return nil 83 } 84 85 func (receiver *receiverNATS) ReceiveUnsubscribe(endpoint communication.MessageEndpoint) { 86 receiver.mu.Lock() 87 defer receiver.mu.Unlock() 88 89 messageTopic := string(endpoint) 90 subscription, found := receiver.subs[messageTopic] 91 if !found { 92 log.Error().Msg("Unknown topic to unsubscribe: " + messageTopic) 93 return 94 } 95 96 if err := subscription.Unsubscribe(); err != nil { 97 log.Error().Err(err).Msg("Failed to unsubscribe from topic: " + messageTopic) 98 return 99 } 100 101 log.Info().Msg("Unsubscribed from " + messageTopic) 102 } 103 104 func (receiver *receiverNATS) Unsubscribe() { 105 receiver.mu.Lock() 106 defer receiver.mu.Unlock() 107 108 for topic, s := range receiver.subs { 109 if err := s.Unsubscribe(); err != nil { 110 log.Error().Err(err).Msg("Failed to unsubscribe from topic: " + topic) 111 return 112 } 113 log.Info().Msg("Unsubscribed from " + topic) 114 } 115 } 116 117 func (receiver *receiverNATS) Respond(consumer communication.RequestConsumer) error { 118 requestEndpoint, err := consumer.GetRequestEndpoint() 119 if err != nil { 120 return err 121 } 122 requestTopic := string(requestEndpoint) 123 124 messageHandler := func(msg *nats.Msg) { 125 log.WithLevel(levelFor(requestTopic)).Msgf("Request %q received: %s", requestTopic, msg.Data) 126 requestPtr := consumer.NewRequest() 127 err := receiver.codec.Unpack(msg.Data, requestPtr) 128 if err != nil { 129 err = errors.Wrapf(err, "failed to unpack request '%s'", requestTopic) 130 log.Error().Err(err).Msg("") 131 return 132 } 133 134 response, err := consumer.Consume(requestPtr) 135 if err != nil { 136 err = errors.Wrapf(err, "failed to process request '%s'", requestTopic) 137 log.Error().Err(err).Msg("") 138 return 139 } 140 141 responseData, err := receiver.codec.Pack(response) 142 if err != nil { 143 err = errors.Wrapf(err, "failed to pack response '%s'", requestTopic) 144 log.Error().Err(err).Msg("") 145 return 146 } 147 148 log.Debug().Msgf("Request %q response: %s", requestTopic, responseData) 149 err = receiver.connection.Publish(msg.Reply, responseData) 150 if err != nil { 151 err = errors.Wrapf(err, "failed to send response '%s'", requestTopic) 152 log.Error().Err(err).Msg("") 153 return 154 } 155 } 156 157 receiver.mu.Lock() 158 defer receiver.mu.Unlock() 159 160 if subscription, ok := receiver.subs[requestTopic]; ok && subscription.IsValid() { 161 log.Debug().Msg("Already subscribed to topic: " + requestTopic) 162 return nil 163 } 164 165 log.Debug().Msgf("Request %q topic has been subscribed to", requestTopic) 166 167 subscription, err := receiver.connection.Subscribe(requestTopic, messageHandler) 168 if err != nil { 169 err = errors.Wrapf(err, "failed subscribe request '%s'", requestTopic) 170 return err 171 } 172 173 receiver.subs[requestTopic] = subscription 174 return nil 175 }