Ticket #2200: twisted.web2.server.patch

File twisted.web2.server.patch, 6.8 KB (added by Cyrus Daboo, 10 years ago)

patch

  • twisted/web2/server.py

     
    11# -*- test-case-name: twisted.web2.test.test_server -*-
    22# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
    33# See LICENSE for details.
     4from twisted.internet.defer import succeed
     5from twisted.web2.dav.util import joinURL
    46
    57
    68"""This is a web-sever which integrates with the twisted.internet
     
    150152            self._initialprepath = kw['prepathuri']
    151153            del kw['prepathuri']
    152154
     155        self._resourcesByURL = {}
     156        self._urlsByResource = {}
     157
    153158        # Copy response filters from the class
    154159        self.responseFilters = self.responseFilters[:]
    155160        self.files = {}
    156161        self.resources = []
    157162        http.Request.__init__(self, *args, **kw)
    158163
    159     def addResponseFilter(self, f, atEnd=False):
     164    def addResponseFilter(self, filter, atEnd=False, onlyOnce=False):
     165        """
     166        Add a response filter to this request.
     167        Response filters are applied to the response to this request in order.
     168        @param filter: a callable which takes an response argument and returns
     169            a response object.
     170        @param atEnd: if C{True}, C{filter} is added at the end of the list of
     171            response filters; if C{False}, it is added to the beginning.
     172        @param onlyOnce: if C{True}, C{filter} is not added to the list of
     173            response filters if it already in the list.
     174        """
     175        if onlyOnce and filter in self.responseFilters:
     176            return
    160177        if atEnd:
    161             self.responseFilters.append(f)
     178            self.responseFilters.append(filter)
    162179        else:
    163             self.responseFilters.insert(0, f)
     180            self.responseFilters.insert(0, filter)
    164181
    165182    def unparseURL(self, scheme=None, host=None, port=None,
    166183                   path=None, params=None, querystring=None, fragment=None):
     
    265282       
    266283        d = defer.Deferred()
    267284        d.addCallback(self._getChild, self.site.resource, self.postpath)
     285        d.addCallback(self._rememberResource, "/" + "/".join(self.prepath))
    268286        d.addCallback(lambda res, req: res.renderHTTP(req), self)
    269287        d.addCallback(self._cbFinishRender)
    270288        d.addErrback(self._processingFailed)
     
    320338                    url = "/" + "/".join(path)
    321339                else:
    322340                    url = "/"
    323        
    324                 self._rememberURLForResource(quote(url), res)
    325341                return res
    326342            #else:
    327343            #    raise ValueError("locateChild must not return StopTraversal with a resource other than self.")
     
    342358                self.prepath.append(self.postpath.pop(0))
    343359
    344360        child = self._getChild(None, newres, newpath, updatepaths=updatepaths)
    345         self._rememberURLForResource(quote(url), child)
    346361
    347362        return child
    348363
    349     _resourcesByURL = weakref.WeakKeyDictionary()
    350 
    351     def _rememberURLForResource(self, url, resource):
     364    def _rememberResource(self, resource, url):
    352365        """
    353         Remember the URL of visited resources.
     366        Remember the URL of a visited resources.
    354367        """
    355368        self._resourcesByURL[resource] = url
     369        self._urlsByResource[url] = resource
     370        return resource
    356371
    357372    def urlForResource(self, resource):
    358373        """
     
    367382
    368383        @return: the URL of C{resource} if known, otherwise C{None}.
    369384        """
    370         try:
    371             return self._resourcesByURL[resource]
    372         except KeyError:
    373             return None
     385        return self._resourcesByURL.get(resource, None)
    374386
    375387    def locateResource(self, url):
    376388        """
     
    385397            The contained response will have a status code of
    386398            L{responsecode.BAD_REQUEST}.
    387399        """
    388         if url is None: return None
     400        if url is None:
     401            return None
    389402
     403        cached = self._urlsByResource.get(url, None)
     404        if cached is not None:
     405            return succeed(cached)
     406
    390407        #
    391408        # Parse the URL
    392409        #
     
    406423                "URL is not on this site (%s://%s/): %s" % (scheme, self.headers.getHeader("host"), url)
    407424            ))
    408425
    409         segments = path.split("/")
     426        segments = unquote(path).split("/")
    410427        assert segments[0] == "", "URL path didn't begin with '/': %s" % (path,)
    411428        segments = segments[1:]
    412         segments = map(unquote, segments)
    413429
    414430        def notFound(f):
    415431            f.trap(http.HTTPError)
    416432            if f.response.code != responsecode.NOT_FOUND:
    417                 raise f
     433                return f
    418434            return None
    419435
    420         return defer.maybeDeferred(self._getChild, None, self.site.resource, segments, updatepaths=False)
     436        d = defer.maybeDeferred(self._getChild, None, self.site.resource, segments, updatepaths=False)
     437        d.addCallback(self._rememberResource, path)
     438        d.addErrback(notFound)
     439        return d
    421440
     441    def locateChildResource(self, parent, child_name):
     442        """
     443        Looks up the child resource with the given name given the parent
     444        resource.  This is similar to locateResource(), but doesn't have to
     445        start the lookup from the root resource, so it is potentially faster.
     446        @param parent: the parent of the resource being looked up.
     447        @param child_name: the name of the child of C{parent} to looked up.
     448            to C{parent}.
     449        @return: a L{Deferred} resulting in the L{IResource} at the
     450            given URL or C{None} if no such resource can be located.
     451        @raise HTTPError: If C{url} is not a URL on the site that this
     452            request is being applied to.  The contained response will
     453            have a status code of L{responsecode.BAD_GATEWAY}.
     454        @raise HTTPError: If C{url} contains a query or fragment.
     455            The contained response will have a status code of
     456            L{responsecode.BAD_REQUEST}.
     457        """
     458        if parent is None or child_name is None:
     459            return None
     460
     461        url = joinURL(self.urlForResource(parent), child_name)
     462
     463        cached = self._urlsByResource.get(url, None)
     464        if cached is not None:
     465            return succeed(cached)
     466
     467        assert "/" not in child_name, "Child name may not contain '/': %s" % (child_name,)
     468
     469        segment = unquote(child_name)
     470
     471        def notFound(f):
     472            f.trap(http.HTTPError)
     473            if f.response.code != responsecode.NOT_FOUND:
     474                return f
     475            return None
     476
     477        d = defer.maybeDeferred(self._getChild, None, parent, [segment], updatepaths=False)
     478        d.addCallback(self._rememberResource, url)
     479        d.addErrback(notFound)
     480        return d
     481
    422482    def _processingFailed(self, reason):
    423483        if reason.check(http.HTTPError) is not None:
    424484            # If the exception was an HTTPError, leave it alone