Ticket #3711: deferred-render-2.patch

File deferred-render-2.patch, 12.3 KB (added by esteve, 8 years ago)
  • twisted/web/server.py

    # Bazaar merge directive format 2 (Bazaar 0.90)
    # revision_id: esteve@fluidinfo.com-20090330195126-0acrlpz52tgv04e6
    # target_branch: ../trunk/
    # testament_sha1: 7f04c737b5b3abdcc69717812cc66b0d10a62ec9
    # timestamp: 2009-03-31 16:52:17 +0200
    # base_revision_id: svn-v4:bbbe8e31-12d6-0310-92fd-\
    #   ac37d47ddeeb:trunk:26540
    # 
    # Begin patch
    === 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):