[CalendarServer-changes] [10157] CalendarServer/trunk/calendarserver

source_changes at macosforge.org source_changes at macosforge.org
Tue Dec 11 18:30:19 PST 2012


Revision: 10157
          http://trac.calendarserver.org//changeset/10157
Author:   cdaboo at apple.com
Date:     2012-12-11 18:30:18 -0800 (Tue, 11 Dec 2012)
Log Message:
-----------
Enhanced method name logging on stats socket.

Modified Paths:
--------------
    CalendarServer/trunk/calendarserver/accesslog.py

Added Paths:
-----------
    CalendarServer/trunk/calendarserver/methodDescriptor.py
    CalendarServer/trunk/calendarserver/test/test_methodDescriptor.py

Modified: CalendarServer/trunk/calendarserver/accesslog.py
===================================================================
--- CalendarServer/trunk/calendarserver/accesslog.py	2012-12-11 21:35:08 UTC (rev 10156)
+++ CalendarServer/trunk/calendarserver/accesslog.py	2012-12-12 02:30:18 UTC (rev 10157)
@@ -35,30 +35,36 @@
     psutil = None
 import time
 
-from twisted.internet import protocol, task
-from twisted.protocols import amp
+from calendarserver.methodDescriptor import getAdjustedMethodName
+
+from twext.python.log import Logger
 from twext.web2 import iweb
-from txdav.xml import element as davxml
 from twext.web2.log import BaseCommonAccessLoggingObserver
 from twext.web2.log import LogWrapperResource
 
-from twext.python.log import Logger
+from twisted.internet import protocol, task
+from twisted.protocols import amp
 
 from twistedcaldav.config import config
 from twistedcaldav.directory.directory import DirectoryService
 
+from txdav.xml import element as davxml
+
 log = Logger()
 
 class DirectoryLogWrapperResource(LogWrapperResource):
-    
+
     def __init__(self, resource, directory):
         super(DirectoryLogWrapperResource, self).__init__(resource)
-        
+
         self.directory = directory
-        
+
+
     def getDirectory(self):
         return self.directory
 
+
+
 class CommonAccessLoggingObserverExtensions(BaseCommonAccessLoggingObserver):
     """
     A base class for our extension to the L{BaseCommonAccessLoggingObserver}
@@ -69,7 +75,7 @@
         format = None
         formatArgs = None
         if eventDict.get("interface") is iweb.IRequest:
-            
+
             request = eventDict["request"]
             response = eventDict["response"]
             loginfo = eventDict["loginfo"]
@@ -82,7 +88,7 @@
                     uidz = None
                     if hasattr(request, "authzUser") and str(request.authzUser.children[0]) != uidn:
                         uidz = str(request.authzUser.children[0])
-                        
+
                     def convertUIDtoShortName(uid):
                         uid = uid.rstrip("/")
                         uid = uid[uid.rfind("/") + 1:]
@@ -94,11 +100,11 @@
                                 return "(%s)%s" % (record.recordType, record.shortNames[0],)
                         else:
                             return uid
-                        
+
                     uidn = convertUIDtoShortName(uidn)
                     if uidz:
                         uidz = convertUIDtoShortName(uidz)
-                        
+
                     if uidn and uidz:
                         uid = '"%s as %s"' % (uidn, uidz,)
                     else:
@@ -142,10 +148,10 @@
             if config.EnableExtendedAccessLog:
                 format += ' i=%(serverInstance)s'
                 formatArgs["serverInstance"] = config.LogID if config.LogID else "0"
-                
+
                 format += ' or=%(outstandingRequests)s'
                 formatArgs["outstandingRequests"] = request.chanRequest.channel.factory.outstandingRequests
-                
+
                 # Tags for time stamps collected along the way - the first one in the list is the initial
                 # time for request creation - we use that to track the entire request/response time
                 nowtime = time.time()
@@ -213,40 +219,42 @@
             if config.EnableExtendedAccessLog:
                 format += ' p=%(serverPort)s'
                 formatArgs["serverPort"] = overloaded.transport.server.port
-                
+
                 format += ' or=%(outstandingRequests)s'
                 formatArgs["outstandingRequests"] = overloaded.outstandingRequests
 
-
         # Write anything we got to the log and stats
         if format is not None:
             # sanitize output to mitigate log injection
-            for k,v in formatArgs.items():
+            for k, v in formatArgs.items():
                 if not isinstance(v, basestring):
                     continue
                 v = v.replace("\r", "\\r")
                 v = v.replace("\n", "\\n")
                 v = v.replace("\"", "\\\"")
                 formatArgs[k] = v
-    
+
             formatArgs["type"] = "access-log"
             formatArgs["log-format"] = format
             self.logStats(formatArgs)
 
+
+
 class RotatingFileAccessLoggingObserver(CommonAccessLoggingObserverExtensions):
     """
     Class to do "apache" style access logging to a rotating log file. The log
     file is rotated after midnight each day.
