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

Changeset 1238

Show
Ignore:
Timestamp:
08/12/06 01:42:49
Author:
fumanchu
Message:

Fix for CP2.2 for #541 (absoluteURI in request line). I also copied whatever wsgiserver bugfixes seemed relevant from trunk. Version upgraded to 2.2.2rc1.

Files:

Legend:

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

    r1065 r1238  
    11"""Global module that all modules developing with CherryPy should import.""" 
    22 
    3 __version__ = '2.2.1' 
     3__version__ = '2.2.2rc1' 
    44 
    55import datetime 
  • branches/cherrypy-2.x/cherrypy/_cpwsgiserver.py

    r1113 r1238  
    11"""A high-speed, production ready, thread pooled, generic WSGI server.""" 
    22 
     3import mimetools # todo: use email 
     4import Queue 
     5import re 
     6quoted_slash = re.compile("(?i)%2F") 
     7import rfc822 
    38import socket 
     9import sys 
    410import threading 
    5 import Queue 
    6 import mimetools # todo: use email 
    7 import sys 
    811import time 
    912import traceback 
    10  
    11 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] 
    12 monthname = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 
    13                    'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] 
     13from urllib import unquote 
     14from urlparse import urlparse 
    1415 
    1516import errno 
     
    2526socket_errors_to_ignore = dict.fromkeys(socket_errors_to_ignore).keys() 
    2627 
     28# These are lowercase because mimetools.Message uses lowercase keys. 
     29comma_separated_headers = [ 
     30    'accept', 'accept-charset', 'accept-encoding', 'accept-language', 
     31    'accept-ranges', 'allow', 'cache-control', 'connection', 'content-encoding', 
     32    'content-language', 'expect', 'if-match', 'if-none-match', 'pragma', 
     33    'proxy-authenticate', 'te', 'trailer', 'transfer-encoding', 'upgrade', 
     34    'vary', 'via', 'warning', 'www-authenticate', 
     35    ] 
    2736 
    2837class HTTPRequest(object): 
     
    5968            self.ready = False 
    6069            return 
    61         method,path,version = request_line.strip().split(" ", 2) 
    62         if "?" in path: 
    63             path, qs = path.split("?", 1) 
    64         else: 
    65             qs = "" 
     70         
     71        method, path, req_protocol = request_line.strip().split(" ", 2) 
    6672        self.environ["REQUEST_METHOD"] = method 
     73         
     74        # path may be an abs_path (including "http://host.domain.tld"); 
     75        scheme, location, path, params, qs, frag = urlparse(path) 
     76        if scheme: 
     77            self.environ["wsgi.url_scheme"] = scheme 
     78        if params: 
     79            path = path + ";" + params 
     80         
     81        # Unquote the path+params (e.g. "/this%20path" -> "this path"). 
     82        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2 
     83        # 
     84        # But note that "...a URI must be separated into its components 
     85        # before the escaped characters within those components can be 
     86        # safely decoded." http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2 
     87        atoms = [unquote(x) for x in quoted_slash.split(path)] 
     88        path = "%2F".join(atoms) 
    6789         
    6890        for mount_point, wsgi_app in self.server.mount_points: 
     
    84106            return 
    85107         
     108        # Note that, like wsgiref and most other WSGI servers, 
     109        # we unquote the path but not the query string. 
    86110        self.environ["QUERY_STRING"] = qs 
    87         self.environ["SERVER_PROTOCOL"] = version 
    88         self.environ["SERVER_NAME"] = self.server.server_name 
     111        self.environ["SERVER_PROTOCOL"] = req_protocol 
     112        # If the Request-URI was an absoluteURI, use its location atom. 
     113        self.environ["SERVER_NAME"] = location or self.server.server_name 
     114         
    89115        if isinstance(self.server.bind_addr, basestring): 
    90116            # AF_UNIX. This isn't really allowed by WSGI, which doesn't 
     
    97123            self.environ["REMOTE_ADDR"] = self.addr[0] 
    98124            self.environ["REMOTE_PORT"] = str(self.addr[1]) 
     125         
    99126        # then all the http headers 
    100127        headers = mimetools.Message(self.rfile) 
     
    111138        self.environ["CONTENT_LENGTH"] = cl or "" 
    112139         
    113         for (k, v) in headers.items(): 
    114             envname = "HTTP_" + k.upper().replace("-","_") 
    115             self.environ[envname] = v 
     140        for k in headers: 
     141            envname = "HTTP_" + k.upper().replace("-", "_") 
     142            if k in comma_separated_headers: 
     143                self.environ[envname] = ", ".join(headers.getheaders(k)) 
     144            else: 
     145                self.environ[envname] = headers[k] 
    116146        self.ready = True 
    117147     
     
    152182            self.close_at_end = True 
    153183        if "date" not in self.outheaderkeys: 
    154             # HTTP 1.1 mandates date output in RFC 1123 format. 
    155             year, month, day, hh, mm, ss, wd, y, z = time.gmtime() 
    156             dt = ("%s, %02d %3s %4d %02d:%02d:%02d GMT" % 
    157                   (weekdayname[wd], day, monthname[month], year, hh, mm, ss)) 
    158             self.outheaders.append(("Date", dt)) 
     184            self.outheaders.append(("Date", rfc822.formatdate())) 
    159185        if "server" not in self.outheaderkeys: 
    160186            self.outheaders.append(("Server", self.server.version)) 
     
    219245 
    220246class CherryPyWSGIServer(object): 
    221      
    222     version = "CherryPy/2.2.1" 
     247    """An HTTP server for WSGI. 
     248     
     249    bind_addr: a (host, port) tuple if TCP sockets are desired; 
     250        for UNIX sockets, supply the filename as a string. 
     251    wsgi_app: the WSGI 'application callable'; multiple WSGI applications 
     252        may be passed as (script_name, callable) pairs. 
     253    numthreads: the number of worker threads to create (default 10). 
     254    server_name: the string to set for WSGI's SERVER_NAME environ entry. 
     255        Defaults to socket.gethostname(). 
     256    max: the maximum number of queued requests (defaults to -1 = no limit). 
     257    request_queue_size: the 'backlog' argument to socket.listen(); 
     258        specifies the maximum number of queued connections (default 5). 
     259    timeout: the timeout in seconds for accepted connections (default 10). 
     260    """ 
     261     
     262    version = "CherryPy/2.2.2rc1" 
     263    protocol = "HTTP/1.0" 
    223264    ready = False 
    224265    interrupt = None 
     
    227268    def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None, 
    228269                 max=-1, request_queue_size=5, timeout=10): 
    229         """Be careful w/ max""" 
    230270        self.requests = Queue.Queue(max) 
    231271         
     
    258298        # If you're using this server with another framework, you should 
    259299        # trap those exceptions in whatever code block calls start(). 
     300         
     301        def bind(family, type, proto=0): 
     302            """Create (or recreate) the actual socket object.""" 
     303            self.socket = socket.socket(family, type, proto) 
     304            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
     305            self.socket.bind(self.bind_addr) 
    260306         
    261307        # Select the appropriate socket 
     
    328374        try: 
    329375            s, addr = self.socket.accept() 
     376            if not self.ready: 
     377                return 
    330378            if hasattr(s, 'settimeout'): 
    331379                s.settimeout(self.timeout) 
     
    337385            # accept() by default 
    338386            return 
     387        except socket.error, x: 
     388            if x.args[1] == "Bad file descriptor": 
     389                # Our socket was closed 
     390                return 
     391            raise 
    339392     
    340393    def stop(self): 
     
    352405        # Don't join currentThread (when stop is called inside a request). 
    353406        current = threading.currentThread() 
    354         for worker in self._workerThreads: 
     407        while self._workerThreads: 
     408            worker = self._workerThreads.pop() 
    355409            if worker is not current and worker.isAlive: 
    356                 worker.join() 
    357          
    358         self._workerThreads = [] 
     410                try: 
     411                    worker.join() 
     412                except AssertionError: 
     413                    pass 
     414 
  • branches/cherrypy-2.x/cherrypy/test/test_objectmapping.py

    r1017 r1238  
    205205         
    206206        self.mount_point = "" 
     207         
     208        # Test absoluteURI's in the Request-Line 
     209        self.getPage('http://localhost/') 
     210        self.assertBody('world') 
    207211         
    208212        # Test that the "isolated" app doesn't leak url's into the root app. 

Hosted by WebFaction

Log in as guest/cpguest to create tickets