[CalendarServer-changes] [14798] CalendarServer/trunk/twistedcaldav

source_changes at macosforge.org source_changes at macosforge.org
Tue May 19 01:30:26 PDT 2015


Revision: 14798
          http://trac.calendarserver.org//changeset/14798
Author:   cdaboo at apple.com
Date:     2015-05-19 01:30:26 -0700 (Tue, 19 May 2015)
Log Message:
-----------
txn time outs now generate a 503/Retry-After HTTP response rather than a 500.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/resource.py
    CalendarServer/trunk/twistedcaldav/stdconfig.py
    CalendarServer/trunk/twistedcaldav/test/test_resource.py

Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py	2015-05-18 23:51:07 UTC (rev 14797)
+++ CalendarServer/trunk/twistedcaldav/resource.py	2015-05-19 08:30:26 UTC (rev 14798)
@@ -29,38 +29,17 @@
     "isAddressBookCollectionResource",
 ]
 
-import hashlib
-from urlparse import urlsplit
-import urllib
-from uuid import UUID
-import uuid
+from calendarserver.push.notifier import getPubSubAPSConfiguration
 
+from twext.enterprise.ienterprise import AlreadyFinishedError
+from twext.python.log import Logger
 
-from zope.interface import implements
-
+from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.internet.defer import succeed, maybeDeferred, fail
-from twisted.internet.defer import inlineCallbacks, returnValue
 
-from twext.python.log import Logger
-
-from txdav.xml import element
-from txdav.xml.element import dav_namespace
-
-from txweb2 import responsecode, http, http_headers
-from txweb2.auth.wrapper import UnauthorizedResponse
-from txweb2.dav.auth import AuthenticationWrapper as SuperAuthenticationWrapper
-from txweb2.dav.idav import IDAVPrincipalCollectionResource
-from txweb2.dav.resource import AccessDeniedError, DAVPrincipalCollectionResource, \
-    davPrivilegeSet
-from txweb2.dav.resource import TwistedACLInheritable
-from txweb2.dav.util import joinURL, parentForURL, normalizeURL
-from txweb2.http import HTTPError, RedirectResponse, StatusResponse, Response, JSONResponse
-from txweb2.dav.http import ErrorResponse
-from txweb2.http_headers import MimeType, ETag
-from txweb2.stream import MemoryStream
-
 from twistedcaldav import caldavxml, customxml
 from twistedcaldav import carddavxml
+from twistedcaldav import ical
 from twistedcaldav.cache import PropfindCacheMixin
 from twistedcaldav.caldavxml import caldav_namespace
 from twistedcaldav.carddavxml import carddav_namespace
@@ -70,19 +49,39 @@
 from twistedcaldav.datafilters.privateevents import PrivateEventFilter
 from twistedcaldav.extensions import DAVResource, DAVPrincipalResource, \
     DAVResourceWithChildrenMixin
-from twistedcaldav import ical
 from twistedcaldav.ical import Component
-
 from twistedcaldav.icaldav import ICalDAVResource, ICalendarPrincipalResource
 from twistedcaldav.linkresource import LinkResource
-from calendarserver.push.notifier import getPubSubAPSConfiguration
 from twistedcaldav.sharing import SharedResourceMixin, SharedHomeMixin
-from txdav.caldav.datastore.util import normalizationLookup
 from twistedcaldav.vcard import Component as vComponent
 
+from txdav.caldav.datastore.util import normalizationLookup
 from txdav.common.icommondatastore import InternalDataStoreError, \
     SyncTokenValidException
+from txdav.xml import element
+from txdav.xml.element import dav_namespace
 
+from txweb2 import responsecode, http, http_headers
+from txweb2.auth.wrapper import UnauthorizedResponse
+from txweb2.dav.auth import AuthenticationWrapper as SuperAuthenticationWrapper
+from txweb2.dav.http import ErrorResponse
+from txweb2.dav.idav import IDAVPrincipalCollectionResource
+from txweb2.dav.resource import AccessDeniedError, DAVPrincipalCollectionResource, \
+    davPrivilegeSet
+from txweb2.dav.resource import TwistedACLInheritable
+from txweb2.dav.util import joinURL, parentForURL, normalizeURL
+from txweb2.http import HTTPError, RedirectResponse, StatusResponse, Response, JSONResponse
+from txweb2.http_headers import MimeType, ETag
+from txweb2.stream import MemoryStream
+
+from urlparse import urlsplit
+from uuid import UUID
+from zope.interface import implements
+import hashlib
+import time
+import urllib
+import uuid
+
 ##
 # Sharing Conts
 ##
