[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