Exemple #1
0
        private static void HandleDequeue()
        {
            if (_StartedThreads >= STAGING_DEQUEUE_THREADS)
            {
                return;
            }

            _StartedThreads++;

            while (true)
            {
                BasicDeliverEventArgs ea = RabbitMq.RabbitMqManager.Dequeue(RabbitMqManager.RabbitMqQueue.InboundStaging, 1, 100).FirstOrDefault();
                if (ea == null)
                {
                    Thread.Sleep(1000);
                    continue;
                }

                MtaQueuedMessage qmsg = Serialisation.Deserialise <MtaQueuedMessage>(ea.Body);
                MtaMessage       msg  = new MtaMessage(qmsg.ID, qmsg.VirtualMTAGroupID, qmsg.InternalSendID, qmsg.MailFrom, qmsg.RcptTo, string.Empty);

                RabbitMqManager.Publish(msg, RabbitMqManager.RabbitMqQueue.Inbound, true);
                RabbitMqManager.Publish(qmsg, RabbitMqManager.RabbitMqQueue.OutboundWaiting, true);
                RabbitMqManager.Ack(RabbitMqManager.RabbitMqQueue.InboundStaging, ea.DeliveryTag, false);
            }
        }
        /// <summary>
        /// Enqueue the message for relaying.
        /// </summary>
        /// <param name="msg">Message to enqueue.</param>
        public static async Task <bool> Enqueue(MtaQueuedMessage msg)
        {
            RabbitMqManager.RabbitMqQueue queue = RabbitMqManager.RabbitMqQueue.OutboundWaiting;

            int secondsUntilNextAttempt = (int)Math.Ceiling((msg.AttemptSendAfterUtc - DateTime.UtcNow).TotalSeconds);

            if (secondsUntilNextAttempt > 0)
            {
                if (secondsUntilNextAttempt < 10)
                {
                    queue = RabbitMqManager.RabbitMqQueue.OutboundWait1;
                }
                else if (secondsUntilNextAttempt < 60)
                {
                    queue = RabbitMqManager.RabbitMqQueue.OutboundWait10;
                }
                else if (secondsUntilNextAttempt < 300)
                {
                    queue = RabbitMqManager.RabbitMqQueue.OutboundWait60;
                }
                else
                {
                    queue = RabbitMqManager.RabbitMqQueue.OutboundWait300;
                }
            }

            var published = await RabbitMqManager.Publish(msg, queue, priority : msg.RabbitMqPriority);

            if (published)
            {
                msg.IsHandled = true;
            }

            return(published);
        }
Exemple #3
0
        private void HandleDequeue()
        {
            while (!IsStopping)
            {
                BasicDeliverEventArgs ea = RabbitMq.RabbitMqManager.Dequeue(RabbitMqManager.RabbitMqQueue.InboundStaging, 1, 100).FirstOrDefault();
                if (ea == null)
                {
                    //await Task.Delay(1000);
                    System.Threading.Thread.Sleep(1000);
                    continue;
                }

                MtaQueuedMessage qmsg = Serialisation.Deserialise <MtaQueuedMessage>(ea.Body).Result;
                MtaMessage       msg  = new MtaMessage
                {
                    ID                = qmsg.ID,
                    InternalSendID    = qmsg.InternalSendID,
                    MailFrom          = qmsg.MailFrom,
                    RcptTo            = qmsg.RcptTo,
                    VirtualMTAGroupID = qmsg.VirtualMTAGroupID
                };

                RabbitMqManager.Publish(msg, RabbitMqManager.RabbitMqQueue.Inbound, true, qmsg.RabbitMqPriority).Wait();
                RabbitMqManager.Publish(qmsg, RabbitMqManager.RabbitMqQueue.OutboundWaiting, true, qmsg.RabbitMqPriority).Wait();
                RabbitMqManager.Ack(RabbitMqManager.RabbitMqQueue.InboundStaging, ea.DeliveryTag, false);
            }
        }
Exemple #4
0
        /// <summary>
        /// Discards the message.
        /// </summary>
        /// <param name="failMsg"></param>
        public static async Task <bool> HandleMessageDiscardAsync(MtaQueuedMessage msg)
        {
            await MtaTransaction.LogTransactionAsync(msg, TransactionStatus.Discarded, string.Empty, null, null);

            msg.IsHandled = true;
            return(true);
        }
