[CalendarServer-changes] [9509] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Tue Jul 31 11:17:59 PDT 2012
Revision: 9509
http://trac.macosforge.org/projects/calendarserver/changeset/9509
Author: sagen at apple.com
Date: 2012-07-31 11:17:58 -0700 (Tue, 31 Jul 2012)
Log Message:
-----------
Advertise APNS refresh-interval to clients, and automatically purge old subscriptions.
Modified Paths:
--------------
CalendarServer/trunk/calendarserver/push/applepush.py
CalendarServer/trunk/calendarserver/push/test/test_applepush.py
CalendarServer/trunk/twistedcaldav/customxml.py
CalendarServer/trunk/twistedcaldav/notify.py
CalendarServer/trunk/twistedcaldav/resource.py
CalendarServer/trunk/twistedcaldav/stdconfig.py
CalendarServer/trunk/txdav/common/datastore/file.py
CalendarServer/trunk/txdav/common/datastore/sql.py
CalendarServer/trunk/txdav/common/icommondatastore.py
Modified: CalendarServer/trunk/calendarserver/push/applepush.py
===================================================================
--- CalendarServer/trunk/calendarserver/push/applepush.py 2012-07-30 20:50:34 UTC (rev 9508)
+++ CalendarServer/trunk/calendarserver/push/applepush.py 2012-07-31 18:17:58 UTC (rev 9509)
@@ -28,6 +28,7 @@
from twisted.internet import protocol
from twisted.internet.defer import inlineCallbacks, returnValue, succeed
from twisted.internet.protocol import ClientFactory, ReconnectingClientFactory
+from twisted.internet.task import LoopingCall
from twistedcaldav.extensions import DAVResource, DAVResourceWithoutChildrenMixin
from twistedcaldav.resource import ReadOnlyNoCopyResourceMixIn
import OpenSSL
@@ -87,6 +88,9 @@
service.providers = {}
service.feedbacks = {}
service.dataHost = settings["DataHost"]
+ service.purgeCall = None
+ service.purgeIntervalSeconds = settings["SubscriptionPurgeIntervalSeconds"]
+ service.purgeSeconds = settings["SubscriptionPurgeSeconds"]
for protocol in ("CalDAV", "CardDAV"):
@@ -134,7 +138,44 @@
return service
+ def startService(self):
+ """
+ In addition to starting the provider and feedback sub-services, start a
+ LoopingCall whose job it is to purge old subscriptions
+ """
+ service.MultiService.startService(self)
+ self.log_debug("ApplePushNotifierService startService")
+ self.purgeCall = LoopingCall(self.purgeOldSubscriptions, self.purgeSeconds)
+ self.purgeCall.start(self.purgeIntervalSeconds, now=False)
+
+
+ def stopService(self):
+ """
+ In addition to stopping the provider and feedback sub-services, stop the
+ LoopingCall
+ """
+ service.MultiService.stopService(self)
+ self.log_debug("ApplePushNotifierService stopService")
+ if self.purgeCall is not None:
+ self.purgeCall.stop()
+ self.purgeCall = None
+
+
@inlineCallbacks
+ def purgeOldSubscriptions(self, purgeSeconds):
+ """
+ Remove any subscriptions that registered more than purgeSeconds ago
+
+ @param purgeSeconds: The cutoff given in seconds
+ @type purgeSeconds: C{int}
+ """
+ self.log_debug("ApplePushNotifierService purgeOldSubscriptions")
+ txn = self.store.newTransaction()
+ yield txn.purgeOldAPNSubscriptions(int(time.time()) - purgeSeconds)
+ yield txn.commit()
+
+
+ @inlineCallbacks
def enqueue(self, op, id):
"""
Sends an Apple Push Notification to any device token subscribed to
@@ -340,6 +381,7 @@
self.store = store
self.noisy = True
self.maxDelay = 30 # max seconds between connection attempts
+ self.shuttingDown = False
def clientConnectionMade(self):
self.log_warn("Connection to APN server made")
@@ -347,7 +389,8 @@
self.delay = 1.0
def clientConnectionLost(self, connector, reason):
- self.log_warn("Connection to APN server lost: %s" % (reason,))
+ if not self.shuttingDown:
+ self.log_warn("Connection to APN server lost: %s" % (reason,))
ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
def clientConnectionFailed(self, connector, reason):
@@ -360,8 +403,12 @@
self.log_warn("Reconnecting to APN server")
ReconnectingClientFactory.retry(self, connector)
+ def stopTrying(self):
+ self.shuttingDown = True
+ ReconnectingClientFactory.stopTrying(self)
+
class APNConnectionService(service.Service, LoggingMixIn):
def __init__(self, host, port, certPath, keyPath, chainPath="",
@@ -578,6 +625,7 @@
yield txn.commit()
+
class APNFeedbackFactory(ClientFactory, LoggingMixIn):
protocol = APNFeedbackProtocol
@@ -594,8 +642,8 @@
class APNFeedbackService(APNConnectionService):
- def __init__(self, store, updateSeconds, host, port, certPath, keyPath,
- chainPath="", passphrase="", sslMethod="TLSv1_METHOD",
+ def __init__(self, store, updateSeconds, host, port,
+ certPath, keyPath, chainPath="", passphrase="", sslMethod="TLSv1_METHOD",
testConnector=None, reactor=None):
APNConnectionService.__init__(self, host, port, certPath, keyPath,
@@ -623,6 +671,7 @@
self.checkForFeedback)
+
class APNSubscriptionResource(ReadOnlyNoCopyResourceMixIn,
DAVResourceWithoutChildrenMixin, DAVResource, LoggingMixIn):
"""
Modified: CalendarServer/trunk/calendarserver/push/test/test_applepush.py
===================================================================
--- CalendarServer/trunk/calendarserver/push/test/test_applepush.py 2012-07-30 20:50:34 UTC (rev 9508)
+++ CalendarServer/trunk/calendarserver/push/test/test_applepush.py 2012-07-31 18:17:58 UTC (rev 9509)
@@ -14,6 +14,8 @@
# limitations under the License.
##
+import struct
+import time
from calendarserver.push.applepush import (
ApplePushNotifierService, APNProviderProtocol
)
@@ -21,7 +23,6 @@
from twistedcaldav.test.util import TestCase
from twisted.internet.defer import inlineCallbacks, succeed
from twisted.internet.task import Clock
-import struct
from txdav.common.datastore.test.util import buildStore, CommonCommonTests
from txdav.common.icommondatastore import InvalidSubscriptionValues
@@ -39,6 +40,8 @@
"Service" : "calendarserver.push.applepush.ApplePushNotifierService",
"Enabled" : True,
"SubscriptionURL" : "apn",
+ "SubscriptionPurgeSeconds" : 24 * 60 * 60,
+ "SubscriptionPurgeIntervalSeconds" : 24 * 60 * 60,
"DataHost" : "calendars.example.com",
"ProviderHost" : "gateway.push.apple.com",
"ProviderPort" : 2195,
@@ -260,7 +263,31 @@
yield txn.commit()
self.assertEquals(subscriptions, [])
+ #
+ # Verify purgeOldAPNSubscriptions
+ #
+ # Create two subscriptions, one old and one new
+ txn = self.store.newTransaction()
+ now = int(time.time())
+ yield txn.addAPNSubscription(token2, key1, now - 2 * 24 * 60 * 60, uid) # old
+ yield txn.addAPNSubscription(token2, key2, now, uid) # recent
+ yield txn.commit()
+
+ # Purge old subscriptions
+ txn = self.store.newTransaction()
+ yield txn.purgeOldAPNSubscriptions(now - 60 * 60)
+ yield txn.commit()
+
+ # Check that only the recent subscription remains
+ txn = self.store.newTransaction()
+ subscriptions = (yield txn.apnSubscriptionsByToken(token2))
+ yield txn.commit()
+ self.assertEquals(len(subscriptions), 1)
+ self.assertEquals(subscriptions[0][0], key2)
+
+ service.stopService()
+
def test_validToken(self):
self.assertTrue(validToken("2d0d55cd7f98bcb81c6e24abcdc35168254c7846a43e2828b1ba5a8f82e219df"))
self.assertFalse(validToken("d0d55cd7f98bcb81c6e24abcdc35168254c7846a43e2828b1ba5a8f82e219df"))
Modified: CalendarServer/trunk/twistedcaldav/customxml.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/customxml.py 2012-07-30 20:50:34 UTC (rev 9508)
+++ CalendarServer/trunk/twistedcaldav/customxml.py 2012-07-31 18:17:58 UTC (rev 9509)
@@ -415,6 +415,14 @@
@registerElement
+class PubSubAPSRefreshIntervalProperty (WebDAVTextElement):
+ namespace = calendarserver_namespace
+ name = "refresh-interval"
+ protected = True
+ hidden = True
+
+
+ at registerElement
class PubSubXMPPPushKeyProperty (WebDAVTextElement):
namespace = calendarserver_namespace
name = "pushkey"
Modified: CalendarServer/trunk/twistedcaldav/notify.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/notify.py 2012-07-30 20:50:34 UTC (rev 9508)
+++ CalendarServer/trunk/twistedcaldav/notify.py 2012-07-31 18:17:58 UTC (rev 9509)
@@ -1310,6 +1310,7 @@
url = "http://%s:%s/%s" % (config.ServerHostName, config.HTTPPort,
applePushSettings.SubscriptionURL)
settings["SubscriptionURL"] = url
+ settings["SubscriptionRefreshIntervalSeconds"] = applePushSettings.SubscriptionRefreshIntervalSeconds
settings["APSEnvironment"] = applePushSettings.Environment
return settings
Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py 2012-07-30 20:50:34 UTC (rev 9508)
+++ CalendarServer/trunk/twistedcaldav/resource.py 2012-07-31 18:17:58 UTC (rev 9509)
@@ -2295,7 +2295,7 @@
if qname == customxml.MaxCollections.qname() and config.MaxCollectionsPerHome:
returnValue(customxml.MaxCollections.fromString(config.MaxCollectionsPerHome))
-
+
elif qname == (customxml.calendarserver_namespace, "push-transports"):
if (config.Notifications.Services.XMPPNotifier.Enabled or
@@ -2322,6 +2322,9 @@
customxml.PubSubAPSEnvironmentProperty(
apsConfiguration["APSEnvironment"]
),
+ customxml.PubSubAPSRefreshIntervalProperty(
+ str(apsConfiguration["SubscriptionRefreshIntervalSeconds"])
+ ),
type="APSD",
)
)
Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py 2012-07-30 20:50:34 UTC (rev 9508)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py 2012-07-31 18:17:58 UTC (rev 9509)
@@ -698,6 +698,9 @@
"Service" : "calendarserver.push.applepush.ApplePushNotifierService",
"Enabled" : False,
"SubscriptionURL" : "apns",
+ "SubscriptionRefreshIntervalSeconds" : 2 * 24 * 60 * 60, # How often the client should re-register (2 days)
+ "SubscriptionPurgeIntervalSeconds" : 12 * 60 * 60, # How often a purge is done (12 hours)
+ "SubscriptionPurgeSeconds" : 14 * 24 * 60 * 60, # How old a subscription must be before it's purged (14 days)
"DataHost" : "",
"ProviderHost" : "gateway.push.apple.com",
"ProviderPort" : 2195,
Modified: CalendarServer/trunk/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/file.py 2012-07-30 20:50:34 UTC (rev 9508)
+++ CalendarServer/trunk/txdav/common/datastore/file.py 2012-07-31 18:17:58 UTC (rev 9509)
@@ -257,6 +257,9 @@
def removeAPNSubscription(self, token, key):
return NotImplementedError
+ def purgeOldAPNSubscriptions(self, purgeSeconds):
+ return NotImplementedError
+
def apnSubscriptionsByToken(self, token):
return NotImplementedError
Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py 2012-07-30 20:50:34 UTC (rev 9508)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py 2012-07-31 18:17:58 UTC (rev 9509)
@@ -518,6 +518,18 @@
@classproperty
+ def _purgeOldAPNSubscriptionQuery(cls): #@NoSelf
+ apn = schema.APN_SUBSCRIPTIONS
+ return Delete(From=apn,
+ Where=(apn.MODIFIED < Parameter("olderThan")))
+
+
+ def purgeOldAPNSubscriptions(self, olderThan):
+ return self._purgeOldAPNSubscriptionQuery.on(self,
+ olderThan=olderThan)
+
+
+ @classproperty
def _apnSubscriptionsByTokenQuery(cls): #@NoSelf
apn = schema.APN_SUBSCRIPTIONS
return Select([apn.RESOURCE_KEY, apn.MODIFIED, apn.SUBSCRIBER_GUID],
Modified: CalendarServer/trunk/txdav/common/icommondatastore.py
===================================================================
--- CalendarServer/trunk/txdav/common/icommondatastore.py 2012-07-30 20:50:34 UTC (rev 9508)
+++ CalendarServer/trunk/txdav/common/icommondatastore.py 2012-07-31 18:17:58 UTC (rev 9509)
@@ -195,6 +195,15 @@
@type key: C{str}
"""
+ def purgeOldAPNSubscriptions(olderThan):
+ """
+ Remove all subscription entries whose modified timestamp
+ is older than the provided timestamp.
+
+ @param olderThan: The cutoff timestamp in seconds
+ @type token: C{int}
+ """
+
def apnSubscriptionsByToken(token):
"""
Retrieve all subscription entries for the token.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120731/2f3c042c/attachment-0001.html>
More information about the calendarserver-changes
mailing list