-    
+
     This class also currently handles the collection of system and log statistics.
     """
 
     def __init__(self, logpath):
-        self.logpath = logpath        
+        self.logpath = logpath
 
         self.systemStats = None
         self.statsByMinute = []
 
+
     def accessLog(self, message, allowrotate=True):
         """
         Log a message to the file and possibly rotate if date has changed.
@@ -261,6 +269,7 @@
             self.rotate()
         self.f.write(message + "\n")
 
+
     def start(self):
         """
         Start logging. Open the log file and log an "open" message.
@@ -270,6 +279,7 @@
         self._open()
         self.accessLog("Log opened - server start: [%s]." % (datetime.datetime.now().ctime(),))
 
+
     def stop(self):
         """
         Stop logging. Close the log file and log an "open" message.
@@ -278,10 +288,11 @@
         self.accessLog("Log closed - server stop: [%s]." % (datetime.datetime.now().ctime(),), False)
         super(RotatingFileAccessLoggingObserver, self).stop()
         self._close()
-        
+
         if self.systemStats is not None:
             self.systemStats.stop()
 
+
     def _open(self):
         """
         Open the log file.
@@ -290,6 +301,7 @@
         self.f = open(self.logpath, "a", 1)
         self.lastDate = self.toDate(os.stat(self.logpath)[8])
 
+
     def _close(self):
         """
         Close the log file.
@@ -297,6 +309,7 @@
 
         self.f.close()
 
+
     def flush(self):
         """
         Flush the log file.
@@ -304,6 +317,7 @@
 
         self.f.flush()
 
+
     def shouldRotate(self):
         """
         Rotate when the date has changed since last write
@@ -314,6 +328,7 @@
         else:
             return False
 
+
     def toDate(self, *args):
         """
         Convert a unixtime to (year, month, day) localtime tuple,
@@ -326,6 +341,7 @@
         # primarily so this can be unit tested easily
         return time.localtime(*args)[:3]
 
+
     def suffix(self, tupledate):
         """
         Return the suffix given a (year, month, day) tuple or unixtime
@@ -337,6 +353,7 @@
             # try taking a float unixtime
             return "_".join(map(str, self.toDate(tupledate)))
 
+
     def rotate(self):
         """
         Rotate the file and create a new one.
@@ -356,10 +373,11 @@
         self._open()
         self.accessLog("Log opened - rotated: [%s]." % (datetime.datetime.now().ctime(),), False)
 
-    def logStats(self, stats): 
-        """ 
+
+    def logStats(self, stats):
+        """
         Update stats
-        """ 
+        """
 
         if self.systemStats is None:
             self.systemStats = SystemMonitor()
@@ -367,18 +385,19 @@
         # Currently only storing stats for access log type
         if "type" not in stats or stats["type"] != "access-log":
             return
-    
+
         currentStats = self.ensureSequentialStats()
         self.updateStats(currentStats, stats)
-        
+
         if stats["type"] == "access-log":
             self.accessLog(stats["log-format"] % stats)
 
-    def getStats(self): 
-        """ 
-        Return the stats 
+
+    def getStats(self):
         """
-        
+        Return the stats
+        """
+
         if self.systemStats is None:
             self.systemStats = SystemMonitor()
 
