Commits

Robert Brewer  committed ee7f5fd

1. Moved onStartResource before processRequestHeader where it should have been in the first place. Most filters also changed as a result.
2. Overrode send_response in _cpwsgi. Fixes ticket 168.
3. Fixed ticket 167 (testCore.py).

  • Participants
  • Parent commits af448b7
  • Branches cherrypy

Comments (0)

Files changed (14)

File _cphttptools.py

     
     def run(self):
         try:
-            self.processRequestHeaders()
-            
-            try:
-                # onStart has to be after processRequestHeaders
-                # so that "path", for example, gets set.
-                applyFilters('onStartResource')
-                
-                applyFilters('beforeRequestBody')
-                if cpg.request.processRequestBody:
-                    self.processRequestBody()
-                
-                applyFilters('beforeMain')
-                if cpg.response.body is None:
-                    main()
-                
-                applyFilters('beforeFinalize')
-                finalize()
+            try:
+                applyFilters('onStartResource')
+                
+                try:
+                    self.processRequestHeaders()
+                    
+                    applyFilters('beforeRequestBody')
+                    if cpg.request.processRequestBody:
+                        self.processRequestBody()
+                    
+                    applyFilters('beforeMain')
+                    if cpg.response.body is None:
+                        main()
+                    
+                    applyFilters('beforeFinalize')
+                    finalize()
+                except basefilter.RequestHandled:
+                    pass
             finally:
                 applyFilters('onEndResource')
-        except basefilter.RequestHandled:
-            pass
         except:
             handleError(sys.exc_info())
     
             self.end_headers()
             self.headers_sent = True
         self.wfile.write(data)
+    
+    def send_response(self, code, message=None):
+        self.log_request(code)
+        if message is None:
+            if code in self.responses:
+                message = self.responses[code][0]
+            else:
+                message = ''
+        if self.request_version != 'HTTP/0.9':
+            self.wfile.write("%s %d %s\r\n" %
+                             (self.protocol_version, code, message))
     
     def handleError(self, exc):
         self.close_connection = 1

