[CalendarServer-changes] [4587] CalendarServer/branches/users/cdaboo/deployment-partition-4524/ twistedcaldav

source_changes at macosforge.org source_changes at macosforge.org
Fri Oct 16 14:02:23 PDT 2009


Revision: 4587
          http://trac.macosforge.org/projects/calendarserver/changeset/4587
Author:   cdaboo at apple.com
Date:     2009-10-16 14:02:22 -0700 (Fri, 16 Oct 2009)
Log Message:
-----------
Backport of: Use reverse proxy for partition. This adds a generic http client pooling feature which can be used
in other areas too (e.g. scheduling with other partition nodes).

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/cache.py
    CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/config.py
    CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/directory.py
    CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/principal.py
    CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/partitions.py
    CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/static.py

Added Paths:
-----------
    CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/client/
    CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/client/__init__.py
    CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/client/pool.py
    CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/client/reverseproxy.py

Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/cache.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/cache.py	2009-10-16 20:58:56 UTC (rev 4586)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/cache.py	2009-10-16 21:02:22 UTC (rev 4587)
@@ -28,7 +28,7 @@
 from twisted.web2.stream import MemoryStream
 
 from twistedcaldav.log import LoggingMixIn
-from twistedcaldav.memcachepool import CachePoolUserMixIn
+from twistedcaldav.memcachepool import CachePoolUserMixIn, defaultCachePool
 from twistedcaldav.config import config
 
 
@@ -171,7 +171,7 @@
         self._cachePool = cachePool
 
 
-    def _tokenForURI(self, uri):
+    def _tokenForURI(self, uri, cachePoolHandle=None):
         """
         Get a property store for the given C{uri}.
 
@@ -179,13 +179,16 @@
         @return: A C{str} representing the token for the URI.
         """
 
-        return self.getCachePool().get('cacheToken:%s' % (uri,))
+        if cachePoolHandle:
+            return defaultCachePool(cachePoolHandle).get('cacheToken:%s' % (uri,))
+        else:
+            return self.getCachePool().get('cacheToken:%s' % (uri,))
 
 
     def _getTokens(self, request):
         def _tokensForURIs((pURI, rURI)):
             tokens = []
