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

Ticket #559: wsgins-improved.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 by using 
     55        # self._head, self.pipeline and self.pipeconf 
     56        if self._head is None: 
     57            pipe = self.pipeline[:] 
     58            pipe.reverse() 
     59            for name, callable in pipe: 
     60                conf = self.pipeconfig.get(name, {}) 
     61                self.wsgiapp = callable(self.wsgiapp, **conf) 
     62        else: 
     63            self.wsgiapp = self._head 
    11664 
     65 
    11766#                            Server components                            # 
    11867 
    11968 
    12069class CPHTTPRequest(_cpwsgiserver.HTTPRequest): 
    12170     
    12271    def parse_request(self): 
    123         mhs = cherrypy.server.max_request_header_size 
     72        mhs = _cherrypy.server.max_request_header_size 
    12473        if mhs > 0: 
    125             self.rfile = http.SizeCheckWrapper(self.rfile, mhs) 
     74            self.rfile = _http.SizeCheckWrapper(self.rfile, mhs) 
    12675         
    12776        try: 
    12877            _cpwsgiserver.HTTPRequest.parse_request(self) 
    129         except http.MaxSizeExceeded: 
     78        except _http.MaxSizeExceeded: 
    13079            self.simple_response("413 Request Entity Too Large") 
    131             cherrypy.log(traceback=True) 
     80            _cherrypy.log(traceback=True) 
    13281     
    13382    def decode_chunked(self): 
    13483        """Decode the 'chunked' transfer coding.""" 
    135         if isinstance(self.rfile, http.SizeCheckWrapper): 
     84        if isinstance(self.rfile, _http.SizeCheckWrapper): 
    13685            self.rfile = self.rfile.rfile 
    137         mbs = cherrypy.server.max_request_body_size 
     86        mbs = _cherrypy.server.max_request_body_size 
    13887        if mbs > 0: 
    139             self.rfile = http.SizeCheckWrapper(self.rfile, mbs) 
     88            self.rfile = _http.SizeCheckWrapper(self.rfile, mbs) 
    14089        try: 
    14190            return _cpwsgiserver.HTTPRequest.decode_chunked(self) 
    142         except http.MaxSizeExceeded: 
     91        except _http.MaxSizeExceeded: 
    14392            self.simple_response("413 Request Entity Too Large") 
    144             cherrypy.log(traceback=True) 
     93            _cherrypy.log(traceback=True) 
    14594            return False 
    14695 
    14796 
     
    163112    ConnectionClass = CPHTTPConnection 
    164113     
    165114    def __init__(self): 
    166         server = cherrypy.server 
     115        server = _cherrypy.server 
    167116        sockFile = server.socket_file 
    168117        if sockFile: 
    169118            bind_addr = sockFile 
     
    173122        s = _cpwsgiserver.CherryPyWSGIServer 
    174123        # We could just pass cherrypy.tree, but by passing tree.apps, 
    175124        # we get correct SCRIPT_NAMEs as early as possible. 
    176         s.__init__(self, bind_addr, cherrypy.tree.apps.items(), 
     125        s.__init__(self, bind_addr, _cherrypy.tree.apps.items(), 
    177126                   server.thread_pool, 
    178127                   server.socket_host, 
    179128                   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