@@ -407,17 +426,18 @@
             self.mergeStats(oneHour, stat)
 
         printStats = {
-            "System":self.systemStats.items,
-            "Current":currentStats,
-            "1 Minute":previousMinute,
-            "5 Minutes":fiveMinutes,
-            "1 Hour":oneHour,
+            "System": self.systemStats.items,
+            "Current": currentStats,
+            "1 Minute": previousMinute,
+            "5 Minutes": fiveMinutes,
+            "1 Hour": oneHour,
         }
         return json.dumps(printStats)
 
+
     def ensureSequentialStats(self):
         """
-        Make sure the list of timed stats is contiguous wrt time. 
+        Make sure the list of timed stats is contiguous wrt time.
         """
         dtindex = int(time.time() / 60.0) * 60
 
@@ -431,8 +451,9 @@
             self.statsByMinute.append((dtindex, self.initStats(),))
         return self.statsByMinute[-1][1]
 
+
     def initStats(self):
-        
+
         def initTimeHistogram():
             return {
                 "<10ms": 0,
@@ -460,12 +481,13 @@
             "cpu"      : self.systemStats.items["cpu use"],
         }
 
+
     def updateStats(self, current, stats):
         # Gather specific information and aggregate into our persistent stats
         if current["requests"] == 0:
             current["cpu"] = 0.0
         current["requests"] += 1
-        current["method"][stats["method"]] += 1
+        current["method"][getAdjustedMethodName(stats["method"], stats["uri"], stats)] += 1
         current["uid"][stats["uid"]] += 1
         if stats["statusCode"] >= 500:
             current["500"] += 1
@@ -473,7 +495,7 @@
         current["t-resp-wr"] += stats.get("t-resp-wr", 0.0)
         current["slots"] += stats.get("outstandingRequests", 0)
         current["cpu"] += self.systemStats.items["cpu use"]
-        
+
         def histogramUpdate(t, key):
             if t >= 60000.0:
                 current[key][">60s"] += 1
@@ -493,7 +515,7 @@
                 current[key]["Over 1s"] += 1
             elif t >= 10000.0:
                 current[key]["Over 10s"] += 1
-            
+
         t = stats.get("t", None)
         if t is not None:
             histogramUpdate(t, "T")
@@ -502,6 +524,7 @@
         if t is not None:
             histogramUpdate(t, "T-RESP-WR")
 
+
     def mergeStats(self, current, stats):
         # Gather specific information and aggregate into our persistent stats
         if current["requests"] == 0:
@@ -516,7 +539,7 @@
         current["t-resp-wr"] += stats["t-resp-wr"]
         current["slots"] += stats["slots"]
         current["cpu"] += stats["cpu"]
-        
+
         def histogramUpdate(t, key):
             if t >= 60000.0:
                 current[key][">60s"] += 1
@@ -536,7 +559,7 @@
                 current[key]["Over 1s"] += 1
             elif t >= 10000.0:
                 current[key]["Over 10s"] += 1
-        
+
         for bin in stats["T"].keys():
             current["T"][bin] += stats["T"][bin]
         current["T-MAX"] = max(current["T-MAX"], stats["T-MAX"])
@@ -547,10 +570,10 @@
 
 class SystemMonitor(object):
     """
-    Keeps track of system usage information. This installs a reacxtor task to
+    Keeps track of system usage information. This installs a reactor task to
     run about once per second and track system use.
     """
-    
+
     CPUStats = collections.namedtuple("CPUStats", ("total", "idle",))
 
     def __init__(self):
@@ -561,24 +584,26 @@
             "memory percent": 0.0,
             "start time"    : time.time(),
         }
-        
+
         if psutil is not None:
             times = psutil.cpu_times()
             self.previous_cpu = SystemMonitor.CPUStats(sum(times), times.idle,)
         else:
             self.previous_cpu = SystemMonitor.CPUStats(0, 0)
-        
+
         self.task = task.LoopingCall(self.update)
         self.task.start(1.0)