-            d1 = self._tokenForURI(pURI)
+            d1 = self._tokenForURI(pURI, "PrincipalToken")
             d1.addCallback(tokens.append)
             d1.addCallback(lambda _ign: self._getRecordForURI(pURI, request))
             d1.addCallback(lambda dToken: tokens.append(hash(dToken)))

Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/client/__init__.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/client/__init__.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/client/__init__.py	2009-10-16 21:02:22 UTC (rev 4587)
@@ -0,0 +1,16 @@
+##
+# Copyright (c) 2009 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.
+##
+

Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/client/pool.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/client/pool.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/client/pool.py	2009-10-16 21:02:22 UTC (rev 4587)
@@ -0,0 +1,360 @@
+##
+# Copyright (c) 2009 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.
+##
+
+__all__ = [
+    "installPools",
+    "installPool",
+    "getReverseProxyPool",
+]
+
+from twisted.internet.address import IPv4Address
+from twisted.internet.defer import Deferred, inlineCallbacks, returnValue
+from twisted.internet.protocol import ReconnectingClientFactory
+from twisted.internet.ssl import DefaultOpenSSLContextFactory
+from twisted.web2 import responsecode
+from twisted.web2.client.http import HTTPClientProtocol
+from twisted.web2.http import StatusResponse, HTTPError
+from twistedcaldav.log import LoggingMixIn
+import OpenSSL
+import urlparse
+
+class ReverseProxyClientFactory(ReconnectingClientFactory, LoggingMixIn):
+    """
+    A client factory for HTTPClient that reconnects and notifies a pool of it's
+    state.
+
+    @ivar connectionPool: A managing connection pool that we notify of events.
+    @ivar deferred: A L{Deferred} that represents the initial connection.
+    @ivar _protocolInstance: The current instance of our protocol that we pass
+        to our connectionPool.
+    """
+    protocol = HTTPClientProtocol
+    connectionPool = None
+    maxRetries = 2
+
+    def __init__(self, reactor, deferred):
+        self.reactor = reactor
+        self.instance = None
+        self.deferred = deferred
+
+    def clientConnectionLost(self, connector, reason):
+        """
+        Notify the connectionPool that we've lost our connection.
+        """
+
+        if self.connectionPool.shutdown_requested:
+            # The reactor is stopping; don't reconnect
+            return
+
+        self.log_error("ReverseProxy connection lost: %s" % (reason,))
+        if self.instance is not None:
+            self.connectionPool.clientGone(self.instance)
+#        if self.instance is not None:
+#            self.connectionPool.clientBusy(self.instance)
+#
+#        ReconnectingClientFactory.clientConnectionLost(
+#            self,
+#            connector,
+#            reason
+#        )
+
+    def clientConnectionFailed(self, connector, reason):
+        """
+        Notify the connectionPool that we're unable to connect
+        """
+        self.log_error("ReverseProxy connection failed: %s" % (reason,))
+#        if self.instance is not None:
+#            self.connectionPool.clientBusy(self.instance)
+
+#        ReconnectingClientFactory.clientConnectionFailed(
+#            self,
+#            connector,
+#            reason
+#        )
+        if hasattr(self, "deferred"):
+            self.reactor.callLater(0, self.deferred.errback, reason)
+            del self.deferred
+
+    def buildProtocol(self, addr):
+        if self.instance is not None:
+            self.connectionPool.clientGone(self.instance)
+
+        self.instance = self.protocol()
+        self.reactor.callLater(0, self.deferred.callback, self.instance)
+        del self.deferred
+        return self.instance
+
+class ReverseProxyPool(LoggingMixIn):
+    """
+    A connection pool for HTTPClientProtocol instances.
+
+    @ivar clientFactory: The L{ClientFactory} implementation that will be used
+        for each protocol.
+
+    @ivar _maxClients: A C{int} indicating the maximum number of clients.
+    @ivar _serverAddress: An L{IAddress} provider indicating the server to
+        connect to.  (Only L{IPv4Address} currently supported.)
+    @ivar _reactor: The L{IReactorTCP} provider used to initiate new
+        connections.
+
+    @ivar _busyClients: A C{set} that contains all currently busy clients.
+    @ivar _freeClients: A C{set} that contains all currently free clients.
+    @ivar _pendingConnects: A C{int} indicating how many connections are in
+        progress.
+    """
+    clientFactory = ReverseProxyClientFactory
+
+    def __init__(self, scheme, serverAddress, maxClients=5, reactor=None):
+        """
+        @param serverAddress: An L{IPv4Address} indicating the server to
+            connect to.
+        @param maxClients: A C{int} indicating the maximum number of clients.
+        @param reactor: An L{IReactorTCP{ provider used to initiate new
+            connections.
+        """
+        self._scheme = scheme
+        self._serverAddress = serverAddress
+        self._maxClients = maxClients
+
+        if reactor is None:
+            from twisted.internet import reactor
+        self._reactor = reactor
+
+        self.shutdown_deferred = None
+        self.shutdown_requested = False
+        reactor.addSystemEventTrigger('before', 'shutdown', self._shutdownCallback)
+
+        self._busyClients = set([])
+        self._freeClients = set([])
+        self._pendingConnects = 0
+        self._commands = []
+
+    def _isIdle(self):
+        return (
+            len(self._busyClients) == 0 and
+            len(self._commands) == 0 and
+            self._pendingConnects == 0
+        )
+
+    def _shutdownCallback(self):
+        self.shutdown_requested = True
+        if self._isIdle():
+            return None
+        self.shutdown_deferred = Deferred()
+        return self.shutdown_deferred
+
+    @inlineCallbacks
+    def _newClientConnection(self):
+        """
+        Create a new client connection.
+
+        @return: A L{Deferred} that fires with the L{IProtocol} instance.
+        """
+        self.log_debug("Initiating new client connection to: %s" % (self._serverAddress,))
+        self._logClientStats()
+
+        self._pendingConnects += 1
+
+        def _connected(client):
+            self._pendingConnects -= 1
+
+            return client
+
+        d = Deferred()
+        factory = self.clientFactory(self._reactor, d)
+        factory.noisy = False
+
+        factory.connectionPool = self
+
+        try:
+            if self._scheme == "https":
+                from twistedcaldav.config import config
+                context = DefaultOpenSSLContextFactory(config.SSLPrivateKey, config.SSLCertificate, sslmethod=OpenSSL.SSL.SSLv3_METHOD)
+                self._reactor.connectSSL(self._serverAddress.host, self._serverAddress.port, factory, context)
+            elif self._scheme == "http":
+                self._reactor.connectTCP(self._serverAddress.host, self._serverAddress.port, factory)
+            else:
+                raise ValueError("URL scheme for client pool not supported")
+            client = (yield d)
+        except:
+            raise HTTPError(StatusResponse(responsecode.BAD_GATEWAY, "Could not connect to reverse proxy host."))
+        finally:
+            self._pendingConnects -= 1
+        returnValue(client)
+
+    @inlineCallbacks
+    def _performRequestOnClient(self, client, request, *args, **kwargs):
+        """
+        Perform the given request on the given client.
+
+        @param client: A L{PooledMemCacheProtocol} that will be used to perform
+            the given request.
+
+        @param command: A C{str} representing an attribute of
+            L{MemCacheProtocol}.
+        @parma args: Any positional arguments that should be passed to
+            C{command}.
+        @param kwargs: Any keyword arguments that should be passed to
+            C{command}.
+
+        @return: A L{Deferred} that fires with the result of the given command.
+        """
+
+        self.clientBusy(client)
+        try:
+            response = (yield client.submitRequest(request, closeAfter=False))
+        finally:
+            self.clientFree(client)
+
+        returnValue(response)
+
+    @inlineCallbacks
+    def submitRequest(self, request, *args, **kwargs):
+        """
+        Select an available client and perform the given request on it.
+
+        @param command: A C{str} representing an attribute of
+            L{MemCacheProtocol}.
+        @parma args: Any positional arguments that should be passed to
+            C{command}.
+        @param kwargs: Any keyword arguments that should be passed to
+            C{command}.
+
+        @return: A L{Deferred} that fires with the result of the given command.
+        """
+
+        client = None
+        if len(self._freeClients) > 0:
+            client = self._freeClients.pop()
+
+        elif len(self._busyClients) + self._pendingConnects >= self._maxClients:
+            d = Deferred()
+            self._commands.append((d, request, args, kwargs))
+            self.log_debug("Request queued: %s, %r, %r" % (request, args, kwargs))
+            self._logClientStats()
+            response = (yield d)
+        else:
+            client = (yield self._newClientConnection())
+
+        if client:
+            response = (yield self._performRequestOnClient(client, request, *args, **kwargs))
+
+        returnValue(response)
+
+    def _logClientStats(self):
+        self.log_debug("Clients #free: %d, #busy: %d, "
+                       "#pending: %d, #queued: %d" % (
+                len(self._freeClients),
+                len(self._busyClients),
+                self._pendingConnects,
+                len(self._commands)))
+
+    def clientGone(self, client):
+        """
+        Notify that the given client is to be removed from the pool completely.
+
+        @param client: An instance of L{PooledMemCacheProtocol}.
+        """
+        if client in self._busyClients:
+            self._busyClients.remove(client)
+
+        elif client in self._freeClients:
+            self._freeClients.remove(client)
+
+        self.log_debug("Removed client: %r" % (client,))
+        self._logClientStats()
+
+    def clientBusy(self, client):
+        """
+        Notify that the given client is being used to complete a request.
+
+        @param client: An instance of C{self.clientFactory}
+        """
+
+        if client in self._freeClients:
+            self._freeClients.remove(client)
+
+        self._busyClients.add(client)
+
+        self.log_debug("Busied client: %r" % (client,))
+        self._logClientStats()
+
+    def clientFree(self, client):
+        """
+        Notify that the given client is free to handle more requests.
+
+        @param client: An instance of C{self.clientFactory}
+        """
+        if client in self._busyClients:
+            self._busyClients.remove(client)
+
+        self._freeClients.add(client)
+
+        if self.shutdown_deferred and self._isIdle():
+            self.shutdown_deferred.callback(None)
+
+        if len(self._commands) > 0:
+            d, command, args, kwargs = self._commands.pop(0)
+
+            self.log_debug("Performing Queued Command: %s, %r, %r" % (
+                    command, args, kwargs))
+            self._logClientStats()
+
+            _ign_d = self.performRequest(command, *args, **kwargs)
+
+            _ign_d.addCallback(d.callback)
+
+        self.log_debug("Freed client: %r" % (client,))
+        self._logClientStats()
+
+    def suggestMaxClients(self, maxClients):
+        """
+        Suggest the maximum number of concurrently connected clients.
+
+        @param maxClients: A C{int} indicating how many client connections we
+            should keep open.
+        """
+        self._maxClients = maxClients
+
+_clientPools = {}     # Maps a host:port to a pool object
+
+def installPools(hosts, maxClients=5, reactor=None):
+    
+    for name, url in hosts:
+        installPool(
+            name,
+            url,
+            maxClients,
+            reactor,
+        )
+
+def installPool(name, url, maxClients=5, reactor=None):
+
+    parsedURL = urlparse.urlparse(url)
+    pool = ReverseProxyPool(
+        parsedURL.scheme,
+        IPv4Address(
+            "TCP",
+            parsedURL.hostname,
+            parsedURL.port,
+        ),
+        maxClients,
+        reactor,
+    )
+    _clientPools[name] = pool
+
+def getReverseProxyPool(name):
+    return _clientPools[name]

Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/client/reverseproxy.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/client/reverseproxy.py	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/client/reverseproxy.py	2009-10-16 21:02:22 UTC (rev 4587)
@@ -0,0 +1,70 @@
+##
+# Copyright (c) 2009 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.
+##
+
+__all__ = [
+    "ReverseProxyResource",
+]
+
+from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.web2 import iweb
+from twisted.web2.client.http import ClientRequest
+from twisted.web2.resource import LeafResource
+
+from twistedcaldav.client.pool import getReverseProxyPool
+from twistedcaldav.log import LoggingMixIn
+
+import urllib
+from zope.interface.declarations import implements
+
+class ReverseProxyResource(LeafResource, LoggingMixIn):
+    """
+    A L{LeafResource} which always performs a reverse proxy operation.
+    """
+    implements(iweb.IResource)
+
+    def __init__(self, poolID, *args, **kwargs):
+        """
+        
+        @param poolID: idenitifier of the pool to use
+        @type poolID: C{str}
+        """
+        
+        self.poolID = poolID
+        self._args   = args
+        self._kwargs = kwargs
+
+    def isCollection(self):
+        return True
+
+    def exists(self):
+        return False
+
+    @inlineCallbacks
+    def renderHTTP(self, request):
+        """
+        Do the reverse proxy request and return the response.
+
+        @param request: the incoming request that needs to be proxied.
+        @type request: L{Request}
+        
+        @return: Deferred L{Response}
+        """
+            
+        self.logger.info("%s %s %s" % (request.method, urllib.unquote(request.uri), "HTTP/%s.%s" % request.clientproto))
+        clientPool = getReverseProxyPool(self.poolID)
+        proxyRequest = ClientRequest(request.method, request.uri, request.headers, request.stream)
+        response = (yield clientPool.submitRequest(proxyRequest))
+        returnValue(response)

Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/config.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/config.py	2009-10-16 20:58:56 UTC (rev 4586)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/config.py	2009-10-16 21:02:22 UTC (rev 4587)
@@ -325,9 +325,12 @@
     #
     # Partitioning
     #
