Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

Changeset 1582

Show
Ignore:
Timestamp:
12/29/06 00:02:24
Author:
dowski
Message:

2.x backport of fixes in [1388] and [1530] (safer WSGI and request close). See #567.

Also buglet fixes in test.py and helper.py.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/cherrypy-2.x/cherrypy/_cpwsgi.py

    r1520 r1582  
    5050        pass 
    5151 
     52class ResponseIter(object): 
     53    def __init__(self, request, body): 
     54        self.body = body 
     55        self.request = request 
     56         
     57    def __iter__(self): 
     58        if not self.body: 
     59            raise StopIteration 
     60        try: 
     61            for chunk in self.body: 
     62                # WSGI requires all data to be of type "str". This coercion should 
     63                # not take any time at all if chunk is already of type "str". 
     64                # If it's unicode, it could be a big performance hit (x ~500). 
     65                if not isinstance(chunk, str): 
     66                    chunk = chunk.encode("ISO-8859-1") 
     67                yield chunk 
     68        except (KeyboardInterrupt, SystemExit), ex: 
     69            raise ex 
     70        except: 
     71            cherrypy.log(traceback=True) 
     72            s, h, b = _cputil.bareError() 
     73            # CherryPy test suite expects bareError body to be output, 
     74            # so don't call start_response (which, according to PEP 333, 
     75            # may raise its own error at that point). 
     76            for chunk in b: 
     77                # WSGI requires all data to be of type "str". This coercion should 
     78                # not take any time at all if chunk is already of type "str". 
     79                # If it's unicode, it could be a big performance hit (x ~500). 
     80                if not isinstance(chunk, str): 
     81                    chunk = chunk.encode("ISO-8859-1") 
     82                yield chunk 
     83     
     84    def close(self): 
     85        try: 
     86            if self.request: 
     87                self.request.close() 
     88        except: 
     89            cherrypy.log(traceback=True) 
     90        self.request = None 
    5291 
    5392def wsgiApp(environ, start_response): 
     
    87126    except: 
    88127        if cherrypy.config.get("server.throw_errors", False): 
     128            if request: 
     129                request.close() 
     130                request = None 
    89131            raise 
    90132        tb = _cputil.formatExc() 
     
    95137        exc = sys.exc_info() 
    96138     
    97     try: 
    98         start_response(s, h, exc) 
    99         for chunk in b: 
    100             # WSGI requires all data to be of type "str". This coercion should 
    101             # not take any time at all if chunk is already of type "str". 
    102             # If it's unicode, it could be a big performance hit (x ~500). 
    103             if not isinstance(chunk, str): 
    104                 chunk = chunk.encode("ISO-8859-1") 
    105             yield chunk 
    106         if request: 
    107             request.close() 
    108         request = None 
    109     except (KeyboardInterrupt, SystemExit), ex: 
    110         try: 
    111             if request: 
    112                 request.close() 
    113         except: 
    114             cherrypy.log(traceback=True) 
    115         request = None 
    116         raise ex 
    117     except: 
    118         cherrypy.log(traceback=True) 
    119         try: 
    120             if request: 
    121                 request.close() 
    122         except: 
    123             cherrypy.log(traceback=True) 
    124         request = None 
    125         s, h, b = _cputil.bareError() 
    126         # CherryPy test suite expects bareError body to be output, 
    127         # so don't call start_response (which, according to PEP 333, 
    128         # may raise its own error at that point). 
    129         for chunk in b: 
    130             if not isinstance(chunk, str): 
    131                 chunk = chunk.encode("ISO-8859-1") 
    132             yield chunk 
     139    start_response(s, h, exc) 
     140    return ResponseIter(request, b) 
     141 
     142 
    133143 
    134144 
     
    283293        self.ssl_private_key = conf("server.ssl_private_key") 
    284294 
     295 
  • branches/cherrypy-2.x/cherrypy/_cpwsgiserver.py

    r1572 r1582  
    236236                            response = request.wsgi_app(request.environ, 
    237237                                                        request.start_response) 
    238                             for line in response: 
    239                                 request.write(line) 
    240                             if hasattr(response, "close"): 
    241                                 response.close() 
     238                            try: 
     239                                for line in response: 
     240                                    request.write(line) 
     241                            finally: 
     242                                if hasattr(response, "close"): 
     243                                    response.close() 
    242244                    except socket.error, e: 
    243245                        errno = e.args[0] 
     
    434436                    pass 
    435437 
     438 
  • branches/cherrypy-2.x/cherrypy/filters/wsgiappfilter.py

    r1008 r1582  
    136136 
    137137        # run the wsgi app and have it set response.body 
    138         cherrypy.response.body = self.app(environ, start_response) 
     138        response = self.app(environ, start_response) 
     139        try: 
     140            cherrypy.response.body = response 
     141        finally: 
     142            if hasattr(response, "close"): 
     143                response.close() 
    139144         
    140145        # tell CP not to handle the request further 
  • branches/cherrypy-2.x/cherrypy/test/helper.py

    r1567 r1582  
    3131     
    3232    mount_point = "" 
     33    scheme = "http" 
    3334     
    3435    def prefix(self): 
  • branches/cherrypy-2.x/cherrypy/test/test.py

    r1569 r1582  
    159159     
    160160    --port=<int>: use a port other than the default (%s). 
    161     --1.1: use HTTP/1.1 servers instead of default HTTP/1.0
     161    --1.0: use HTTP/1.0 servers instead of default HTTP/1.1
    162162     
    163163    --cover: turn on the code-coverage tool. 
  • branches/cherrypy-2.x/cherrypy/test/test_custom_filters.py

    r1017 r1582  
    11"""Test the various means of instantiating and invoking filters.""" 
    22 
     3import time 
    34import types 
    45import test 
     
    103104            raise ValueError() 
    104105         
     106        def stream(self): 
     107            for i in xrange(100000000): 
     108                yield str(i) 
     109         
    105110        def errinstream(self): 
    106111            raise ValueError() 
     
    134139            'stream_response': True, 
    135140        }, 
     141        '/cpfilterlist/stream': { 
     142            'stream_response': True, 
     143        }, 
    136144        '/cpfilterlist/err_in_onstart': { 
    137145            # Because this isn't a dict, on_start_resource will error. 
     
    159167 
    160168class FilterTests(helper.CPWebCase): 
    161      
    162169    def testCPFilterList(self): 
    163170        self.getPage("/cpfilterlist/") 
     
    184191        # If this fails, then on_end_request isn't being called at all. 
    185192        self.getPage("/cpfilterlist/ended/5") 
     193        self.assertBody("True") 
     194 
     195        # Test that on_end_request is called even if the client drops. 
     196        self.persistent = True 
     197        try: 
     198            conn = self.HTTP_CONN 
     199            conn.putrequest('GET', '/cpfilterlist/stream', skip_host=True) 
     200            conn.putheader('Host', self.HOST) 
     201            conn.endheaders() 
     202            # Skip the rest of the request and close the conn. This will  
     203            # cause the server's active socket to error, which *should*  
     204            # result in the request being aborted, and request.close being  
     205            # called all the way up the stack (including WSGI middleware),  
     206            # eventually calling our on_end_request hook. 
     207        finally: 
     208            self.persistent = False 
     209        time.sleep(0.1) 
     210        # on_end_request should have been called 
     211        self.getPage('/cpfilterlist/ended/7') 
    186212        self.assertBody("True") 
    187213         

Hosted by WebFaction

Log in as guest/cpguest to create tickets