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

Ticket #559: wsgins.patch

  • __init__.py

    old new  
    1515from cherrypy import _cptree 
    1616tree = _cptree.Tree() 
    1717from cherrypy._cptree import Application 
     18from cherrypy import _cpwsgi as wsgi 
    1819from cherrypy import _cpengine 
    1920engine = _cpengine.Engine() 
    2021from cherrypy import _cpserver 
  • _cptree.py

    old new  
    1 from cherrypy import _cpconfig, _cplogging, _cpwsgi 
     1"""CherryPy Application and Tree objects.""" 
    22 
     3import sys 
     4import cherrypy 
     5from cherrypy import _cpconfig, _cplogging 
     6from cherrypy._cperror import format_exc, bare_error 
     7from cherrypy.lib import http 
    38 
     9 
    410class Application(object): 
    511    """A CherryPy Application. 
    612     
     
    6773            host += ":%s" % port 
    6874        return scheme + host + self.script_name 
    6975     
     76    def wsgiapp(self, environ, start_response): 
     77        return wsgi_handler(environ, start_response, app=self) 
     78     
    7079    def __call__(self, environ, start_response): 
    71         return _cpwsgi._wsgi_callable(environ, start_response, app=self
     80        return self.wsgiapp(environ, start_response
    7281 
    7382 
     83headerNames = {'HTTP_CGI_AUTHORIZATION': 'Authorization', 
     84               'CONTENT_LENGTH': 'Content-Length', 
     85               'CONTENT_TYPE': 'Content-Type', 
     86               'REMOTE_HOST': 'Remote-Host', 
     87               'REMOTE_ADDR': 'Remote-Addr', 
     88               } 
     89 
     90def translate_headers(environ): 
     91    """Translate CGI-environ header names to HTTP header names.""" 
     92    for cgiName in environ: 
     93        # We assume all incoming header keys are uppercase already. 
     94        if cgiName in headerNames: 
     95            yield headerNames[cgiName], environ[cgiName] 
     96        elif cgiName[:5] == "HTTP_": 
     97            # Hackish attempt at recovering original header names. 
     98            translatedHeader = cgiName[5:].replace("_", "-") 
     99            yield translatedHeader, environ[cgiName] 
     100 
     101 
     102def wsgi_handler(environ, start_response, app): 
     103    request = None 
     104    try: 
     105        env = environ.get 
     106        local = http.Host('', int(env('SERVER_PORT', 80)), 
     107                          env('SERVER_NAME', '')) 
     108        remote = http.Host(env('REMOTE_ADDR', ''), 
     109                           int(env('REMOTE_PORT', -1)), 
     110                           env('REMOTE_HOST', '')) 
     111        request = cherrypy.engine.request(local, remote, 
     112                                          env('wsgi.url_scheme'), 
     113                                          env('ACTUAL_SERVER_PROTOCOL', "HTTP/1.1")) 
     114         
     115        # LOGON_USER is served by IIS, and is the name of the 
     116        # user after having been mapped to a local account. 
     117        # Both IIS and Apache set REMOTE_USER, when possible. 
     118        request.login = env('LOGON_USER') or env('REMOTE_USER') or None 
     119         
     120        request.multithread = environ['wsgi.multithread'] 
     121        request.multiprocess = environ['wsgi.multiprocess'] 
     122        request.wsgi_environ = environ 
     123         
     124        request.app = app 
     125         
     126        path = env('SCRIPT_NAME', '') + env('PATH_INFO', '') 
     127        response = request.run(environ['REQUEST_METHOD'], path, 
     128                               env('QUERY_STRING'), 
     129                               env('SERVER_PROTOCOL'), 
     130                               translate_headers(environ), 
     131                               environ['wsgi.input']) 
     132        s, h, b = response.status, response.header_list, response.body 
     133        exc = None 
     134    except (KeyboardInterrupt, SystemExit), ex: 
     135        try: 
     136            if request: 
     137                request.close() 
     138        except: 
     139            cherrypy.log(traceback=True) 
     140        request = None 
     141        raise ex 
     142    except: 
     143        if request and request.throw_errors: 
     144            raise 
     145        tb = format_exc() 
     146        cherrypy.log(tb) 
     147        if request and not request.show_tracebacks: 
     148            tb = "" 
     149        s, h, b = bare_error(tb) 
     150        exc = sys.exc_info() 
     151     
     152    try: 
     153        start_response(s, h, exc) 
     154        for chunk in b: 
     155            # WSGI requires all data to be of type "str". This coercion should 
     156            # not take any time at all if chunk is already of type "str". 
     157            # If it's unicode, it could be a big performance hit (x ~500). 
     158            if not isinstance(chunk, str): 
     159                chunk = chunk.encode("ISO-8859-1") 
     160            yield chunk 
     161        if request: 
     162            request.close() 
     163        request = None 
     164    except (KeyboardInterrupt, SystemExit), ex: 
     165        try: 
     166            if request: 
     167                request.close() 
     168        except: 
     169            cherrypy.log(traceback=True) 
     170        request = None 
     171        raise ex 
     172    except: 
     173        cherrypy.log(traceback=True) 
     174        try: 
     175            if request: 
     176                request.close() 
     177        except: 
     178            cherrypy.log(traceback=True) 
     179        request = None 
     180        s, h, b = bare_error() 
     181        # CherryPy test suite expects bare_error body to be output, 
     182        # so don't call start_response (which, according to PEP 333, 
     183        # may raise its own error at that point). 
     184        for chunk in b: 
     185            if not isinstance(chunk, str): 
     186                chunk = chunk.encode("ISO-8859-1") 
     187            yield chunk 
     188 
     189 
    74190class Tree(object): 
    75191    """A registry of CherryPy applications, mounted at diverse points. 
    76192     
  • _cpwsgi.py

    old new  
    11"""WSGI interface (see PEP 333).""" 
    22 
    3 import sys 
    4 import cherrypy 
     3import cherrypy as _cherrypy 
    54from cherrypy import _cpwsgiserver 
    6 from cherrypy._cperror import format_exc, bare_error 
    7 from cherrypy.lib import http 
     5from cherrypy.lib import http as _http 
    86 
    97 
    10 headerNames = {'HTTP_CGI_AUTHORIZATION': 'Authorization', 
    11                'CONTENT_LENGTH': 'Content-Length', 
    12                'CONTENT_TYPE': 'Content-Type', 
    13                'REMOTE_HOST': 'Remote-Host', 
    14                'REMOTE_ADDR': 'Remote-Addr', 
    15                } 
    16  
    17 def translate_headers(environ): 
    18     """Translate CGI-environ header names to HTTP header names.""" 
    19     for cgiName in environ: 
    20         # We assume all incoming header keys are uppercase already. 
    21         if cgiName in headerNames: 
    22             yield headerNames[cgiName], environ[cgiName] 
    23         elif cgiName[:5] == "HTTP_": 
    24             # Hackish attempt at recovering original header names. 
    25             translatedHeader = cgiName[5:].replace("_", "-") 
    26             yield translatedHeader, environ[cgiName] 
    27  
    28  
    29 def _wsgi_callable(environ, start_response, app): 
    30     request = None 
    31     try: 
    32         env = environ.get 
    33         local = http.Host('', int(env('SERVER_PORT', 80)), 
    34                           env('SERVER_NAME', '')) 
    35         remote = http.Host(env('REMOTE_ADDR', ''), 
    36                            int(env('REMOTE_PORT', -1)), 
    37                            env('REMOTE_HOST', '')) 
    38         request = cherrypy.engine.request(local, remote, 
    39                                           env('wsgi.url_scheme'), 
    40                                           env('ACTUAL_SERVER_PROTOCOL', "HTTP/1.1")) 
    41          
    42         # LOGON_USER is served by IIS, and is the name of the 
    43         # user after having been mapped to a local account. 
    44         # Both IIS and Apache set REMOTE_USER, when possible. 
    45         request.login = env('LOGON_USER') or env('REMOTE_USER') or None 
    46          
    47         request.multithread = environ['wsgi.multithread'] 
    48         request.multiprocess = environ['wsgi.multiprocess'] 
    49         request.wsgi_environ = environ 
    50          
    51         request.app = app 
    52          
    53         path = env('SCRIPT_NAME', '') + env('PATH_INFO', '') 
    54         response = request.run(environ['REQUEST_METHOD'], path, 
    55                                env('QUERY_STRING'), 
    56                                env('SERVER_PROTOCOL'), 
    57                                translate_headers(environ), 
    58                                environ['wsgi.input']) 
    59         s, h, b = response.status, response.header_list, response.body 
    60         exc = None 
    61     except (KeyboardInterrupt, SystemExit), ex: 
    62         try: 
    63             if request: 
    64                 request.close() 
    65         except: 
    66             cherrypy.log(traceback=True) 
    67         request = None 
    68         raise ex 
    69     except: 
    70         if request and request.throw_errors: 
    71             raise 
    72         tb = format_exc() 
    73         cherrypy.log(tb) 
    74         if request and not request.show_tracebacks: 
    75             tb = "" 
    76         s, h, b = bare_error(tb) 
    77         exc = sys.exc_info() 
     8class Pipeline(_cherrypy.Application): 
     9    """A CherryPy Application plus configurable WSGI middleware. 
    7810     
    79     try: 
    80         start_response(s, h, exc) 
    81         for chunk in b: 
    82             # WSGI requires all data to be of type "str". This coercion should 
    83             # not take any time at all if chunk is already of type "str". 
    84             # If it's unicode, it could be a big performance hit (x ~500). 
    85             if not isinstance(chunk, str): 
    86                 chunk = chunk.encode("ISO-8859-1") 
    87             yield chunk 
    88         if request: 
    89             request.close() 
    90         request = None 
    91     except (KeyboardInterrupt, SystemExit), ex: 
    92         try: 
    93             if request: 
    94                 request.close() 
    95         except: 
    96             cherrypy.log(traceback=True) 
    97         request = None 
    98         raise ex 
    99     except: 
    100         cherrypy.log(traceback=True) 
    101         try: 
    102             if request: 
    103                 request.close() 
    104         except: 
    105             cherrypy.log(traceback=True) 
    106         request = None 
    107         s, h, b = bare_error() 
    108         # CherryPy test suite expects bare_error body to be output, 
    109         # so don't call start_response (which, according to PEP 333, 
    110         # may raise its own error at that point). 
    111         for chunk in b: 
    112             if not isinstance(chunk, str): 
    113                 chunk = chunk.encode("ISO-8859-1") 
    114             yield chunk 
     11    An instance of this class may also be used as a WSGI callable 
     12    (WSGI application object) for itself. 
     13     
     14    root: the top-most container of page handlers for this app. 
     15    script_name: the URL "mount point" for this app; for example, 
     16        if script_name is "/my/cool/app", then the URL 
     17        "http://my.domain.tld/my/cool/app/page1" might be handled 
     18        by a "page1" method on the root object. If script_name is 
     19        explicitly set to None, then CherryPy will attempt to provide 
     20        it each time from request.wsgi_environ['SCRIPT_NAME']. 
     21    conf: a dict of {path: pathconf} pairs, where 'pathconf' is itself 
     22        a dict of {key: value} pairs. 
     23     
     24    pipeline: a list of (name, wsgiapp) pairs. The 'wsgiapp' MUST be a 
     25        constructor that takes an initial, positional wsgiapp argument, 
     26        plus optional keyword arguments, and return a WSGI application 
     27        that takes environ and start_response arguments. The 'name' can be 
     28        any you choose, and will correspond to keys in self.config. 
     29    pipeconfig: a dict whose keys match names listed in the pipeline. Each 
     30        value is a further dict which will be passed to the corresponding 
     31        named WSGI callable (from the pipeline) as keyword arguments. 
     32    """ 
     33     
     34    def __init__(self, root, script_name=""): 
     35        _cherrypy.Application.__init__(self, root, script_name="") 
     36        self.pipeline = [] 
     37        self._head = None 
     38        self.pipeconfig = {} 
     39        self.namespaces["wsgi"] = self.wsgi_namespace_handler 
     40     
     41    def wsgi_namespace_handler(self, k, v): 
     42        """Config handler for the 'wsgi' namespace.""" 
     43        if k == "pipeline": 
     44            self.pipeline = v 
     45        else: 
     46            name, arg = k.split(".", 1) 
     47            bucket = self.pipeconfig.setdefault(name, {}) 
     48            bucket[arg] = v 
     49     
     50    def __call__(self, environ, start_response): 
     51        if self._head is None: 
     52            self._head = self.wsgiapp 
     53            pipe = self.pipeline[:] 
     54            pipe.reverse() 
     55            for name, callable in pipe: 
     56                conf = self.pipeconfig.get(name, {}) 
     57                self._head = callable(self._head, **conf) 
     58        return self._head(environ, start_response) 
    11559 
    11660 
    11761#                            Server components                            # 
     
    12064class CPHTTPRequest(_cpwsgiserver.HTTPRequest): 
    12165     
    12266    def parse_request(self): 
    123         mhs = cherrypy.server.max_request_header_size 
     67        mhs = _cherrypy.server.max_request_header_size 
    12468        if mhs > 0: 
    125             self.rfile = http.SizeCheckWrapper(self.rfile, mhs) 
     69            self.rfile = _http.SizeCheckWrapper(self.rfile, mhs) 
    12670         
    12771        try: 
    12872            _cpwsgiserver.HTTPRequest.parse_request(self) 
    129         except http.MaxSizeExceeded: 
     73        except _http.MaxSizeExceeded: 
    13074            self.simple_response("413 Request Entity Too Large") 
    131             cherrypy.log(traceback=True) 
     75            _cherrypy.log(traceback=True) 
    13276     
    13377    def decode_chunked(self): 
    13478        """Decode the 'chunked' transfer coding.""" 
    135         if isinstance(self.rfile, http.SizeCheckWrapper): 
     79        if isinstance(self.rfile, _http.SizeCheckWrapper): 
    13680            self.rfile = self.rfile.rfile 
    137         mbs = cherrypy.server.max_request_body_size 
     81        mbs = _cherrypy.server.max_request_body_size 
    13882        if mbs > 0: 
    139             self.rfile = http.SizeCheckWrapper(self.rfile, mbs) 
     83            self.rfile = _http.SizeCheckWrapper(self.rfile, mbs) 
    14084        try: 
    14185            return _cpwsgiserver.HTTPRequest.decode_chunked(self) 
    142         except http.MaxSizeExceeded: 
     86        except _http.MaxSizeExceeded: 
    14387            self.simple_response("413 Request Entity Too Large") 
    144             cherrypy.log(traceback=True) 
     88            _cherrypy.log(traceback=True) 
    14589            return False 
    14690 
    14791 
     
    163107    ConnectionClass = CPHTTPConnection 
    164108     
    165109    def __init__(self): 
    166         server = cherrypy.server 
     110        server = _cherrypy.server 
    167111        sockFile = server.socket_file 
    168112        if sockFile: 
    169113            bind_addr = sockFile 
     
    173117        s = _cpwsgiserver.CherryPyWSGIServer 
    174118        # We could just pass cherrypy.tree, but by passing tree.apps, 
    175119        # we get correct SCRIPT_NAMEs as early as possible. 
    176         s.__init__(self, bind_addr, cherrypy.tree.apps.items(), 
     120        s.__init__(self, bind_addr, _cherrypy.tree.apps.items(), 
    177121                   server.thread_pool, 
    178122                   server.socket_host, 
    179123                   request_queue_size = server.socket_queue_size, 
  • test/test_wsgi_ns.py

    old new  
     1from cherrypy.test import test 
     2test.prefer_parent_path() 
     3 
     4 
     5def setup_server(): 
     6     
     7    import cherrypy 
     8    from cherrypy import _cpwsgi 
     9     
     10    class ChangeCase(object): 
     11         
     12        def __init__(self, app, to=None): 
     13            self.app = app 
     14            self.to = to 
     15         
     16        def __call__(self, environ, start_response): 
     17            res = ''.join(self.app(environ, start_response)) 
     18            return [getattr(res, self.to)()] 
     19     
     20     
     21    class Root(object): 
     22         
     23        def index(self): 
     24            return "HeLlO WoRlD!" 
     25        index.exposed = True 
     26     
     27     
     28    root_conf = {'wsgi.pipeline': [('changecase', ChangeCase)], 
     29                 'wsgi.changecase.to': 'upper', 
     30                 } 
     31     
     32    cherrypy.config.update({'environment': 'test_suite'}) 
     33     
     34    app = cherrypy.wsgi.Pipeline(Root()) 
     35    cherrypy.tree.mount(app, conf={'/': root_conf}) 
     36 
     37 
     38from cherrypy.test import helper 
     39 
     40 
     41class WSGI_Namespace_Test(helper.CPWebCase): 
     42     
     43    def test_01_standard_app(self): 
     44        self.getPage("/") 
     45        self.assertBody("HELLO WORLD!") 
     46 
     47if __name__ == '__main__': 
     48    setup_server() 
     49    helper.testmain() 
     50 

Hosted by WebFaction

Log in as guest/cpguest to create tickets