2018-01-11 04:55:33 +03:00
|
|
|
package servicebus
|
2018-01-22 22:30:27 +03:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2018-02-18 21:20:45 +03:00
|
|
|
|
2018-02-02 04:20:37 +03:00
|
|
|
"github.com/satori/go.uuid"
|
2018-01-22 22:30:27 +03:00
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"pack.ag/amqp"
|
|
|
|
)
|
|
|
|
|
2018-01-30 04:37:40 +03:00
|
|
|
// receiver provides session and link handling for a receiving entity path
|
|
|
|
type (
|
|
|
|
receiver struct {
|
2018-02-02 04:20:37 +03:00
|
|
|
sb *serviceBus
|
2018-01-30 04:37:40 +03:00
|
|
|
session *session
|
|
|
|
receiver *amqp.Receiver
|
|
|
|
entityPath string
|
2018-02-18 23:40:22 +03:00
|
|
|
done func()
|
2018-02-02 04:20:37 +03:00
|
|
|
Name uuid.UUID
|
2018-01-30 04:37:40 +03:00
|
|
|
}
|
|
|
|
)
|
2018-01-22 22:30:27 +03:00
|
|
|
|
2018-01-30 04:37:40 +03:00
|
|
|
// newReceiver creates a new Service Bus message listener given an AMQP client and an entity path
|
2018-02-02 04:20:37 +03:00
|
|
|
func (sb *serviceBus) newReceiver(entityPath string) (*receiver, error) {
|
2018-01-30 04:37:40 +03:00
|
|
|
receiver := &receiver{
|
2018-02-02 04:20:37 +03:00
|
|
|
sb: sb,
|
2018-01-22 22:30:27 +03:00
|
|
|
entityPath: entityPath,
|
|
|
|
}
|
|
|
|
err := receiver.newSessionAndLink()
|
2018-02-18 23:40:22 +03:00
|
|
|
return receiver, err
|
2018-01-22 22:30:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Close will close the AMQP session and link of the receiver
|
2018-01-30 04:37:40 +03:00
|
|
|
func (r *receiver) Close() error {
|
2018-02-18 23:40:22 +03:00
|
|
|
// This isn't safe to be called concurrently with Listen
|
|
|
|
if r.done != nil {
|
|
|
|
r.done()
|
|
|
|
}
|
2018-01-23 19:17:44 +03:00
|
|
|
err := r.receiver.Close()
|
2018-01-22 22:30:27 +03:00
|
|
|
if err != nil {
|
2018-02-18 23:40:22 +03:00
|
|
|
// ensure session is closed on receiver error
|
|
|
|
_ = r.session.Close()
|
2018-01-22 22:30:27 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-02-18 21:52:27 +03:00
|
|
|
return r.session.Close()
|
2018-01-22 22:30:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Recover will attempt to close the current session and link, then rebuild them
|
2018-01-30 04:37:40 +03:00
|
|
|
func (r *receiver) Recover() error {
|
2018-01-22 22:30:27 +03:00
|
|
|
err := r.Close()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-02-18 21:52:27 +03:00
|
|
|
return r.newSessionAndLink()
|
2018-01-22 22:30:27 +03:00
|
|
|
}
|
|
|
|
|
2018-01-23 02:47:09 +03:00
|
|
|
// Listen start a listener for messages sent to the entity path
|
2018-01-30 04:37:40 +03:00
|
|
|
func (r *receiver) Listen(handler Handler) {
|
2018-02-18 23:40:22 +03:00
|
|
|
ctx, done := context.WithCancel(context.Background())
|
|
|
|
r.done = done
|
2018-01-23 02:47:09 +03:00
|
|
|
messages := make(chan *amqp.Message)
|
2018-02-18 23:40:22 +03:00
|
|
|
go r.listenForMessages(ctx, messages)
|
|
|
|
go r.handleMessages(ctx, messages, handler)
|
2018-01-23 02:47:09 +03:00
|
|
|
}
|
|
|
|
|
2018-02-18 23:40:22 +03:00
|
|
|
func (r *receiver) handleMessages(ctx context.Context, messages chan *amqp.Message, handler Handler) {
|
2018-01-23 02:47:09 +03:00
|
|
|
for {
|
|
|
|
select {
|
2018-02-18 23:40:22 +03:00
|
|
|
case <-ctx.Done():
|
2018-01-23 02:47:09 +03:00
|
|
|
log.Debug("done handling messages")
|
|
|
|
return
|
|
|
|
case msg := <-messages:
|
2018-02-18 23:40:22 +03:00
|
|
|
var id interface{} = "null"
|
2018-01-23 02:47:09 +03:00
|
|
|
if msg.Properties != nil {
|
|
|
|
id = msg.Properties.MessageID
|
|
|
|
}
|
2018-02-18 23:40:22 +03:00
|
|
|
|
2018-01-23 19:17:44 +03:00
|
|
|
log.Debugf("Message id: %s is being passed to handler", id)
|
|
|
|
err := handler(ctx, msg)
|
2018-01-23 02:47:09 +03:00
|
|
|
if err != nil {
|
|
|
|
msg.Reject()
|
|
|
|
log.Debugf("Message rejected: id: %s", id)
|
2018-02-18 23:40:22 +03:00
|
|
|
continue
|
2018-01-23 02:47:09 +03:00
|
|
|
}
|
2018-02-18 23:40:22 +03:00
|
|
|
|
|
|
|
// Accept message
|
|
|
|
msg.Accept()
|
|
|
|
log.Debugf("Message accepted: id: %s", id)
|
2018-01-23 02:47:09 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-18 23:40:22 +03:00
|
|
|
func (r *receiver) listenForMessages(ctx context.Context, msgChan chan *amqp.Message) {
|
2018-01-23 02:47:09 +03:00
|
|
|
for {
|
2018-02-18 23:40:22 +03:00
|
|
|
//log.Debug("attempting to receive messages")
|
|
|
|
msg, err := r.receiver.Receive(ctx)
|
|
|
|
// TODO (vcabbage): This previously checked `net.Error.Timeout() == true`, which
|
|
|
|
// should never happen. If it does it's a bug in pack.ag/amqp.
|
|
|
|
if err != nil {
|
|
|
|
if ctx.Err() != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO (vcabbage): I'm not sure what the appropriate action is here, this was
|
|
|
|
// previously a call to `log.Fatalln`, which calls os.Exit(1).
|
|
|
|
log.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var id interface{} = "null"
|
|
|
|
if msg.Properties != nil {
|
|
|
|
id = msg.Properties.MessageID
|
|
|
|
}
|
|
|
|
log.Debugf("Message received: %s", id)
|
|
|
|
|
2018-01-23 02:47:09 +03:00
|
|
|
select {
|
2018-02-18 23:40:22 +03:00
|
|
|
case msgChan <- msg:
|
|
|
|
case <-ctx.Done():
|
2018-01-23 02:47:09 +03:00
|
|
|
return
|
2018-01-22 22:30:27 +03:00
|
|
|
}
|
2018-01-23 02:47:09 +03:00
|
|
|
}
|
2018-01-22 22:30:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// newSessionAndLink will replace the session and link on the receiver
|
2018-01-30 04:37:40 +03:00
|
|
|
func (r *receiver) newSessionAndLink() error {
|
2018-02-02 04:20:37 +03:00
|
|
|
if r.sb.claimsBasedSecurityEnabled() {
|
|
|
|
err := r.sb.negotiateClaim(r.entityPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
amqpSession, err := r.sb.newSession()
|
2018-01-22 22:30:27 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-01-23 19:30:25 +03:00
|
|
|
amqpReceiver, err := amqpSession.NewReceiver(
|
2018-02-02 05:26:25 +03:00
|
|
|
amqp.LinkSourceAddress(r.entityPath),
|
2018-02-18 23:40:22 +03:00
|
|
|
amqp.LinkCredit(10),
|
|
|
|
)
|
2018-01-22 22:30:27 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-01-30 04:37:40 +03:00
|
|
|
r.session = newSession(amqpSession)
|
2018-01-22 22:30:27 +03:00
|
|
|
r.receiver = amqpReceiver
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|