[CalendarServer-changes] [270] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Thu Oct 12 11:45:06 PDT 2006


Revision: 270
          http://trac.macosforge.org/projects/calendarserver/changeset/270
Author:   cdaboo at apple.com
Date:     2006-10-12 11:45:06 -0700 (Thu, 12 Oct 2006)

Log Message:
-----------
Use MD5 hash for ETag value. The server now adds a private property to each resource as it is written. That property
contains the MD5 hash of the resource content. The etag property has been changed to look for that property and
return the hash value, or fall back to existing weak/last-modified behavior.

Also fixed a problem with recent principal property changes. 

Modified Paths:
--------------
    CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch
    CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.resource.patch
    CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.static.patch
    CalendarServer/trunk/twistedcaldav/ical.py
    CalendarServer/trunk/twistedcaldav/method/put_common.py
    CalendarServer/trunk/twistedcaldav/resource.py

Added Paths:
-----------
    CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.__init__.patch
    CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.stream.patch

Added: CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.__init__.patch
===================================================================
--- CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.__init__.patch	                        (rev 0)
+++ CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.__init__.patch	2006-10-12 18:45:06 UTC (rev 270)
@@ -0,0 +1,12 @@
+Index: twisted/web2/dav/__init__.py
+===================================================================
+--- twisted/web2/dav/__init__.py	(revision 18375)
++++ twisted/web2/dav/__init__.py	(working copy)
+@@ -45,6 +45,7 @@
+     "noneprops",
+     "resource",
+     "static",
++    "stream",
+     "util",
+     "xattrprops",
+ ]

Modified: CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch
===================================================================
--- CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch	2006-10-12 18:18:04 UTC (rev 269)
+++ CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch	2006-10-12 18:45:06 UTC (rev 270)
@@ -2,7 +2,7 @@
 ===================================================================
 --- twisted/web2/dav/method/put_common.py	(revision 0)
 +++ twisted/web2/dav/method/put_common.py	(revision 0)
-@@ -0,0 +1,234 @@
+@@ -0,0 +1,252 @@
 +##
 +# Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved.
 +#
@@ -34,10 +34,14 @@
 +from twisted.python.filepath import FilePath
 +from twisted.web2 import responsecode
 +from twisted.web2.dav.fileop import copy, delete, put
++from twisted.web2.dav.resource import TwistedGETContentMD5
++from twisted.web2.dav.stream import MD5StreamWrapper
 +from twisted.web2.http import HTTPError
 +from twisted.web2.http import StatusResponse
 +from twisted.web2.iweb import IResponse
 +