Exemple #5
0
        /// <summary>
        /// Enqueue the message for relaying.
        /// </summary>
        /// <param name="msg">Message to enqueue.</param>
        public static bool Enqueue(MtaQueuedMessage msg)
        {
            RabbitMqManager.RabbitMqQueue queue = RabbitMqManager.RabbitMqQueue.OutboundWaiting;

            int secondsUntilNextAttempt = (int)Math.Ceiling((msg.AttemptSendAfterUtc - DateTime.UtcNow).TotalSeconds);

            if (secondsUntilNextAttempt > 0)
            {
                if (secondsUntilNextAttempt < 10)
                {
                    queue = RabbitMqManager.RabbitMqQueue.OutboundWait1;
                }
                else if (secondsUntilNextAttempt < 60)
                {
                    queue = RabbitMqManager.RabbitMqQueue.OutboundWait10;
                }
                else if (secondsUntilNextAttempt < 300)
                {
                    queue = RabbitMqManager.RabbitMqQueue.OutboundWait60;
                }
                else
                {
                    queue = RabbitMqManager.RabbitMqQueue.OutboundWait300;
                }
            }

            if (!RabbitMqManager.Publish(msg, queue))
            {
                return(false);
            }
            msg.IsHandled = true;
            return(true);
        }
Exemple #6
0
        /// <summary>
        /// This method handle successful delivery.
        /// Logs success
        /// Deletes queued data
        /// </summary>
        public static async Task <bool> HandleDeliverySuccessAsync(MtaQueuedMessage msg, VirtualMTA ipAddress, MXRecord mxRecord, string response)
        {
            await MtaTransaction.LogTransactionAsync(msg, TransactionStatus.Success, response, ipAddress, mxRecord);

            msg.IsHandled = true;
            return(true);
        }
        /// <summary>
        /// Enqueue the messages in the collection for relaying.
        /// </summary>
        /// <param name="inboundMessages">Messages to enqueue.</param>
        public static void Enqueue(IList <MtaMessage> inboundMessages)
        {
            Parallel.ForEach(inboundMessages, message => {
                Enqueue(MtaQueuedMessage.CreateNew(message)).Wait();
            });

            RabbitMqManager.Ack(RabbitMqManager.RabbitMqQueue.Inbound, inboundMessages.Max(m => m.RabbitMqDeliveryTag), true);
        }
Exemple #8
0
        /// <summary>
        /// Handles a service unavailable event, should be same as defer but only wait 1 minute before next retry.
        /// </summary>
        /// <param name="sndIpAddress"></param>
        internal static async Task <bool> HandleServiceUnavailableAsync(MtaQueuedMessage msg, VirtualMTA ipAddress)
        {
            // Log deferral
            await MtaTransaction.LogTransactionAsync(msg, TransactionStatus.Deferred, "Service Unavailable", ipAddress, null);

            // Set next retry time and release the lock.
            msg.AttemptSendAfterUtc = DateTime.UtcNow.AddSeconds(15);
            await Requeue(msg);

            return(true);
        }
        /// <summary>
        /// This method handles message throttle.
        ///	Logs throttle
        /// Sets the next rety date time
        /// </summary>
        internal static async Task <bool> HandleDeliveryThrottleAsync(MtaQueuedMessage msg, VirtualMTA ipAddress, MXRecord mxRecord)
        {
            // Log deferral
            await MtaTransaction.LogTransactionAsync(msg, TransactionStatus.Throttled, string.Empty, ipAddress, mxRecord);

            // Set next retry time and release the lock.
            msg.AttemptSendAfterUtc = DateTime.UtcNow.AddMinutes(1);
            await Requeue(msg);

            return(true);
        }
Exemple #10
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="ipAddress"></param>
 /// <param name="mxRecord"></param>
 /// <returns></returns>
 public static async Task <bool> HandleFailedToConnectAsync(MtaQueuedMessage msg, VirtualMTA ipAddress, MXRecord mxRecord)
 {
     // If there was no MX record in DNS, so using A, we should fail and not retry.
     if (mxRecord.MxRecordSrc == MxRecordSrc.A)
     {
         return(await HandleDeliveryFailAsync(msg, "550 Failed to connect", ipAddress, mxRecord));
     }
     else
     {
         return(await HandleDeliveryDeferralAsync(msg, "Failed to connect", ipAddress, mxRecord, false, 15));
     }
 }
