Commits

Anonymous committed 3788480

\r\n > \n again, after header patch

Comments (0)

Files changed (27)

-"""
-Copyright (c) 2004, CherryPy Team (team@cherrypy.org)
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, 
-are permitted provided that the following conditions are met:
-
-    * Redistributions of source code must retain the above copyright notice, 
-      this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above copyright notice, 
-      this list of conditions and the following disclaimer in the documentation 
-      and/or other materials provided with the distribution.
-    * Neither the name of the CherryPy Team nor the names of its contributors 
-      may be used to endorse or promote products derived from this software 
-      without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""
-
-import _cputil, ConfigParser, cpg
-
-        
-def setDefaultConfigOption():
-    """ Return an EmptyClass instance with the default config options """
-
-    cpg.configOption = _cputil.EmptyClass()
-
-    # Set default values for all options
-
-    # Parameters used for logging
-    cpg.configOption.logToScreen = 1
-    cpg.configOption.logFile = ''
-
-    # Parameters used to tell which socket the server should listen on
-    # Note that socketPort and socketFile conflict wich each
-    # other: if one has a non-null value, the other one should be null
-    cpg.configOption.socketHost = ''
-    cpg.configOption.socketPort = 8080
-    cpg.configOption.socketFile = '' # Used if server should listen on
-                                 # AF_UNIX socket
-    cpg.configOption.reverseDNS = 0
-    cpg.configOption.socketQueueSize = 5 # Size of the socket queue
-    cpg.configOption.protocolVersion = "HTTP/1.0"
-
-    # Parameters used to tell what kind of server we want
-    # Note that numberOfProcesses, threading and forking conflict
-    # wich each other: if one has a non-null value, the other
-    # ones should be null (for numberOfProcesses, null means equal to one)
-    cpg.configOption.processPool = 0 # Used if we want to fork n processes
-                                 # at the beginning. In this case, all
-                                 # processes will listen on the same
-                                 # socket (this only works on unix)
-    cpg.configOption.threading = 0 # Used if we want to create a new
-                               # thread for each request
-    cpg.configOption.forking = 0 # Used if we want to create a new process
-                             # for each request
-    cpg.configOption.threadPool = 0 # Used if we want to create a pool
-                                # of threads at the beginning
-
-    # Variables used to tell if this is an SSL server
-    cpg.configOption.sslKeyFile = ""
-    cpg.configOption.sslCertificateFile = ""
-    cpg.configOption.sslClientCertificateVerification = 0
-    cpg.configOption.sslCACertificateFile = ""
-    cpg.configOption.sslVerifyDepth = 1
-
-    # Variable used to flush cache
-    cpg.configOption.flushCacheDelay=0
-
-    # Variable used for enabling debugging
-    cpg.configOption.debugMode=0
-
-    # Variable used to serve static content
-    cpg.configOption.staticContentList = []
-
-    # Variable used for session handling
-    cpg.configOption.sessionStorageType = ""
-    cpg.configOption.sessionTimeout = 60 # In minutes
-    cpg.configOption.sessionCleanUpDelay = 60 # In minutes
-    cpg.configOption.sessionCookieName = "CherryPySession"
-    cpg.configOption.sessionStorageFileDir = ""
-
-def parseConfigFile(configFile = None, parsedConfigFile = None):
-    """
-        Parse the config file and set values in cpg.configOption
-    """
-    _cpLogMessage = _cputil.getSpecialFunction('_cpLogMessage')
-    if configFile:
-        cpg.parsedConfigFile = ConfigParser.ConfigParser()
-        if hasattr(configFile, 'read'):
-            _cpLogMessage("Reading infos from configFile stream", 'CONFIG')
-            cpg.parsedConfigFile.readfp(configFile)
-        else:
-            _cpLogMessage("Reading infos from configFile: %s" % configFile, 'CONFIG')
-            cpg.parsedConfigFile.read(configFile)
-    else:
-        cpg.parsedConfigFile = parsedConfigFile
-
-    # Read parameters from configFile
-    for sectionName, optionName, valueType in [
-            ('server', 'logToScreen', 'int'),
-            ('server', 'logFile', 'str'),
-            ('server', 'socketHost', 'str'),
-            ('server', 'protocolVersion', 'str'),
-            ('server', 'socketPort', 'int'),
-            ('server', 'socketFile', 'str'),
-            ('server', 'reverseDNS', 'int'),
-            ('server', 'processPool', 'int'),
-            ('server', 'threadPool', 'int'),
-            ('server', 'threading', 'int'),
-            ('server', 'forking', 'int'),
-            ('server', 'sslKeyFile', 'str'),
-            ('server', 'sslCertificateFile', 'str'),
-            ('server', 'sslClientCertificateVerification', 'int'),
-            ('server', 'sslCACertificateFile', 'str'),
-            ('server', 'sslVerifyDepth', 'int'),
-            ('session', 'storageType', 'str'),
-            ('session', 'timeout', 'float'),
-            ('session', 'cleanUpDelay', 'float'),
-            ('session', 'cookieName', 'str'),
-            ('session', 'storageFileDir', 'str')
-            ]:
-        try:
-            value = cpg.parsedConfigFile.get(sectionName, optionName)
-            if valueType == 'int': value = int(value)
-            elif valueType == 'float': value = float(value)
-            if sectionName == 'session':
-                optionName = 'session' + optionName[0].upper() + optionName[1:]
-            setattr(cpg.configOption, optionName, value)
-        except:
-            pass
-
-    try:
-        staticDirList = cpg.parsedConfigFile.options('staticContent')
-        for staticDir in staticDirList:
-            staticDirTarget = cpg.parsedConfigFile.get('staticContent', staticDir)
-            cpg.configOption.staticContentList.append((staticDir, staticDirTarget))
-    except: pass
-
-def outputConfigOptions():
-    _cpLogMessage = _cputil.getSpecialFunction('_cpLogMessage')
-    _cpLogMessage("Server parameters:", 'CONFIG')
-    _cpLogMessage("  logToScreen: %s" % cpg.configOption.logToScreen, 'CONFIG')
-    _cpLogMessage("  logFile: %s" % cpg.configOption.logFile, 'CONFIG')
-    _cpLogMessage("  protocolVersion: %s" % cpg.configOption.protocolVersion, 'CONFIG')
-    _cpLogMessage("  socketHost: %s" % cpg.configOption.socketHost, 'CONFIG')
-    _cpLogMessage("  socketPort: %s" % cpg.configOption.socketPort, 'CONFIG')
-    _cpLogMessage("  socketFile: %s" % cpg.configOption.socketFile, 'CONFIG')
-    _cpLogMessage("  reverseDNS: %s" % cpg.configOption.reverseDNS, 'CONFIG')
-    _cpLogMessage("  socketQueueSize: %s" % cpg.configOption.socketQueueSize, 'CONFIG')
-    _cpLogMessage("  processPool: %s" % cpg.configOption.processPool, 'CONFIG')
-    _cpLogMessage("  threadPool: %s" % cpg.configOption.threadPool, 'CONFIG')
-    _cpLogMessage("  threading: %s" % cpg.configOption.threading, 'CONFIG')
-    _cpLogMessage("  forking: %s" % cpg.configOption.forking, 'CONFIG')
-    _cpLogMessage("  sslKeyFile: %s" % cpg.configOption.sslKeyFile, 'CONFIG')
-    if cpg.configOption.sslKeyFile:
-        _cpLogMessage("  sslCertificateFile: %s" % cpg.configOption.sslCertificateFile, 'CONFIG')
-        _cpLogMessage("  sslClientCertificateVerification: %s" % cpg.configOption.sslClientCertificateVerification, 'CONFIG')
-        _cpLogMessage("  sslCACertificateFile: %s" % cpg.configOption.sslCACertificateFile, 'CONFIG')
-        _cpLogMessage("  sslVerifyDepth: %s" % cpg.configOption.sslVerifyDepth, 'CONFIG')
-        _cpLogMessage("  flushCacheDelay: %s min" % cpg.configOption.flushCacheDelay, 'CONFIG')
-    _cpLogMessage("  sessionStorageType: %s" % cpg.configOption.sessionStorageType, 'CONFIG')
-    if cpg.configOption.sessionStorageType:
-        _cpLogMessage("  sessionTimeout: %s min" % cpg.configOption.sessionTimeout, 'CONFIG')
-        _cpLogMessage("  cleanUpDelay: %s min" % cpg.configOption.sessionCleanUpDelay, 'CONFIG')
-        _cpLogMessage("  sessionCookieName: %s" % cpg.configOption.sessionCookieName, 'CONFIG')
-        _cpLogMessage("  sessionStorageFileDir: %s" % cpg.configOption.sessionStorageFileDir, 'CONFIG')
-    _cpLogMessage("  staticContent: %s" % cpg.configOption.staticContentList, 'CONFIG')
-
-def dummy():
-    # Check that parameters are correct and that they don't conflict with each other
-    if _protocolVersion not in ("HTTP/1.1", "HTTP/1.0"):
-        raise "CherryError: protocolVersion must be 'HTTP/1.1' or 'HTTP/1.0'"
-    if _reverseDNS not in (0,1): raise "CherryError: reverseDNS must be '0' or '1'"
-    if _socketFile and not hasattr(socket, 'AF_UNIX'): raise "CherryError: Configuration file has socketFile, but this is only available on Unix machines"
-    if _processPool!=1 and not hasattr(os, 'fork'): raise "CherryError: Configuration file has processPool, but forking is not available on this operating system"
-    if _forking and not hasattr(os, 'fork'): raise "CherryError: Configuration file has forking, but forking is not available on this operating system"
-    if _sslKeyFile:
-        try:
-            global SSL
-            from OpenSSL import SSL
-        except: raise "CherryError: PyOpenSSL 0.5.1 or later must be installed to use SSL. You can get it from http://pyopenssl.sourceforge.net"
-    if _socketPort and _socketFile: raise "CherryError: In configuration file: socketPort and socketFile conflict with each other"
-    if not _socketFile and not _socketPort: _socketPort=8000 # Default port
-    if _processPool==1: severalProcs=0
-    else: severalProcs=1
-    if _threadPool==1: severalThreads=0
-    else: severalThreads=1
-    if severalThreads+severalProcs+_threading+_forking>1: raise "CherryError: In configuration file: threadPool, processPool, threading and forking conflict with each other"
-    if _sslKeyFile and not _sslCertificateFile: raise "CherryError: Configuration file has sslKeyFile but no sslCertificateFile"
-    if _sslCertificateFile and not _sslKeyFile: raise "CherryError: Configuration file has sslCertificateFile but no sslKeyFile"
-    try: sys.stdout.flush()
-    except: pass
-
-    if _sessionStorageType not in ('', 'custom', 'ram', 'file', 'cookie'): raise "CherryError: Configuration file an invalid sessionStorageType: '%s'"%_sessionStorageType
-    if _sessionStorageType in ('custom', 'ram', 'cookie') and _sessionStorageFileDir!='': raise "CherryError: Configuration file has sessionStorageType set to 'custom, 'ram' or 'cookie' but a sessionStorageFileDir is specified"
-    if _sessionStorageType=='file' and _sessionStorageFileDir=='': raise "CherryError: Configuration file has sessionStorageType set to 'file' but no sessionStorageFileDir"
-    if _sessionStorageType=='ram' and (_forking or severalProcs):
-        print "CherryWarning: 'ram' sessions might be buggy when using several processes"
-
+"""
+Copyright (c) 2004, CherryPy Team (team@cherrypy.org)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, 
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, 
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice, 
+      this list of conditions and the following disclaimer in the documentation 
+      and/or other materials provided with the distribution.
+    * Neither the name of the CherryPy Team nor the names of its contributors 
+      may be used to endorse or promote products derived from this software 
+      without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import _cputil, ConfigParser, cpg
+
+        
+def setDefaultConfigOption():
+    """ Return an EmptyClass instance with the default config options """
+
+    cpg.configOption = _cputil.EmptyClass()
+
+    # Set default values for all options
+
+    # Parameters used for logging
+    cpg.configOption.logToScreen = 1
+    cpg.configOption.logFile = ''
+
+    # Parameters used to tell which socket the server should listen on
+    # Note that socketPort and socketFile conflict wich each
+    # other: if one has a non-null value, the other one should be null
+    cpg.configOption.socketHost = ''
+    cpg.configOption.socketPort = 8080
+    cpg.configOption.socketFile = '' # Used if server should listen on
+                                 # AF_UNIX socket
+    cpg.configOption.reverseDNS = 0
+    cpg.configOption.socketQueueSize = 5 # Size of the socket queue
+    cpg.configOption.protocolVersion = "HTTP/1.0"
+
+    # Parameters used to tell what kind of server we want
+    # Note that numberOfProcesses, threading and forking conflict
+    # wich each other: if one has a non-null value, the other
+    # ones should be null (for numberOfProcesses, null means equal to one)
+    cpg.configOption.processPool = 0 # Used if we want to fork n processes
+                                 # at the beginning. In this case, all
+                                 # processes will listen on the same
+                                 # socket (this only works on unix)
+    cpg.configOption.threading = 0 # Used if we want to create a new
+                               # thread for each request
+    cpg.configOption.forking = 0 # Used if we want to create a new process
+                             # for each request
+    cpg.configOption.threadPool = 0 # Used if we want to create a pool
+                                # of threads at the beginning
+
+    # Variables used to tell if this is an SSL server
+    cpg.configOption.sslKeyFile = ""
+    cpg.configOption.sslCertificateFile = ""
+    cpg.configOption.sslClientCertificateVerification = 0
+    cpg.configOption.sslCACertificateFile = ""
+    cpg.configOption.sslVerifyDepth = 1
+
+    # Variable used to flush cache
+    cpg.configOption.flushCacheDelay=0
+
+    # Variable used for enabling debugging
+    cpg.configOption.debugMode=0
+
+    # Variable used to serve static content
+    cpg.configOption.staticContentList = []
+
+    # Variable used for session handling
+    cpg.configOption.sessionStorageType = ""
+    cpg.configOption.sessionTimeout = 60 # In minutes
+    cpg.configOption.sessionCleanUpDelay = 60 # In minutes
+    cpg.configOption.sessionCookieName = "CherryPySession"
+    cpg.configOption.sessionStorageFileDir = ""
+
+def parseConfigFile(configFile = None, parsedConfigFile = None):
+    """
+        Parse the config file and set values in cpg.configOption
+    """
+    _cpLogMessage = _cputil.getSpecialFunction('_cpLogMessage')
+    if configFile:
+        cpg.parsedConfigFile = ConfigParser.ConfigParser()
+        if hasattr(configFile, 'read'):
+            _cpLogMessage("Reading infos from configFile stream", 'CONFIG')
+            cpg.parsedConfigFile.readfp(configFile)
+        else:
+            _cpLogMessage("Reading infos from configFile: %s" % configFile, 'CONFIG')
+            cpg.parsedConfigFile.read(configFile)
+    else:
+        cpg.parsedConfigFile = parsedConfigFile
+
+    # Read parameters from configFile
+    for sectionName, optionName, valueType in [
+            ('server', 'logToScreen', 'int'),
+            ('server', 'logFile', 'str'),
+            ('server', 'socketHost', 'str'),
+            ('server', 'protocolVersion', 'str'),
+            ('server', 'socketPort', 'int'),
+            ('server', 'socketFile', 'str'),
+            ('server', 'reverseDNS', 'int'),
+            ('server', 'processPool', 'int'),
+            ('server', 'threadPool', 'int'),
+            ('server', 'threading', 'int'),
+            ('server', 'forking', 'int'),
+            ('server', 'sslKeyFile', 'str'),
+            ('server', 'sslCertificateFile', 'str'),
+            ('server', 'sslClientCertificateVerification', 'int'),
+            ('server', 'sslCACertificateFile', 'str'),
+            ('server', 'sslVerifyDepth', 'int'),
+            ('session', 'storageType', 'str'),
+            ('session', 'timeout', 'float'),
+            ('session', 'cleanUpDelay', 'float'),
+            ('session', 'cookieName', 'str'),
+            ('session', 'storageFileDir', 'str')
+            ]:
+        try:
+            value = cpg.parsedConfigFile.get(sectionName, optionName)
+            if valueType == 'int': value = int(value)
+            elif valueType == 'float': value = float(value)
+            if sectionName == 'session':
+                optionName = 'session' + optionName[0].upper() + optionName[1:]
+            setattr(cpg.configOption, optionName, value)
+        except:
+            pass
+
+    try:
+        staticDirList = cpg.parsedConfigFile.options('staticContent')
+        for staticDir in staticDirList:
+            staticDirTarget = cpg.parsedConfigFile.get('staticContent', staticDir)
+            cpg.configOption.staticContentList.append((staticDir, staticDirTarget))
+    except: pass
+
+def outputConfigOptions():
+    _cpLogMessage = _cputil.getSpecialFunction('_cpLogMessage')
+    _cpLogMessage("Server parameters:", 'CONFIG')
+    _cpLogMessage("  logToScreen: %s" % cpg.configOption.logToScreen, 'CONFIG')
+    _cpLogMessage("  logFile: %s" % cpg.configOption.logFile, 'CONFIG')
+    _cpLogMessage("  protocolVersion: %s" % cpg.configOption.protocolVersion, 'CONFIG')
+    _cpLogMessage("  socketHost: %s" % cpg.configOption.socketHost, 'CONFIG')
+    _cpLogMessage("  socketPort: %s" % cpg.configOption.socketPort, 'CONFIG')
+    _cpLogMessage("  socketFile: %s" % cpg.configOption.socketFile, 'CONFIG')
+    _cpLogMessage("  reverseDNS: %s" % cpg.configOption.reverseDNS, 'CONFIG')
+    _cpLogMessage("  socketQueueSize: %s" % cpg.configOption.socketQueueSize, 'CONFIG')
+    _cpLogMessage("  processPool: %s" % cpg.configOption.processPool, 'CONFIG')
+    _cpLogMessage("  threadPool: %s" % cpg.configOption.threadPool, 'CONFIG')
+    _cpLogMessage("  threading: %s" % cpg.configOption.threading, 'CONFIG')
+    _cpLogMessage("  forking: %s" % cpg.configOption.forking, 'CONFIG')
+    _cpLogMessage("  sslKeyFile: %s" % cpg.configOption.sslKeyFile, 'CONFIG')
+    if cpg.configOption.sslKeyFile:
+        _cpLogMessage("  sslCertificateFile: %s" % cpg.configOption.sslCertificateFile, 'CONFIG')
+        _cpLogMessage("  sslClientCertificateVerification: %s" % cpg.configOption.sslClientCertificateVerification, 'CONFIG')
+        _cpLogMessage("  sslCACertificateFile: %s" % cpg.configOption.sslCACertificateFile, 'CONFIG')
+        _cpLogMessage("  sslVerifyDepth: %s" % cpg.configOption.sslVerifyDepth, 'CONFIG')
+        _cpLogMessage("  flushCacheDelay: %s min" % cpg.configOption.flushCacheDelay, 'CONFIG')
+    _cpLogMessage("  sessionStorageType: %s" % cpg.configOption.sessionStorageType, 'CONFIG')
+    if cpg.configOption.sessionStorageType:
+        _cpLogMessage("  sessionTimeout: %s min" % cpg.configOption.sessionTimeout, 'CONFIG')
+        _cpLogMessage("  cleanUpDelay: %s min" % cpg.configOption.sessionCleanUpDelay, 'CONFIG')
+        _cpLogMessage("  sessionCookieName: %s" % cpg.configOption.sessionCookieName, 'CONFIG')
+        _cpLogMessage("  sessionStorageFileDir: %s" % cpg.configOption.sessionStorageFileDir, 'CONFIG')
+    _cpLogMessage("  staticContent: %s" % cpg.configOption.staticContentList, 'CONFIG')
+
+def dummy():
+    # Check that parameters are correct and that they don't conflict with each other
+    if _protocolVersion not in ("HTTP/1.1", "HTTP/1.0"):
+        raise "CherryError: protocolVersion must be 'HTTP/1.1' or 'HTTP/1.0'"
+    if _reverseDNS not in (0,1): raise "CherryError: reverseDNS must be '0' or '1'"
+    if _socketFile and not hasattr(socket, 'AF_UNIX'): raise "CherryError: Configuration file has socketFile, but this is only available on Unix machines"
+    if _processPool!=1 and not hasattr(os, 'fork'): raise "CherryError: Configuration file has processPool, but forking is not available on this operating system"
+    if _forking and not hasattr(os, 'fork'): raise "CherryError: Configuration file has forking, but forking is not available on this operating system"
+    if _sslKeyFile:
+        try:
+            global SSL
+            from OpenSSL import SSL
+        except: raise "CherryError: PyOpenSSL 0.5.1 or later must be installed to use SSL. You can get it from http://pyopenssl.sourceforge.net"
+    if _socketPort and _socketFile: raise "CherryError: In configuration file: socketPort and socketFile conflict with each other"
+    if not _socketFile and not _socketPort: _socketPort=8000 # Default port
+    if _processPool==1: severalProcs=0
+    else: severalProcs=1
+    if _threadPool==1: severalThreads=0
+    else: severalThreads=1
+    if severalThreads+severalProcs+_threading+_forking>1: raise "CherryError: In configuration file: threadPool, processPool, threading and forking conflict with each other"
+    if _sslKeyFile and not _sslCertificateFile: raise "CherryError: Configuration file has sslKeyFile but no sslCertificateFile"
+    if _sslCertificateFile and not _sslKeyFile: raise "CherryError: Configuration file has sslCertificateFile but no sslKeyFile"
+    try: sys.stdout.flush()
+    except: pass
+
+    if _sessionStorageType not in ('', 'custom', 'ram', 'file', 'cookie'): raise "CherryError: Configuration file an invalid sessionStorageType: '%s'"%_sessionStorageType
+    if _sessionStorageType in ('custom', 'ram', 'cookie') and _sessionStorageFileDir!='': raise "CherryError: Configuration file has sessionStorageType set to 'custom, 'ram' or 'cookie' but a sessionStorageFileDir is specified"
+    if _sessionStorageType=='file' and _sessionStorageFileDir=='': raise "CherryError: Configuration file has sessionStorageType set to 'file' but no sessionStorageFileDir"
+    if _sessionStorageType=='ram' and (_forking or severalProcs):
+        print "CherryWarning: 'ram' sessions might be buggy when using several processes"
+
-"""
-Copyright (c) 2004, CherryPy Team (team@cherrypy.org)
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, 
-are permitted provided that the following conditions are met:
-
-    * Redistributions of source code must retain the above copyright notice, 
-      this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above copyright notice, 
-      this list of conditions and the following disclaimer in the documentation 
-      and/or other materials provided with the distribution.
-    * Neither the name of the CherryPy Team nor the names of its contributors 
-      may be used to endorse or promote products derived from this software 
-      without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""
-
-
-"""
-A module containing a few utility classes/functions used by CherryPy
-"""
-
-import time, thread, os, pickle, cpg
-
-def _cpLogMessage(msg, context = '', severity = 0):
-    """ Default method for logging messages """
-
-    nowTuple = time.localtime(time.time())
-    nowStr = '%04d/%02d/%02d %02d:%02d:%02d' % (nowTuple[:6])
-    if severity == 0: level = "INFO"
-    elif severity == 1: level = "WARNING"
-    elif severity == 2: level = "ERROR"
-    try:
-        logToScreen = int(cpg.parsedConfigFile.get('server', 'logToScreen'))
-    except:
-        logToScreen = True
-    s = nowStr + ' ' + context + ' ' + level + ' ' + msg
-    if logToScreen:
-        print s
-    if cpg.configOption.logFile:
-        f = open(cpg.configOption.logFile, 'ab')
-        f.write(s + '\n')
-        f.close()
-
-def _cpOnError():
-    """ Default _cpOnError method """
-
-    import traceback, StringIO
-    bodyFile = StringIO.StringIO()
-    traceback.print_exc(file = bodyFile)
-    cpg.response.body = bodyFile.getvalue()
-    cpg.response.headerMap['Content-Type'] = 'text/plain'
-
-def _cpSaveSessionData(sessionId, sessionData, expirationTime,
-        threadPool = None, sessionStorageType = None,
-        sessionStorageFileDir = None):
-    """ Save session data if needed """
-
-    if threadPool is None:
-        threadPool = cpg.configOption.threadPool
-    if sessionStorageType is None:
-        sessionStorageType =  cpg.configOption.sessionStorageType
-    if sessionStorageFileDir is None:
-        sessionStorageFileDir = cpg.configOption.sessionStorageFileDir
-
-    t = time.localtime(expirationTime)
-    if sessionStorageType == 'file':
-        fname=os.path.join(sessionStorageFileDir,sessionId)
-        if threadPool > 1:
-            cpg._sessionFileLock.acquire()
-        f = open(fname,"wb")
-        pickle.dump((sessionData, expirationTime), f)
-        f.close()
-        if threadPool > 1:
-            cpg._sessionFileLock.release()
-
-    elif sessionStorageType=="ram":
-        # Update expiration time
-        cpg._sessionMap[sessionId] = (sessionData, expirationTime)
-
-    """ TODO: implement cookie storage type
-    elif sessionStorageType == "cookie":
-        
-         TODO: set siteKey in _cpConfig
-            # Get site key from config file or compute it
-            try: cpg._SITE_KEY_ = configFile.get('server','siteKey')
-            except:
-                _SITE_KEY_ = ''
-                for i in range(30):
-                    _SITE_KEY_ += random.choice(string.letters)
-
-        # Update expiration time
-        sessionData = (sessionData, expirationTime)
-        dumpStr = pickle.dumps(_sessionData)
-        try: dumpStr = zlib.compress(dumpStr)
-        except: pass # zlib is not available in all python distros
-        dumpStr = binascii.hexlify(dumpStr) # Need to hexlify it because it will be stored in a cookie
-        cpg.response.simpleCookie['CSession'] = dumpStr
-        cpg.response.simpleCookie['CSession-sig'] = md5.md5(dumpStr + cpg.configOption.siteKey).hexdigest()
-        cpg.response.simpleCookie['CSession']['path'] = '/'
-        cpg.response.simpleCookie['CSession']['max-age'] = sessionTimeout * 60
-        cpg.response.simpleCookie['CSession-sig']['path'] = '/'
-        cpg.response.simpleCookie['CSession-sig']['max-age'] = sessionTimeout * 60
-    """
-
-def _cpLoadSessionData(sessionId, threadPool = None, sessionStorageType = None,
-        sessionStorageFileDir = None):
-    """ Return the session data for a given sessionId.
-        The _expirationTime will be checked by the caller of this function
-    """
-
-    if threadPool is None:
-        threadPool = cpg.configOption.threadPool
-    if sessionStorageType is None:
-        sessionStorageType =  cpg.configOption.sessionStorageType
-    if sessionStorageFileDir is None:
-        sessionStorageFileDir = cpg.configOption.sessionStorageFileDir
-
-    if sessionStorageType == "ram":
-        if cpg._sessionMap.has_key(sessionId):
-            return cpg._sessionMap[sessionId]
-        else: return None
-
-    elif sessionStorageType == "file":
-        fname = os.path.join(sessionStorageFileDir, sessionId)
-        if os.path.exists(fname):
-            if threadPool > 1:
-                cpg._sessionFileLock.acquire()
-            f = open(fname, "rb")
-            sessionData = pickle.load(f)
-            f.close()
-            if threadPool > 1:
-                cpg._sessionFileLock.release()
-            return sessionData
-        else: return None
-
-    """ TODO: implement cookie storage type
-    elif _sessionStorageType == "cookie":
-        if request.simpleCookie.has_key('CSession') and request.simpleCookie.has_key('CSession-sig'):
-            data = request.simpleCookie['CSession'].value
-            sig  = request.simpleCookie['CSession-sig'].value
-            if md5.md5(data + cpg.configOption.siteKey).hexdigest() == sig:
-                try:
-                    dumpStr = binascii.unhexlify(data)
-                    try: dumpStr = zlib.decompress(dumpStr)
-                    except: pass # zlib is not available in all python distros
-                    dumpStr = pickle.loads(dumpStr)
-                    return dumpStr
-                except: pass
-        return None
-    """
-
-def _cpCleanUpOldSessions(threadPool = None, sessionStorageType = None,
-        sessionStorageFileDir = None):
-    """ Clean up old sessions """
-
-    if threadPool is None:
-        threadPool = cpg.configOption.threadPool
-    if sessionStorageType is None:
-        sessionStorageType =  cpg.configOption.sessionStorageType
-    if sessionStorageFileDir is None:
-        sessionStorageFileDir = cpg.configOption.sessionStorageFileDir
-
-    # Clean up old session data
-    now = time.time()
-    if sessionStorageType == "ram":
-        sessionIdToDeleteList = []
-        for sessionId, (dummy, expirationTime) in cpg._sessionMap.items():
-            if expirationTime < now:
-                sessionIdToDeleteList.append(sessionId)
-        for sessionId in sessionIdToDeleteList:
-            del cpg._sessionMap[sessionId]
-
-    elif sessionStorageType=="file":
-        # This process is very expensive because we go through all files, parse them and then delete them if the session is expired
-        # One optimization would be to just store a list of (sessionId, expirationTime) in *one* file
-        sessionFileList = os.listdir(sessionStorageFileDir)
-        for sessionId in sessionFileList:
-            try:
-                dummy, expirationTime = _cpLoadSessionData(sessionId)
-                if expirationTime < now:
-                    os.remove(os.path.join(sessionStorageFileDir, sessionId))
-            except:
-                pass
-
-    elif sessionStorageType == "cookie":
-        # Nothing to do in this case: the session data is stored on the client
-        pass
-
-_cpFilterList = []
+"""
+Copyright (c) 2004, CherryPy Team (team@cherrypy.org)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, 
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, 
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice, 
+      this list of conditions and the following disclaimer in the documentation 
+      and/or other materials provided with the distribution.
+    * Neither the name of the CherryPy Team nor the names of its contributors 
+      may be used to endorse or promote products derived from this software 
+      without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+
+"""
+A module containing a few utility classes/functions used by CherryPy
+"""
+
+import time, thread, os, pickle, cpg
+
+def _cpLogMessage(msg, context = '', severity = 0):
+    """ Default method for logging messages """
+
+    nowTuple = time.localtime(time.time())
+    nowStr = '%04d/%02d/%02d %02d:%02d:%02d' % (nowTuple[:6])
+    if severity == 0: level = "INFO"
+    elif severity == 1: level = "WARNING"
+    elif severity == 2: level = "ERROR"
+    try:
+        logToScreen = int(cpg.parsedConfigFile.get('server', 'logToScreen'))
+    except:
+        logToScreen = True
+    s = nowStr + ' ' + context + ' ' + level + ' ' + msg
+    if logToScreen:
+        print s
+    if cpg.configOption.logFile:
+        f = open(cpg.configOption.logFile, 'ab')
+        f.write(s + '\n')
+        f.close()
+
+def _cpOnError():
+    """ Default _cpOnError method """
+
+    import traceback, StringIO
+    bodyFile = StringIO.StringIO()
+    traceback.print_exc(file = bodyFile)
+    cpg.response.body = bodyFile.getvalue()
+    cpg.response.headerMap['Content-Type'] = 'text/plain'
+
+def _cpSaveSessionData(sessionId, sessionData, expirationTime,
+        threadPool = None, sessionStorageType = None,
+        sessionStorageFileDir = None):
+    """ Save session data if needed """
+
+    if threadPool is None:
+        threadPool = cpg.configOption.threadPool
+    if sessionStorageType is None:
+        sessionStorageType =  cpg.configOption.sessionStorageType
+    if sessionStorageFileDir is None:
+        sessionStorageFileDir = cpg.configOption.sessionStorageFileDir
+
+    t = time.localtime(expirationTime)
+    if sessionStorageType == 'file':
+        fname=os.path.join(sessionStorageFileDir,sessionId)
+        if threadPool > 1:
+            cpg._sessionFileLock.acquire()
+        f = open(fname,"wb")
+        pickle.dump((sessionData, expirationTime), f)
+        f.close()
+        if threadPool > 1:
+            cpg._sessionFileLock.release()
+
+    elif sessionStorageType=="ram":
+        # Update expiration time
+        cpg._sessionMap[sessionId] = (sessionData, expirationTime)
+
+    """ TODO: implement cookie storage type
+    elif sessionStorageType == "cookie":
+        
+         TODO: set siteKey in _cpConfig
+            # Get site key from config file or compute it
+            try: cpg._SITE_KEY_ = configFile.get('server','siteKey')
+            except:
+                _SITE_KEY_ = ''
+                for i in range(30):
+                    _SITE_KEY_ += random.choice(string.letters)
+
+        # Update expiration time
+        sessionData = (sessionData, expirationTime)
+        dumpStr = pickle.dumps(_sessionData)
+        try: dumpStr = zlib.compress(dumpStr)
+        except: pass # zlib is not available in all python distros
+        dumpStr = binascii.hexlify(dumpStr) # Need to hexlify it because it will be stored in a cookie
+        cpg.response.simpleCookie['CSession'] = dumpStr
+        cpg.response.simpleCookie['CSession-sig'] = md5.md5(dumpStr + cpg.configOption.siteKey).hexdigest()
+        cpg.response.simpleCookie['CSession']['path'] = '/'
+        cpg.response.simpleCookie['CSession']['max-age'] = sessionTimeout * 60
+        cpg.response.simpleCookie['CSession-sig']['path'] = '/'
+        cpg.response.simpleCookie['CSession-sig']['max-age'] = sessionTimeout * 60
+    """
+
+def _cpLoadSessionData(sessionId, threadPool = None, sessionStorageType = None,
+        sessionStorageFileDir = None):
+    """ Return the session data for a given sessionId.
+        The _expirationTime will be checked by the caller of this function
+    """
+
+    if threadPool is None:
+        threadPool = cpg.configOption.threadPool
+    if sessionStorageType is None:
+        sessionStorageType =  cpg.configOption.sessionStorageType
+    if sessionStorageFileDir is None:
+        sessionStorageFileDir = cpg.configOption.sessionStorageFileDir
+
+    if sessionStorageType == "ram":
+        if cpg._sessionMap.has_key(sessionId):
+            return cpg._sessionMap[sessionId]
+        else: return None
+
+    elif sessionStorageType == "file":
+        fname = os.path.join(sessionStorageFileDir, sessionId)
+        if os.path.exists(fname):
+            if threadPool > 1:
+                cpg._sessionFileLock.acquire()
+            f = open(fname, "rb")
+            sessionData = pickle.load(f)
+            f.close()
+            if threadPool > 1:
+                cpg._sessionFileLock.release()
+            return sessionData
+        else: return None
+
+    """ TODO: implement cookie storage type
+    elif _sessionStorageType == "cookie":
+        if request.simpleCookie.has_key('CSession') and request.simpleCookie.has_key('CSession-sig'):
+            data = request.simpleCookie['CSession'].value
+            sig  = request.simpleCookie['CSession-sig'].value
+            if md5.md5(data + cpg.configOption.siteKey).hexdigest() == sig:
+                try:
+                    dumpStr = binascii.unhexlify(data)
+                    try: dumpStr = zlib.decompress(dumpStr)
+                    except: pass # zlib is not available in all python distros
+                    dumpStr = pickle.loads(dumpStr)
+                    return dumpStr
+                except: pass
+        return None
+    """
+
+def _cpCleanUpOldSessions(threadPool = None, sessionStorageType = None,
+        sessionStorageFileDir = None):
+    """ Clean up old sessions """
+
+    if threadPool is None:
+        threadPool = cpg.configOption.threadPool
+    if sessionStorageType is None:
+        sessionStorageType =  cpg.configOption.sessionStorageType
+    if sessionStorageFileDir is None:
+        sessionStorageFileDir = cpg.configOption.sessionStorageFileDir
+
+    # Clean up old session data
+    now = time.time()
+    if sessionStorageType == "ram":
+        sessionIdToDeleteList = []
+        for sessionId, (dummy, expirationTime) in cpg._sessionMap.items():
+            if expirationTime < now:
+                sessionIdToDeleteList.append(sessionId)
+        for sessionId in sessionIdToDeleteList:
+            del cpg._sessionMap[sessionId]
+
+    elif sessionStorageType=="file":
+        # This process is very expensive because we go through all files, parse them and then delete them if the session is expired
+        # One optimization would be to just store a list of (sessionId, expirationTime) in *one* file
+        sessionFileList = os.listdir(sessionStorageFileDir)
+        for sessionId in sessionFileList:
+            try:
+                dummy, expirationTime = _cpLoadSessionData(sessionId)
+                if expirationTime < now:
+                    os.remove(os.path.join(sessionStorageFileDir, sessionId))
+            except:
+                pass
+
+    elif sessionStorageType == "cookie":
+        # Nothing to do in this case: the session data is stored on the client
+        pass
+
+_cpFilterList = []
-"""
-Copyright (c) 2004, CherryPy Team (team@cherrypy.org)
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, 
-are permitted provided that the following conditions are met:
-
-    * Redistributions of source code must retain the above copyright notice, 
-      this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above copyright notice, 
-      this list of conditions and the following disclaimer in the documentation 
-      and/or other materials provided with the distribution.
-    * Neither the name of the CherryPy Team nor the names of its contributors 
-      may be used to endorse or promote products derived from this software 
-      without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""
-
-import cpg, sys, threading, SocketServer, _cphttptools
-import BaseHTTPServer, socket, Queue, _cputil
-
-def stop():
-    cpg._server.shutdown()
-
-def start():
-    """ Prepare the HTTP server and then run it """
-
-    # If sessions are stored in files and we
-    # use threading, we need a lock on the file
-    if (cpg.configOption.threadPool > 1) and \
-            cpg.configOption.sessionStorageType == 'file':
-        cpg._sessionFileLock = threading.RLock()
-
-
-    if cpg.configOption.socketFile:
-        # AF_UNIX socket
-        # TODO: Handle threading here
-        class MyCherryHTTPServer(CherryHTTPServer): address_family = socket.AF_UNIX
-    else:
-        # AF_INET socket
-        if cpg.configOption.threadPool > 1:
-            MyCherryHTTPServer = PooledThreadServer
-        else:
-            MyCherryHTTPServer = CherryHTTPServer
-
-    MyCherryHTTPServer.request_queue_size = cpg.configOption.socketQueueSize
-
-    # Set protocol_version
-    CherryHTTPRequestHandler.protocol_version = cpg.configOption.protocolVersion
-
-    run_server(CherryHTTPRequestHandler, MyCherryHTTPServer, \
-        (cpg.configOption.socketHost, cpg.configOption.socketPort), \
-        cpg.configOption.socketFile)
-
-def run_server(HandlerClass, ServerClass, server_address, socketFile):
-    """Run the HTTP request handler class."""
-
-    if cpg.configOption.socketFile:
-        try: os.unlink(cpg.configOption.socketFile) # So we can reuse the socket
-        except: pass
-        server_address = cpg.configOption.socketFile
-    if cpg.configOption.threadPool > 1:
-        myCherryHTTPServer = ServerClass(server_address, cpg.configOption.threadPool, HandlerClass)
-    else:
-        myCherryHTTPServer = ServerClass(server_address, HandlerClass)
-    cpg._server = myCherryHTTPServer
-    if cpg.configOption.socketFile:
-        try: os.chmod(socketFile, 0777) # So everyone can access the socket
-        except: pass
-    global _cpLogMessage
-    _cpLogMessage = _cputil.getSpecialFunction('_cpLogMessage')
-
-    servingWhat = "HTTP"
-    if cpg.configOption.socketPort: onWhat = "socket: ('%s', %s)" % (cpg.configOption.socketHost, cpg.configOption.socketPort)
-    else: onWhat = "socket file: %s" % cpg.configOption.socketFile
-    _cpLogMessage("Serving %s on %s" % (servingWhat, onWhat), 'HTTP')
-
-    try:
-        # Call the functions from cpg.server.onStartServerList
-        for func in cpg.server.onStartServerList:
-            func()
-        myCherryHTTPServer.serve_forever()
-    except KeyboardInterrupt:
-        _cpLogMessage("<Ctrl-C> hit: shutting down", "HTTP")
-        myCherryHTTPServer.server_close()
-    # Call the functions from cpg.server.onStartServerList
-    for func in cpg.server.onStopServerList:
-        func()
-
-class CherryHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
-
-    """CherryPy HTTP request handler with the following commands:
-
-        o  GET
-        o  HEAD
-        o  POST
-        o  HOTRELOAD
-
-    """
-
-    def address_string(self):
-        """ Try to do a reverse DNS based on [server]reverseDNS in the config file """
-        if cpg.configOption.reverseDNS:
-            return BaseHTTPServer.BaseHTTPRequestHandler.address_string(self)
-        else:
-            return self.client_address[0]
-
-    def do_GET(self):
-        """Serve a GET request."""
-        cpg.request.method = 'GET'
-        _cphttptools.doRequest(
-            self.client_address[0],
-            self.address_string(),
-            self.raw_requestline,
-            self.headers,
-            self.rfile,
-            self.wfile
-        )
-
-    def do_HEAD(self): # Head is not implemented
-        """Serve a HEAD request."""
-        cpg.request.method = 'HEAD'
-        _cphttptools.doRequest(
-            self.client_address[0],
-            self.address_string(),
-            self.raw_requestline,
-            self.headers,
-            self.rfile,
-            self.wfile
-        )
-
-    def do_POST(self):
-        """Serve a POST request."""
-        cpg.request.method = 'POST'
-        _cphttptools.doRequest(
-            self.client_address[0],
-            self.address_string(),
-            self.raw_requestline,
-            self.headers,
-            self.rfile,
-            self.wfile
-        )
-
-        self.connection = self.request
-
-    def log_message(self, format, *args):
-        """ We have to override this to use our own logging mechanism """
-        _cputil.getSpecialFunction('_cpLogMessage')(format % args, "HTTP")
-
-
-class CherryHTTPServer(BaseHTTPServer.HTTPServer):
-    def server_activate(self):
-        """Override server_activate to set timeout on our listener socket"""
-        self.socket.settimeout(1)
-        BaseHTTPServer.HTTPServer.server_activate(self)
-
-    def server_bind(self):
-        # Removed getfqdn call because it was timing out on localhost when calling gethostbyaddr
-        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-        self.socket.bind(self.server_address)
-
-    def get_request(self):
-        # With Python 2.3 it seems that an accept socket in timeout (nonblocking) mode
-        #  results in request sockets that are also set in nonblocking mode. Since that doesn't play
-        #  well with makefile() (where wfile and rfile are set in SocketServer.py) we explicitly set
-        #  the request socket to blocking
-
-        request, client_address = self.socket.accept()
-        request.setblocking(1)
-        return request, client_address
-
-    def handle_request(self):
-        """Override handle_request to trap timeout exception."""
-        try:
-            BaseHTTPServer.HTTPServer.handle_request(self)
-        except socket.timeout:
-            # The only reason for the timeout is so we can notice keyboard
-            # interrupts on Win32, which don't interrupt accept() by default
-            return 1
-        except KeyboardInterrupt:
-            _cpLogMessage("<Ctrl-C> hit: shutting down", "HTTP")
-            self.shutdown()
-
-    def serve_forever(self):
-        """Override serve_forever to handle shutdown."""
-        self.__running = 1
-        while self.__running:
-            self.handle_request()
-
-    def shutdown(self):
-        self.__running = 0
-
-_SHUTDOWNREQUEST = (0,0)
-
-class ServerThread(threading.Thread):
-    def __init__(self, RequestHandlerClass, requestQueue, threadIndex):
-        threading.Thread.__init__(self)
-        self._RequestHandlerClass = RequestHandlerClass
-        self._requestQueue = requestQueue
-        self._threadIndex = threadIndex
-        
-    def run(self):
-        # Call the functions from cpg.server.onStartThreadList
-        for func in cpg.server.onStartThreadList:
-            func()
-        while 1:
-            request, client_address = self._requestQueue.get()
-            if (request, client_address) == _SHUTDOWNREQUEST:
-                # Call the functions from cpg.server.onStopThreadList
-                for func in cpg.server.onStopThreadList:
-                    func()
-                return
-            if self.verify_request(request, client_address):            
-                try:
-                    self.process_request(request, client_address)
-                except:
-                    self.handle_error(request, client_address)
-                    self.close_request(request)
-            else:
-                self.close_request(request)
-
-    def verify_request(self, request, client_address):
-        """ Verify the request.  May be overridden.
-            Return 1 if we should proceed with this request. """
-        return 1
-
-    def process_request(self, request, client_address):
-        self._RequestHandlerClass(request, client_address, self)        
-        self.close_request(request)
-
-    def close_request(self, request):
-        """ Called to clean up an individual request. """
-        request.close()
-
-    def handle_error(self, request, client_address):
-        """ Handle an error gracefully.  May be overridden.
-            The default is to print a traceback and continue.
-        """
-        import traceback, StringIO
-        bodyFile=StringIO.StringIO()
-        traceback.print_exc(file=bodyFile)
-        errorBody=bodyFile.getvalue()
-        bodyFile.close()
-        _cputil.getSpecialFunction('_cpLogMessage')(errorBody)
-        
-
-class PooledThreadServer(SocketServer.TCPServer):
-
-    allow_reuse_address = 1
-
-    """A TCP Server using a pool of worker threads. This is superior to the
-       alternatives provided by the Python standard library, which only offer
-       (1) handling a single request at a time, (2) handling each request in
-       a separate thread (via ThreadingMixIn), or (3) handling each request in
-       a separate process (via ForkingMixIn). It's also superior in some ways
-       to the pure async approach used by Twisted because it allows a more
-       straightforward and simple programming model in the face of blocking
-       requests (i.e. you don't have to bother with Deferreds).""" 
-    def __init__(self, serverAddress, numThreads, RequestHandlerClass, ThreadClass=ServerThread):
-        assert(numThreads > 0)
-        # I know it says "do not override", but I have to in order to implement SSL support !
-        SocketServer.BaseServer.__init__(self, serverAddress, RequestHandlerClass)
-        self.socket=socket.socket(self.address_family, self.socket_type)
-        self.server_bind()
-        self.server_activate()
-
-        self._numThreads = numThreads        
-        self._RequestHandlerClass = RequestHandlerClass
-        self._ThreadClass = ThreadClass
-        self._requestQueue = Queue.Queue()
-        self._workerThreads = []
-
-    def createThread(self, threadIndex):
-        return self._ThreadClass(self._RequestHandlerClass, self._requestQueue, threadIndex)
-            
-    def start(self):
-        if self._workerThreads != []:
-            return
-        for i in xrange(self._numThreads):
-            self._workerThreads.append(self.createThread(i))        
-        for worker in self._workerThreads:
-            worker.start()
-            
-    def server_close(self):
-        """Override server_close to shutdown thread pool"""
-        SocketServer.TCPServer.server_close(self)
-        for worker in self._workerThreads:
-            self._requestQueue.put(_SHUTDOWNREQUEST)
-        for worker in self._workerThreads:
-            worker.join()
-        self._workerThreads = []
-
-    def server_activate(self):
-        """Override server_activate to set timeout on our listener socket"""
-        self.socket.settimeout(1)
-        SocketServer.TCPServer.server_activate(self)
-
-    def server_bind(self):
-        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-        self.socket.bind(self.server_address)
-
-    def shutdown(self):
-        """Gracefully shutdown a server that is serve_forever()ing."""
-        self.__running = 0
-
-    def serve_forever(self):
-        """Handle one request at a time until doomsday (or shutdown is called)."""
-        if self._workerThreads == []:
-            self.start()
-        self.__running = 1
-        while self.__running:
-            if not self.handle_request():
-                break
-        self.server_close()            
-        
-    def handle_request(self):
-        """Override handle_request to enqueue requests rather than handle
-           them synchronously. Return 1 by default, 0 to shutdown the
-           server."""
-        try:
-            request, client_address = self.get_request()
-        except KeyboardInterrupt:
-            _cpLogMessage("<Ctrl-C> hit: shutting down", "HTTP")
-            return 0
-        except socket.error, e:
-            return 1
-        self._requestQueue.put((request, client_address))
-        return 1
-
-    def get_request(self):
-        # With Python 2.3 it seems that an accept socket in timeout (nonblocking) mode
-        #  results in request sockets that are also set in nonblocking mode. Since that doesn't play
-        #  well with makefile() (where wfile and rfile are set in SocketServer.py) we explicitly set
-        #  the request socket to blocking
-
-        request, client_address = self.socket.accept()
-        if hasattr(request,'setblocking'):
-            request.setblocking(1)
-        return request, client_address
-
+"""
+Copyright (c) 2004, CherryPy Team (team@cherrypy.org)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, 
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, 
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice, 
+      this list of conditions and the following disclaimer in the documentation 
+      and/or other materials provided with the distribution.
+    * Neither the name of the CherryPy Team nor the names of its contributors 
+      may be used to endorse or promote products derived from this software 
+      without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import cpg, sys, threading, SocketServer, _cphttptools
+import BaseHTTPServer, socket, Queue, _cputil
+
+def stop():
+    cpg._server.shutdown()
+
+def start():
+    """ Prepare the HTTP server and then run it """
+
+    # If sessions are stored in files and we
+    # use threading, we need a lock on the file
+    if (cpg.configOption.threadPool > 1) and \
+            cpg.configOption.sessionStorageType == 'file':
+        cpg._sessionFileLock = threading.RLock()
+
+
+    if cpg.configOption.socketFile:
+        # AF_UNIX socket
+        # TODO: Handle threading here
+        class MyCherryHTTPServer(CherryHTTPServer): address_family = socket.AF_UNIX
+    else:
+        # AF_INET socket
+        if cpg.configOption.threadPool > 1:
+            MyCherryHTTPServer = PooledThreadServer
+        else:
+            MyCherryHTTPServer = CherryHTTPServer
+
+    MyCherryHTTPServer.request_queue_size = cpg.configOption.socketQueueSize
+
+    # Set protocol_version
+    CherryHTTPRequestHandler.protocol_version = cpg.configOption.protocolVersion
+
+    run_server(CherryHTTPRequestHandler, MyCherryHTTPServer, \
+        (cpg.configOption.socketHost, cpg.configOption.socketPort), \
+        cpg.configOption.socketFile)
+
+def run_server(HandlerClass, ServerClass, server_address, socketFile):
+    """Run the HTTP request handler class."""
+
+    if cpg.configOption.socketFile:
+        try: os.unlink(cpg.configOption.socketFile) # So we can reuse the socket
+        except: pass
+        server_address = cpg.configOption.socketFile
+    if cpg.configOption.threadPool > 1:
+        myCherryHTTPServer = ServerClass(server_address, cpg.configOption.threadPool, HandlerClass)
+    else:
+        myCherryHTTPServer = ServerClass(server_address, HandlerClass)
+    cpg._server = myCherryHTTPServer
+    if cpg.configOption.socketFile:
+        try: os.chmod(socketFile, 0777) # So everyone can access the socket
+        except: pass
+    global _cpLogMessage
+    _cpLogMessage = _cputil.getSpecialFunction('_cpLogMessage')
+
+    servingWhat = "HTTP"
+    if cpg.configOption.socketPort: onWhat = "socket: ('%s', %s)" % (cpg.configOption.socketHost, cpg.configOption.socketPort)
+    else: onWhat = "socket file: %s" % cpg.configOption.socketFile
+    _cpLogMessage("Serving %s on %s" % (servingWhat, onWhat), 'HTTP')
+
+    try:
+        # Call the functions from cpg.server.onStartServerList
+        for func in cpg.server.onStartServerList:
+            func()
+        myCherryHTTPServer.serve_forever()
+    except KeyboardInterrupt:
+        _cpLogMessage("<Ctrl-C> hit: shutting down", "HTTP")
+        myCherryHTTPServer.server_close()
+    # Call the functions from cpg.server.onStartServerList
+    for func in cpg.server.onStopServerList:
+        func()
+
+class CherryHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+
+    """CherryPy HTTP request handler with the following commands:
+
+        o  GET
+        o  HEAD
+        o  POST
+        o  HOTRELOAD
+
+    """
+
+    def address_string(self):
+        """ Try to do a reverse DNS based on [server]reverseDNS in the config file """
+        if cpg.configOption.reverseDNS:
+            return BaseHTTPServer.BaseHTTPRequestHandler.address_string(self)
+        else:
+            return self.client_address[0]
+
+    def do_GET(self):
+        """Serve a GET request."""
+        cpg.request.method = 'GET'
+        _cphttptools.doRequest(
+            self.client_address[0],
+            self.address_string(),
+            self.raw_requestline,
+            self.headers,
+            self.rfile,
+            self.wfile
+        )
+
+    def do_HEAD(self): # Head is not implemented
+        """Serve a HEAD request."""
+        cpg.request.method = 'HEAD'
+        _cphttptools.doRequest(
+            self.client_address[0],
+            self.address_string(),
+            self.raw_requestline,
+            self.headers,
+            self.rfile,
+            self.wfile
+        )
+
+    def do_POST(self):
+        """Serve a POST request."""
+        cpg.request.method = 'POST'
+        _cphttptools.doRequest(
+            self.client_address[0],
+            self.address_string(),
+            self.raw_requestline,
+            self.headers,
+            self.rfile,
+            self.wfile
+        )
+
+        self.connection = self.request
+
+    def log_message(self, format, *args):
+        """ We have to override this to use our own logging mechanism """
+        _cputil.getSpecialFunction('_cpLogMessage')(format % args, "HTTP")
+
+
+class CherryHTTPServer(BaseHTTPServer.HTTPServer):
+    def server_activate(self):
+        """Override server_activate to set timeout on our listener socket"""
+        self.socket.settimeout(1)
+        BaseHTTPServer.HTTPServer.server_activate(self)
+
+    def server_bind(self):
+        # Removed getfqdn call because it was timing out on localhost when calling gethostbyaddr
+        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self.socket.bind(self.server_address)
+
+    def get_request(self):
+        # With Python 2.3 it seems that an accept socket in timeout (nonblocking) mode
+        #  results in request sockets that are also set in nonblocking mode. Since that doesn't play
+        #  well with makefile() (where wfile and rfile are set in SocketServer.py) we explicitly set
+        #  the request socket to blocking
+
+        request, client_address = self.socket.accept()
+        request.setblocking(1)
+        return request, client_address
+
+    def handle_request(self):
+        """Override handle_request to trap timeout exception."""
+        try:
+            BaseHTTPServer.HTTPServer.handle_request(self)
+        except socket.timeout:
+            # The only reason for the timeout is so we can notice keyboard
+            # interrupts on Win32, which don't interrupt accept() by default
+            return 1
+        except KeyboardInterrupt:
+            _cpLogMessage("<Ctrl-C> hit: shutting down", "HTTP")
+            self.shutdown()
+
+    def serve_forever(self):
+        """Override serve_forever to handle shutdown."""
+        self.__running = 1
+        while self.__running:
+            self.handle_request()
+
+    def shutdown(self):
+        self.__running = 0
+
+_SHUTDOWNREQUEST = (0,0)
+
+class ServerThread(threading.Thread):
+    def __init__(self, RequestHandlerClass, requestQueue, threadIndex):
+        threading.Thread.__init__(self)
+        self._RequestHandlerClass = RequestHandlerClass
+        self._requestQueue = requestQueue
+        self._threadIndex = threadIndex
+        
+    def run(self):
+        # Call the functions from cpg.server.onStartThreadList
+        for func in cpg.server.onStartThreadList:
+            func()
+        while 1:
+            request, client_address = self._requestQueue.get()
+            if (request, client_address) == _SHUTDOWNREQUEST:
+                # Call the functions from cpg.server.onStopThreadList
+                for func in cpg.server.onStopThreadList:
+                    func()
+                return
+            if self.verify_request(request, client_address):            
+                try:
+                    self.process_request(request, client_address)
+                except:
+                    self.handle_error(request, client_address)
+                    self.close_request(request)
+            else:
+                self.close_request(request)
+
+    def verify_request(self, request, client_address):
+        """ Verify the request.  May be overridden.
+            Return 1 if we should proceed with this request. """
+        return 1
+
+    def process_request(self, request, client_address):
+        self._RequestHandlerClass(request, client_address, self)        
+        self.close_request(request)
+
+    def close_request(self, request):
+        """ Called to clean up an individual request. """
+        request.close()
+
+    def handle_error(self, request, client_address):
+        """ Handle an error gracefully.  May be overridden.
+            The default is to print a traceback and continue.
+        """
+        import traceback, StringIO
+        bodyFile=StringIO.StringIO()
+        traceback.print_exc(file=bodyFile)
+        errorBody=bodyFile.getvalue()
+        bodyFile.close()
+        _cputil.getSpecialFunction('_cpLogMessage')(errorBody)
+        
+
+class PooledThreadServer(SocketServer.TCPServer):
+
+    allow_reuse_address = 1
+
+    """A TCP Server using a pool of worker threads. This is superior to the
+       alternatives provided by the Python standard library, which only offer
+       (1) handling a single request at a time, (2) handling each request in
+       a separate thread (via ThreadingMixIn), or (3) handling each request in
+       a separate process (via ForkingMixIn). It's also superior in some ways
+       to the pure async approach used by Twisted because it allows a more
+       straightforward and simple programming model in the face of blocking
+       requests (i.e. you don't have to bother with Deferreds).""" 
+    def __init__(self, serverAddress, numThreads, RequestHandlerClass, ThreadClass=ServerThread):
+        assert(numThreads > 0)
+        # I know it says "do not override", but I have to in order to implement SSL support !
+        SocketServer.BaseServer.__init__(self, serverAddress, RequestHandlerClass)
+        self.socket=socket.socket(self.address_family, self.socket_type)
+        self.server_bind()
+        self.server_activate()
+
+        self._numThreads = numThreads        
+        self._RequestHandlerClass = RequestHandlerClass
+        self._ThreadClass = ThreadClass
+        self._requestQueue = Queue.Queue()
+        self._workerThreads = []
+
+    def createThread(self, threadIndex):
+        return self._ThreadClass(self._RequestHandlerClass, self._requestQueue, threadIndex)
+            
+    def start(self):
+        if self._workerThreads != []:
+            return
+        for i in xrange(self._numThreads):
+            self._workerThreads.append(self.createThread(i))        
+        for worker in self._workerThreads:
+            worker.start()
+            
+    def server_close(self):
+        """Override server_close to shutdown thread pool"""
+        SocketServer.TCPServer.server_close(self)
+        for worker in self._workerThreads:
+            self._requestQueue.put(_SHUTDOWNREQUEST)
+        for worker in self._workerThreads:
+            worker.join()
+        self._workerThreads = []
+
+    def server_activate(self):
+        """Override server_activate to set timeout on our listener socket"""
+        self.socket.settimeout(1)
+        SocketServer.TCPServer.server_activate(self)
+
+    def server_bind(self):
+        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self.socket.bind(self.server_address)
+
+    def shutdown(self):
+        """Gracefully shutdown a server that is serve_forever()ing."""
+        self.__running = 0
+
+    def serve_forever(self):
+        """Handle one request at a time until doomsday (or shutdown is called)."""
+        if self._workerThreads == []:
+            self.start()
+        self.__running = 1
+        while self.__running:
+            if not self.handle_request():
+                break
+        self.server_close()            
+        
+    def handle_request(self):
+        """Override handle_request to enqueue requests rather than handle
+           them synchronously. Return 1 by default, 0 to shutdown the
+           server."""
+        try:
+            request, client_address = self.get_request()
+        except KeyboardInterrupt:
+            _cpLogMessage("<Ctrl-C> hit: shutting down", "HTTP")
+            return 0
+        except socket.error, e:
+            return 1
+        self._requestQueue.put((request, client_address))
+        return 1
+
+    def get_request(self):
+        # With Python 2.3 it seems that an accept socket in timeout (nonblocking) mode
+        #  results in request sockets that are also set in nonblocking mode. Since that doesn't play
+        #  well with makefile() (where wfile and rfile are set in SocketServer.py) we explicitly set
+        #  the request socket to blocking
+
+        request, client_address = self.socket.accept()
+        if hasattr(request,'setblocking'):
+            request.setblocking(1)
+        return request, client_address
+
-"""
-Copyright (c) 2004, CherryPy Team (team@cherrypy.org)
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, 
-are permitted provided that the following conditions are met:
-
-    * Redistributions of source code must retain the above copyright notice, 
-      this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above copyright notice, 
-      this list of conditions and the following disclaimer in the documentation 
-      and/or other materials provided with the distribution.
-    * Neither the name of the CherryPy Team nor the names of its contributors 
-      may be used to endorse or promote products derived from this software 
-      without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""
-
-import cpg, urllib, sys, time, traceback, types, StringIO, cgi, os
-import mimetypes, sha, random, string, _cputil, cperror, Cookie
-from lib.filter import basefilter
-
-"""
-Common Service Code for CherryPy
-"""
-
-mimetypes.types_map['.dwg']='image/x-dwg'
-mimetypes.types_map['.ico']='image/x-icon'
-
-weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
-monthname = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
-
-class IndexRedirect(Exception): pass
-
-def parseFirstLine(data):
-    cpg.request.path = data.split()[1]
-    cpg.request.queryString = ""
-    cpg.request.browserUrl = cpg.request.path
-    cpg.request.paramMap = {}
-    cpg.request.paramList = [] # Only used for Xml-Rpc
-    cpg.request.filenameMap = {}
-    cpg.request.fileTypeMap = {}
-    i = cpg.request.path.find('?')
-    if i != -1:
-        # Parse parameters from URL
-        if cpg.request.path[i+1:]:
-            k = cpg.request.path[i+1:].find('?')
-            if k != -1:
-                j = cpg.request.path[:k].rfind('=')
-                if j != -1:
-                    cpg.request.path = cpg.request.path[:j+1] + \
-                        urllib.quote_plus(cpg.request.path[j+1:])
-            for paramStr in cpg.request.path[i+1:].split('&'):
-                sp = paramStr.split('=')
-                if len(sp) > 2:
-                    j = paramStr.find('=')
-                    sp = (paramStr[:j], paramStr[j+1:])
-                if len(sp) == 2:
-                    key, value = sp
-                    value = urllib.unquote_plus(value)
-                    if cpg.request.paramMap.has_key(key):
-                        # Already has a value: make a list out of it
-                        if type(cpg.request.paramMap[key]) == type([]):
-                            # Already is a list: append the new value to it
-                            cpg.request.paramMap[key].append(value)
-                        else:
-                            # Only had one value so far: start a list
-                            cpg.request.paramMap[key] = [cpg.request.paramMap[key], value]
-                    else:
-                        cpg.request.paramMap[key] = value
-        cpg.request.queryString = cpg.request.path[i+1:]
-        cpg.request.path = cpg.request.path[:i]
-
-def cookHeaders(clientAddress, remoteHost, headers, requestLine):
-    """Process the headers into the request.headerMap"""
-    cpg.request.headerMap = {}
-    cpg.request.requestLine = requestLine
-    cpg.request.simpleCookie = Cookie.SimpleCookie()
-
-    # Build headerMap
-    for item in headers.items():
-        # Warning: if there is more than one header entry for cookies (AFAIK, only Konqueror does that)
-        # only the last one will remain in headerMap (but they will be correctly stored in request.simpleCookie)
-        insertIntoHeaderMap(item[0],item[1])
-
-    # Handle cookies differently because on Konqueror, multiple cookies come on different lines with the same key
-    cookieList = headers.getallmatchingheaders('cookie')
-    for cookie in cookieList:
-        cpg.request.simpleCookie.load(cookie)
-
-    cpg.request.remoteAddr = clientAddress
-    cpg.request.remoteHost = remoteHost
-
-    # Set peer_certificate (in SSL mode) so the web app can examinate the client certificate
-    try: cpg.request.peerCertificate = self.request.get_peer_certificate()
-    except: pass
-
-    _cputil.getSpecialFunction('_cpLogMessage')("%s - %s" % (cpg.request.remoteAddr, requestLine[:-2]), "HTTP")
-
-
-def parsePostData(rfile):
-    # Read request body and put it in data
-    len = int(cpg.request.headerMap.get("Content-Length","0"))
-    if len: data = rfile.read(len)
-    else: data=""
-
-    # Put data in a StringIO so FieldStorage can read it
-    newRfile = StringIO.StringIO(data)
-    # Create a copy of headerMap with lowercase keys because
-    #   FieldStorage doesn't work otherwise
-    lowerHeaderMap = {}
-    for key, value in cpg.request.headerMap.items():
-        lowerHeaderMap[key.lower()] = value
-    forms = cgi.FieldStorage(fp = newRfile, headers = lowerHeaderMap, environ = {'REQUEST_METHOD':'POST'}, keep_blank_values = 1)
-    for key in forms.keys():
-        # Check if it's a list or not
-        valueList = forms[key]
-        if type(valueList) == type([]):
-            # It's a list of values
-            cpg.request.paramMap[key] = []
-            cpg.request.filenameMap[key] = []
-            cpg.request.fileTypeMap[key] = []
-            for item in valueList:
-                cpg.request.paramMap[key].append(item.value)
-                cpg.request.filenameMap[key].append(item.filename)
-                cpg.request.fileTypeMap[key].append(item.type)
-        else:
-            # It's a single value
-            # In case it's a file being uploaded, we save the filename in a map (user might need it)
-            cpg.request.paramMap[key] = valueList.value
-            cpg.request.filenameMap[key] = valueList.filename
-            cpg.request.fileTypeMap[key] = valueList.type
-
-def applyFilterList(methodName):
-    try:
-        filterList = _cputil.getSpecialFunction('_cpFilterList')
-        for filter in filterList:
-            method = getattr(filter, methodName, None)
-            if method:
-                method()
-    except basefilter.InternalRedirect:
-        # If we get an InternalRedirect, we start the filter list  
-        #   from scratch. Is cpg.request.path or cpg.request.objectPath
-        #   has been modified by the hook, then a new filter list
-        #   will be applied.  
-        # We use recursion so if there is an infinite loop, we'll  
-        #   get the regular python "recursion limit exceeded" exception.  
-        applyFilterList(methodName) 
-
-
-def insertIntoHeaderMap(key,value):
-    normalizedKey = '-'.join([s.capitalize() for s in key.split('-')])
-    cpg.request.headerMap[normalizedKey] = value
-
-def initRequest(clientAddress, remoteHost, requestLine, headers, rfile, wfile):
-    parseFirstLine(requestLine)
-    cookHeaders(clientAddress, remoteHost, headers, requestLine)
-
-    cpg.request.base = "http://" + cpg.request.headerMap['Host']
-    cpg.request.browserUrl = cpg.request.base + cpg.request.browserUrl
-    cpg.request.isStatic = False
-    cpg.request.parsePostData = True
-    cpg.request.rfile = rfile
-
-    # Change objectPath in filters to change the object that will get rendered
-    cpg.request.objectPath = None 
-
-    applyFilterList('afterRequestHeader')
-
-    if cpg.request.method == 'POST' and cpg.request.parsePostData:
-        parsePostData(rfile)
-
-    applyFilterList('afterRequestBody')
-
-def doRequest(clientAddress, remoteHost, requestLine, headers, rfile, wfile):
-    initRequest(clientAddress, remoteHost, requestLine, headers, rfile, wfile)
-
-    # Prepare response variables
-    now = time.time()
-    year, month, day, hh, mm, ss, wd, y, z = time.gmtime(now)
-    date = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (weekdayname[wd], day, monthname[month], year, hh, mm, ss)
-    cpg.response.headerMap = {
-        "protocolVersion": cpg.configOption.protocolVersion,
-        "Status": "200 OK",
-        "Content-Type": "text/html",
-        "Server": "CherryPy/" + cpg.__version__,
-        "Date": date,
-        "Set-Cookie": [],
-        "Content-Length": 0
-    }
-    cpg.response.simpleCookie = Cookie.SimpleCookie()
-    cpg.response.wfile = wfile
-    cpg.response.sendResponse = 1
-
-    try:
-        handleRequest(wfile)
-    except:
-        err = ""
-        exc_info_1 = sys.exc_info()[1]
-        if hasattr(exc_info_1, 'args') and len(exc_info_1.args) >= 1:
-            err = exc_info_1.args[0]
-
-        try:
-            _cputil.getSpecialFunction('_cpOnError')()
-
-            # Still save session data
-            if cpg.configOption.sessionStorageType and not cpg.request.isStatic:
-                sessionId = cpg.response.simpleCookie[cpg.configOption.sessionCookieName].value
-                expirationTime = time.time() + cpg.configOption.sessionTimeout * 60
-                _cputil.getSpecialFunction('_cpSaveSessionData')(sessionId, cpg.request.sessionMap, expirationTime)
-
-            wfile.write('%s %s\r\n' % (cpg.response.headerMap['protocolVersion'], cpg.response.headerMap['Status']))
-
-            if (cpg.response.headerMap.has_key('Content-Length') and
-                    cpg.response.headerMap['Content-Length']==0):
-  	 	        buf = StringIO.StringIO()
-  	 	        [buf.write(x) for x in cpg.response.body]
-  	 	        buf.seek(0)
-  	 	        cpg.response.body = [buf.read()]
-  	 	        cpg.response.headerMap['Content-Length'] = len(cpg.response.body[0])
-
-            for key, valueList in cpg.response.headerMap.items():
-                if key not in ('Status', 'protocolVersion'):
-                    if type(valueList) != type([]): valueList = [valueList]
-                    for value in valueList:
-                        wfile.write('%s: %s\r\n'%(key, value))
-            wfile.write('\r\n')
-            for line in cpg.response.body:
-                wfile.write(line)
-        except:
-            bodyFile = StringIO.StringIO()
-            traceback.print_exc(file = bodyFile)
-            body = bodyFile.getvalue()
-            wfile.write('%s 200 OK\r\n' % cpg.configOption.protocolVersion)
-            wfile.write('Content-Type: text/plain\r\n')
-            wfile.write('Content-Length: %s\r\n' % len(body))
-            wfile.write('\r\n')
-            wfile.write(body)
-
-def sendResponse(wfile):
-    applyFilterList('beforeResponse')
-
-    # Save session data
-    if cpg.configOption.sessionStorageType and not cpg.request.isStatic:
-        sessionId = cpg.response.simpleCookie[cpg.configOption.sessionCookieName].value
-        expirationTime = time.time() + cpg.configOption.sessionTimeout * 60
-        _cputil.getSpecialFunction('_cpSaveSessionData')(sessionId, cpg.request.sessionMap, expirationTime)
-
-    # Set the content-length
-    if (cpg.response.headerMap.has_key('Content-Length') and
-            cpg.response.headerMap['Content-Length']==0):
-        buf = StringIO.StringIO()
-        [buf.write(x) for x in cpg.response.body]
-        buf.seek(0)
-        cpg.response.body = [buf.read()]
-        cpg.response.headerMap['Content-Length'] = len(cpg.response.body[0])
-
-    wfile.write('%s %s\r\n' % (cpg.response.headerMap['protocolVersion'], cpg.response.headerMap['Status']))
-    for key, valueList in cpg.response.headerMap.items():
-        if key not in ('Status', 'protocolVersion'):
-            if type(valueList) != type([]): valueList = [valueList]
-            for value in valueList:
-                wfile.write('%s: %s\r\n'%(key, value))
-
-    # Send response cookies
-    cookie = cpg.response.simpleCookie.output()
-    if cookie:
-        wfile.write(cookie+'\r\n')
-    wfile.write('\r\n')
-
-    for line in cpg.response.body:
-        wfile.write(line)
-    
-    # finalization hook for filter cleanup & logging purposes
-    applyFilterList('afterResponse')
-
-def handleRequest(wfile):
-    # Clean up expired sessions if needed:
-    now = time.time()
-    if cpg.configOption.sessionStorageType and cpg.configOption.sessionCleanUpDelay and cpg._lastSessionCleanUpTime + cpg.configOption.sessionCleanUpDelay * 60 <= now:
-        cpg._lastSessionCleanUpTime = now
-        _cputil.getSpecialFunction('_cpCleanUpOldSessions')()
-
-    # Save original values (in case they get modified by filters)
-    cpg.request.originalPath = cpg.request.path
-    cpg.request.originalParamMap = cpg.request.paramMap
-    cpg.request.originalParamList = cpg.request.paramList
-
-    path = cpg.request.path
-    if path.startswith('/'): path = path[1:] # Remove leading slash
-    if path.endswith('/'): path = path[:-1] # Remove trailing slash
-
-    # Handle static directories
-    for urlDir, fsDir in cpg.configOption.staticContentList:
-        if path == urlDir or path[:len(urlDir)+1]==urlDir+'/':
-
-            cpg.request.isStatic = 1
-
-            fname = fsDir + path[len(urlDir):]
-            try:
-                stat = os.stat(fname)
-            except OSError:
-                raise cperror.NotFound
-            if type(stat) == type(()): # Python2.1
-                modifTime = stat[9]
-            else:
-                modifTime = stat.st_mtime
-
-            strModifTime = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(modifTime))
-
-            # Check if browser sent "if-modified-since" in request header
-            if cpg.request.headerMap.has_key('If-Modified-Since'):
-                # Check if if-modified-since date is the same as strModifTime
-                if cpg.request.headerMap['If-Modified-Since'] == strModifTime:
-                    cpg.response.headerMap = {'Status': 304, 'protocolVersion': cpg.configOption.protocolVersion, 'Date': cpg.response.headerMap['Date']}
-                    cpg.response.body = ''
-                    sendResponse(wfile)
-                    return
-
-            cpg.response.headerMap['Last-Modified'] = strModifTime
-            f=open(fname, 'rb')
-            cpg.response.body = f.read()
-            f.close()
-            # Set content-type based on filename extension
-            i = path.rfind('.')
-            if i != -1: ext = path[i:]
-            else: ext = ""
-            contentType = mimetypes.types_map.get(ext, "text/plain")
-            cpg.response.headerMap['Content-Type'] = contentType
-            sendResponse(wfile)
-            return
-
-    # Get session data
-    if cpg.configOption.sessionStorageType and not cpg.request.isStatic:
-        now = time.time()
-        # First, get sessionId from cookie
-        try: sessionId = cpg.request.simpleCookie[cpg.configOption.sessionCookieName].value
-        except: sessionId=None
-        if sessionId:
-            # Load session data from wherever it was stored
-            sessionData = _cputil.getSpecialFunction('_cpLoadSessionData')(sessionId)
-            if sessionData == None:
-                sessionId = None
-            else:
-                cpg.request.sessionMap, expirationTime = sessionData
-                # Check that is hasn't expired
-                if now > expirationTime:
-                    # Session expired
-                    sessionId = None
-
-        # Create a new sessionId if needed
-        if not sessionId:
-            cpg.request.sessionMap = {}
-            sessionId = generateSessionId()
-            cpg.request.sessionMap['_sessionId'] = sessionId
-
-        cpg.response.simpleCookie[cpg.configOption.sessionCookieName] = sessionId
-        cpg.response.simpleCookie[cpg.configOption.sessionCookieName]['path'] = '/'
-        cpg.response.simpleCookie[cpg.configOption.sessionCookieName]['version'] = 1
-
-    try:
-        func, objectPathList, virtualPathList = mapPathToObject()
-    except IndexRedirect, inst:
-        # For an IndexRedirect, we don't go through the regular
-        #   mechanism: we return the redirect immediately
-        newUrl = canonicalizeUrl(inst.args[0])
-        wfile.write('%s 302\r\n' % (cpg.response.headerMap['protocolVersion']))
-        cpg.response.headerMap['Location'] = newUrl
-        for key, valueList in cpg.response.headerMap.items():
-            if key not in ('Status', 'protocolVersion'):
-                if type(valueList) != type([]): valueList = [valueList]
-                for value in valueList:
-                    wfile.write('%s: %s\r\n'%(key, value))
-        wfile.write('\r\n')
-        return
-         
-    # Remove "root" from objectPathList and join it to get objectPath
-    cpg.request.objectPath = '/' + '/'.join(objectPathList[1:])
-    body = func(*(virtualPathList + cpg.request.paramList), **(cpg.request.paramMap))
-    
-    # builds a uniform return type
-    if not isinstance(body, types.GeneratorType):
-        cpg.response.body = [body]
-    else:
-        cpg.response.body = body
-
-    if cpg.response.sendResponse:
-        sendResponse(wfile)
-
-def generateSessionId():
-    s = ''
-    for i in range(50):
-        s += random.choice(string.letters+string.digits)
-    s += '%s'%time.time()
-    return sha.sha(s).hexdigest()
-
-def getObjFromPath(objPathList, objCache):
-    """ For a given objectPathList (like ['root', 'a', 'b', 'index']),
-         return the object (or None if it doesn't exist).
-         Also keep a cache for maximum efficiency
-    """
-    # Let cpg be the first valid object.
-    validObjects = ["cpg"]
-     
-    # Scan the objPathList in order from left to right
-    for index, obj in enumerate(objPathList):
-        # currentObjStr holds something like 'cpg.root.something.else'
-        currentObjStr = ".".join(validObjects)
-
-        #---------------
-        #   Cache check
-        #---------------
-        # Generate a cacheKey from the first 'index' elements of objPathList
-        cacheKey = tuple(objPathList[:index+1])
-        # Is this cacheKey in the objCache?
-        if cacheKey in objCache: 
-            # And is its value not None?
-            if objCache[cacheKey]:
-                # Yes, then add it to the list of validObjects
-                validObjects.append(obj)
-                # OK, go to the next iteration
-                continue
-            # Its value is None, so we stop
-            # (This means it is not a valid object)
-            break
-        
-        #-----------------
-        # Attribute check
-        #-----------------
-        if getattr(eval(currentObjStr), obj, None):
-            #  obj is a valid attribute of the current object
-            validObjects.append(obj)
-            #  Store it in the cache
-            objCache[cacheKey] = eval(".".join(validObjects))
-        else:
-            # obj is not a valid attribute
-            # Store None in the cache
-            objCache[cacheKey] = None
-            # Stop, we won't process the remaining objPathList
-            break
-
-    # Return the last cached object (even if its None)
-    return objCache[cacheKey]
-
-def mapPathToObject(path = None):
-    # Traverse path:
-    # for /a/b?arg=val, we'll try:
-    #   root.a.b.index -> redirect to /a/b/?arg=val
-    #   root.a.b.default(arg='val') -> redirect to /a/b/?arg=val
-    #   root.a.b(arg='val')
-    #   root.a.default('b', arg='val')
-    #   root.default('a', 'b', arg='val')
-
-    # Also, we ignore trailing slashes
-    # Also, a method has to have ".exposed = True" in order to be exposed
-
-    if path is None:
-        path = cpg.request.objectPath or cpg.request.path
-    if path.startswith('/'): path = path[1:] # Remove leading slash
-    if path.endswith('/'): path = path[:-1] # Remove trailing slash
-
-    if not path:
-        objectPathList = []
-    else:
-        objectPathList = path.split('/')
-    objectPathList = ['root'] + objectPathList + ['index']
-
-    # Try successive objects... (and also keep the remaining object list)
-    objCache = {}
-    isFirst = True
-    isDefault = False
-    foundIt = False
-    virtualPathList = []
-    while objectPathList:
-        candidate = getObjFromPath(objectPathList, objCache)
-        if callable(candidate) and getattr(candidate, 'exposed', False):
-            foundIt = True
-            break
-        # Couldn't find the object: pop one from the list and try "default"
-        lastObj = objectPathList.pop()
-        if not isFirst:
-            virtualPathList.insert(0, lastObj)
-            objectPathList.append('default')
-            candidate = getObjFromPath(objectPathList, objCache)
-            if callable(candidate) and getattr(candidate, 'exposed', False):
-                foundIt = True
-                isDefault = True
-                break
-            objectPathList.pop() # Remove "default"
-        isFirst = False
-
-    # Check results of traversal
-    if not foundIt:
-        raise cperror.NotFound # We didn't find anything
-
-    if isFirst:
-        # We found the extra ".index"
-        # Check if the original path had a trailing slash (otherwise, do
-        #   a redirect)
-        if cpg.request.path[-1] != '/':
-            newUrl = cpg.request.path + '/'
-            if cpg.request.queryString: newUrl += cpg.request.queryString
-            raise IndexRedirect(newUrl)
-
-    return candidate, objectPathList, virtualPathList
-    
-def canonicalizeUrl(newUrl):
-    if not newUrl.startswith('http://') and not newUrl.startswith('https://'):
-        # If newUrl is not canonical, we must make it canonical
-        if newUrl[0] == '/':
-            # URL was absolute: we just add the request.base in front of it
-            newUrl = cpg.request.base + newUrl
-        else:
-            # URL was relative
-            if cpg.request.browserUrl == cpg.request.base:
-                # browserUrl is request.base
-                newUrl = cpg.request.base + '/' + newUrl
-            else:
-                newUrl = cpg.request.browserUrl[:i+1] + newUrl
-    return newUrl
+"""
+Copyright (c) 2004, CherryPy Team (team@cherrypy.org)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, 
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, 
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice, 
+      this list of conditions and the following disclaimer in the documentation 
+      and/or other materials provided with the distribution.
+    * Neither the name of the CherryPy Team nor the names of its contributors 
+      may be used to endorse or promote products derived from this software 
+      without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import cpg, urllib, sys, time, traceback, types, StringIO, cgi, os
+import mimetypes, sha, random, string, _cputil, cperror, Cookie
+from lib.filter import basefilter
+
+"""
+Common Service Code for CherryPy
+"""
+
+mimetypes.types_map['.dwg']='image/x-dwg'
+mimetypes.types_map['.ico']='image/x-icon'
+
+weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
+monthname = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+
+class IndexRedirect(Exception): pass
+
+def parseFirstLine(data):
+    cpg.request.path = data.split()[1]
+    cpg.request.queryString = ""
+    cpg.request.browserUrl = cpg.request.path
+    cpg.request.paramMap = {}
+    cpg.request.paramList = [] # Only used for Xml-Rpc
+    cpg.request.filenameMap = {}
+    cpg.request.fileTypeMap = {}
+    i = cpg.request.path.find('?')
+    if i != -1:
+        # Parse parameters from URL
+        if cpg.request.path[i+1:]:
+            k = cpg.request.path[i+1:].find('?')
+            if k != -1:
+                j = cpg.request.path[:k].rfind('=')
+                if j != -1:
+                    cpg.request.path = cpg.request.path[:j+1] + \
+                        urllib.quote_plus(cpg.request.path[j+1:])
+            for paramStr in cpg.request.path[i+1:].split('&'):
+                sp = paramStr.split('=')
+                if len(sp) > 2:
+                    j = paramStr.find('=')
+                    sp = (paramStr[:j], paramStr[j+1:])
+                if len(sp) == 2:
+                    key, value = sp
+                    value = urllib.unquote_plus(value)
+                    if cpg.request.paramMap.has_key(key):
+                        # Already has a value: make a list out of it
+                        if type(cpg.request.paramMap[key]) == type([]):
+                            # Already is a list: append the new value to it
+                            cpg.request.paramMap[key].append(value)
+                        else:
+                            # Only had one value so far: start a list
+                            cpg.request.paramMap[key] = [cpg.request.paramMap[key], value]
+                    else:
+                        cpg.request.paramMap[key] = value
+        cpg.request.queryString = cpg.request.path[i+1:]
+        cpg.request.path = cpg.request.path[:i]
+
+def cookHeaders(clientAddress, remoteHost, headers, requestLine):
+    """Process the headers into the request.headerMap"""
+    cpg.request.headerMap = {}
+    cpg.request.requestLine = requestLine
+    cpg.request.simpleCookie = Cookie.SimpleCookie()
+
+    # Build headerMap
+    for item in headers.items():
+        # Warning: if there is more than one header entry for cookies (AFAIK, only Konqueror does that)
+        # only the last one will remain in headerMap (but they will be correctly stored in request.simpleCookie)
+        insertIntoHeaderMap(item[0],item[1])
+
+    # Handle cookies differently because on Konqueror, multiple cookies come on different lines with the same key
+    cookieList = headers.getallmatchingheaders('cookie')
+    for cookie in cookieList:
+        cpg.request.simpleCookie.load(cookie)
+
+    cpg.request.remoteAddr = clientAddress
+    cpg.request.remoteHost = remoteHost
+
+    # Set peer_certificate (in SSL mode) so the web app can examinate the client certificate
+    try: cpg.request.peerCertificate = self.request.get_peer_certificate()
+    except: pass
+
+    _cputil.getSpecialFunction('_cpLogMessage')("%s - %s" % (cpg.request.remoteAddr, requestLine[:-2]), "HTTP")
+
+
+def parsePostData(rfile):
+    # Read request body and put it in data
+    len = int(cpg.request.headerMap.get("Content-Length","0"))
+    if len: data = rfile.read(len)
+    else: data=""
+
+    # Put data in a StringIO so FieldStorage can read it
+    newRfile = StringIO.StringIO(data)
+    # Create a copy of headerMap with lowercase keys because
+    #   FieldStorage doesn't work otherwise
+    lowerHeaderMap = {}
+    for key, value in cpg.request.headerMap.items():
+        lowerHeaderMap[key.lower()] = value
+    forms = cgi.FieldStorage(fp = newRfile, headers = lowerHeaderMap, environ = {'REQUEST_METHOD':'POST'}, keep_blank_values = 1)
+    for key in forms.keys():
+        # Check if it's a list or not
+        valueList = forms[key]
+        if type(valueList) == type([]):
+            # It's a list of values
+            cpg.request.paramMap[key] = []
+            cpg.request.filenameMap[key] = []
+            cpg.request.fileTypeMap[key] = []
+            for item in valueList:
+                cpg.request.paramMap[key].append(item.value)
+                cpg.request.filenameMap[key].append(item.filename)
+                cpg.request.fileTypeMap[key].append(item.type)
+        else:
+            # It's a single value
+            # In case it's a file being uploaded, we save the filename in a map (user might need it)
+            cpg.request.paramMap[key] = valueList.value
+            cpg.request.filenameMap[key] = valueList.filename
+            cpg.request.fileTypeMap[key] = valueList.type
+
+def applyFilterList(methodName):
+    try:
+        filterList = _cputil.getSpecialFunction('_cpFilterList')
+        for filter in filterList:
+            method = getattr(filter, methodName, None)
+            if method:
+                method()
+    except basefilter.InternalRedirect:
+        # If we get an InternalRedirect, we start the filter list  
+        #   from scratch. Is cpg.request.path or cpg.request.objectPath
+        #   has been modified by the hook, then a new filter list
+        #   will be applied.  
+        # We use recursion so if there is an infinite loop, we'll  
+        #   get the regular python "recursion limit exceeded" exception.  
+        applyFilterList(methodName) 
+
+
+def insertIntoHeaderMap(key,value):
+    normalizedKey = '-'.join([s.capitalize() for s in key.split('-')])
+    cpg.request.headerMap[normalizedKey] = value
+
+def initRequest(clientAddress, remoteHost, requestLine, headers, rfile, wfile):
+    parseFirstLine(requestLine)
+    cookHeaders(clientAddress, remoteHost, headers, requestLine)
+
+    cpg.request.base = "http://" + cpg.request.headerMap['Host']
+    cpg.request.browserUrl = cpg.request.base + cpg.request.browserUrl
+    cpg.request.isStatic = False
+    cpg.request.parsePostData = True
+    cpg.request.rfile = rfile
+
+    # Change objectPath in filters to change the object that will get rendered
+    cpg.request.objectPath = None 
+