File lib/filter/baseurlfilter.py

     Useful when running a CP server behind Apache.
     """
     
-    def onStartResource(self):
+    def beforeRequestBody(self):
         # We have to dynamically import cpg because Python can't handle
         #   circular module imports :-(
         global cpg
         from cherrypy import cpg
-        cpg.threadData.baseUrlFilterOn = cpg.config.get('baseUrlFilter.on', False)
-        cpg.threadData.baseUrlFilterBaseUrl = cpg.config.get('baseUrlFilter.baseUrl', 'http://localhost')
-        cpg.threadData.baseUrlFilterUseXForwardedHost = cpg.config.get('baseUrlFilter.useXForwardedHost', True)
-    
-    def beforeRequestBody(self):
-        if not cpg.threadData.baseUrlFilterOn:
+        
+        if not cpg.config.get('baseUrlFilter.on', False):
             return
         
-        newBaseUrl = cpg.threadData.baseUrlFilterBaseUrl
-        if cpg.threadData.baseUrlFilterUseXForwardedHost:
+        newBaseUrl = cpg.config.get('baseUrlFilter.baseUrl', 'http://localhost')
+        if cpg.config.get('baseUrlFilter.useXForwardedHost', True):
             newBaseUrl = cpg.request.headerMap.get("X-Forwarded-Host", newBaseUrl)
         
         if newBaseUrl.find("://") == -1:

File lib/filter/cachefilter.py

         self.maxsize = maxsize
         self.maxobjects = maxobjects
     
-    def onStartResource(self):
+    def beforeMain(self):
         # We have to dynamically import cpg because Python can't handle
         #   circular module imports :-(
         global cpg
         from cherrypy import cpg
-        cpg.threadData.cacheFilterOn = cpg.config.get('cacheFilter.on', False)
-        if cpg.threadData.cacheFilterOn and not hasattr(cpg, '_cache'):
+        if not cpg.config.get('cacheFilter.on', False):
+            return
+        
+        if not hasattr(cpg, '_cache'):
             cpg._cache = self.CacheClass(self.key, self.delay,
                 self.maxobjsize, self.maxsize, self.maxobjects)
-    
-    def beforeMain(self):
-        """Checks if the page is already in the cache"""
-        if not cpg.threadData.cacheFilterOn:
-            return
         
         cacheData = cpg._cache.get()
         if cacheData:
     
     def onEndResource(self):
         """Close & fix the cache entry after content was fully written"""
-        if not cpg.threadData.cacheFilterOn:
+        if not cpg.config.get('cacheFilter.on', False):
             return
         
         if cpg.threadData.cacheable:

File lib/filter/decodingfilter.py

 class DecodingFilter(BaseFilter):
     """Automatically decodes request parameters (except uploads)."""
     
-    def onStartResource(self):
+    def beforeMain(self):
         # We have to dynamically import cpg because Python can't handle
         #   circular module imports :-(
         global cpg
         from cherrypy import cpg
-        cpg.threadData.decodingFilterOn = cpg.config.get('decodingFilter.on', False)
-        cpg.threadData.decodingFilterEncoding = cpg.config.get('decodingFilter.encoding', 'utf-8')
     
-    def beforeMain(self):
-        if not cpg.threadData.decodingFilterOn:
+        if not cpg.config.get('decodingFilter.on', False):
             return
         
-        enc = cpg.threadData.decodingFilterEncoding
+        enc = cpg.config.get('decodingFilter.encoding', 'utf-8')
         for key, value in cpg.request.paramMap.items():
             if cpg.request.filenameMap.get(key):
                 # This is a file being uploaded: skip it

File lib/filter/encodingfilter.py

 class EncodingFilter(BaseFilter):
     """Filter that automatically encodes the response."""
     
-    def onStartResource(self):
+    def beforeFinalize(self):
         # We have to dynamically import cpg because Python can't handle
         #   circular module imports :-(
         global cpg
         from cherrypy import cpg
-        cpg.threadData.encodingFilterOn = cpg.config.get('encodingFilter.on', False)
-        cpg.threadData.encodingFilterEncoding = cpg.config.get('encodingFilter.encoding', 'utf-8')
-        cpg.threadData.encodingFilterMimeTypeList = cpg.config.get('encodingFilter.mimeTypeList', ['text/html'])
-    
-    def beforeFinalize(self):
-        if not cpg.threadData.encodingFilterOn:
+        
+        if not cpg.config.get('encodingFilter.on', False):
             return
         
         contentType = cpg.response.headerMap.get("Content-Type")
         if contentType:
             ctlist = contentType.split(';')[0]
-            if (ctlist in cpg.threadData.encodingFilterMimeTypeList):
-                enc = cpg.threadData.encodingFilterEncoding
+            if (ctlist in cpg.config.get('encodingFilter.mimeTypeList', ['text/html'])):
+                enc = cpg.config.get('encodingFilter.encoding', 'utf-8')
                 
                 # Add "charset=..." to response Content-Type header
                 if contentType and 'charset' not in contentType:

File lib/filter/gzipfilter.py

 class GzipFilter(BaseFilter):
     """Filter that gzips the response."""
     
-    def onStartResource(self):
+    def beforeFinalize(self):
         # We have to dynamically import cpg because Python can't handle
         #   circular module imports :-(
         global cpg
         from cherrypy import cpg
-        cpg.threadData.gzipFilterOn = cpg.config.get('gzipFilter.on', False)
-        cpg.threadData.gzipFilterMimeTypeList = cpg.config.get('gzipFilter.mimeTypeList', ['text/html'])
-        cpg.threadData.gzipFilterCompressLevel = cpg.config.get('gzipFilter.compresslevel', 9)
-    
-    def beforeFinalize(self):
-        if not cpg.threadData.gzipFilterOn:
+        if not cpg.config.get('gzipFilter.on', False):
             return
         
         if not cpg.response.body:
         
         ct = cpg.response.headerMap.get('Content-Type').split(';')[0]
         ae = cpg.request.headerMap.get('Accept-Encoding', '')
-        if (ct in cpg.threadData.gzipFilterMimeTypeList) and ('gzip' in ae):
+        if (ct in cpg.config.get('gzipFilter.mimeTypeList', ['text/html'])
+            and ('gzip' in ae)):
             cpg.response.headerMap['Content-Encoding'] = 'gzip'
-            # Return a generator that compresses the page
-            cpg.response.body = self.zip_body(cpg.response.body)
-
+            # Return a generator that compresses the page
+            level = cpg.config.get('gzipFilter.compresslevel', 9)
+            cpg.response.body = self.zip_body(cpg.response.body, level)
+    
     def write_gzip_header(self):
         """Adapted from the gzip.py standard module code"""
         
         header += '\002'
         header += '\377'
         return header
-            
+    
     def write_gzip_trailer(self, crc, size):
         footer = struct.pack("<l", crc)
         footer += struct.pack("<L", size & 0xFFFFFFFFL)
         return footer
-
-    def zip_body(self, body):
+    
+    def zip_body(self, body, compress_level):
         # Compress page
         yield self.write_gzip_header()
         crc = zlib.crc32("")
         size = 0
-        zobj = zlib.compressobj(cpg.threadData.gzipFilterCompressLevel,
+        zobj = zlib.compressobj(compress_level,
                                 zlib.DEFLATED, -zlib.MAX_WBITS,
                                 zlib.DEF_MEM_LEVEL, 0)
         for line in body:

File lib/filter/logdebuginfofilter.py

 class LogDebugInfoFilter(BaseFilter):
     """Filter that adds debug information to the page"""
     
-    def onStartResource(self):
+    def beforeMain(self):
         # We have to dynamically import cpg because Python can't handle
         #   circular module imports :-(
         global cpg
         from cherrypy import cpg
+        cpg.request.startBuilTime = time.time()
+    
+    def beforeFinalize(self):
         if cpg.config.get('server.environment') == 'dev':
             # In "dev" environment, log everything by default
             defaultOn = True
         else:
             defaultOn = False
-        
-        cpg.threadData.logDebugInfoFilterOn = cpg.config.get('logDebugInfoFilter.on', defaultOn)
-        cpg.threadData.logDebugInfoFilterMimeTypeList = cpg.config.get('logDebugInfoFilter.mimeTypeList', ['text/html'])
-        cpg.threadData.logDebugInfoFilterLogBuildTime = cpg.config.get('logDebugInfoFilter.logBuildTime', True)
-        cpg.threadData.logDebugInfoFilterLogPageSize = cpg.config.get('logDebugInfoFilter.logPageSize', True)
-        cpg.threadData.logDebugInfoFilterLogSessionSize = cpg.config.get('logDebugInfoFilter.logSessionSize', True)
-        cpg.threadData.logDebugInfoFilterLogAsComment = cpg.config.get('logDebugInfoFilter.logAsComment', False)
-    
-    def beforeMain(self):
-        if not cpg.threadData.logDebugInfoFilterOn:
+        
+        if not cpg.config.get('logDebugInfoFilter.on', defaultOn):
             return
         
-        cpg.request.startBuilTime = time.time()
-    
-    def beforeFinalize(self):
-        if not cpg.threadData.logDebugInfoFilterOn:
-            return
-        
+        mimelist = cpg.config.get('logDebugInfoFilter.mimeTypeList', ['text/html'])
         ct = cpg.response.headerMap.get('Content-Type').split(';')[0]
-        if ct in cpg.threadData.logDebugInfoFilterMimeTypeList:
+        if ct in mimelist:
             body = ''.join(cpg.response.body)
-            debuginfo = '\n'
-            if cpg.threadData.logDebugInfoFilterLogAsComment:
+            debuginfo = '\n'
+            
+            logAsComment = cpg.config.get('logDebugInfoFilter.logAsComment', False)
+            if logAsComment:
                 debuginfo += '<!-- '
             else:
                 debuginfo += "<br/><br/>"
             logList = []
-            if cpg.threadData.logDebugInfoFilterLogBuildTime:
+            
+            if cpg.config.get('logDebugInfoFilter.logBuildTime', True):
                 logList.append("Build time: %.03fs" % (
                     time.time() - cpg.request.startBuilTime))
-            if cpg.threadData.logDebugInfoFilterLogPageSize:
+            
+            if cpg.config.get('logDebugInfoFilter.logPageSize', True):
                 logList.append("Page size: %.02fKB" % (
                     len(body)/float(1024)))
-            if cpg.threadData.logDebugInfoFilterLogSessionSize and \
-                    cpg.config.get('session.storageType'):
+            
+            if (cpg.config.get('logDebugInfoFilter.logSessionSize', True)
+                and cpg.config.get('session.storageType')):
                 # Pickle session data to get its size
                 try:
                     f = StringIO.StringIO()
                     logList.append("Session data size: Unable to pickle session")
             
             debuginfo += ', '.join(logList)
-            if cpg.threadData.logDebugInfoFilterLogAsComment:
+            if logAsComment:
                 debuginfo += '-->'
             
             cpg.response.body = [body, debuginfo]

File lib/filter/sessionfilter.py

 
 from basefilter import BaseFilter
 import random, sha, string, time
+alphanum = string.letters + string.digits
 
 
 class SessionFilter(BaseFilter):
     
-    def onStartResource(self):
+    def beforeMain(self):
         # We have to dynamically import cpg because Python can't handle
         #   circular module imports :-(
         global cpg, _cputil
         from cherrypy import cpg, _cputil
         cpg.threadData.sessionFilterOn = bool(cpg.config.get('session.storageType'))
-    
-    def beforeMain(self):
+        
         if cpg.threadData.sessionFilterOn:
             cleanupSessionData()
             if not cpg.request.isStatic:
     cpg.response.simpleCookie[cookieName]['path'] = '/'
     cpg.response.simpleCookie[cookieName]['version'] = 1
 
-def generateSessionId():
-    s = ''
-    for i in xrange(50):
-        s += random.choice(string.letters + string.digits)
+def generateSessionId():
+    s = ''.join([random.choice(alphanum) for i in xrange(50)])
     s += '%s' % time.time()
     return sha.sha(s).hexdigest()
 

File lib/filter/staticfilter.py

 class StaticFilter(BaseFilter):
     """Filter that handles static content."""
     
-    def onStartResource(self):
+    def beforeMain(self):
         # We have to dynamically import cpg because Python can't handle
         #   circular module imports :-(
         global cpg, _cphttptools, cperror
         from cherrypy import cpg, _cphttptools, cperror
-        cpg.threadData.staticFilterOn = p = cpg.config.get('staticFilter.on', False)
-        cpg.threadData.staticFilterFile = cpg.config.get('staticFilter.file')
-        cpg.threadData.staticFilterDir = cpg.config.get('staticFilter.dir')
-        if cpg.threadData.staticFilterDir:
-            cpg.threadData.staticFilterConfigSection = \
-                cpg.config.get('staticFilter.dir', returnSection = True)
-    
-    def beforeMain(self):
-        if not cpg.threadData.staticFilterOn:
+        
+        if not cpg.config.get('staticFilter.on', False):
             return
         
-        if cpg.threadData.staticFilterFile:
-            filename = cpg.threadData.staticFilterFile
-        else:
-            section = cpg.threadData.staticFilterConfigSection
+        filename = cpg.config.get('staticFilter.file')
+        if not filename:
+            staticDir = cpg.config.get('staticFilter.dir')
+            section = cpg.config.get('staticFilter.dir', returnSection=True)
             extraPath = cpg.request.path[len(section) + 1:]
-            filename = os.path.join(cpg.threadData.staticFilterDir,
-                extraPath)
+            filename = os.path.join(staticDir, extraPath)
         
         # Serve filename
         try:

File lib/filter/tidyfilter.py

     server would also crash)
     """
     
