Ticket #2200: twisted.web2.server.patch

File twisted.web2.server.patch, 6.8 KB (added by Cyrus Daboo, 8 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