Exemple #11
0
        /// <summary>
        /// Enqueues the Email that we are going to relay in RabbitMQ.
        /// </summary>
        /// <param name="messageID">ID of the Message being Queued.</param>
        /// <param name="ipGroupID">ID of the Virtual MTA Group to send the Message through.</param>
        /// <param name="internalSendID">ID of the Send the Message is apart of.</param>
        /// <param name="mailFrom">The envelope mailfrom, should be return-path in most instances.</param>
        /// <param name="rcptTo">The envelope rcpt to.</param>
        /// <param name="message">The Email.</param>
        /// <returns>True if the Email has been enqueued in RabbitMQ.</returns>
        public static bool Enqueue(Guid messageID, int ipGroupID, int internalSendID, string mailFrom, string[] rcptTo, string message)
        {
            // Create the thing we are going to queue in RabbitMQ.
            MtaMessage recordToSave = new MtaMessage(messageID,
                                                     ipGroupID,
                                                     internalSendID,
                                                     mailFrom,
                                                     rcptTo,
                                                     message);

            return(RabbitMqManager.Publish(MtaQueuedMessage.CreateNew(recordToSave), RabbitMqManager.RabbitMqQueue.InboundStaging, true));
        }
Exemple #12
0
        /// <summary>
        /// Dequeue a message from RabbitMQ.
        /// </summary>
        /// <returns>A dequeued message or null if there weren't any.</returns>
        public static async Task <MtaQueuedMessage> Dequeue()
        {
            BasicDeliverEventArgs ea = RabbitMqManager.Dequeue(RabbitMqManager.RabbitMqQueue.OutboundWaiting, 1, 100).FirstOrDefault();

            if (ea == null)
            {
                return(null);
            }

            MtaQueuedMessage qmsg = await Serialisation.Deserialise <MtaQueuedMessage>(ea.Body);

            qmsg.RabbitMqDeliveryTag = ea.DeliveryTag;
            qmsg.IsHandled           = false;
            return(qmsg);
        }
        /// <summary>
        /// Enqueues the Email that we are going to relay in RabbitMQ.
        /// </summary>
        /// <param name="messageID">ID of the Message being Queued.</param>
        /// <param name="ipGroupID">ID of the Virtual MTA Group to send the Message through.</param>
        /// <param name="internalSendID">ID of the Send the Message is apart of.</param>
        /// <param name="mailFrom">The envelope mailfrom, should be return-path in most instances.</param>
        /// <param name="rcptTo">The envelope rcpt to.</param>
        /// <param name="message">The Email.</param>
        /// <param name="priority">Priority of message.</param>
        /// <returns>True if the Email has been enqueued in RabbitMQ.</returns>
        public static async Task <bool> Enqueue(Guid messageID, int ipGroupID, int internalSendID, string mailFrom, string[] rcptTo, string message, RabbitMqPriority priority)
        {
            // Create the thing we are going to queue in RabbitMQ.
            var recordToSave = new MtaMessage
            {
                ID                = messageID,
                InternalSendID    = internalSendID,
                MailFrom          = mailFrom,
                Message           = message,
                RcptTo            = rcptTo,
                VirtualMTAGroupID = ipGroupID,
                RabbitMqPriority  = priority
            };

            return(await RabbitMqManager.Publish(MtaQueuedMessage.CreateNew(recordToSave), RabbitMqManager.RabbitMqQueue.InboundStaging, true, priority));
        }
