[CalendarServer-changes] [8361] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Thu Dec 1 12:53:41 PST 2011


Revision: 8361
          http://trac.macosforge.org/projects/calendarserver/changeset/8361
Author:   sagen at apple.com
Date:     2011-12-01 12:53:39 -0800 (Thu, 01 Dec 2011)
Log Message:
-----------
Fix for handling APN feedback in cases where data received does not line up with expected message length.

Modified Paths:
--------------
    CalendarServer/trunk/calendarserver/push/applepush.py
    CalendarServer/trunk/calendarserver/push/test/test_applepush.py
    CalendarServer/trunk/twistedcaldav/upgrade.py

Modified: CalendarServer/trunk/calendarserver/push/applepush.py
===================================================================
--- CalendarServer/trunk/calendarserver/push/applepush.py	2011-11-30 23:22:46 UTC (rev 8360)
+++ CalendarServer/trunk/calendarserver/push/applepush.py	2011-12-01 20:53:39 UTC (rev 8361)
@@ -26,7 +26,7 @@
 from twext.web2.server import parsePOSTData
 from twisted.application import service
 from twisted.internet import reactor, protocol
-from twisted.internet.defer import inlineCallbacks, returnValue, succeed
+from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.internet.protocol import ClientFactory, ReconnectingClientFactory
 from twistedcaldav.extensions import DAVResource, DAVResourceWithoutChildrenMixin
 from twistedcaldav.resource import ReadOnlyNoCopyResourceMixIn
@@ -212,7 +212,7 @@
         # Clear the reference to us from the factory
         self.factory.connection = None
 
-    def dataReceived(self, data):
+    def dataReceived(self, data, fn=None):
         self.log_debug("ProviderProtocol dataReceived %d bytes" % (len(data),))
         command, status, identifier = struct.unpack("!BBI", data)
         if command == self.COMMAND_ERROR:
@@ -369,20 +369,38 @@
     Implements the Feedback portion of APNS
     """
 
+    MESSAGE_LENGTH = 38
+
     def connectionMade(self):
         self.log_debug("FeedbackProtocol connectionMade")
+        self.buffer = ""
 
-    def dataReceived(self, data):
+    @inlineCallbacks
+    def dataReceived(self, data, fn=None):
+        """
+        Buffer and divide up received data into feedback messages which are
+        always 38 bytes long
+        """
+
+        if fn is None:
+            fn = self.processFeedback
+
         self.log_debug("FeedbackProtocol dataReceived %d bytes" % (len(data),))
-        try:
-            timestamp, tokenLength, binaryToken = struct.unpack("!IH32s", data)
-        except struct.error:
-            self.log_warn("FeedbackProtocol received malformed data: %s" %
-                (data.encode("hex"),))
-            return succeed(None)
-        token = binaryToken.encode("hex").lower()
-        return self.processFeedback(timestamp, token)
+        self.buffer += data
 
+        while len(self.buffer) >= self.MESSAGE_LENGTH:
+            message = self.buffer[:self.MESSAGE_LENGTH]
+            self.buffer = self.buffer[self.MESSAGE_LENGTH:]
+
+            try:
+                timestamp, tokenLength, binaryToken = struct.unpack("!IH32s",
+                    message)
+                token = binaryToken.encode("hex").lower()
+                yield fn(timestamp, token)
+            except Exception, e:
+                self.log_warn("FeedbackProtocol could not process message: %s (%s)" %
+                    (message.encode("hex"), e))
+
     @inlineCallbacks
     def processFeedback(self, timestamp, token):
         """

Modified: CalendarServer/trunk/calendarserver/push/test/test_applepush.py
===================================================================
--- CalendarServer/trunk/calendarserver/push/test/test_applepush.py	2011-11-30 23:22:46 UTC (rev 8360)
+++ CalendarServer/trunk/calendarserver/push/test/test_applepush.py	2011-12-01 20:53:39 UTC (rev 8361)
@@ -18,7 +18,7 @@
     ApplePushNotifierService, APNProviderProtocol
 )
 from twistedcaldav.test.util import TestCase
-from twisted.internet.defer import inlineCallbacks
+from twisted.internet.defer import inlineCallbacks, succeed
 from twisted.internet.task import Clock
 import struct
 from txdav.common.datastore.test.util import buildStore, CommonCommonTests
