Example #1
0
        private async Task SendBatch()
        {
            batchId++;
            if (batchId >= long.MaxValue)
            {
                batchId = 1;
            }

            // Pause the timer
            timerBatchWait.Change(Timeout.Infinite, Timeout.Infinite);

            if (notifications.Count <= 0)
            {
                return;
            }

            // Let's store the batch items to send internally
            var toSend = new BlockingCollection <CompletableApnsNotification>();

            while (notifications.Count > 0 && toSend.Count < Configuration.InternalBatchSize)
            {
                CompletableApnsNotification n;
                if (notifications.TryDequeue(out n))
                {
                    toSend.Add(n);
                }
            }

            Log.Info("APNS-Client[{0}]: Sending Batch ID={1}, Count={2}", id, batchId, toSend.Count);

            try
            {
                var data = createBatch(toSend);

                if (data != null && data.Length > 0)
                {
                    for (var i = 0; i <= Configuration.InternalBatchFailureRetryCount; i++)
                    {
                        await connectingSemaphore.WaitAsync();

                        try
                        {
                            // See if we need to connect
                            if (!socketCanWrite() || i > 0)
                            {
                                await connect();
                            }
                        }
                        finally
                        {
                            connectingSemaphore.Release();
                        }

                        try
                        {
                            await networkStream.WriteAsync(data, 0, data.Length).ConfigureAwait(false);

                            break;
                        }
                        catch (Exception ex) when(i != Configuration.InternalBatchFailureRetryCount)
                        {
                            Log.Info("APNS-CLIENT[{0}]: Retrying Batch: Batch ID={1}, Error={2}", id, batchId, ex);
                        }
                    }

                    foreach (var n in toSend)
                    {
                        // If the notification failed validation before being sent (i.e. it was skipped)
                        if (n.State == NotificationState.Failed)
                        {
                            continue;
                        }

                        sent.Add(new SentNotification(n));
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("APNS-CLIENT[{0}]: Send Batch Error: Batch ID={1}, Error={2}", id, batchId, ex);
                foreach (var n in toSend)
                {
                    n.CompleteFailed(new ApnsNotificationException(ApnsNotificationErrorStatusCode.ConnectionError, n.Notification, ex));
                }

                var errorNotificationToSend = new List <CompletableApnsNotification>();
                //Check to see if any notification items have a bad registration id

                foreach (var notificationItem in toSend)
                {
                    if (!notificationItem.Notification.IsDeviceRegistrationIdValid())
                    {
                        errorNotificationToSend.Add(notificationItem);
                    }
                }
                if (errorNotificationToSend.Count > 0)
                {
                    //If any devices had a bad registration id assume this exception was caused by those bad ids and requeue the other notifications
                    foreach (var notificationItem in toSend.Except(errorNotificationToSend))
                    {
                        notifications.Enqueue(notificationItem);
                    }
                    //Report invalid token errors for each invalid registration id
                    foreach (var n in errorNotificationToSend)
                    {
                        n.CompleteFailed(new ApnsNotificationException(ApnsNotificationErrorStatusCode.InvalidToken, n.Notification, ex));
                    }
                }
                else
                {
                    //If there were no invalid registration ids then report the errors as normal
                    foreach (var n in toSend)
                    {
                        n.CompleteFailed(new ApnsNotificationException(ApnsNotificationErrorStatusCode.ConnectionError, n.Notification, ex));
                    }
                }
            }

            Log.Info("APNS-Client[{0}]: Sent Batch, waiting for possible response...", id);

            try
            {
                await Reader();
            }
            catch (Exception ex)
            {
                Log.Error("APNS-Client[{0}]: Reader Exception: {1}", id, ex);
            }

            Log.Info("APNS-Client[{0}]: Done Reading for Batch ID={1}, reseting batch timer...", id, batchId);

            // Restart the timer for the next batch
            timerBatchWait.Change(Configuration.InternalBatchingWaitPeriod, Timeout.InfiniteTimeSpan);
        }