Exemple #14
0
        /// <summary>
        /// This method handles message deferal.
        ///	Logs deferral
        ///	Fails the message if timed out
        /// or
        /// Sets the next rety date time
        /// </summary>
        /// <param name="defMsg">The deferal message from the SMTP server.</param>
        /// <param name="ipAddress">IP Address that send was attempted from.</param>
        /// <param name="mxRecord">MX Record of the server tried to send too.</param>
        /// <param name="isServiceUnavailable">If false will backoff the retry, if true will use the MtaParameters.MtaRetryInterval,
        /// this is needed to reduce the tail when sending as a message could get multiple try again laters and soon be 1h+ before next retry.</param>
        public static async Task <bool> HandleDeliveryDeferralAsync(MtaQueuedMessage msg, string defMsg, VirtualMTA ipAddress, MXRecord mxRecord, bool isServiceUnavailable = false, int?overrideTimeminutes = null)
        {
            // Log the deferral.
            await MtaTransaction.LogTransactionAsync(msg, TransactionStatus.Deferred, defMsg, ipAddress, mxRecord);

            // This holds the maximum interval between send retries. Should be put in the database.
            int maxInterval = 3 * 60;

            // Increase the defered count as the queued messages has been deferred.
            msg.DeferredCount++;

            // Hold the minutes to wait until next retry.
            double nextRetryInterval = MtaParameters.MtaRetryInterval;

            if (overrideTimeminutes.HasValue)
            {
                nextRetryInterval = overrideTimeminutes.Value;
            }
            else
            {
                if (!isServiceUnavailable)
                {
                    // Increase the deferred wait interval by doubling for each retry.
                    for (int i = 1; i < msg.DeferredCount; i++)
                    {
                        nextRetryInterval = nextRetryInterval * 2;
                    }

                    // If we have gone over the max interval then set to the max interval value.
                    if (nextRetryInterval > maxInterval)
                    {
                        nextRetryInterval = maxInterval;
                    }
                }
                else
                {
                    nextRetryInterval = 1;                     // For service unavalible use 1 minute between retries.
                }
            }

            // Set next retry time and release the lock.
            msg.AttemptSendAfterUtc = DateTime.UtcNow.AddMinutes(nextRetryInterval);
            await Requeue(msg);

            return(true);
        }
Exemple #15
0
        /// <summary>
        /// This method handles failure of delivery.
        /// Logs failure
        /// Deletes queued data
        /// </summary>
        /// <param name="failMsg"></param>
        public static async Task <bool> HandleDeliveryFailAsync(MtaQueuedMessage msg, string failMsg, VirtualMTA ipAddress, MXRecord mxRecord)
        {
            await MtaTransaction.LogTransactionAsync(msg, TransactionStatus.Failed, failMsg, ipAddress, mxRecord);

            try
            {
                // Send fails to Manta.Core.Events
                for (int i = 0; i < msg.RcptTo.Length; i++)
                {
                    EmailProcessingDetails processingInfo = null;
                    EventsManager.Instance.ProcessSmtpResponseMessage(failMsg, msg.RcptTo[i], msg.InternalSendID, out processingInfo);
                }
            }
            catch (Exception)
            {
            }

            msg.IsHandled = true;

            return(true);
        }
        private static async Task HandleDequeue()
        {
            if (_StartedThreads >= STAGING_DEQUEUE_THREADS)
            {
                return;
            }

            _StartedThreads++;

            while (true)
            {
                BasicDeliverEventArgs ea = RabbitMq.RabbitMqManager.Dequeue(RabbitMqManager.RabbitMqQueue.InboundStaging, 1, 100).FirstOrDefault();
                if (ea == null)
                {
                    await Task.Delay(1000);

                    continue;
                }

                MtaQueuedMessage qmsg = await Serialisation.Deserialise <MtaQueuedMessage>(ea.Body);

                MtaMessage msg = new MtaMessage
                {
                    ID                = qmsg.ID,
                    InternalSendID    = qmsg.InternalSendID,
                    MailFrom          = qmsg.MailFrom,
                    RcptTo            = qmsg.RcptTo,
                    VirtualMTAGroupID = qmsg.VirtualMTAGroupID
                };

                await RabbitMqManager.Publish(msg, RabbitMqManager.RabbitMqQueue.Inbound, true, (RabbitMqPriority)qmsg.RabbitMqPriority);

                await RabbitMqManager.Publish(qmsg, RabbitMqManager.RabbitMqQueue.OutboundWaiting, true, (RabbitMqPriority)qmsg.RabbitMqPriority);

                RabbitMqManager.Ack(RabbitMqManager.RabbitMqQueue.InboundStaging, ea.DeliveryTag, false);
            }
        }