-    def onStartResource(self):
+    def beforeFinalize(self):
         # We have to dynamically import cpg because Python can't handle
         #   circular module imports :-(
         global cpg
         from cherrypy import cpg
-        cpg.threadData.tidyFilterOn = cpg.config.get('tidyFilter.on', False)
-        cpg.threadData.tifyFilterTidyPath = cpg.config.get('tidyFilter.tidyPath')
-        cpg.threadData.tifyFilterTmpDir = cpg.config.get('tidyFilter.tmpDir')
-        cpg.threadData.tidyFilterStrictXml = cpg.config.get('tidyFilter.strictXml', False)
-        cpg.threadData.tidyFilterErrorsToIgnore = cpg.config.get('encodingFilter.errorsToIgnore', [])
-    
-    def beforeFinalize(self):
-        if not cpg.threadData.tidyFilterOn:
+        
+        if not cpg.config.get('tidyFilter.on', False):
             return
         
         # the tidy filter, by its very nature it's not generator friendly, 
         
         ct = cpg.response.headerMap.get('Content-Type', '').split(';')[0]
         if ct == 'text/html':
-            tmpdir = cpg.threadData.tifyFilterTmpDir
+            tmpdir = cpg.config.get('tidyFilter.tmpDir')
             pageFile = os.path.join(tmpdir, 'page.html')
             outFile = os.path.join(tmpdir, 'tidy.out')
             errFile = os.path.join(tmpdir, 'tidy.err')
             encoding = encoding.replace('utf-8', 'utf8')
             if encoding:
                 encoding = '-' + encoding