-    
+
+
     def stop(self):
         """
         Just stop the task
         """
         self.task.stop()
 
+
     def update(self):
-        
+
         # CPU usage based on diff'ing CPU times
         if psutil is not None:
             times = psutil.cpu_times()
@@ -588,18 +613,20 @@
             except ZeroDivisionError:
                 self.items["cpu use"] = 0.0
             self.previous_cpu = cpu_now
-        
+
         # Memory usage
         if psutil is not None:
             mem = psutil.virtual_memory()
             self.items["memory used"] = mem.used
             self.items["memory percent"] = mem.percent
 
-    
+
+
 class LogStats(amp.Command):
     arguments = [("message", amp.String())]
 
 
+
 class AMPCommonAccessLoggingObserver(CommonAccessLoggingObserverExtensions):
     def __init__(self):
         self.protocol = None
@@ -620,18 +647,18 @@
         self.flushBuffer()
 
 
-    def logStats(self, message): 
-        """ 
-        Log server stats via the remote AMP Protocol 
-        """ 
+    def logStats(self, message):
+        """
+        Log server stats via the remote AMP Protocol
+        """
 
         if self.protocol is not None:
-            message=json.dumps(message)
+            message = json.dumps(message)
             if isinstance(message, unicode):
                 message = message.encode("utf-8")
-            d = self.protocol.callRemote(LogStats, message=message) 
-            d.addErrback(log.err) 
-        else: 
+            d = self.protocol.callRemote(LogStats, message=message)
+            d.addErrback(log.err)
+        else:
             self._buffer.append(message)
 
 
@@ -646,10 +673,11 @@
 
         super(AMPLoggingProtocol, self).__init__()
 
-    def logStats(self, message): 
+
+    def logStats(self, message):
         stats = json.loads(message)
-        self.observer.logStats(stats) 
-        return {} 
+        self.observer.logStats(stats)
+        return {}
 
     LogStats.responder(logStats)
 
@@ -670,6 +698,3 @@
 
     def buildProtocol(self, addr):
         return AMPLoggingProtocol(self.observer)
-
-
-