@@ -115,17 +115,40 @@
         yield txn.commit()
         self.assertEquals(len(subscriptions), 2)
 
-        # Simulate malformed feedback
+
+        # Simulate feedback with a single token
         connector = service.feedbacks["CalDAV"].testConnector
-        yield connector.receiveData("malformed")
-
-        # Simulate feedback
         timestamp = 2000
         binaryToken = token.decode("hex")
         feedbackData = struct.pack("!IH32s", timestamp, len(binaryToken),
             binaryToken)
         yield connector.receiveData(feedbackData)
 
+        # Simulate feedback with multiple tokens, and dataReceived called
+        # with amounts of data not fitting message boundaries
+        history = []
+        def testFunction(timestamp, token):
+            history.append((timestamp, token))
+            return succeed(None)
+        timestamp = 2000
+        binaryToken = token.decode("hex")
+        feedbackData = struct.pack("!IH32sIH32s",
+            timestamp, len(binaryToken), binaryToken,
+            timestamp, len(binaryToken), binaryToken,
+            )
+        # Send 1st 10 bytes
+        yield connector.receiveData(feedbackData[:10], fn=testFunction)
+        # Send remaining bytes
+        yield connector.receiveData(feedbackData[10:], fn=testFunction)
+        self.assertEquals(history, [(timestamp, token), (timestamp, token)])
+        # Buffer is empty
+        self.assertEquals(len(connector.service.protocol.buffer), 0)
+
+        # Sending 39 bytes
+        yield connector.receiveData("!" * 39, fn=testFunction)
+        # Buffer has 1 byte remaining
+        self.assertEquals(len(connector.service.protocol.buffer), 1)
+
         # The second subscription should now be gone
         # Prior to feedback, there are 2 subscriptions
         txn = self.store.newTransaction()
@@ -143,8 +166,8 @@
         self.transport = StubTransport()
         service.protocol.makeConnection(self.transport)
 
-    def receiveData(self, data):
-        return self.service.protocol.dataReceived(data)
+    def receiveData(self, data, fn=None):
+        return self.service.protocol.dataReceived(data, fn=fn)
 
 
 class StubTransport(object):

Modified: CalendarServer/trunk/twistedcaldav/upgrade.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/upgrade.py	2011-11-30 23:22:46 UTC (rev 8360)
+++ CalendarServer/trunk/twistedcaldav/upgrade.py	2011-12-01 20:53:39 UTC (rev 8361)
@@ -873,21 +873,22 @@
     # Fetch the autoSchedule assignments from resourceinfo.sqlite and store
     # the values in augments
     augmentService = directory.augmentService
-    augmentRecords = []
-    dbPath = os.path.join(config.DataRoot, ResourceInfoDatabase.dbFilename)
-    if os.path.exists(dbPath):
-        resourceInfoDatabase = ResourceInfoDatabase(config.DataRoot)
-        results = resourceInfoDatabase._db_execute(
-            "select GUID, AUTOSCHEDULE from RESOURCEINFO"
-        )
-        for guid, autoSchedule in results:
-            record = directory.recordWithGUID(guid)
-            if record is not None:
-                augmentRecord = (yield augmentService.getAugmentRecord(guid, record.recordType))
-                augmentRecord.autoSchedule = autoSchedule
-                augmentRecords.append(augmentRecord)
+    if augmentService:
+        augmentRecords = []
+        dbPath = os.path.join(config.DataRoot, ResourceInfoDatabase.dbFilename)
+        if os.path.exists(dbPath):
+            resourceInfoDatabase = ResourceInfoDatabase(config.DataRoot)
+            results = resourceInfoDatabase._db_execute(
+                "select GUID, AUTOSCHEDULE from RESOURCEINFO"
+            )
+            for guid, autoSchedule in results:
+                record = directory.recordWithGUID(guid)
+                if record is not None:
+                    augmentRecord = (yield augmentService.getAugmentRecord(guid, record.recordType))
+                    augmentRecord.autoSchedule = autoSchedule
+                    augmentRecords.append(augmentRecord)
 
-    yield augmentService.addAugmentRecords(augmentRecords)
+        yield augmentService.addAugmentRecords(augmentRecords)
 
 
 class UpgradeFileSystemFormatService(Service, object):
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20111201/50b68197/attachment.html>


More information about the calendarserver-changes mailing list