+            
             strictXml = ""
-            if cpg.threadData.tidyFilterStrictXml:
+            if cpg.config.get('tidyFilter.strictXml', False):
                 strictXml = ' -xml'
             os.system('"%s" %s%s -f %s -o %s %s' %
-                      (cpg.threadData.tifyFilterTidyPath, encoding,
+                      (cpg.config.get('tidyFilter.tidyPath'), encoding,
                        strictXml, errFile, outFile, pageFile))
             f = open(errFile, 'rb')
             err = f.read()
             for err in errList:
                 if (err.find('Warning') != -1 or err.find('Error') != -1):
                     ignore = 0
-                    for errIgn in cpg.threadData.tidyFilterErrorsToIgnore:
+                    for errIgn in cpg.config.get('encodingFilter.errorsToIgnore', []):
                         if err.find(errIgn) != -1:
                             ignore = 1
                             break
-                    if not ignore: newErrList.append(err)
+                    if not ignore:
+                        newErrList.append(err)
             
             if newErrList:
                 newBody = "Wrong HTML:<br>" + cgi.escape('\n'.join(newErrList)).replace('\n','<br>')
                 newBody += '<br><br>'
-                i=0
+                i = 0
                 for line in originalBody.splitlines():
                     i += 1
                     newBody += "%03d - "%i + cgi.escape(line).replace('\t','    ').replace(' ','&nbsp;') + '<br>'
                 
                 cpg.response.body = [newBody]
 
-            elif cpg.threadData.tidyFilterStrictXml:
+            elif strictXml:
                 # The HTML is OK, but is it valid XML
                 # Use elementtree to parse XML
                 from elementtree.ElementTree import parse
                     
                     newBody = "Wrong XML:<br>" + cgi.escape(bodyFile.getvalue().replace('\n','<br>'))
                     newBody += '<br><br>'
-                    i=0
+                    i = 0
                     for line in originalBody.splitlines():
                         i += 1
                         newBody += "%03d - "%i + cgi.escape(line).replace('\t','    ').replace(' ','&nbsp;') + '<br>'

File lib/filter/virtualhostfilter.py

     See CherryPy recipes for the documentation.
     """
     
-    def onStartResource(self):
+    def beforeRequestBody(self):
         # We have to dynamically import cpg because Python can't handle
         #   circular module imports :-(
         global cpg, _cphttptools
         from cherrypy import cpg, _cphttptools
-        cpg.threadData.virtualFilterOn = cpg.config.get('virtualHostFilter.on', False)
-        cpg.threadData.virtualFilterPrefix = cpg.config.get('virtualHostFilter.prefix', '/')
     
-    def beforeRequestBody(self):
-        if not cpg.threadData.virtualFilterOn:
+        if not cpg.config.get('virtualHostFilter.on', False):
             return
         
         domain = cpg.request.base.split('//')[1]
-        # Re-use "mapPathToObject" function to find the actual objectPath
-        virtualPath = cpg.threadData.virtualFilterPrefix + cpg.request.path
+        # Re-use "mapPathToObject" function to find the actual objectPath
+        prefix = cpg.config.get('virtualHostFilter.prefix', '/')
+        virtualPath = prefix + cpg.request.path
         c, objectPathList, v = _cphttptools.mapPathToObject(virtualPath)
         cpg.request.objectPath = '/' + '/'.join(objectPathList[1:])
         #raise basefilter.InternalRedirect

File lib/filter/xmlrpcfilter.py

           (marshalled) data.
     """
     
-    def onStartResource(self):
-        # We have to dynamically import cpg because Python can't handle
-        #   circular module imports :-(
-        global cpg
-        from cherrypy import cpg
-        cpg.threadData.xmlRpcFilterOn = cpg.config.get('xmlRpcFilter.on', False)
-    
     def testValidityOfRequest(self):
         # test if the content-length was sent
         result = int(cpg.request.headerMap.get('Content-Length',0)) > 0
     
     def beforeRequestBody(self):
         """ Called after the request header has been read/parsed"""
+        # We have to dynamically import cpg because Python can't handle
+        #   circular module imports :-(
+        global cpg
+        from cherrypy import cpg
+        cpg.threadData.xmlRpcFilterOn = cpg.config.get('xmlRpcFilter.on', False)
+        
         if not cpg.threadData.xmlRpcFilterOn:
             return True
         

File test/testCore.py

         raise ValueError
     
     def page_http_1_1(self):
-        cpg.response.headerMap["Content-Length"] = 5
+        cpg.response.headerMap["Content-Length"] = 39
         def inner():
             yield "hello"
-            raise ValueError
+            raise ValueError
+            yield "oops"
         return inner()
 
 cpg.config.update({