-    "EnablePartitions":    False,   # Partitioning enabled or not
-    "ServerPartitionID":   "",      # Unique ID for this server's partition instance.
-    "PartitionConfigFile": "/etc/caldavd/partitions.plist", # File path for partition information
+    "Partitioning" : {
+        "Enabled":             False,   # Partitioning enabled or not
+        "ServerPartitionID":   "",      # Unique ID for this server's partition instance.
+        "PartitionConfigFile": "/etc/caldavd/partitions.plist", # File path for partition information
+        "MaxClients":          5,       # Pool size for connections to each partition
+    },
 
     #
     # Performance tuning
@@ -629,10 +632,12 @@
         # Partitions
         #
     
-        if "EnablePartitions" in items:
-            if items["EnablePartitions"]:
-                partitions.setSelfPartition(items.get("ServerPartitionID", ""))
-                partitions.readConfig(items.get("PartitionConfigFile", ""))
+        if "Partitioning" in items:
+            if items["Partitioning"]["Enabled"]:
+                partitions.setSelfPartition(items["Partitioning"]["ServerPartitionID"])
+                partitions.setMaxClients(items["Partitioning"]["MaxClients"])
+                partitions.readConfig(items["Partitioning"]["PartitionConfigFile"])
+                partitions.installReverseProxies()
             else:
                 partitions.clear()
 

Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/directory.py	2009-10-16 20:58:56 UTC (rev 4586)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/directory.py	2009-10-16 21:02:22 UTC (rev 4587)
@@ -233,7 +233,7 @@
         return False
 
     def locallyHosted(self):