Exemple #17
0
        private async Task SendMessageAsync(MtaQueuedMessage msg)
        {
            // Check that the message next attempt after has passed.
            if (msg.AttemptSendAfterUtc > DateTime.UtcNow)
            {
                await RabbitMqOutboundQueueManager.Enqueue(msg);

                await Task.Delay(50);                 // To prevent a tight loop within a Task thread we should sleep here.

                return;
            }

            if (await Data.MtaTransaction.HasBeenHandledAsync(msg.ID))
            {
                msg.IsHandled = true;
                return;
            }

            // Get the send that this message belongs to so that we can check the send state.
            var snd = await SendManager.Instance.GetSendAsync(msg.InternalSendID);

            switch (snd.SendStatus)
            {
            // The send is being discarded so we should discard the message.
            case SendStatus.Discard:
                await MtaMessageHelper.HandleMessageDiscardAsync(msg);

                return;

            // The send is paused, the handle pause state will delay, without deferring, the message for a while so we can move on to other messages.
            case SendStatus.Paused:
                await MtaMessageHelper.HandleSendPaused(msg);

                return;

            // Send is active so we don't need to do anything.
            case SendStatus.Active:
                break;

            // Unknown send state, requeue the message and log error. Cannot send!
            default:
                msg.AttemptSendAfterUtc = DateTime.UtcNow.AddMinutes(1);
                await RabbitMqOutboundQueueManager.Enqueue(msg);

                Logging.Error("Failed to send message. Unknown SendStatus[" + snd.SendStatus + "]!");
                return;
            }

            // Check the message hasn't timed out. If it has don't attempt to send it.
            // Need to do this here as there may be a massive backlog on the server
            // causing messages to be waiting for ages after there AttemptSendAfter
            // before picking up. The MAX_TIME_IN_QUEUE should always be enforced.
            if (msg.AttemptSendAfterUtc - msg.QueuedTimestampUtc > new TimeSpan(0, MtaParameters.MtaMaxTimeInQueue, 0))
            {
                await MtaMessageHelper.HandleDeliveryFailAsync(msg, MtaParameters.TIMED_OUT_IN_QUEUE_MESSAGE, null, null);
            }
            else
            {
                MailAddress rcptTo    = new MailAddress(msg.RcptTo[0]);
                MailAddress mailFrom  = new MailAddress(msg.MailFrom);
                MXRecord[]  mXRecords = DNSManager.GetMXRecords(rcptTo.Host);
                // If mxs is null then there are no MX records.
                if (mXRecords == null || mXRecords.Length < 1)
                {
                    await MtaMessageHelper.HandleDeliveryFailAsync(msg, "550 Domain Not Found.", null, null);
                }
                else if (IsMxBlacklisted(mXRecords))
                {
                    await MtaMessageHelper.HandleDeliveryFailAsync(msg, "550 Domain blacklisted.", null, mXRecords[0]);
                }
                else
                {
                    var vMtaGroup  = VirtualMtaManager.GetVirtualMtaGroup(msg.VirtualMTAGroupID);
                    var sendResult = await MantaSmtpClientPoolCollection.Instance.SendAsync(mailFrom, rcptTo, vMtaGroup, mXRecords, msg.Message);

                    switch (sendResult.MantaOutboundClientResult)
                    {
                    case MantaOutboundClientResult.FailedToConnect:
                        await MtaMessageHelper.HandleFailedToConnectAsync(msg, sendResult.VirtualMTA, sendResult.MXRecord);

                        break;

                    case MantaOutboundClientResult.MaxConnections:
                        await RabbitMqOutboundQueueManager.Enqueue(msg);

                        break;

                    case MantaOutboundClientResult.MaxMessages:
                        await MtaMessageHelper.HandleDeliveryThrottleAsync(msg, sendResult.VirtualMTA, sendResult.MXRecord);

                        break;

                    case MantaOutboundClientResult.RejectedByRemoteServer:
                        if (string.IsNullOrWhiteSpace(sendResult.Message))
                        {
                            Logging.Error("RejectedByRemoteServer but no message!");
                            await MtaMessageHelper.HandleDeliveryDeferralAsync(msg, sendResult.Message, sendResult.VirtualMTA, sendResult.MXRecord);
                        }
                        if (sendResult.Message[0] == '4')
                        {
                            await MtaMessageHelper.HandleDeliveryDeferralAsync(msg, sendResult.Message, sendResult.VirtualMTA, sendResult.MXRecord);
                        }
                        else
                        {
                            await MtaMessageHelper.HandleDeliveryFailAsync(msg, sendResult.Message, sendResult.VirtualMTA, sendResult.MXRecord);
                        }
                        break;

                    case MantaOutboundClientResult.ServiceNotAvalible:
                        await MtaMessageHelper.HandleServiceUnavailableAsync(msg, sendResult.VirtualMTA);

                        break;

                    case MantaOutboundClientResult.Success:
                        await MtaMessageHelper.HandleDeliverySuccessAsync(msg, sendResult.VirtualMTA, sendResult.MXRecord, sendResult.Message);

                        break;

                    default:
                        // Something weird happening with this message, get it out of the way for a bit.
                        msg.AttemptSendAfterUtc = DateTime.UtcNow.AddMinutes(5);
                        await RabbitMqOutboundQueueManager.Enqueue(msg);

                        break;
                    }
                }
            }
        }
