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

Ticket #559: wsgins-improved.2.patch

  • _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        super(Pipeline, self).__init__(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 
    11549 
     50    def merge(self, conf): 
     51        """Merge the given config into self.config.""" 
     52        super(Pipeline, self).merge(conf) 
     53        # at this point the wsgi.* namespace has been handled 
     54        # and we can setup self.wsgiapp once for all avoiding 
     55        # the need to repeat the same task at every invocation 
     56        # of this application 
     57        if self._head is None: 
     58            pipe = self.pipeline[:] 
     59            pipe.reverse() 
     60            for name, callable in pipe: 
     61                conf = self.pipeconfig.get(name, {}) 
     62                self.wsgiapp = callable(self.wsgiapp, **conf) 
     63        else: 
     64            self.wsgiapp = self._head 
    11665 
     66 
    11767#                            Server components                            # 
    11868 
    11969 
    12070class CPHTTPRequest(_cpwsgiserver.HTTPRequest): 
    12171     
    12272    def parse_request(self): 
    123         mhs = cherrypy.server.max_request_header_size 
     73        mhs = _cherrypy.server.max_request_header_size 
    12474        if mhs > 0: 
    125             self.rfile = http.SizeCheckWrapper(self.rfile, mhs) 
     75            self.rfile = _http.SizeCheckWrapper(self.rfile, mhs) 
    12676         
    12777        try: 
    12878            _cpwsgiserver.HTTPRequest.parse_request(self) 
    129         except http.MaxSizeExceeded: 
     79        except _http.MaxSizeExceeded: 
    13080            self.simple_response("413 Request Entity Too Large") 
    131             cherrypy.log(traceback=True) 
     81            _cherrypy.log(traceback=True) 
    13282     
    13383    def decode_chunked(self): 
    13484        """Decode the 'chunked' transfer coding.""" 
    135         if isinstance(self.rfile, http.SizeCheckWrapper): 
     85        if isinstance(self.rfile, _http.SizeCheckWrapper): 
    13686            self.rfile = self.rfile.rfile 
    137         mbs = cherrypy.server.max_request_body_size 
     87        mbs = _cherrypy.server.max_request_body_size 
    13888        if mbs > 0: 
    139             self.rfile = http.SizeCheckWrapper(self.rfile, mbs) 
     89            self.rfile = _http.SizeCheckWrapper(self.rfile, mbs) 
    14090        try: 
    14191            return _cpwsgiserver.HTTPRequest.decode_chunked(self) 
    142         except http.MaxSizeExceeded: 
     92        except _http.MaxSizeExceeded: 
    14393            self.simple_response("413 Request Entity Too Large") 
    144             cherrypy.log(traceback=True) 
     94            _cherrypy.log(traceback=True) 
    14595            return False 
    14696 
    14797 
     
    163113    ConnectionClass = CPHTTPConnection 
    164114     
    165115    def __init__(self): 
    166         server = cherrypy.server 
     116        server = _cherrypy.server 
    167117        sockFile = server.socket_file 
    168118        if sockFile: 
    169119            bind_addr = sockFile 
     
    173123        s = _cpwsgiserver.CherryPyWSGIServer 
    174124        # We could just pass cherrypy.tree, but by passing tree.apps, 
    175125        # we get correct SCRIPT_NAMEs as early as possible. 
    176         s.__init__(self, bind_addr, cherrypy.tree.apps.items(), 
     126        s.__init__(self, bind_addr, _cherrypy.tree.apps.items(), 
    177127                   server.thread_pool, 
    178128                   server.socket_host, 
    179129                   request_queue_size = server.socket_queue_size, 
  • __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     

Hosted by WebFaction

Log in as guest/cpguest to create tickets