++import md5
++
 +def storeResource(
 +    request,
 +    source=None, source_uri=None,
@@ -192,11 +196,25 @@
 +        if source is not None:
 +            response = maybeDeferred(copy, source.fp, destination.fp, destination_uri, depth)
 +        else:
-+            response = maybeDeferred(put, request.stream, destination.fp)
++            md5 = MD5StreamWrapper(request.stream)
++            response = maybeDeferred(put, md5, destination.fp)
++
 +        response = waitForDeferred(response)
 +        yield response
 +        response = response.getResult()
 +
++        # Update the MD5 value on the resource
++        if source is not None:
++            # Copy MD5 value from source to destination
++            if source.hasDeadProperty(TwistedGETContentMD5):
++                md5 = source.readDeadProperty(TwistedGETContentMD5)
++                destination.writeDeadProperty(md5)
++        else:
++            # Finish MD5 calc and write dead property
++            md5.close()
++            md5 = md5.getMD5()
++            destination.writeDeadProperty(TwistedGETContentMD5.fromString(md5))
++
 +        response = IResponse(response)
 +        
 +        # Do quota check on destination

Modified: CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.resource.patch
===================================================================
--- CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.resource.patch	2006-10-12 18:18:04 UTC (rev 269)
+++ CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.resource.patch	2006-10-12 18:45:06 UTC (rev 270)
@@ -1,6 +1,6 @@
 Index: twisted/web2/dav/resource.py
 ===================================================================
---- twisted/web2/dav/resource.py	(revision 18219)
+--- twisted/web2/dav/resource.py	(revision 18375)
 +++ twisted/web2/dav/resource.py	(working copy)
 @@ -130,6 +130,8 @@
          (dav_namespace, "acl-restrictions"          ), # RFC 3744, section 5.6
@@ -355,10 +355,19 @@
      # HTTP
      ##
  
-@@ -1702,6 +2011,28 @@
+@@ -1702,6 +2011,37 @@
  davxml.registerElement(TwistedACLInheritable)
  davxml.ACE.allowed_children[(twisted_dav_namespace, "inheritable")] = (0, 1)
  
++class TwistedGETContentMD5 (davxml.WebDAVTextElement):
++    """
++    MD5 hash of the resource content.
++    """
++    namespace = twisted_dav_namespace
++    name = "getcontentmd5"
++
++davxml.registerElement(TwistedGETContentMD5)
++
 +"""
 +When set on a collection, this property indicates that the collection has a quota limit for
 +the size of all resources stored in the collection (and any associate meta-data such as properties).

Modified: CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.static.patch
===================================================================
--- CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.static.patch	2006-10-12 18:18:04 UTC (rev 269)
+++ CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.static.patch	2006-10-12 18:45:06 UTC (rev 270)
@@ -1,16 +1,43 @@
 Index: twisted/web2/dav/static.py
 ===================================================================
---- twisted/web2/dav/static.py	(revision 18219)
+--- twisted/web2/dav/static.py	(revision 18375)
 +++ twisted/web2/dav/static.py	(working copy)
-@@ -21,6 +21,7 @@
- #
- # DRI: Wilfredo Sanchez, wsanchez at apple.com
- ##
-+from os import path
+@@ -28,16 +28,16 @@
  
- """
- WebDAV-aware static resources.
-@@ -98,6 +99,50 @@
+ __all__ = ["DAVFile"]
+ 
+-import os
+-
+-from twisted.python import log
+ from twisted.internet.defer import succeed, deferredGenerator, waitForDeferred
+-from twisted.web2.static import File
++from twisted.python import log
++from twisted.web2 import http_headers
+ from twisted.web2 import responsecode, dirlist
+-from twisted.web2.http import RedirectResponse
+ from twisted.web2.dav import davxml
+ from twisted.web2.dav.resource import DAVResource, davPrivilegeSet
++from twisted.web2.dav.resource import TwistedGETContentMD5
+ from twisted.web2.dav.util import bindMethods
++from twisted.web2.http import RedirectResponse
++from twisted.web2.static import File
+ 
+ try:
+     from twisted.web2.dav.xattrprops import xattrPropertyStore as DeadPropertyStore
+@@ -75,6 +75,12 @@
+     # WebDAV
+     ##
+ 
++    def etag(self):
++        if self.hasDeadProperty(TwistedGETContentMD5):
++            return http_headers.ETag(str(self.readDeadProperty(TwistedGETContentMD5)))
++        else:
++            return super(DAVFile, self).etag()
++
+     def davComplianceClasses(self):
+         return ("1", "access-control") # Add "2" when we have locking
+ 
+@@ -98,6 +104,50 @@
          return succeed(davPrivilegeSet)
  
      ##
@@ -61,7 +88,7 @@
      # Workarounds for issues with File
      ##
  
-@@ -135,8 +180,12 @@
+@@ -135,8 +185,12 @@
                  else:
                      children = []
  

Added: CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.stream.patch
===================================================================
--- CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.stream.patch	                        (rev 0)
+++ CalendarServer/trunk/lib-patches/Twisted/twisted.web2.dav.stream.patch	2006-10-12 18:45:06 UTC (rev 270)
@@ -0,0 +1,79 @@
+Index: twisted/web2/dav/stream.py
+===================================================================
+--- twisted/web2/dav/stream.py	(revision 0)
++++ twisted/web2/dav/stream.py	(revision 0)
+@@ -0,0 +1,74 @@
++##
++# 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.
++#
++# DRI: Cyrus Daboo, cdaboo at apple.com
++##
++
++"""
++Class that implements a stream that calculates the MD5 hash of the data
++as the data is read.
++"""
++
++__all__ = ["MD5StreamWrapper"]
++
++from twisted.internet.defer import Deferred
++from twisted.web2.stream import SimpleStream
++
++import md5
++
++class MD5StreamWrapper(SimpleStream):
++ 
++    def __init__(self, wrap):
++        
++        assert wrap is not None, "Must have a stream to wrap."
++
++        self.stream = wrap
++
++        # Init MD5
++        self.md5 = md5.new()
++    
++    def read(self):
++        assert self.md5 is not None, "Cannot call read after close."
++
++        # Read from wrapped stream first
++        b = self.stream.read()
++        
++        if isinstance(b, Deferred):
++            def _gotData(data):
++                self.md5.update(data)
++                return data
++            b.addCallback(_gotData)
++        else:
++            # Update current MD5 state
++            self.md5.update(str(b))
++    
++        return b
++    
++    def close(self):
++        # Close out the MD5 hash
++        self.md5value = self.md5.hexdigest()
++        self.md5 = None
++
++        SimpleStream.close(self)
++    
++    def getMD5(self):
++        assert hasattr(self, "md5value"), "Stream has to be closed first"
++        return self.md5value

Modified: CalendarServer/trunk/twistedcaldav/ical.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/ical.py	2006-10-12 18:18:04 UTC (rev 269)
+++ CalendarServer/trunk/twistedcaldav/ical.py	2006-10-12 18:45:06 UTC (rev 270)
@@ -174,6 +174,8 @@
             return clazz(None, vobject=readComponents(stream).next())
         except vParseError, e:
             raise ValueError(e)
+        except StopIteration, e:
+            raise ValueError(e)
 
     @classmethod
     def fromIStream(clazz, stream):

Modified: CalendarServer/trunk/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/put_common.py	2006-10-12 18:18:04 UTC (rev 269)
+++ CalendarServer/trunk/twistedcaldav/method/put_common.py	2006-10-12 18:45:06 UTC (rev 270)
@@ -32,6 +32,8 @@
 from twisted.web2.dav.element.base import PCDATAElement
 from twisted.web2.dav.fileop import copy, delete, put
 from twisted.web2.dav.http import ErrorResponse
+from twisted.web2.dav.resource import TwistedGETContentMD5
+from twisted.web2.dav.stream import MD5StreamWrapper
 from twisted.web2.dav.util import joinURL, parentForURL
 from twisted.web2.http import HTTPError, StatusResponse
 from twisted.web2.iweb import IResponse
@@ -427,11 +429,24 @@
         if source is not None:
             response = maybeDeferred(copy, source.fp, destination.fp, destination_uri, "0")
         else:
-            response = maybeDeferred(put, MemoryStream(calendardata), destination.fp)
+            md5 = MD5StreamWrapper(MemoryStream(calendardata))
+            response = maybeDeferred(put, md5, destination.fp)
         response = waitForDeferred(response)
         yield response
         response = response.getResult()
 
+        # Update the MD5 value on the resource
+        if source is not None:
+            # Copy MD5 value from source to destination
+            if source.hasDeadProperty(TwistedGETContentMD5):
+                md5 = source.readDeadProperty(TwistedGETContentMD5)
+                destination.writeDeadProperty(md5)
+        else:
+            # Finish MD5 calc and write dead property
+            md5.close()
+            md5 = md5.getMD5()
+            destination.writeDeadProperty(TwistedGETContentMD5.fromString(md5))
+
         response = IResponse(response)
         
         def doDestinationIndex(caltoindex):
@@ -543,7 +558,7 @@
         yield response
         return
 
-    except:
+    except Exception, e:
         if reserved:
             destination_index.unreserveUID(uid)
             reserved = False

Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py	2006-10-12 18:18:04 UTC (rev 269)
+++ CalendarServer/trunk/twistedcaldav/resource.py	2006-10-12 18:45:06 UTC (rev 270)
@@ -633,7 +633,7 @@
             yield ()
             return
 
-        has = waitForDeferred(inbox.hasProperty((caldav_namespace, "calendar-free-busy-set", request)))
+        has = waitForDeferred(inbox.hasProperty((caldav_namespace, "calendar-free-busy-set"), request))
         yield has
         has = has.getResult()
         
@@ -641,7 +641,7 @@
             yield ()
             return
 
-        fbset = waitForDeferred(inbox.readProperty((caldav_namespace, "calendar-free-busy-set", request)))
+        fbset = waitForDeferred(inbox.readProperty((caldav_namespace, "calendar-free-busy-set"), request))
         yield fbset
         fbset = fbset.getResult()
 

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


More information about the calendarserver-changes mailing list