Commits

Anonymous committed 9226728

Changed notices to 79 chars wrap.

Comments (0)

Files changed (27)

cherrypy/_cpconfig.py

-"""
-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"
+

cherrypy/_cpdefaults.py

-"""
-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 = []

cherrypy/_cphttpserver.py

-"""
-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
+

cherrypy/_cphttptools.py

-"""
-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