Added: CalendarServer/trunk/calendarserver/methodDescriptor.py
===================================================================
--- CalendarServer/trunk/calendarserver/methodDescriptor.py	                        (rev 0)
+++ CalendarServer/trunk/calendarserver/methodDescriptor.py	2012-12-12 02:30:18 UTC (rev 10157)
@@ -0,0 +1,330 @@
+##
+# Copyright (c) 2012 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+# Adjust method names
+
+# PROPFINDs
+METHOD_PROPFIND_CALENDAR_HOME = "PROPFIND Calendar Home"
+METHOD_PROPFIND_CACHED_CALENDAR_HOME = "PROPFIND cached Calendar Home"
+METHOD_PROPFIND_CALENDAR = "PROPFIND Calendar"
+METHOD_PROPFIND_INBOX = "PROPFIND Inbox"
+METHOD_PROPFIND_ADDRESSBOOK_HOME = "PROPFIND Adbk Home"
+METHOD_PROPFIND_CACHED_ADDRESSBOOK_HOME = "PROPFIND cached Adbk Home"
+METHOD_PROPFIND_ADDRESSBOOK = "PROPFIND Adbk"
+METHOD_PROPFIND_DIRECTORY = "PROPFIND Directory"
+METHOD_PROPFIND_PRINCIPALS = "PROPFIND Principals"
+METHOD_PROPFIND_CACHED_PRINCIPALS = "PROPFIND cached Principals"
+
+# PROPPATCHs
+METHOD_PROPPATCH_CALENDAR = "PROPPATCH Calendar"
+METHOD_PROPPATCH_ADDRESSBOOK = "PROPPATCH Adbk Home"
+
+# REPORTs
+METHOD_REPORT_CALENDAR_MULTIGET = "REPORT cal-multi"
+METHOD_REPORT_CALENDAR_QUERY = "REPORT cal-query"
+METHOD_REPORT_CALENDAR_FREEBUSY = "REPORT freebusy"
+METHOD_REPORT_CALENDAR_SYNC = "REPORT cal-sync"
+METHOD_REPORT_ADDRESSBOOK_MULTIGET = "REPORT adbk-multi"
+METHOD_REPORT_ADDRESSBOOK_QUERY = "REPORT adbk-query"
+METHOD_REPORT_DIRECTORY_QUERY = "REPORT dir-query"
+METHOD_REPORT_ADDRESSBOOK_SYNC = "REPORT adbk-sync"
+METHOD_REPORT_P_SEARCH_P_SET = "REPORT p-set"
+METHOD_REPORT_P_P_SEARCH = "REPORT p-search"
+METHOD_REPORT_EXPAND_P = "REPORT expand"
+
+# POSTs
+METHOD_POST_CALENDAR_HOME = "POST Calendar Home"
+METHOD_POST_CALENDAR = "POST Calendar"
+METHOD_POST_CALENDAR_OBJECT = "POST Calendar Object"
+METHOD_POST_ADDRESSBOOK_HOME = "POST Adbk Home"
+METHOD_POST_ADDRESSBOOK = "POST Adbk"
+METHOD_POST_ISCHEDULE_FREEBUSY = "POST Freebusy iSchedule"
+METHOD_POST_ISCHEDULE = "POST iSchedule"
+METHOD_POST_TIMEZONES = "POST Timezones"
+METHOD_POST_FREEBUSY = "POST Freebusy"
+METHOD_POST_ORGANIZER = "POST Organizer"
+METHOD_POST_ATTENDEE = "POST Attendee"
+METHOD_POST_OUTBOX = "POST Outbox"
+METHOD_POST_APNS = "POST apns"
+
+# PUTs
+METHOD_PUT_ICS = "PUT ics"
+METHOD_PUT_ORGANIZER = "PUT Organizer"
+METHOD_PUT_ATTENDEE = "PUT Attendee"
+METHOD_PUT_DROPBOX = "PUT dropbox"
+METHOD_PUT_VCF = "PUT VCF"
+
+# GETs
+METHOD_GET_CALENDAR_HOME = "GET Calendar Home"
+METHOD_GET_CALENDAR = "GET Calendar"
+METHOD_GET_ICS = "GET ics"
+METHOD_GET_INBOX_ICS = "GET inbox ics"
+METHOD_GET_DROPBOX = "GET dropbox"
+METHOD_GET_ADDRESSBOOK_HOME = "GET Adbk Home"
+METHOD_GET_ADDRESSBOOK = "GET Adbk"
+METHOD_GET_VCF = "GET VCF"
+METHOD_GET_TIMEZONES = "GET Timezones"
+
+# DELETEs
+METHOD_DELETE_CALENDAR_HOME = "DELETE Calendar Home"
+METHOD_DELETE_CALENDAR = "DELETE Calendar"
+METHOD_DELETE_ICS = "DELETE ics"
+METHOD_DELETE_INBOX_ICS = "DELETE inbox ics"
+METHOD_DELETE_DROPBOX = "DELETE dropbox"
+METHOD_DELETE_ADDRESSBOOK_HOME = "DELETE Adbk Home"
+METHOD_DELETE_ADDRESSBOOK = "DELETE Adbk"
+METHOD_DELETE_VCF = "DELETE vcf"
+
+
+def getAdjustedMethodName(method, uri, extended):
+
+    uribits = uri.rstrip("/").split('/')[1:]
+    if len(uribits) == 0:
+        uribits = [uri]
+
+    calendar_specials = ("attachments", "dropbox", "notification", "freebusy", "outbox",)
+    adbk_specials = ("notification",)
+
+    def _PROPFIND():
+        cached = "cached" in extended
+
+        if uribits[0] == "calendars":
+
+            if len(uribits) == 3:
+                return METHOD_PROPFIND_CACHED_CALENDAR_HOME if cached else METHOD_PROPFIND_CALENDAR_HOME
+            elif len(uribits) > 3:
+                if uribits[3] in calendar_specials:
+                    return "PROPFIND %s" % (uribits[3],)
+                elif len(uribits) == 4:
+                    if uribits[3] == "inbox":
+                        return METHOD_PROPFIND_INBOX
+                    else:
+                        return METHOD_PROPFIND_CALENDAR
+
+        elif uribits[0] == "addressbooks":
+
+            if len(uribits) == 3:
+                return METHOD_PROPFIND_CACHED_ADDRESSBOOK_HOME if cached else METHOD_PROPFIND_ADDRESSBOOK_HOME
+            elif len(uribits) > 3:
+                if uribits[3] in adbk_specials:
+                    return "PROPFIND %s" % (uribits[3],)
+                elif len(uribits) == 4:
+                    return METHOD_PROPFIND_ADDRESSBOOK
+
+        elif uribits[0] == "directory":
+            return METHOD_PROPFIND_DIRECTORY
+
+        elif uribits[0] == "principals":
+            return METHOD_PROPFIND_CACHED_PRINCIPALS if cached else METHOD_PROPFIND_PRINCIPALS
+
+        return method
+
+
+    def _REPORT():
+
+        if "(" in method:
+            report_type = method.split("}" if "}" in method else ":")[1][:-1]
+            if report_type == "addressbook-query":
+                if uribits[0] == "directory":
+                    report_type = "directory-query"
+            if report_type == "sync-collection":
+                if uribits[0] == "calendars":
+                    report_type = "cal-sync"
+                elif uribits[0] == "addressbooks":
+                    report_type = "adbk-sync"
+            mappedNames = {
+                "calendar-multiget"             : METHOD_REPORT_CALENDAR_MULTIGET,
+                "calendar-query"                : METHOD_REPORT_CALENDAR_QUERY,
+                "free-busy-query"               : METHOD_REPORT_CALENDAR_FREEBUSY,
+                "cal-sync"                      : METHOD_REPORT_CALENDAR_SYNC,
+                "addressbook-multiget"          : METHOD_REPORT_ADDRESSBOOK_MULTIGET,
+                "addressbook-query"             : METHOD_REPORT_ADDRESSBOOK_QUERY,
+                "directory-query"               : METHOD_REPORT_DIRECTORY_QUERY,
+                "adbk-sync"                     : METHOD_REPORT_ADDRESSBOOK_SYNC,
+                "principal-search-property-set" : METHOD_REPORT_P_SEARCH_P_SET,
+                "principal-property-search"     : METHOD_REPORT_P_P_SEARCH,
+                "expand-property"               : METHOD_REPORT_EXPAND_P,
+            }
+            return mappedNames.get(report_type, "REPORT %s" % (report_type,))
+
+        return method
+
+
+    def _PROPPATCH():
+
+        if uribits[0] == "calendars":
+            return METHOD_PROPPATCH_CALENDAR
+        elif uribits[0] == "addressbooks":
+            return METHOD_PROPPATCH_ADDRESSBOOK
+
+        return method
+
+
+    def _POST():
+
+        if uribits[0] == "calendars":
+
+            if len(uribits) == 3:
+                return METHOD_POST_CALENDAR_HOME
+            elif len(uribits) == 4:
+                if uribits[3] == "outbox":
+                    if "recipients" in extended:
+                        return METHOD_POST_FREEBUSY
+                    elif "freebusy" in extended:
+                        return METHOD_POST_FREEBUSY
+                    elif "itip.request" in extended or "itip.cancel" in extended:
+                        return METHOD_POST_ORGANIZER
+                    elif "itip.reply" in extended:
+                        return METHOD_POST_ATTENDEE
+                    else:
+                        return METHOD_POST_OUTBOX
+                elif uribits[3] in calendar_specials:
+                    pass
+                else:
+                    return METHOD_POST_CALENDAR
+            elif len(uribits) == 5:
+                return METHOD_POST_CALENDAR_OBJECT
+
+        elif uribits[0] == "addressbooks":
+
+            if len(uribits) == 3:
+                return METHOD_POST_ADDRESSBOOK_HOME
+            elif len(uribits) == 4:
+                if uribits[3] in adbk_specials:
+                    pass
+                else:
+                    return METHOD_POST_ADDRESSBOOK
+
+        elif uribits[0] == "ischedule":
+            if "fb-cached" in extended or "fb-uncached" in extended or "freebusy" in extended:
+                return METHOD_POST_ISCHEDULE_FREEBUSY
+            else:
+                return METHOD_POST_ISCHEDULE
+
+        elif uribits[0].startswith("timezones"):
+            return METHOD_POST_TIMEZONES
+
+        elif uribits[0].startswith("apns"):
+            return METHOD_POST_APNS
+
+        return method
+
+
+    def _PUT():
+
+        if uribits[0] == "calendars":
+            if len(uribits) > 3:
+                if uribits[3] in calendar_specials:
+                    return "PUT %s" % (uribits[3],)
+                elif len(uribits) == 4:
+                    pass
+                else:
+                    if "itip.requests" in extended:
+                        return METHOD_PUT_ORGANIZER
+                    elif "itip.reply" in extended:
+                        return METHOD_PUT_ATTENDEE
+                    else:
+                        return METHOD_PUT_ICS
+
+        elif uribits[0] == "addressbooks":
+            if len(uribits) > 3:
+                if uribits[3] in adbk_specials:
+                    return "PUT %s" % (uribits[3],)
+                elif len(uribits) == 4:
+                    pass
+                else:
+                    return METHOD_PUT_VCF
+
+        return method
+
+
+    def _GET():
+
+        if uribits[0] == "calendars":
+
+            if len(uribits) == 3:
+                return METHOD_GET_CALENDAR_HOME
+            elif len(uribits) > 3:
+                if uribits[3] in calendar_specials:
+                    return "GET %s" % (uribits[3],)
+                elif len(uribits) == 4:
+                    return METHOD_GET_CALENDAR
+                elif uribits[3] == "inbox":
+                    return METHOD_GET_INBOX_ICS
+                else:
+                    return METHOD_GET_ICS
+
+        elif uribits[0] == "addressbooks":
+
+            if len(uribits) == 3:
+                return METHOD_GET_ADDRESSBOOK_HOME
+            elif len(uribits) > 3:
+                if uribits[3] in adbk_specials:
+                    return "GET %s" % (uribits[3],)
+                elif len(uribits) == 4:
+                    return METHOD_GET_ADDRESSBOOK
+                else:
+                    return METHOD_GET_VCF
+
+        elif uribits[0].startswith("timezones"):
+            return METHOD_GET_TIMEZONES
+
+        return method
+
+
+    def _DELETE():
+
+        if uribits[0] == "calendars":
+
+            if len(uribits) == 3:
+                return METHOD_DELETE_CALENDAR_HOME
+            elif len(uribits) > 3:
+                if uribits[3] in calendar_specials:
+                    return "DELETE %s" % (uribits[3],)
+                elif len(uribits) == 4:
+                    return METHOD_DELETE_CALENDAR
+                elif uribits[3] == "inbox":
+                    return METHOD_DELETE_INBOX_ICS
+                else:
+                    return METHOD_DELETE_ICS
+
+        elif uribits[0] == "addressbooks":
+
+            if len(uribits) == 3:
+                return METHOD_DELETE_ADDRESSBOOK_HOME
+            elif len(uribits) > 3:
+                if uribits[3] in adbk_specials:
+                    return "DELETE %s" % (uribits[3],)
+                elif len(uribits) == 4:
+                    return METHOD_DELETE_ADDRESSBOOK
+                else:
+                    return METHOD_DELETE_VCF
+
+        return method
+
+
+    def _ANY():
+        return method
+
+    return {
+        "DELETE" : _DELETE,
+        "GET" : _GET,
+        "POST" : _POST,
+        "PROPFIND" : _PROPFIND,
+        "PROPPATCH" : _PROPPATCH,
+        "PUT" : _PUT,
+        "REPORT" : _REPORT,
+    }.get(method.split("(")[0], _ANY)()