Exemple #18
0
 /// <summary>
 /// This method handles message deferal.
 ///	Logs deferral
 ///	Fails the message if timed out
 /// or
 /// Sets the next rety date time
 /// </summary>
 /// <param name="defMsg">The deferal message from the SMTP server.</param>
 /// <param name="ipAddress">IP Address that send was attempted from.</param>
 /// <param name="mxRecord">MX Record of the server tried to send too.</param>
 /// <param name="isServiceUnavailable">If false will backoff the retry, if true will use the MtaParameters.MtaRetryInterval,
 /// this is needed to reduce the tail when sending as a message could get multiple try again laters and soon be 1h+ before next retry.</param>
 public static void HandleDeliveryDeferral(MtaQueuedMessage msg, string defMsg, VirtualMTA ipAddress, MXRecord mxRecord, bool isServiceUnavailable = false)
 {
     HandleDeliveryDeferralAsync(msg, defMsg, ipAddress, mxRecord, isServiceUnavailable).Wait();
 }
Exemple #19
0
        /// <summary>
        /// Requeue the message in RabbitMQ.
        /// </summary>
        private static async Task Requeue(MtaQueuedMessage msg)
        {
            await RabbitMq.RabbitMqOutboundQueueManager.Enqueue(msg);

            msg.IsHandled = true;
        }
Exemple #20
0
        public void Start()
        {
            Thread t = new Thread(new ThreadStart(() => {
                // Dictionary will hold a single int for each running task. The int means nothing.
                ConcurrentDictionary <Guid, int> runningTasks = new ConcurrentDictionary <Guid, int>();

                Action <MtaQueuedMessage> taskWorker = (qMsg) => {
                    // Generate a unique ID for this task.
                    Guid taskID = Guid.NewGuid();

                    // Add this task to the running list.
                    if (!runningTasks.TryAdd(taskID, 1))
                    {
                        return;
                    }

                    Task.Run(async() =>
                    {
                        try
                        {
                            // Loop while there is a task message to send.
                            while (qMsg != null && !_IsStopping)
                            {
                                // Send the message.
                                await SendMessageAsync(qMsg);

                                if (!qMsg.IsHandled)
                                {
                                    Logging.Warn("Message not handled " + qMsg.ID);
                                    qMsg.AttemptSendAfterUtc = DateTime.UtcNow.AddMinutes(1);
                                    RabbitMq.RabbitMqOutboundQueueManager.Enqueue(qMsg);
                                }

                                // Acknowledge of the message.
                                RabbitMqOutboundQueueManager.Ack(qMsg);

                                // Try to get another message to send.
                                qMsg = RabbitMq.RabbitMqOutboundQueueManager.Dequeue();
                            }
                        }
                        catch (Exception ex)
                        {
                            // Log if we can't send the message.
                            Logging.Debug("Failed to send message", ex);
                        }
                        finally
                        {
                            // If there is still a acknowledge of the message.
                            if (qMsg != null)
                            {
                                if (!qMsg.IsHandled)
                                {
                                    Logging.Warn("Message not handled " + qMsg.ID);
                                    qMsg.AttemptSendAfterUtc = DateTime.UtcNow.AddMinutes(1);
                                    RabbitMq.RabbitMqOutboundQueueManager.Enqueue(qMsg);
                                }

                                RabbitMqOutboundQueueManager.Ack(qMsg);
                            }

                            // Remove this task from the dictionary
                            int value;
                            runningTasks.TryRemove(taskID, out value);
                        }
                    });
                };

                Action startWorkerTasks = () => {
                    while ((runningTasks.Count < MAX_SENDING_WORKER_TASKS) && !_IsStopping)
                    {
                        MtaQueuedMessage qmsg = RabbitMq.RabbitMqOutboundQueueManager.Dequeue();
                        if (qmsg == null)
                        {
                            break;                             // Nothing to do, so don't start anymore workers.
                        }
                        taskWorker(qmsg);
                    }
                };

                while (!_IsStopping)
                {
                    if (runningTasks.Count >= MAX_SENDING_WORKER_TASKS)
                    {
                        Thread.Sleep(100);
                        continue;
                    }

                    startWorkerTasks();
                }
            }));

            t.Start();
        }
