/// <summary> /// Gets permission to attempt a send of a message. /// </summary> /// <param name="ipAddress">The IP Address we wan't to send from.</param> /// <param name="mxRecord">The MX Record of the destination.</param> /// <returns>TRUE if we can send FALSE if we should throttle.</returns> public bool TryGetSendAuth(VirtualMTA ipAddress, MXRecord mxRecord) { int mxPatternID = -1; int maxMessagesHour = OutboundRuleManager.GetMaxMessagesDestinationHour(ipAddress, mxRecord, out mxPatternID); // If the Max messages is -1 then unlimited so can just return true here. // No need for any logging or calculating. if (maxMessagesHour == -1) { return(true); } // Create or get this outbound IP/mx pattern send history. ThrottleManager.MxPatternThrottlingSendHistory mxSndHist = null; if (!this._sendHistory.TryGetValue(ipAddress.IPAddress.ToString(), out mxSndHist)) { mxSndHist = new ThrottleManager.MxPatternThrottlingSendHistory(); this._sendHistory.AddOrUpdate(ipAddress.IPAddress.ToString(), mxSndHist, (string s, ThrottleManager.MxPatternThrottlingSendHistory sh) => mxSndHist); } //this._sendHistory.GetOrAdd(ipAddress.IPAddress.ToString(), new ThrottleManager.MxPatternThrottlingSendHistory()); List <DateTime> sndHistory = mxSndHist.GetOrAdd(mxPatternID, new List <DateTime>()); // Only calculate if needed. if (mxSndHist.IntervalValuesNeedRecalcTimestamp <= DateTime.UtcNow) { int maxMessages = 0; int maxMessagesIntervalMinute = 0; while (maxMessages < 1 && maxMessagesIntervalMinute <= 60) { maxMessagesIntervalMinute++; maxMessages = (int)Math.Floor((double)maxMessagesHour / 60d * (double)maxMessagesIntervalMinute); } mxSndHist.IntervalMaxMessages = maxMessages; mxSndHist.IntervalMinutes = maxMessagesIntervalMinute; mxSndHist.IntervalValuesNeedRecalcTimestamp = DateTime.UtcNow.AddMinutes(MtaParameters.MTA_CACHE_MINUTES); } lock (sndHistory) { // Remove sends that happened over "Interval" minute(s) ago. DateTime sendsAfterTimestamp = DateTime.UtcNow.AddMinutes((double)(mxSndHist.IntervalMinutes * -1)); sndHistory.RemoveAll((DateTime d) => d <= sendsAfterTimestamp); // Check for throttling if (sndHistory.Count < mxSndHist.IntervalMaxMessages) { // Not hit throttle limit yet. // Log send and return true. sndHistory.Add(DateTime.UtcNow); return(true); } else { // THROTTLED! return(false); } } }
/// <summary> /// Keeps the send history clean by removing old values. /// This should only be called on background thread as it will run forever. /// </summary> private void DoSendHistoryCleaning() { while (true) { // Stopwatch is used to time the cleaning process. Stopwatch timer = new Stopwatch(); timer.Start(); // Loop through all outbound IPs send history foreach (KeyValuePair <string, ThrottleManager.MxPatternThrottlingSendHistory> ipHistory in this._sendHistory) { ThrottleManager.MxPatternThrottlingSendHistory ipMxPtnHistory = ipHistory.Value; // Loop through each MX Pattern within each outbound IP foreach (KeyValuePair <int, List <DateTime> > mxPatternHistory in ipMxPtnHistory) { // Lock the ArrayList that contains the send history. lock (mxPatternHistory.Value) { // ArrayList will hold the position of elements to remove from mxPatternHistory.Value ArrayList toRemove = new ArrayList(); // Go through every log send and check that it hasn't expired. for (int i = 0; i < mxPatternHistory.Value.Count; i++) { if (mxPatternHistory.Value[i].AddMinutes((double)ipMxPtnHistory.IntervalMinutes) < DateTime.UtcNow) { toRemove.Add(i); } } // Remove send history that is no longer required. for (int z = toRemove.Count - 1; z >= 0; z--) { mxPatternHistory.Value.RemoveAt((int)toRemove[z]); } } } } // We don't wan't to have the cleaner thread running indefinitely if there isn't anything // to do. Sleep thread so it only runs once every 15 seconds. Unless it's taking longer than // 15 seconds to clean in which case go again instantly. timer.Stop(); TimeSpan ts = (TimeSpan.FromSeconds(15) - timer.Elapsed); if (ts > TimeSpan.FromSeconds(0)) { Thread.Sleep(ts); } } }