Added: CalendarServer/trunk/calendarserver/test/test_methodDescriptor.py
===================================================================
--- CalendarServer/trunk/calendarserver/test/test_methodDescriptor.py	                        (rev 0)
+++ CalendarServer/trunk/calendarserver/test/test_methodDescriptor.py	2012-12-12 02:30:18 UTC (rev 10157)
@@ -0,0 +1,67 @@
+##
+# Copyright (c) 2012 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from twisted.trial.unittest import TestCase
+from calendarserver.methodDescriptor import getAdjustedMethodName
+
+class MethodDescriptor(TestCase):
+    """
+    Tests for L{getAdjustedMethodName}.
+    """
+    def test_getAdjustedMethodName(self):
+        """
+        L{getAdjustedMethodName} returns the appropriate method.
+        """
+
+        data = (
+            ("PROPFIND", "/calendars/users/user01/", {}, "PROPFIND Calendar Home",),
+            ("PROPFIND", "/calendars/users/user01/", {"cached": "1"}, "PROPFIND cached Calendar Home",),
+            ("PROPFIND", "/calendars/users/user01/ABC/", {}, "PROPFIND Calendar",),
+            ("PROPFIND", "/calendars/users/user01/inbox/", {}, "PROPFIND Inbox",),
+            ("PROPFIND", "/addressbooks/users/user01/", {}, "PROPFIND Adbk Home",),
+            ("PROPFIND", "/addressbooks/users/user01/", {"cached": "1"}, "PROPFIND cached Adbk Home",),
+            ("PROPFIND", "/addressbooks/users/user01/ABC/", {}, "PROPFIND Adbk",),
+            ("PROPFIND", "/addressbooks/users/user01/inbox/", {}, "PROPFIND Adbk",),
+            ("PROPFIND", "/principals/users/user01/", {}, "PROPFIND Principals",),
+            ("PROPFIND", "/principals/users/user01/", {"cached": "1"}, "PROPFIND cached Principals",),
+            ("PROPFIND", "/.well-known/caldav", {}, "PROPFIND",),
+
+            ("REPORT(CalDAV:sync-collection)", "/calendars/users/user01/ABC/", {}, "REPORT cal-sync",),
+            ("REPORT(CalDAV:calendar-query)", "/calendars/users/user01/ABC/", {}, "REPORT cal-query",),
+
+            ("POST", "/calendars/users/user01/", {}, "POST Calendar Home",),
+            ("POST", "/calendars/users/user01/outbox/", {"recipients": "1"}, "POST Freebusy",),
+            ("POST", "/calendars/users/user01/outbox/", {}, "POST Outbox",),
+            ("POST", "/apns", {}, "POST apns",),
+
+            ("PUT", "/calendars/users/user01/calendar/1.ics", {}, "PUT ics",),
+            ("PUT", "/calendars/users/user01/calendar/1.ics", {"itip.requests": "1"}, "PUT Organizer",),
+            ("PUT", "/calendars/users/user01/calendar/1.ics", {"itip.reply": "1"}, "PUT Attendee",),
+
+            ("GET", "/calendars/users/user01/", {}, "GET Calendar Home",),
+            ("GET", "/calendars/users/user01/calendar/", {}, "GET Calendar",),
+            ("GET", "/calendars/users/user01/calendar/1.ics", {}, "GET ics",),
+
+            ("DELETE", "/calendars/users/user01/", {}, "DELETE Calendar Home",),
+            ("DELETE", "/calendars/users/user01/calendar/", {}, "DELETE Calendar",),
+            ("DELETE", "/calendars/users/user01/calendar/1.ics", {}, "DELETE ics",),
+            ("DELETE", "/calendars/users/user01/inbox/1.ics", {}, "DELETE inbox ics",),
+
+            ("ACL", "/calendars/users/user01/", {}, "ACL",),
+        )
+
+        for method, uri, extras, result in data:
+            self.assertEqual(getAdjustedMethodName(method, uri, extras), result, "Failed getAdjustedMethodName: %s" % (result,))
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20121211/3b6c225f/attachment-0001.html>


More information about the calendarserver-changes mailing list