Exemple #21
0
 /// <summary>
 /// Handle the message for a paused send.
 /// Should increase attempt send after timestamp and requeue in RabbitMQ.
 /// </summary>
 internal static async Task HandleSendPaused(MtaQueuedMessage msg)
 {
     msg.AttemptSendAfterUtc = DateTime.UtcNow.AddMinutes(1);
     await Requeue(msg);
 }
Exemple #22
0
        private async Task <bool> SendMessageAsync(MtaQueuedMessage msg)
        {
            // Check that the message next attempt after has passed.
            if (msg.AttemptSendAfterUtc > DateTime.UtcNow)
            {
                RabbitMqOutboundQueueManager.Enqueue(msg);
                await Task.Delay(50);                 // To prevent a tight loop within a Task thread we should sleep here.

                return(false);
            }

            if (await MtaTransaction.HasBeenHandledAsync(msg.ID))
            {
                msg.IsHandled = true;
                return(true);
            }

            // Get the send that this message belongs to so that we can check the send state.
            Send snd = await SendManager.Instance.GetSendAsync(msg.InternalSendID);

            switch (snd.SendStatus)
            {
            // The send is being discarded so we should discard the message.
            case SendStatus.Discard:
                await msg.HandleMessageDiscardAsync();

                return(false);

            // The send is paused, the handle pause state will delay, without deferring, the message for a while so we can move on to other messages.
            case SendStatus.Paused:
                msg.HandleSendPaused();
                return(false);

            // Send is active so we don't need to do anything.
            case SendStatus.Active:
                break;

            // Unknown send state, requeue the message and log error. Cannot send!
            default:
                msg.AttemptSendAfterUtc = DateTime.UtcNow.AddMinutes(1);
                RabbitMqOutboundQueueManager.Enqueue(msg);
                Logging.Error("Failed to send message. Unknown SendStatus[" + snd.SendStatus + "]!");
                return(false);
            }


            bool result;

            // Check the message hasn't timed out. If it has don't attempt to send it.
            // Need to do this here as there may be a massive backlog on the server
            // causing messages to be waiting for ages after there AttemptSendAfter
            // before picking up. The MAX_TIME_IN_QUEUE should always be enforced.
            if (msg.AttemptSendAfterUtc - msg.QueuedTimestampUtc > new TimeSpan(0, MtaParameters.MtaMaxTimeInQueue, 0))
            {
                await msg.HandleDeliveryFailAsync(MtaParameters.TIMED_OUT_IN_QUEUE_MESSAGE, null, null);

                result = false;
            }
            else
            {
                MailAddress mailAddress = new MailAddress(msg.RcptTo[0]);
                MailAddress mailFrom    = new MailAddress(msg.MailFrom);
                MXRecord[]  mXRecords   = DNSManager.GetMXRecords(mailAddress.Host);
                // If mxs is null then there are no MX records.
                if (mXRecords == null || mXRecords.Length < 1)
                {
                    await msg.HandleDeliveryFailAsync("550 Domain Not Found.", null, null);

                    result = false;
                }
                else if (IsMxBlacklisted(mXRecords))
                {
                    await msg.HandleDeliveryFailAsync("550 Domain blacklisted.", null, mXRecords[0]);

                    result = false;
                }
                else
                {
                    // The IP group that will be used to send the queued message.
                    VirtualMtaGroup virtualMtaGroup = VirtualMtaManager.GetVirtualMtaGroup(msg.VirtualMTAGroupID);
                    VirtualMTA      sndIpAddress    = virtualMtaGroup.GetVirtualMtasForSending(mXRecords[0]);

                    SmtpOutboundClientDequeueResponse dequeueResponse = await SmtpClientPool.Instance.DequeueAsync(sndIpAddress, mXRecords);

                    switch (dequeueResponse.DequeueResult)
                    {
                    case SmtpOutboundClientDequeueAsyncResult.Success:
                    case SmtpOutboundClientDequeueAsyncResult.NoMxRecords:
                    case SmtpOutboundClientDequeueAsyncResult.FailedToAddToSmtpClientQueue:
                    case SmtpOutboundClientDequeueAsyncResult.Unknown:
                        break;                                 // Don't need to do anything for these results.

                    case SmtpOutboundClientDequeueAsyncResult.FailedToConnect:
                        await msg.HandleFailedToConnectAsync(sndIpAddress, mXRecords[0]);

                        break;

                    case SmtpOutboundClientDequeueAsyncResult.ServiceUnavalible:
                        await msg.HandleServiceUnavailableAsync(sndIpAddress);

                        break;

                    case SmtpOutboundClientDequeueAsyncResult.Throttled:
                        await msg.HandleDeliveryThrottleAsync(sndIpAddress, mXRecords[0]);

                        break;

                    case SmtpOutboundClientDequeueAsyncResult.FailedMaxConnections:
                        msg.AttemptSendAfterUtc = DateTime.UtcNow.AddSeconds(2);
                        RabbitMqOutboundQueueManager.Enqueue(msg);
                        break;
                    }

                    SmtpOutboundClient smtpClient = dequeueResponse.SmtpOutboundClient;

                    // If no client was dequeued then we can't currently send.
                    // This is most likely a max connection issue. Return false but don't
                    // log any deferal or throttle.
                    if (smtpClient == null)
                    {
                        result = false;
                    }
                    else
                    {
                        try
                        {
                            Action <string> failedCallback = delegate(string smtpResponse)
                            {
                                // If smtpRespose starts with 5 then perm error should cause fail
                                if (smtpResponse.StartsWith("5"))
                                {
                                    msg.HandleDeliveryFailAsync(smtpResponse, sndIpAddress, smtpClient.MXRecord).Wait();
                                }
                                else
                                {
                                    // If the MX is actively denying use service access, SMTP code 421 then we should inform
                                    // the ServiceNotAvailableManager manager so it limits our attepts to this MX to 1/minute.
                                    if (smtpResponse.StartsWith("421"))
                                    {
                                        ServiceNotAvailableManager.Add(smtpClient.SmtpStream.LocalAddress.ToString(), smtpClient.MXRecord.Host, DateTime.UtcNow);
                                        msg.HandleDeliveryDeferral(smtpResponse, sndIpAddress, smtpClient.MXRecord, true);
                                    }
                                    else
                                    {
                                        // Otherwise message is deferred
                                        msg.HandleDeliveryDeferral(smtpResponse, sndIpAddress, smtpClient.MXRecord, false);
                                    }
                                }
                                throw new SmtpTransactionFailedException();
                            };
                            // Run each SMTP command after the last.
                            await smtpClient.ExecHeloOrRsetAsync(failedCallback);

                            await smtpClient.ExecMailFromAsync(mailFrom, failedCallback);

                            await smtpClient.ExecRcptToAsync(mailAddress, failedCallback);

                            await smtpClient.ExecDataAsync(msg.Message, failedCallback, async (response) => {
                                await msg.HandleDeliverySuccessAsync(sndIpAddress, smtpClient.MXRecord, response);
                            });

                            SmtpClientPool.Instance.Enqueue(smtpClient);

                            result = true;
                        }
                        catch (SmtpTransactionFailedException)
                        {
                            // Exception is thrown to exit transaction, logging of deferrals/failers already handled.
                            result = false;
                        }
                        catch (Exception ex)
                        {
                            Logging.Error("MessageSender error.", ex);
                            if (msg != null)
                            {
                                msg.HandleDeliveryDeferral("Connection was established but ended abruptly.", sndIpAddress, smtpClient.MXRecord, false);
                            }
                            result = false;
                        }
                        finally
                        {
                            if (smtpClient != null)
                            {
                                smtpClient.IsActive   = false;
                                smtpClient.LastActive = DateTime.UtcNow;
                            }
                        }
                    }
                }
            }
            return(result);
        }
 /// <summary>
 /// Acknowledge the message as handled.
 /// </summary>
 /// <param name="msg">The message to acknowledge.</param>
 internal static void Ack(MtaQueuedMessage msg)
 {
     RabbitMqManager.Ack(RabbitMqManager.RabbitMqQueue.OutboundWaiting, msg.RabbitMqDeliveryTag, false);
 }