[CalendarServer-changes] [2274] CalendarServer/branches/users/cdaboo/better-proxy-db-2269/ twistedcaldav

source_changes at macosforge.org source_changes at macosforge.org
Wed Apr 2 10:02:54 PDT 2008


Revision: 2274
          http://trac.macosforge.org/projects/calendarserver/changeset/2274
Author:   cdaboo at apple.com
Date:     2008-04-02 10:02:53 -0700 (Wed, 02 Apr 2008)

Log Message:
-----------
Add a new implementation of PROPFIND that uses findChildrenFaster to optimize child privilege calculations.
Also fixed a number of bugs in findChildrenFaster that only appeared for Depth:infinity. This included a
"None type iteration" problem that has occasionally been seen in the error logs. Not sure whether this fix
actually addresses the problem seen in the log, but it might.

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/better-proxy-db-2269/twistedcaldav/extensions.py
    CalendarServer/branches/users/cdaboo/better-proxy-db-2269/twistedcaldav/method/__init__.py
    CalendarServer/branches/users/cdaboo/better-proxy-db-2269/twistedcaldav/static.py

Added Paths:
-----------
    CalendarServer/branches/users/cdaboo/better-proxy-db-2269/twistedcaldav/method/propfind.py

Modified: CalendarServer/branches/users/cdaboo/better-proxy-db-2269/twistedcaldav/extensions.py
===================================================================
--- CalendarServer/branches/users/cdaboo/better-proxy-db-2269/twistedcaldav/extensions.py	2008-04-01 20:40:46 UTC (rev 2273)
+++ CalendarServer/branches/users/cdaboo/better-proxy-db-2269/twistedcaldav/extensions.py	2008-04-02 17:02:53 UTC (rev 2274)
@@ -83,10 +83,10 @@
     def authorizationPrincipal(self, request, authid, authnPrincipal):
         """
         Determine the authorization principal for the given request and authentication principal.
-        This implementation looks for an X-Authorize-As header value to use as the authoization principal.
+        This implementation looks for an X-Authorize-As header value to use as the authorization principal.
         
         @param request: the L{IRequest} for the request in progress.
-        @param authid: a string containing the uthentication/authorization identifier
+        @param authid: a string containing the authentication/authorization identifier
             for the principal to lookup.
         @param authnPrincipal: the L{IDAVPrincipal} for the authenticated principal
         @return: a deferred result C{tuple} of (L{IDAVPrincipal}, C{str}) containing the authorization principal
@@ -175,6 +175,7 @@
 
         if depth == "0" or not self.isCollection():
             yield None
+            return
 
         # First find all depth 1 children
         #children = []
@@ -229,10 +230,13 @@
                     for resource, url in items[2]:
                         badcallback(resource, url)
 
-        # TODO: Depth: inifinity support
+        # TODO: Depth: infinity support
         if depth == "infinity":
             for collection, url in allowed_collections:
-                d = waitForDeferred(collection.findChildrenFaster(request, depth, okcallback, badcallback, names, privileges, inherited_aces=None))
+                collection_inherited_aces = waitForDeferred(collection.inheritedACEsforChildren(request))
+                yield collection_inherited_aces
+                collection_inherited_aces = collection_inherited_aces.getResult()
+                d = waitForDeferred(collection.findChildrenFaster(depth, request, okcallback, badcallback, names, privileges, inherited_aces=collection_inherited_aces))
                 yield d
                 d.getResult()
                 
@@ -242,6 +246,10 @@
 
     def checkACLPrivilege(self, request, acl, privyset, privileges, inherited_aces):
         
+        if acl is None:
+            yield False
+            return
+
         principal = self.currentPrincipal(request)
 
         # Other principal types don't make sense as actors.

Modified: CalendarServer/branches/users/cdaboo/better-proxy-db-2269/twistedcaldav/method/__init__.py
===================================================================
--- CalendarServer/branches/users/cdaboo/better-proxy-db-2269/twistedcaldav/method/__init__.py	2008-04-01 20:40:46 UTC (rev 2273)
+++ CalendarServer/branches/users/cdaboo/better-proxy-db-2269/twistedcaldav/method/__init__.py	2008-04-02 17:02:53 UTC (rev 2274)
@@ -27,6 +27,7 @@
     "get",
     "mkcalendar",
     "mkcol",
+    "propfind",
     "put",
     "report",
     "report_calquery",

Added: CalendarServer/branches/users/cdaboo/better-proxy-db-2269/twistedcaldav/method/propfind.py
===================================================================
--- CalendarServer/branches/users/cdaboo/better-proxy-db-2269/twistedcaldav/method/propfind.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/better-proxy-db-2269/twistedcaldav/method/propfind.py	2008-04-02 17:02:53 UTC (rev 2274)
@@ -0,0 +1,209 @@
+# -*- test-case-name: twisted.web2.dav.test.test_prop.PROP.test_PROPFIND -*-
+##
+# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+# 
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+##
+
+"""
+WebDAV PROPFIND method
+"""
+
+__all__ = ["http_PROPFIND"]
+
+from twisted.python import log
+from twisted.python.failure import Failure
+from twisted.internet.defer import deferredGenerator, waitForDeferred
+from twisted.web2.http import HTTPError
+from twisted.web2 import responsecode
+from twisted.web2.http import StatusResponse
+from twisted.web2.dav import davxml
+from twisted.web2.dav.http import MultiStatusResponse, statusForFailure
+from twisted.web2.dav.util import normalizeURL, davXMLFromStream
+
+"""
+This is a direct copy of the twisted implementation of PROPFIND except that it uses the
+findChildrenFaster method to optimize child privilege checking.
+"""
+
+def http_PROPFIND(self, request):
+    """
+    Respond to a PROPFIND request. (RFC 2518, section 8.1)
+    """
+    if not self.exists():
+        log.err("File not found: %s" % (self.fp.path,))
+        raise HTTPError(responsecode.NOT_FOUND)
+
+    #
+    # Check authentication and access controls
+    #
+    x = waitForDeferred(self.authorize(request, (davxml.Read(),)))
+    yield x
+    x.getResult()
+
+    #
+    # Read request body
+    #
+    try:
+        doc = waitForDeferred(davXMLFromStream(request.stream))
+        yield doc
+        doc = doc.getResult()
+    except ValueError, e:
+        log.err("Error while handling PROPFIND body: %s" % (e,))
+        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
+
+    if doc is None:
+        # No request body means get all properties.
+        search_properties = "all"
+    else:
+        #
+        # Parse request
+        #
+        find = doc.root_element
+        if not isinstance(find, davxml.PropertyFind):
+            error = ("Non-%s element in PROPFIND request body: %s"
+                     % (davxml.PropertyFind.sname(), find))
+            log.err(error)
+            raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, error))
+
+        container = find.children[0]
+
+        if isinstance(container, davxml.AllProperties):
+            # Get all properties
+            search_properties = "all"
+        elif isinstance(container, davxml.PropertyName):
+            # Get names only
+            search_properties = "names"
+        elif isinstance(container, davxml.PropertyContainer):
+            properties = container.children
+            search_properties = [(p.namespace, p.name) for p in properties]
+        else:
+            raise AssertionError("Unexpected element type in %s: %s"
+                                 % (davxml.PropertyFind.sname(), container))
+
+    #
+    # Generate XML output stream
+    #
+    request_uri = request.uri
+    depth = request.headers.getHeader("depth", "infinity")
+
+    xml_responses = []
+
+    # FIXME: take advantage of the new generative properties of findChildren
+
+    my_url = normalizeURL(request_uri)
+    if self.isCollection() and not my_url.endswith("/"):
+        my_url += "/"
+
+    # Do some optimization of access control calculation by determining any inherited ACLs outside of
+    # the child resource loop and supply those to the checkPrivileges on each child.
+    filtered_aces = waitForDeferred(self.inheritedACEsforChildren(request))
+    yield filtered_aces
+    filtered_aces = filtered_aces.getResult()
+
+    resources = [(self, my_url)]
+
+    d = self.findChildrenFaster(depth, request, lambda x, y: resources.append((x, y)), None, None, (davxml.Read(),), inherited_aces=filtered_aces)
+    x = waitForDeferred(d)
+    yield x
+    x.getResult()
+
+    for resource, uri in resources:
+        if search_properties is "names":
+            try:
+                resource_properties = waitForDeferred(resource.listProperties(request))
+                yield resource_properties
+                resource_properties = resource_properties.getResult()
+            except:
+                log.err("Unable to get properties for resource %r" % (resource,))
+                raise
+
+            properties_by_status = {
+                responsecode.OK: [propertyName(p) for p in resource_properties]
+            }
+        else:
+            properties_by_status = {
+                responsecode.OK        : [],
+                responsecode.NOT_FOUND : [],
+            }
+
+            if search_properties is "all":
+                properties_to_enumerate = waitForDeferred(resource.listAllprop(request))
+                yield properties_to_enumerate
+                properties_to_enumerate = properties_to_enumerate.getResult()
+            else:
+                properties_to_enumerate = search_properties
+
+            for property in properties_to_enumerate:
+                has = waitForDeferred(resource.hasProperty(property, request))
+                yield has
+                has = has.getResult()
+                if has:
+                    try:
+                        resource_property = waitForDeferred(resource.readProperty(property, request))
+                        yield resource_property
+                        resource_property = resource_property.getResult()
+                    except:
+                        f = Failure()
+
+                        log.err("Error reading property %r for resource %s: %s" % (property, uri, f.value))
+
+                        status = statusForFailure(f, "getting property: %s" % (property,))
+                        if status not in properties_by_status:
+                            properties_by_status[status] = []
+                        properties_by_status[status].append(propertyName(property))
+                    else:
+                        properties_by_status[responsecode.OK].append(resource_property)
+                else:
+                    properties_by_status[responsecode.NOT_FOUND].append(propertyName(property))
+
+        propstats = []
+
+        for status in properties_by_status:
+            properties = properties_by_status[status]
+            if not properties: continue
+
+            xml_status    = davxml.Status.fromResponseCode(status)
+            xml_container = davxml.PropertyContainer(*properties)
+            xml_propstat  = davxml.PropertyStatus(xml_container, xml_status)
+
+            propstats.append(xml_propstat)
+
+        xml_resource = davxml.HRef(uri)
+        xml_response = davxml.PropertyStatusResponse(xml_resource, *propstats)
+
+        xml_responses.append(xml_response)
+
+    #
+    # Return response
+    #
+    yield MultiStatusResponse(xml_responses)
+
+http_PROPFIND = deferredGenerator(http_PROPFIND)
+
+##
+# Utilities
+##
+
+def propertyName(name):
+    property_namespace, property_name = name
+    pname = davxml.WebDAVUnknownElement()
+    pname.namespace = property_namespace
+    pname.name = property_name
+    return pname

Modified: CalendarServer/branches/users/cdaboo/better-proxy-db-2269/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/users/cdaboo/better-proxy-db-2269/twistedcaldav/static.py	2008-04-01 20:40:46 UTC (rev 2273)
+++ CalendarServer/branches/users/cdaboo/better-proxy-db-2269/twistedcaldav/static.py	2008-04-02 17:02:53 UTC (rev 2274)
@@ -832,4 +832,5 @@
 twistedcaldav.method.delete.CalDAVFile   = CalDAVFile
 twistedcaldav.method.get.CalDAVFile      = CalDAVFile
 twistedcaldav.method.mkcol.CalDAVFile    = CalDAVFile
+twistedcaldav.method.propfind.CalDAVFile = CalDAVFile
 twistedcaldav.method.put.CalDAVFile      = CalDAVFile

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20080402/f54bcfc2/attachment-0001.html


More information about the calendarserver-changes mailing list