Ticket #3711: deferred-render-3.patch

File deferred-render-3.patch, 13.6 KB (added by esteve, 8 years ago)
  • twisted/web/resource.py

    # Bazaar merge directive format 2 (Bazaar 0.90)
    # revision_id: esteve@fluidinfo.com-20090331192619-j7gwbw96irvmsyqq
    # target_branch: ../trunk/
    # testament_sha1: 27792133b71a691a8c3b15dfd81dff9ca3164b69
    # timestamp: 2009-03-31 22:07:46 +0200
    # base_revision_id: svn-v4:bbbe8e31-12d6-0310-92fd-\
    #   ac37d47ddeeb:trunk:26540
    # 
    # Begin patch
    === modified file 'twisted/web/resource.py'
     
    4343        Render a request. This is called on the leaf resource for
    4444        a request. Render must return either a string, which will
    4545        be sent to the browser as the HTML for the request, or
    46         server.NOT_DONE_YET. If NOT_DONE_YET is returned,
     46        a Deferred containing a string as its resoult value.
     47        Render can also return server.NOT_DONE_YET, but
    4748        at some point later (in a Deferred callback, usually)
    4849        call request.write("<html>") to write data to the request,
    4950        and request.finish() to send the data to the browser.
     51        The latter behavior is deprecated in favor of returning
     52        Deferreds.
    5053        """
    5154
    5255
  • twisted/web/server.py

    === modified file 'twisted/web/server.py'
     
    127127
    128128
    129129    def render(self, resrc):
    130         try:
    131             body = resrc.render(self)
    132         except UnsupportedMethod, e:
    133             allowedMethods = e.allowedMethods
    134             if (self.method == "HEAD") and ("GET" in allowedMethods):
    135                 # We must support HEAD (RFC 2616, 5.1.1).  If the
    136                 # resource doesn't, fake it by giving the resource
    137                 # a 'GET' request and then return only the headers,
    138                 # not the body.
    139                 log.msg("Using GET to fake a HEAD request for %s" %
    140                         (resrc,))
    141                 self.method = "GET"
    142                 body = resrc.render(self)
     130        def _cbRender(body):
     131            if body == NOT_DONE_YET:
     132                warnings.warn(
     133                    "Returning NOT_DONE_YET is deprecated, return a Deferred instead.",
     134                    DeprecationWarning, stacklevel=2)
     135                return
     136            if type(body) is not types.StringType:
     137                body = resource.ErrorPage(
     138                    http.INTERNAL_SERVER_ERROR,
     139                    "Request did not return a string",
     140                    "Request: " + html.PRE(reflect.safe_repr(self)) + "<br />" +
     141                    "Resource: " + html.PRE(reflect.safe_repr(resrc)) + "<br />" +
     142                    "Value: " + html.PRE(reflect.safe_repr(body))).render(self)
    143143
    144                 if body is NOT_DONE_YET:
    145                     log.msg("Tried to fake a HEAD request for %s, but "
    146                             "it got away from me." % resrc)
    147                     # Oh well, I guess we won't include the content length.
    148                 else:
     144            if self.method == "HEAD":
     145                if len(body) > 0:
     146                    # This is a Bad Thing (RFC 2616, 9.4)
     147                    log.msg("Warning: HEAD request %s for resource %s is"
     148                            " returning a message body."
     149                            "  I think I'll eat it."
     150                            % (self, resrc))
    149151                    self.setHeader('content-length', str(len(body)))
    150 
    151152                self.write('')
    152                 self.finish()
    153                 return
    154 
    155             if self.method in (supportedMethods):
    156                 # We MUST include an Allow header
    157                 # (RFC 2616, 10.4.6 and 14.7)
    158                 self.setHeader('Allow', allowedMethods)
    159                 s = ('''Your browser approached me (at %(URI)s) with'''
    160                      ''' the method "%(method)s".  I only allow'''
    161                      ''' the method%(plural)s %(allowed)s here.''' % {
    162                     'URI': self.uri,
    163                     'method': self.method,
    164                     'plural': ((len(allowedMethods) > 1) and 's') or '',
    165                     'allowed': string.join(allowedMethods, ', ')
    166                     })
    167                 epage = resource.ErrorPage(http.NOT_ALLOWED,
    168                                            "Method Not Allowed", s)
    169                 body = epage.render(self)
    170153            else:
    171                 epage = resource.ErrorPage(http.NOT_IMPLEMENTED, "Huh?",
    172                                            "I don't know how to treat a"
    173                                            " %s request." % (self.method,))
    174                 body = epage.render(self)
    175         # end except UnsupportedMethod
    176 
    177         if body == NOT_DONE_YET:
    178             return
    179         if type(body) is not types.StringType:
    180             body = resource.ErrorPage(
    181                 http.INTERNAL_SERVER_ERROR,
    182                 "Request did not return a string",
    183                 "Request: " + html.PRE(reflect.safe_repr(self)) + "<br />" +
    184                 "Resource: " + html.PRE(reflect.safe_repr(resrc)) + "<br />" +
    185                 "Value: " + html.PRE(reflect.safe_repr(body))).render(self)
    186 
    187         if self.method == "HEAD":
    188             if len(body) > 0:
    189                 # This is a Bad Thing (RFC 2616, 9.4)
    190                 log.msg("Warning: HEAD request %s for resource %s is"
    191                         " returning a message body."
    192                         "  I think I'll eat it."
    193                         % (self, resrc))
    194154                self.setHeader('content-length', str(len(body)))
    195             self.write('')
    196         else:
    197             self.setHeader('content-length', str(len(body)))
    198             self.write(body)
    199         self.finish()
     155                self.write(body)
     156            self.finish()
     157
     158        def _ebRender(fail):
     159            r = fail.trap(UnsupportedMethod)
     160            if r == UnsupportedMethod:
     161                allowedMethods = fail.value.allowedMethods
     162
     163                if (self.method == "HEAD") and ("GET" in allowedMethods):
     164                    # We must support HEAD (RFC 2616, 5.1.1).  If the
     165                    # resource doesn't, fake it by giving the resource
     166                    # a 'GET' request and then return only the headers,
     167                    # not the body.
     168                    log.msg("Using GET to fake a HEAD request for %s" %
     169                            (resrc,))
     170                    self.method = "GET"
     171                    body = resrc.render(self)
     172
     173                    if body is NOT_DONE_YET:
     174                        log.msg("Tried to fake a HEAD request for %s, but "
     175                                "it got away from me." % resrc)
     176                        # Oh well, I guess we won't include the content length.
     177                    else:
     178                        self.setHeader('content-length', str(len(body)))
     179
     180                    self.write('')
     181                    self.finish()
     182                    return
     183
     184                if self.method in (supportedMethods):
     185                    # We MUST include an Allow header
     186                    # (RFC 2616, 10.4.6 and 14.7)
     187                    self.setHeader('Allow', allowedMethods)
     188                    s = ('''Your browser approached me (at %(URI)s) with'''
     189                         ''' the method "%(method)s".  I only allow'''
     190                         ''' the method%(plural)s %(allowed)s here.''' % {
     191                        'URI': self.uri,
     192                        'method': self.method,
     193                        'plural': ((len(allowedMethods) > 1) and 's') or '',
     194                        'allowed': string.join(allowedMethods, ', ')
     195                        })
     196                    epage = resource.ErrorPage(http.NOT_ALLOWED,
     197                                               "Method Not Allowed", s)
     198                    body = epage.render(self)
     199                else:
     200                    epage = resource.ErrorPage(http.NOT_IMPLEMENTED, "Huh?",
     201                                               "I don't know how to treat a"
     202                                               " %s request." % (self.method,))
     203                    body = epage.render(self)
     204                # end except UnsupportedMethod
     205                return _cbRender(body)
     206
     207        d = defer.maybeDeferred(resrc.render, self)
     208        d.addCallbacks(_cbRender, _ebRender)
     209        return d
    200210
    201211    def processingFailed(self, reason):
    202212        log.err(reason)
  • twisted/web/test/test_web.py

    === modified file 'twisted/web/test/test_web.py'
     
    181181        else:
    182182            return "correct"
    183183
     184class SimpleDeferredResource(resource.Resource):
     185    def render(self, request):
     186        d = defer.Deferred()
     187        if http.CACHED in (request.setLastModified(10),
     188                           request.setETag('MatchingTag')):
     189            reactor.callLater(0, d.callback, '')
     190        else:
     191            reactor.callLater(0, d.callback, "correct")
     192        return d
    184193
    185194class DummyChannel:
    186195    class TCP:
     
    231240            site.getResourceFor(DummyRequest([''])),
    232241            sres2, "Got the wrong resource.")
    233242
     243    def test_simplestSiteDeferred(self):
     244        """
     245        L{Site.getResourceFor} returns the C{""} child of the root resource it
     246        is constructed with when processing a request for I{/}.
     247        """
     248        sres1 = SimpleDeferredResource()
     249        sres2 = SimpleDeferredResource()
     250        sres1.putChild("",sres2)
     251        site = server.Site(sres1)
     252        self.assertIdentical(
     253            site.getResourceFor(DummyRequest([''])),
     254            sres2, "Got the wrong resource.")
    234255
    235256
    236257class SessionTest(unittest.TestCase):