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

Changeset 615

Show
Ignore:
Timestamp:
09/08/05 10:40:38
Author:
mikerobi
Message:

merged the trunk back into mikerobi-experiemental

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/mikerobi-experimental

    • Property svn:ignore set to build
  • branches/mikerobi-experimental/CHANGELOG.txt

    r383 r615  
     12005-08-17: 
     2    * The session filter has been simplified, the mechanism for creating multiple sessions has been removed. However .. (mikerobi) 
     3    * The session filter can placed inside a _cpFilterList to provide an alternative to the default session.            (mikerobi) 
     4     
     52005-08-09: 
     6    * The config section [global] is now distinct from, and the parent of, [/]. (fumanchu) 
     7    * cherrypy.request.path is now parsed and set before onStartResource filter methods are called. (fumanchu) 
     8    * Absolute Request-URI's are now acceptable, and are converted to relative paths. (fumanchu) 
     9    * A Request-URI of "*" now maps to the [global] config section, and to cherrypy.root._global. (fumanchu) 
     10 
     112005-07-12: 
     12    * CherryPy-2.1.0-beta released. Check http://www.cherrypy.org/wiki/WhatsNewIn21 for instructions on how to upgrade from 2.0. (remi) 
     13 
     142005-07-09: 
     15    * Request entities that are not form params now get stuck in cherrypy.request.body (as a temp file object). (fumanchu) 
     16    * cherrypy.config.update can be called with an the keyword override=False to prevent values from being overwritten. 
     172005-07-06: 
     18    * Added code-coverage tools. (fumanchu) 
     19 
    1202005-06-25: 
    221    * BACKWARD INCOMPATIBILITY: Removed cpg module, renamed _cpserver to server, _cpconfig to config, cperror to _cperror. See http://www.cherrypy.org/wiki/PackageLayout21 (fumanchu) 
    322 
    4232005-06-21: 
    5     * New generic HTTPRedirect exception for 3xx responses (fumanchu) 
     24    * New generic HTTPRedirect exception for 3xx responses. (fumanchu) 
    625 
    7262005-06-17: 
    8     * Handle all HTTP methods (fumanchu) 
     27    * Handle all HTTP methods. (fumanchu) 
    928 
    10292005-06-16: 
  • branches/mikerobi-experimental/CHERRYPYTEAM.txt

    r278 r615  
    1 See http://trac.cherrypy.org/wiki/CherryPyTeam 
     1See http://www.cherrypy.org/wiki/CherryPyTeam 
  • branches/mikerobi-experimental/MANIFEST.in

    r290 r615  
    11include CHANGELOG.txt 
    22include CHERRYPYTEAM.txt 
     3include cherrypy/favicon.ico 
    34include cherrypy/tutorial/*.conf 
    45include cherrypy/tutorial/README.txt 
  • branches/mikerobi-experimental/cherrypy

    • Property svn:ignore set to *.pyc .*.swp
  • branches/mikerobi-experimental/cherrypy/__init__.py

    r418 r615  
    3131""" 
    3232 
    33 __version__ = '2.1.0 alpha' 
     33__version__ = '2.1.0-beta' 
     34 
     35import datetime 
    3436 
    3537from _cperror import * 
    36  
    3738import config 
    3839import server 
     
    6061threadData = local() 
    6162 
     63# Create variables needed for session (see lib/sessionfilter.py for more info) 
     64from lib.filter import sessionfilter 
     65session = sessionfilter.SessionWrapper() 
     66_sessionDataHolder = {} # Needed for RAM sessions only 
     67_sessionLockDict = {} # Needed for RAM sessions only 
     68_sessionLastCleanUpTime = datetime.datetime.now() 
     69 
    6270# decorator function for exposing methods 
    6371def expose(func): 
     
    6674 
    6775def log(msg, context='', severity=0): 
    68     """Syntactic sugar for writing to the log.""" 
     76    """Syntactic sugar for writing to the (error) log.""" 
     77    # Load _cputil lazily to avoid circular references, and 
     78    # to allow profiler and coverage tools to work on it. 
    6979    import _cputil 
    7080    logfunc = _cputil.getSpecialAttribute('_cpLogMessage') 
  • branches/mikerobi-experimental/cherrypy/_cpcgifs.py

    r321 r615  
    11import cgi 
     2import cherrypy 
    23 
     4try: 
     5    from threading import local 
     6except ImportError: 
     7    from cherrypy._cpthreadinglocal import local 
     8 
     9class LocalInt: 
     10    def __init__(self, value): 
     11        self.__local = local() 
     12        self.__local.value = value 
     13     
     14    def setValue(self, value): 
     15        self.__local.value = value 
     16 
     17    def __int__(self): 
     18        return self.__local.value 
     19 
     20    def __nonzero__(self): 
     21        return bool(self.__local.value) 
     22     
     23    def __str__(self): 
     24        return str(self.__local.value) 
     25 
     26cgi.maxlen = LocalInt(0) 
    327 
    428class FieldStorage(cgi.FieldStorage): 
    5      
     29    def __init__(self, *args, **kwds): 
     30        maxlen = cherrypy.config.get('server.maxRequestSize') 
     31        cgi.maxlen.setValue(maxlen) 
     32        try: 
     33            cgi.FieldStorage.__init__(self, *args, **kwds) 
     34        except ValueError: 
     35            raise cherrypy.HTTPStatusError(status=413) 
     36 
    637    def read_lines_to_eof(self): 
    738        """Internal: read lines until EOF.""" 
  • branches/mikerobi-experimental/cherrypy/_cperror.py

    r405 r615  
    4141    pass 
    4242 
    43 class NotFound(Error): 
    44     """ Happens when a URL couldn't be mapped to any class.method """ 
    45     pass 
    46  
    4743class WrongResponseType(Error): 
    4844    """ Happens when the cherrypy.response.body is not a string """ 
     
    7672        import cgi 
    7773         
     74        # Set a 'path' member attribute so that code which traps this 
     75        # error can have access to it. 
    7876        self.path = path 
     77         
    7978        if params is not None: 
    8079            if isinstance(params, basestring): 
     
    8685                cherrypy.request.paramMap = pm 
    8786            else: 
     87                cherrypy.request.queryString = urllib.urlencode(params) 
    8888                cherrypy.request.paramMap = params.copy() 
    89                 cherrypy.request.queryString = urllib.urlencode(params) 
     89         
    9090        cherrypy.request.browserUrl = cherrypy.request.base + path 
    9191 
     
    9696     
    9797    The new URL must be passed as the first argument to the Exception, e.g., 
    98         cperror.HTTPRedirect(newUrl). Multiple URLs are allowed. 
     98        cperror.HTTPRedirect(newUrl). Multiple URLs are allowed. If a URL 
     99        is absolute, it will be used as-is. If it is relative, it is assumed 
     100        to be relative to the current cherrypy.request.path. 
    99101    """ 
    100102     
     
    108110        abs_urls = [] 
    109111        for url in urls: 
    110             if url.startswith("/"): 
    111                 url = urlparse.urljoin(cherrypy.request.base, url) 
     112            # Note that urljoin will "do the right thing" whether url is: 
     113            #  1. a complete URL with host (e.g. "http://www.dummy.biz/test") 
     114            #  2. a URL relative to root (e.g. "/dummy") 
     115            #  3. a URL relative to the current path 
     116            url = urlparse.urljoin(cherrypy.request.browserUrl, url) 
    112117            abs_urls.append(url) 
    113118        self.urls = abs_urls 
    114119         
    115120        # RFC 2616 indicates a 301 response code fits our goal; however, 
    116         # browser support for 301 is quite messy. Do 302 instead. 
     121        # browser support for 301 is quite messy. Do 302 instead. See 
    117122        # http://ppewww.ph.gla.ac.uk/~flavell/www/post-redirect.html 
    118123        if status is None: 
    119             if cherrypy.request.protocol == "HTTP/1.1": 
     124            if cherrypy.request.version >= "1.1": 
    120125                status = 303 
    121126            else: 
     
    164169        else: 
    165170            raise ValueError("The %s status code is unknown." % status) 
     171 
     172 
     173_missing = object() 
     174 
     175class HTTPStatusError(Error): 
     176    """Exception raised when the client has made an error in its request.""" 
     177     
     178    def __init__(self, status=400, message=_missing): 
     179        self.status = status = int(status) 
     180        if status < 400 or status > 599: 
     181            raise ValueError("status must be between 400 and 599.") 
     182         
     183        # these 4 lines might dissapear 
     184        import cherrypy 
     185        cherrypy.response.status = status 
     186        if message is not _missing: 
     187            cherrypy.response.body=message 
     188 
     189        self.message = message 
     190     
     191    def getArgs(self): 
     192        return (self.status, self.message) 
     193 
     194 
     195class NotFound(HTTPStatusError): 
     196    """ Happens when a URL couldn't be mapped to any class.method """ 
     197     
     198    def __init__(self, path): 
     199        self.args = (path,) 
     200        HTTPStatusError.__init__(self, 404) 
  • branches/mikerobi-experimental/cherrypy/_cphttpserver.py

    r388 r615  
    2727""" 
    2828 
    29 import threading, SocketServer, BaseHTTPServer, socket, Queue 
     29import threading, os, socket 
     30import SocketServer, BaseHTTPServer, Queue 
    3031import cherrypy 
    3132from cherrypy import _cputil, _cphttptools 
     
    8384        cherrypy.request.multithread = cherrypy.config.get("server.threadPool") > 1 
    8485        cherrypy.request.multiprocess = False 
    85         cherrypy.server.request(self.client_address[0]
     86        cherrypy.server.request(self.client_address
    8687                                self.address_string(), 
    8788                                self.raw_requestline, 
     
    9192        wfile.write("%s %s\r\n" % (self.protocol_version, cherrypy.response.status)) 
    9293         
     94        has_close_conn = False 
    9395        for name, value in cherrypy.response.headers: 
    9496            wfile.write("%s: %s\r\n" % (name, value)) 
     97            if name.lower == 'connection' and value.lower == 'close': 
     98                has_close_conn = True 
     99        if not has_close_conn: 
     100            # This server doesn't support persistent connections yet, so we 
     101            # must add a "Connection: close" header to tell the client that 
     102            # we will close the connection when we're done sending output. 
     103            #  
     104            # From RFC 2616 sec 14.10: 
     105            # HTTP/1.1 defines the "close" connection option for the sender 
     106            # to signal that the connection will be closed after completion 
     107            # of the response. For example, 
     108            #  
     109            #    Connection: close 
     110            #  
     111            # in either the request or the response header fields indicates 
     112            # that the connection SHOULD NOT be considered `persistent' 
     113            # (section 8.1) after the current request/response is complete. 
     114            #  
     115            # HTTP/1.1 applications that do not support persistent connections 
     116            # MUST include the "close" connection option in every message. 
     117            wfile.write("Connection: close\r\n") 
    95118         
    96119        wfile.write("\r\n") 
     
    105128        if self.command == "POST": 
    106129            self.connection = self.request 
     130         
     131        # Close the conn, since we do not yet support persistent connections. 
     132        self.close_connection = 1 
    107133     
    108134    def log_message(self, format, *args): 
     
    112138 
    113139class CherryHTTPServer(BaseHTTPServer.HTTPServer): 
     140     
     141    def __init__(self): 
     142        # Set protocol_version 
     143        proto = cherrypy.config.get('server.protocolVersion') or "HTTP/1.0" 
     144        CherryHTTPRequestHandler.protocol_version = proto 
     145         
     146        # Select the appropriate server based on config options 
     147        sockFile = cherrypy.config.get('server.socketFile') 
     148        if sockFile: 
     149            # AF_UNIX socket 
     150            self.address_family = socket.AF_UNIX 
     151             
     152            # So we can reuse the socket 
     153            try: os.unlink(sockFile) 
     154            except: pass 
     155             
     156            # So everyone can access the socket 
     157            try: os.chmod(sockFile, 0777) 
     158            except: pass 
     159             
     160            server_address = sockFile 
     161        else: 
     162            # AF_INET socket 
     163            server_address = (cherrypy.config.get('server.socketHost'), 
     164                              cherrypy.config.get('server.socketPort')) 
     165         
     166        self.request_queue_size = cherrypy.config.get('server.socketQueueSize') 
     167         
     168        BaseHTTPServer.HTTPServer.__init__(self, server_address, CherryHTTPRequestHandler) 
    114169     
    115170    def server_activate(self): 
     
    211266    allow_reuse_address = 1 
    212267     
    213     def __init__(self, serverAddress, numThreads, RequestHandlerClass, ThreadClass=ServerThread): 
    214         assert(numThreads > 0) 
     268    def __init__(self): 
     269        # Set protocol_version 
     270        proto = cherrypy.config.get('server.protocolVersion') or "HTTP/1.0" 
     271        CherryHTTPRequestHandler.protocol_version = proto 
     272         
     273        # Select the appropriate server based on config options 
     274        threadPool = cherrypy.config.get('server.threadPool') 
     275        server_address = (cherrypy.config.get('server.socketHost'), 
     276                          cherrypy.config.get('server.socketPort')) 
     277        self.request_queue_size = cherrypy.config.get('server.socketQueueSize') 
    215278         
    216279        # I know it says "do not override", but I have to in order to implement SSL support ! 
    217         SocketServer.BaseServer.__init__(self, serverAddress, RequestHandlerClass
    218         self.socket=socket.socket(self.address_family, self.socket_type) 
     280        SocketServer.BaseServer.__init__(self, server_address, CherryHTTPRequestHandler
     281        self.socket = socket.socket(self.address_family, self.socket_type) 
    219282        self.server_bind() 
    220283        self.server_activate() 
    221284         
    222         self._numThreads = numThreads 
    223         self._RequestHandlerClass = RequestHandlerClass 
    224         self._ThreadClass = ThreadClass 
     285        self._numThreads = threadPool 
     286        self._RequestHandlerClass = CherryHTTPRequestHandler 
     287        self._ThreadClass = ServerThread 
    225288        self._requestQueue = Queue.Queue() 
    226289        self._workerThreads = [] 
     
    296359    """Selects and instantiates the appropriate server.""" 
    297360     
    298     # Set protocol_version 
    299     proto = cherrypy.config.get('server.protocolVersion') 
    300     if not proto: 
    301         proto = "HTTP/1.0" 
    302     CherryHTTPRequestHandler.protocol_version = proto 
    303      
    304361    # Select the appropriate server based on config options 
    305362    sockFile = cherrypy.config.get('server.socketFile') 
    306363    threadPool = cherrypy.config.get('server.threadPool') 
    307     if sockFile: 
    308         # AF_UNIX socket 
    309         # TODO: Handle threading here 
    310         class ServerClass(CherryHTTPServer): address_family = socket.AF_UNIX 
    311          
    312         # So we can reuse the socket 
    313         try: os.unlink(sockFile) 
    314         except: pass 
    315          
    316         server_address = sockFile 
     364    if threadPool > 1 and not sockFile: 
     365        ServerClass = PooledThreadServer 
    317366    else: 
    318         # AF_INET socket 
    319         if threadPool > 1: 
    320             ServerClass = PooledThreadServer 
    321         else: 
    322             ServerClass = CherryHTTPServer 
    323         server_address = (cherrypy.config.get('server.socketHost'), 
    324                           cherrypy.config.get('server.socketPort')) 
    325      
    326     ServerClass.request_queue_size = cherrypy.config.get('server.socketQueueSize') 
    327      
    328     if handler is None: 
    329         handler = CherryHTTPRequestHandler 
    330      
    331     if threadPool > 1: 
    332         myCherryHTTPServer = ServerClass(server_address, threadPool, handler) 
    333     else: 
    334         myCherryHTTPServer = ServerClass(server_address, handler) 
    335      
    336     # So everyone can access the socket 
    337     if sockFile: 
    338         try: os.chmod(sockFile, 0777) 
    339         except: pass 
    340      
    341     return myCherryHTTPServer 
    342  
     367        ServerClass = CherryHTTPServer 
     368    return ServerClass() 
     369 
  • branches/mikerobi-experimental/cherrypy/_cphttptools.py

    r422 r615  
    3131""" 
    3232 
    33 import urllib, os, sys, time, types, cgi 
    34 import mimetypes, Cookie, urlparse 
     33import cgi 
     34 
     35from BaseHTTPServer import BaseHTTPRequestHandler 
     36responseCodes = BaseHTTPRequestHandler.responses 
     37 
     38import Cookie 
     39import os 
     40import re 
     41import sys 
     42import types 
     43import urllib 
     44from urlparse import urlparse 
    3545 
    3646import cherrypy 
    3747from cherrypy import _cputil, _cpcgifs 
    38  
    39 # Can't use cStringIO; doesn't support unicode strings   
    40 # See http://www.python.org/sf/216388   
    41 import StringIO 
    42  
    43 from BaseHTTPServer import BaseHTTPRequestHandler 
    44 responseCodes = BaseHTTPRequestHandler.responses 
    45  
    46 mimetypes.types_map['.dwg']='image/x-dwg' 
    47 mimetypes.types_map['.ico']='image/x-icon' 
    48  
    49 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] 
    50 monthname = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] 
     48from cherrypy.lib import cptools 
     49 
     50 
     51class Version(object): 
     52     
     53    """A version, such as "2.1 beta 3", which can be compared atom-by-atom. 
     54     
     55    If a string is provided to the constructor, it will be split on word 
     56    boundaries; that is, "1.4.13 beta 9" -> ["1", "4", "13", "beta", "9"]. 
     57     
     58    Comparisons are performed atom-by-atom, numerically if both atoms are 
     59    numeric. Therefore, "2.12" is greater than "2.4", and "3.0 beta" is 
     60    greater than "3.0 alpha" (only because "b" > "a"). If an atom is 
     61    provided in one Version and not another, the longer Version is 
     62    greater than the shorter, that is: "4.8 alpha" > "4.8". 
     63    """ 
     64     
     65    def __init__(self, atoms): 
     66        """A Version object. A str argument will be split on word boundaries.""" 
     67        if isinstance(atoms, basestring): 
     68            self.atoms = re.split(r'\W', atoms) 
     69        else: 
     70            self.atoms = [str(x) for x in atoms] 
     71     
     72    def from_http(cls, version_str): 
     73        """Return a Version object from the given 'HTTP/x.y' string.""" 
     74        return cls(version_str[5:]) 
     75    from_http = classmethod(from_http) 
     76     
     77    def to_http(self): 
     78        """Return a 'HTTP/x.y' string for this Version object.""" 
     79        return "HTTP/%s.%s" % tuple(self.atoms[:2]) 
     80     
     81    def __str__(self): 
     82        return ".".join([str(x) for x in self.atoms]) 
     83     
     84    def __cmp__(self, other): 
     85        cls = self.__class__ 
     86        if not isinstance(other, cls): 
     87            # Try to coerce other to a Version instance. 
     88            other = cls(other) 
     89         
     90        index = 0 
     91        while index < len(self.atoms) and index < len(other.atoms): 
     92            mine, theirs = self.atoms[index], other.atoms[index] 
     93            if mine.isdigit() and theirs.isdigit(): 
     94                mine, theirs = int(mine), int(theirs) 
     95            if mine < theirs: 
     96                return -1 
     97            if mine > theirs: 
     98                return 1 
     99            index += 1 
     100        if index < len(other.atoms): 
     101            return -1 
     102        if index < len(self.atoms): 
     103            return 1 
     104        return 0 
    51105 
    52106 
    53107class KeyTitlingDict(dict): 
    54     """dict subclass which changes each key to str(key).title() 
    55      
    56     This should allow response headers to be case-insensitive and 
     108     
     109    """A dict subclass which changes each key to str(key).title() 
     110     
     111    This allows response headers to be case-insensitive and 
    57112    avoid duplicates. 
     113     
    58114    """ 
    59115     
     
    100156 
    101157class Request(object): 
    102     """Process a request and yield a series of response chunks. 
    103      
    104     headers should be a list of (name, value) tuples. 
    105     """ 
     158     
     159    """Process an HTTP request and set cherrypy.response attributes.""" 
    106160     
    107161    def __init__(self, clientAddress, remoteHost, requestLine, headers, 
    108162                 rfile, scheme="http"): 
    109         # When __init__ is finished, cherrypy.response should have three attributes: 
    110         #   status, e.g. "200 OK" 
    111         #   headers, a list of (name, value) tuples 
    112         #   body, an iterable yielding strings 
    113         # Consumer code should then access these three attributes 
    114         # to build the outbound stream. 
    115          
    116         self.requestLine = requestLine 
     163        """Populate a new Request object. 
     164         
     165        clientAddress should be a tuple of client IP address, client Port 
     166        remoteHost should be string of the client's IP address. 
     167        requestLine should be of the form "GET /path HTTP/1.0". 
     168        headers should be a list of (name, value) tuples. 
     169        rfile should be a file-like object containing the HTTP request 
     170            entity. 
     171        scheme should be a string, either "http" or "https". 
     172         
     173        When __init__ is done, cherrypy.response should have 3 attributes: 
     174          status, e.g. "200 OK" 
     175          headers, a list of (name, value) tuples 
     176          body, an iterable yielding strings 
     177         
     178        Consumer code (HTTP servers) should then access these response 
     179        attributes to build the outbound stream. 
     180         
     181        """ 
     182         
     183        cherrypy.request.method = "" 
     184        cherrypy.request.requestLine = requestLine.strip() 
     185        self.parseFirstLine() 
     186         
    117187        self.requestHeaders = headers 
    118188         
    119189        # Prepare cherrypy.request variables 
    120         cherrypy.request.remoteAddr = clientAddress 
     190        cherrypy.request.remoteAddr = clientAddress[0] 
     191        cherrypy.request.remotePort = clientAddress[1] 
    121192        cherrypy.request.remoteHost = remoteHost 
    122193        cherrypy.request.paramList = [] # Only used for Xml-Rpc 
    123194        cherrypy.request.headerMap = {} 
    124         cherrypy.request.requestLine = requestLine 
    125195        cherrypy.request.simpleCookie = Cookie.SimpleCookie() 
    126196        cherrypy.request.rfile = rfile 
     
    132202        cherrypy.response.body = None 
    133203         
    134         year, month, day, hh, mm, ss, wd, y, z = time.gmtime() 
    135         date = ("%s, %02d %3s %4d %02d:%02d:%02d GMT" % 
    136                 (weekdayname[wd], day, monthname[month], year, hh, mm, ss)) 
    137204        cherrypy.response.headerMap = KeyTitlingDict() 
    138205        cherrypy.response.headerMap.update({ 
    139206            "Content-Type": "text/html", 
    140207            "Server": "CherryPy/" + cherrypy.__version__, 
    141             "Date": date
     208            "Date": cptools.HTTPDate()
    142209            "Set-Cookie": [], 
    143             "Content-Length": 0 
     210            "Content-Length": None 
    144211        }) 
    145212        cherrypy.response.simpleCookie = Cookie.SimpleCookie() 
     
    150217            # HEAD requests MUST NOT return a message-body in the response. 
    151218            cherrypy.response.body = [] 
     219         
     220        _cputil.getSpecialAttribute("_cpLogAccess")() 
     221     
     222    def parseFirstLine(self): 
     223        # This has to be done very early in the request process, 
     224        # because request.path is used for config lookups right away. 
     225        request = cherrypy.request 
     226         
     227        # Parse first line 
     228        request.method, path, request.protocol = request.requestLine.split() 
     229        request.processRequestBody = request.method in ("POST", "PUT") 
     230         
     231        # separate the queryString, or set it to "" if not found 
     232        if "?" in path: 
     233            path, request.queryString = path.split("?", 1) 
     234        else: 
     235            path, request.queryString = path, "" 
     236         
     237        # Unquote the path (e.g. "/this%20path" -> "this path"). 
     238        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2 
     239        # Note that cgi.parse_qs will decode the querystring for us. 
     240        path = urllib.unquote(path) 
     241         
     242        if path == "*": 
     243            # "...the request does not apply to a particular resource, 
     244            # but to the server itself". See 
     245            # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2 
     246            path = "global" 
     247        elif not path.startswith("/"): 
     248            # path is an absolute path (including "http://host.domain.tld"); 
     249            # convert it to a relative path, so configMap lookups work. This 
     250            # default method assumes all hosts are valid for this server. 
     251            scheme, location, p, pm, q, f = urlparse(path) 
     252            path = path[len(scheme + "://" + location):] 
     253         
     254        # Save original value (in case it gets modified by filters) 
     255        request.path = request.originalPath = path 
     256         
     257        # Change objectPath in filters to change 
     258        # the object that will get rendered 
     259        request.objectPath = None 
    152260     
    153261    def run(self): 
     262        """Process the Request.""" 
    154263        try: 
    155264            try: 
     
    179288                applyFilters('onEndResource') 
    180289        except: 
     290            # This includes HTTPStatusError and NotFound 
    181291            handleError(sys.exc_info()) 
    182292     
    183293    def processRequestHeaders(self): 
    184         req = cherrypy.request 
    185          
    186         # Parse first line 
    187         req.method, path, req.protocol = self.requestLine.split() 
    188         req.processRequestBody = req.method in ("POST", "PUT") 
    189          
    190         # find the queryString, or set it to "" if not found 
    191         if "?" in path: 
    192             req.path, req.queryString = path.split("?", 1) 
    193         else: 
    194             req.path, req.queryString = path, "" 
     294        request = cherrypy.request 
     295         
     296        # Compare request and server HTTP versions, in case our server does 
     297        # not support the requested version. We can't tell the server what 
     298        # version number to write in the response, so we limit our output 
     299        # to min(req, server). We want the following output: 
     300        #   request version   server version   response version   features 
     301        # a       1.0              1.0               1.0            1.0 
     302        # b       1.0              1.1               1.1            1.0 
     303        # c       1.1              1.0               1.0            1.0 
     304        # d       1.1              1.1               1.1            1.1 
     305        # Notice that, in (b), the response will be "HTTP/1.1" even though 
     306        # the client only understands 1.0. RFC 2616 10.5.6 says we should 
     307        # only return 505 if the _major_ version is different. 
     308        request_v = Version.from_http(request.protocol) 
     309        server_v = cherrypy.config.get("server.protocolVersion", "HTTP/1.0") 
     310        server_v = Version.from_http(server_v) 
     311        cherrypy.request.version = min(request_v, server_v) 
    195312         
    196313        # build a paramMap dictionary from queryString 
    197         pm = cgi.parse_qs(req.queryString, keep_blank_values=True) 
    198         for key, val in pm.items(): 
    199             if len(val) == 1: 
    200                 pm[key] = val[0] 
    201         req.paramMap = pm 
     314        if re.match(r"[0-9]+,[0-9]+", request.queryString): 
     315            # Server-side image map. Map the coords to 'x' and 'y' 
     316            # (like CGI::Request does). 
     317            pm = request.queryString.split(",") 
     318            pm = {'x': int(pm[0]), 'y': int(pm[1])} 
     319        else: 
     320            pm = cgi.parse_qs(request.queryString, keep_blank_values=True) 
     321            for key, val in pm.items(): 
     322                if len(val) == 1: 
     323                    pm[key] = val[0] 
     324        request.paramMap = pm 
    202325         
    203326        # Process the headers into request.headerMap 
     
    208331            # only Konqueror does that), only the last one will remain in headerMap 
    209332            # (but they will be correctly stored in request.simpleCookie). 
    210             req.headerMap[name] = value 
     333            request.headerMap[name] = value 
    211334             
    212             # Handle cookies differently because on Konqueror, multiple cookies 
    213             # come on different lines with the same key 
     335            # Handle cookies differently because on Konqueror, multiple 
     336            # cookies come on different lines with the same key 
    214337            if name == 'Cookie': 
    215                 req.simpleCookie.load(value) 
    216          
    217         msg = "%s - %s" % (req.remoteAddr, self.requestLine.strip()) 
    218         cherrypy.log(msg, "HTTP") 
    219          
    220         req.base = "%s://%s" % (req.scheme, req.headerMap.get('Host', '')) 
    221         req.browserUrl = req.base + path 
    222          
    223         # Change objectPath in filters to change 
    224         # the object that will get rendered 
    225         req.objectPath = None 
     338                request.simpleCookie.load(value) 
     339         
     340        # Write a message to the error.log only if there is no access.log. 
     341        # This is only here for backwards-compatibility (with the time 
     342        # before the access.log existed), and should be removed in CP 2.2. 
     343        fname = cherrypy.config.get('server.logAccessFile', '') 
     344        if not fname: 
     345            msg = "%s - %s" % (request.remoteAddr, request.requestLine) 
     346            cherrypy.log(msg, "HTTP") 
    226347         
    227348        # Save original values (in case they get modified by filters) 
    228         req.originalPath = req.path 
    229         req.originalParamMap = req.paramMap 
    230         req.originalParamList = req.paramList 
     349        request.originalParamMap = request.paramMap 
     350        request.originalParamList = request.paramList 
     351         
     352        if cherrypy.request.version >= "1.1": 
     353            # All Internet-based HTTP/1.1 servers MUST respond with a 400 
     354            # (Bad Request) status code to any HTTP/1.1 request message 
     355            # which lacks a Host header field. 
     356            if not request.headerMap.has_key("Host"): 
     357                cherrypy.response.status = 400 
     358                cherrypy.response.body = ["HTTP/1.1 requires a 'Host' request header."] 
     359                finalize() 
     360                raise cherrypy.RequestHandled() 
     361        request.base = "%s://%s" % (request.scheme, request.headerMap.get('Host', '')) 
     362        request.browserUrl = request.base + request.path 
    231363     
    232364    def processRequestBody(self): 
    233         req = cherrypy.request 
     365        request = cherrypy.request 
    234366         
    235367        # Create a copy of headerMap with lowercase keys because 
    236368        # FieldStorage doesn't work otherwise 
    237369        lowerHeaderMap = {} 
    238         for key, value in req.headerMap.items(): 
     370        for key, value in request.headerMap.items(): 
    239371            lowerHeaderMap[key.lower()] = value 
    240372         
    241         methenv = {'REQUEST_METHOD': req.method} 
    242         forms = _cpcgifs.FieldStorage(fp=req.rfile, 
     373        # FieldStorage only recognizes POST, so fake it. 
     374        methenv = {'REQUEST_METHOD': "POST"} 
     375        forms =