-        return not self.hostedAt or not config.EnablePartitions or self.hostedAt == config.ServerPartitionID
+        return not self.hostedAt or not config.Partitioning.Enabled or self.hostedAt == config.Partitioning.ServerPartitionID
     
     def hostedURL(self):
         return partitions.getPartitionURL(self.hostedAt)

Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/principal.py	2009-10-16 20:58:56 UTC (rev 4586)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/principal.py	2009-10-16 21:02:22 UTC (rev 4587)
@@ -738,8 +738,6 @@
             url = home.url()
             if name:
                 url = joinURL(url, name)
-            if not self.locallyHosted():
-                url = joinURL(self.hostedURL(), url)
                 
             return url
 

Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/partitions.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/partitions.py	2009-10-16 20:58:56 UTC (rev 4586)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/partitions.py	2009-10-16 21:02:22 UTC (rev 4587)
@@ -14,6 +14,7 @@
 # limitations under the License.
 ##
 
+from twistedcaldav.client.pool import installPool
 from twistedcaldav.log import Logger
 from twistedcaldav.py.plistlib import readPlist
 
@@ -32,6 +33,7 @@
     def clear(self):
         self.partitions = {}
         self.ownUID = ""
+        self.maxClients = 5
 
     def readConfig(self, plistpath):
         try:
@@ -49,8 +51,21 @@
     def setSelfPartition(self, uid):
         self.ownUID = uid
 
+    def setMaxClients(self, maxClients):
+        self.maxClients = maxClients
+
     def getPartitionURL(self, uid):
         # When the UID matches this server return an empty string
         return self.partitions.get(uid, None) if uid != self.ownUID else ""
 
+    def installReverseProxies(self):
+        
+        for partition, url in self.partitions.iteritems():
+            if partition != self.ownUID:
+                installPool(
+                    partition,
+                    url,
+                    self.maxClients,
+                )
+
 partitions = Partitions()

Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/static.py	2009-10-16 20:58:56 UTC (rev 4586)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/static.py	2009-10-16 21:02:22 UTC (rev 4587)
@@ -37,7 +37,6 @@
 import os
 import errno
 from urlparse import urlsplit
-import urlparse
 
 from twisted.internet.defer import fail, succeed, inlineCallbacks, returnValue
 from twisted.python.failure import Failure
@@ -50,11 +49,11 @@
 from twisted.web2.dav.resource import AccessDeniedError
 from twisted.web2.dav.resource import davPrivilegeSet
 from twisted.web2.dav.util import parentForURL, bindMethods, joinURL
-from twisted.web2.resource import RedirectResource
 
 from twistedcaldav import caldavxml
 from twistedcaldav import customxml
 from twistedcaldav.caldavxml import caldav_namespace
+from twistedcaldav.client.reverseproxy import ReverseProxyResource
 from twistedcaldav.config import config
 from twistedcaldav.customxml import TwistedCalendarAccessProperty
 from twistedcaldav.extensions import DAVFile
@@ -652,22 +651,21 @@
         
         else:
             childPath = self.fp.child(name[0:2]).child(name[2:4]).child(name)
-            child = CalendarHomeRedirectFile(childPath.path, self, record)
+            child = CalendarHomeReverseProxyFile(childPath.path, self, record)
 
         return child
 
     def createSimilarFile(self, path):
         raise HTTPError(responsecode.NOT_FOUND)
 
-class CalendarHomeRedirectFile(RedirectResource):
+class CalendarHomeReverseProxyFile(ReverseProxyResource):
     
     def __init__(self, path, parent, record):
         self.path = path
         self.parent = parent
         self.record = record
         
-        parsedURL = urlparse.urlparse(self.record.hostedURL())
-        super(CalendarHomeRedirectFile, self).__init__(scheme=parsedURL.scheme, host=parsedURL.hostname, port=parsedURL.port)
+        super(CalendarHomeReverseProxyFile, self).__init__(self.record.hostedAt)
     
     def url(self):
         return joinURL(self.parent.url(), self.record.guid)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20091016/6776abea/attachment-0001.html>


More information about the calendarserver-changes mailing list