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

Ticket #559: pure_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        # This is here instead of __call__ because it's really hard 
     78        # to overwrite special methods like __call__ per instance. 
     79        return wsgi_handler(environ, start_response, app=self) 
     80     
    7081    def __call__(self, environ, start_response): 
    71         return _cpwsgi._wsgi_callable(environ, start_response, app=self
     82        return self.wsgiapp(environ, start_response
    7283 
    7384 
     85headerNames = {'HTTP_CGI_AUTHORIZATION': 'Authorization', 
     86               'CONTENT_LENGTH': 'Content-Length', 
     87               'CONTENT_TYPE': 'Content-Type', 
     88               'REMOTE_HOST': 'Remote-Host', 
     89               'REMOTE_ADDR': 'Remote-Addr', 
     90               } 
     91 
     92def translate_headers(environ): 
     93    """Translate CGI-environ header names to HTTP header names.""" 
     94    for cgiName in environ: 
     95        # We assume all incoming header keys are uppercase already. 
     96        if cgiName in headerNames: 
     97            yield headerNames[cgiName], environ[cgiName] 
     98        elif cgiName[:5] == "HTTP_": 
     99            # Hackish attempt at recovering original header names. 
     100            translatedHeader = cgiName[5:].replace("_", "-") 
     101            yield translatedHeader, environ[cgiName] 
     102 
     103 
     104def wsgi_handler(environ, start_response, app): 
     105    request = None 
     106    try: 
     107        env = environ.get 
     108        local = http.Host('', int(env('SERVER_PORT', 80)), 
     109                          env('SERVER_NAME', '')) 
     110        remote = http.Host(env('REMOTE_ADDR', ''), 
     111                           int(env('REMOTE_PORT', -1)), 
     112                           env('REMOTE_HOST', '')) 
     113        request = cherrypy.engine.request(local, remote, 
     114                                          env('wsgi.url_scheme'), 
     115                                          env('ACTUAL_SERVER_PROTOCOL', "HTTP/1.1")) 
     116         
     117        # LOGON_USER is served by IIS, and is the name of the 
     118        # user after having been mapped to a local account. 
     119        # Both IIS and Apache set REMOTE_USER, when possible. 
     120        request.login = env('LOGON_USER') or env('REMOTE_USER') or None 
     121         
     122        request.multithread = environ['wsgi.multithread'] 
     123        request.multiprocess = environ['wsgi.multiprocess'] 
     124        request.wsgi_environ = environ 
     125         
     126        request.app = app 
     127         
     128        path = env('SCRIPT_NAME', '') + env('PATH_INFO', '') 
     129        response = request.run(environ['REQUEST_METHOD'], path, 
     130                               env('QUERY_STRING'), 
     131                               env('SERVER_PROTOCOL'), 
     132                               translate_headers(environ), 
     133                               environ['wsgi.input']) 
     134        s, h, b = response.status, response.header_list, response.body 
     135        exc = None 
     136    except (KeyboardInterrupt, SystemExit), ex: 
     137        try: 
     138            if request: 
     139                request.close() 
     140        except: 
     141            cherrypy.log(traceback=True) 
     142        request = None 
     143        raise ex 
     144    except: 
     145        if request and request.throw_errors: 
     146            raise 
     147        tb = format_exc() 
     148        cherrypy.log(tb) 
     149        if request and not request.show_tracebacks: 
     150            tb = "" 
     151        s, h, b = bare_error(tb) 
     152        exc = sys.exc_info() 
     153     
     154    try: 
     155        start_response(s, h, exc) 
     156        for chunk in b: 
     157            # WSGI requires all data to be of type "str". This coercion should 
     158            # not take any time at all if chunk is already of type "str". 
     159            # If it's unicode, it could be a big performance hit (x ~500). 
     160            if not isinstance(chunk, str): 
     161                chunk = chunk.encode("ISO-8859-1") 
     162            yield chunk 
     163        if request: 
     164            request.close() 
     165        request = None 
     166    except (KeyboardInterrupt, SystemExit), ex: 
     167        try: 
     168            if request: 
     169                request.close() 
     170        except: 
     171            cherrypy.log(traceback=True) 
     172        request = None 
     173        raise ex 
     174    except: 
     175        cherrypy.log(traceback=True) 
     176        try: 
     177            if request: 
     178                request.close() 
     179        except: 
     180            cherrypy.log(traceback=True) 
     181        request = None 
     182        s, h, b = bare_error() 
     183        # CherryPy test suite expects bare_error body to be output, 
     184        # so don't call start_response (which, according to PEP 333, 
     185        # may raise its own error at that point). 
     186        for chunk in b: 
     187            if not isinstance(chunk, str): 
     188                chunk = chunk.encode("ISO-8859-1") 
     189            yield chunk 
     190 
     191 
    74192class Tree(object): 
    75193    """A registry of CherryPy applications, mounted at diverse points. 
    76194     
  • _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(list): 
     9    """An ordered list of 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    self: a list of (name, wsgiapp) pairs. Each 'wsgiapp' MUST be a 
     12        constructor that takes an initial, positional wsgiapp argument, 
     13        plus optional keyword arguments, and returns a WSGI application 
     14        (that takes environ and start_response arguments). The 'name' can 
     15        be any you choose, and will correspond to keys in self.config. 
     16    config: a dict whose keys match names listed in the pipeline. Each 
     17        value is a further dict which will be passed to the corresponding 
     18        named WSGI callable (from the pipeline) as keyword arguments. 
     19    """ 
     20     
     21    def __new__(cls, app): 
     22        return list.__new__(cls) 
     23     
     24    def __init__(self, app, key="wsgi"): 
     25        self.app = app 
     26        self.head = None 
     27        self.tail = None 
     28        self.config = {} 
     29        app.namespaces[key] = self.namespace_handler 
     30     
     31    def namespace_handler(self, k, v): 
     32        """Config handler for our namespace.""" 
     33        if k == "pipeline": 
     34            while self: 
     35                self.pop() 
     36            self.extend(v) 
     37            if self: 
     38                # If self is empty, there's no need to replace app.wsgiapp. 
     39                self.tail = self.app.wsgiapp 
     40                self.app.wsgiapp = self.__call__ 
     41        else: 
     42            name, arg = k.split(".", 1) 
     43            bucket = self.config.setdefault(name, {}) 
     44            bucket[arg] = v 
     45     
     46    def __call__(self, environ, start_response): 
     47        if not self.head: 
     48            # This class may be used without calling namespace_handler, 
     49            # in which case self.tail may still be None. 
     50            self.head = self.tail or self.app.wsgiapp 
     51            pipe = self[:] 
     52            pipe.reverse() 
     53            for name, callable in pipe: 
     54                conf = self.config.get(name, {}) 
     55                self.head = callable(self.head, **conf) 
     56        return self.head(environ, start_response) 
    11557 
    11658 
     59 
    11760#                            Server components                            # 
    11861 
    11962 
    12063class CPHTTPRequest(_cpwsgiserver.HTTPRequest): 
    12164     
    12265    def parse_request(self): 
    123         mhs = cherrypy.server.max_request_header_size 
     66        mhs = _cherrypy.server.max_request_header_size 
    12467        if mhs > 0: 
    125             self.rfile = http.SizeCheckWrapper(self.rfile, mhs) 
     68            self.rfile = _http.SizeCheckWrapper(self.rfile, mhs) 
    12669         
    12770        try: 
    12871            _cpwsgiserver.HTTPRequest.parse_request(self) 
    129         except http.MaxSizeExceeded: 
     72        except _http.MaxSizeExceeded: 
    13073            self.simple_response("413 Request Entity Too Large") 
    131             cherrypy.log(traceback=True) 
     74            _cherrypy.log(traceback=True) 
    13275     
    13376    def decode_chunked(self): 
    13477        """Decode the 'chunked' transfer coding.""" 
    135         if isinstance(self.rfile, http.SizeCheckWrapper): 
     78        if isinstance(self.rfile, _http.SizeCheckWrapper): 
    13679            self.rfile = self.rfile.rfile 
    137         mbs = cherrypy.server.max_request_body_size 
     80        mbs = _cherrypy.server.max_request_body_size 
    13881        if mbs > 0: 
    139             self.rfile = http.SizeCheckWrapper(self.rfile, mbs) 
     82            self.rfile = _http.SizeCheckWrapper(self.rfile, mbs) 
    14083        try: 
    14184            return _cpwsgiserver.HTTPRequest.decode_chunked(self) 
    142         except http.MaxSizeExceeded: 
     85        except _http.MaxSizeExceeded: 
    14386            self.simple_response("413 Request Entity Too Large") 
    144             cherrypy.log(traceback=True) 
     87            _cherrypy.log(traceback=True) 
    14588            return False 
    14689 
    14790 
     
    163106    ConnectionClass = CPHTTPConnection 
    164107     
    165108    def __init__(self): 
    166         server = cherrypy.server 
     109        server = _cherrypy.server 
    167110        sockFile = server.socket_file 
    168111        if sockFile: 
    169112            bind_addr = sockFile 
     
    173116        s = _cpwsgiserver.CherryPyWSGIServer 
    174117        # We could just pass cherrypy.tree, but by passing tree.apps, 
    175118        # we get correct SCRIPT_NAMEs as early as possible. 
    176         s.__init__(self, bind_addr, cherrypy.tree.apps.items(), 
     119        s.__init__(self, bind_addr, _cherrypy.tree.apps.items(), 
    177120                   server.thread_pool, 
    178121                   server.socket_host, 
    179122                   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.Application(Root()) 
     35    cherrypy.wsgi.Pipeline(app) 
     36    cherrypy.tree.mount(app, conf={'/': root_conf}) 
     37 
     38 
     39from cherrypy.test import helper 
     40 
     41 
     42class WSGI_Namespace_Test(helper.CPWebCase): 
     43     
     44    def test_01_standard_app(self): 
     45        self.getPage("/") 
     46        self.assertBody("HELLO WORLD!") 
     47 
     48if __name__ == '__main__': 
     49    setup_server() 
     50    helper.testmain() 
     51 

Hosted by WebFaction

Log in as guest/cpguest to create tickets