@@ -325,12 +324,23 @@
         @param transaction: optional transaction to use instead of associated transaction
         @type transaction: L{txdav.caldav.idav.ITransaction}
         """
-        response = yield super(CalDAVResource, self).renderHTTP(request)
+        try:
+            response = yield super(CalDAVResource, self).renderHTTP(request)
+        except AlreadyFinishedError:
+            self._transactionError = True
         if transaction is None:
             transaction = self._associatedTransaction
         if transaction is not None:
             if self._transactionError:
-                yield transaction.abort()
+                try:
+                    yield transaction.abort()
+                except AlreadyFinishedError:
+                    if transaction.timedout:
+                        response = http.StatusResponse(responsecode.SERVICE_UNAVAILABLE, responsecode.RESPONSES[responsecode.SERVICE_UNAVAILABLE])
+                        response.headers.setHeader("Retry-After", time.time() + config.TransactionHTTPRetrySeconds)
+                        raise HTTPError(response)
+                    else:
+                        raise
             else:
                 yield transaction.commit()
 

Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py	2015-05-18 23:51:07 UTC (rev 14797)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py	2015-05-19 08:30:26 UTC (rev 14798)
@@ -201,9 +201,11 @@
 
     "UseDatabase": True, # True: database; False: files
 
-    "TransactionTimeoutSeconds": 300, # Timeout transactions that take longer than
-                                      # the specified number of seconds. Zero means
-                                      # no timeouts. 5 minute default.
+    "TransactionTimeoutSeconds": 300,   # Timeout transactions that take longer than
+                                        # the specified number of seconds. Zero means
+                                        # no timeouts. 5 minute default.
+    "TransactionHTTPRetrySeconds": 300, # When a transactions times out tell HTTP clients
+                                        # clients to retry after this amount of time
 
     "DBType": "", # 2 possible values: empty, meaning 'spawn postgres
                   # yourself', or 'postgres', meaning 'connect to a

Modified: CalendarServer/trunk/twistedcaldav/test/test_resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_resource.py	2015-05-18 23:51:07 UTC (rev 14797)
+++ CalendarServer/trunk/twistedcaldav/test/test_resource.py	2015-05-19 08:30:26 UTC (rev 14798)
@@ -14,22 +14,26 @@
 # limitations under the License.
 ##
 
-from twisted.internet.defer import inlineCallbacks
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, returnValue, Deferred
+
 from twistedcaldav import carddavxml
 from twistedcaldav.config import config
 from twistedcaldav.notifications import NotificationCollectionResource
-from twistedcaldav.resource import (
-    CalDAVResource, CommonHomeResource,
+from twistedcaldav.resource import \
+    CalDAVResource, CommonHomeResource, \
     CalendarHomeResource, AddressBookHomeResource
-)
-from twistedcaldav.test.util import (
+from twistedcaldav.storebridge import CalendarCollectionResource
+from twistedcaldav.test.util import TestCase
+from twistedcaldav.test.util import \
     InMemoryPropertyStore, StoreTestCase, SimpleStoreRequest
-)
-from twistedcaldav.test.util import TestCase
+
+from txdav.xml import element
 from txdav.xml.element import HRef
+
+from txweb2 import responsecode
 from txweb2.http import HTTPError
 from txweb2.test.test_server import SimpleRequest
-from txdav.xml import element
 
 
 
@@ -86,6 +90,47 @@
 
 
 
+class TransactionTimeoutTests(StoreTestCase):
+
+    @inlineCallbacks
+    def setUp(self):
+        yield super(TransactionTimeoutTests, self).setUp()
+
+
+    @inlineCallbacks
+    def test_timeoutRetry(self):
+        """
+        Test that a timed out transaction during an HTTP request results in a 503 error
+        with a Retry-After header.
+        """
+
+        # Patch request handling to add a delay to trigger the txn time out
+        original = CalendarCollectionResource.iCalendarRolledup
+        @inlineCallbacks
+        def _iCalendarRolledup(self, request):
+            d = Deferred()
+            reactor.callLater(2, d.callback, None)
+            yield d
+            result = yield original(self, request)
+            returnValue(result)
+        self.patch(CalendarCollectionResource, "iCalendarRolledup", _iCalendarRolledup)
+
+        self.patch(self.store, "timeoutTransactions", 1)
+
+        # Run delayed request
+        authPrincipal = yield self.actualRoot.findPrincipalForAuthID("user01")
+        request = SimpleStoreRequest(self, "GET", "/calendars/__uids__/user01/calendar/", authPrincipal=authPrincipal)
+        try:
+            yield self.send(request)
+        except HTTPError as e:
+            self.assertEqual(e.response.code, responsecode.SERVICE_UNAVAILABLE)
+            self.assertTrue(e.response.headers.hasHeader("Retry-After"))
+            self.assertApproximates(int(e.response.headers.getRawHeaders("Retry-After")[0]), config.TransactionHTTPRetrySeconds, 1)
+        else:
+            self.fail("HTTPError not raised")
+
+
+
 class CommonHomeResourceTests(TestCase):
 
     def test_commonHomeliveProperties(self):
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20150519/7dbe6c66/attachment-0001.html>


More information about the calendarserver-changes mailing list