0001"""
0002A Webware HTTPRequest object, implemented based on the WSGI request
0003environment dictionary.
0004"""
0005
0006import time
0007import traceback
0008import cgi
0009import sys, os
0010from wkcommon import NoDefault, requestURI, deprecated
0011from Cookie import SimpleCookie as Cookie
0012
0013class HTTPRequest(object):
0014
0015    def __init__(self, transaction, environ):
0016        self._environ = environ
0017        self._transaction = transaction
0018        if environ.has_key('webkit.time'):
0019            self._time = environ['webkit.time']
0020        else:
0021            self._time = time.time()
0022        self._input = environ['wsgi.input']
0023        self._setupPath()
0024        self._setupFields()
0025        self._setupCookies()
0026        self._pathInfo = None
0027        self._serverRootPath = ""
0028        # @@: I'm leaving out automatic path sessions
0029        self._sessionExpired = False
0030
0031    def _setupPath(self):
0032        self._environ['PATH_INFO'] = self._environ.get('PATH_INFO', '')
0033        if not self._environ.has_key('REQUEST_URI'):
0034            self._environ['REQUEST_URI'] = requestURI(self._environ)
0035        # @@: Not necessarily true for WSGI:
0036        self._adapterName = self._environ.get('SCRIPT_NAME')
0037
0038    def _setupFields(self):
0039        self._environ.setdefault('QUERY_STRING', '')
0040        self._fieldStorage = cgi.FieldStorage(
0041            self._input,
0042            environ=self._environ,
0043            keep_blank_values=True,
0044            strict_parsing=False)
0045        try:
0046            keys = self._fieldStorage.keys()
0047        except TypeError:
0048            # Maybe an XML-RPC request
0049            keys = []
0050        dict = {}
0051        for key in keys:
0052            value = self._fieldStorage[key]
0053            if not isinstance(value, list):
0054                if not value.filename:
0055                    # Turn the MiniFieldStorage into a string:
0056                    value = value.value
0057            else:
0058                value = [v.value for v in value]
0059            dict[key] = value
0060        if self._environ['REQUEST_METHOD'].upper() == 'POST':
0061            # Then we must also parse GET variables
0062            self._getFields = cgi.parse_qs(
0063                self._environ.get('QUERY_STRING', ''),
0064                keep_blank_values=True,
0065                strict_parsing=False)
0066            for name, value in self._getFields.items():
0067                if not dict.has_key(name):
0068                    if isinstance(value, list) and len(value) == 1:
0069                        # parse_qs always returns a list of lists,
0070                        # while FieldStorage only uses lists for
0071                        # keys that actually repeat; this fixes that.
0072                        value = value[0]
0073                    dict[name] = value
0074        self._fields = dict
0075
0076    def _setupCookies(self):
0077        cookies = Cookie()
0078        if self._environ.has_key('HTTP_COOKIE'):
0079            try:
0080                cookies.load(self._environ['HTTP_COOKIE'])
0081            except:
0082                traceback.print_exc(file=self._environ['wsgi.errors'])
0083        dict = {}
0084        for key in cookies.keys():
0085            dict[key] = cookies[key].value
0086        self._cookies = dict
0087
0088    def protocol(self):
0089        return 'HTTP/1.0'
0090
0091    def time(self):
0092        return self._time
0093
0094    def timeStamp(self):
0095        return time.asctime(time.localtime(self.time()))
0096
0097    ## Transactions ##
0098
0099    def transaction(self):
0100        return self._transaction
0101
0102    def setTransaction(self, trans):
0103        self._transaction = trans
0104
0105    ## Values ##
0106
0107    def value(self, name, default=NoDefault):
0108        if self._fields.has_key(name):
0109            return self._fields[name]
0110        else:
0111            return self.cookie(name, default)
0112
0113    def hasValue(self, name):
0114        return self._fields.has_key(name) or self._cookies.has_key(name)
0115
0116    def extraURLPath(self):
0117        return self._environ.get('PATH_INFO', '')
0118
0119    ## Fields ##
0120
0121
0122    def fieldStorage(self):
0123        return self._fieldStorage
0124
0125    def field(self, name, default=NoDefault):
0126        if default is NoDefault:
0127            return self._fields[name]
0128        else:
0129            return self._fields.get(name, default)
0130
0131    def hasField(self, name):
0132        return self._fields.has_key(name)
0133
0134    def fields(self):
0135        return self._fields
0136
0137    def setField(self, name, value):
0138        self._fields[name] = value
0139
0140    def delField(self, name):
0141        del self._fields[name]
0142
0143    ## Cookies ##
0144
0145    def cookie(self, name, default=NoDefault):
0146        """ Returns the value of the specified cookie. """
0147        if default is NoDefault:
0148            return self._cookies[name]
0149        else:
0150            return self._cookies.get(name, default)
0151
0152    def hasCookie(self, name):
0153        return self._cookies.has_key(name)
0154
0155    def cookies(self):
0156        """
0157        Returns a dictionary-style object of all Cookie objects the
0158        client sent with this request."""
0159        return self._cookies
0160
0161    ## Variables passed by server ##
0162    def serverDictionary(self):
0163        """
0164        Returns a dictionary with the data the web server gave us,
0165        like HTTP_HOST or HTTP_USER_AGENT.  """
0166        return self._environ
0167
0168    ## Sessions ##
0169
0170    def session(self):
0171        """ Returns the session associated with this request, either
0172        as specified by sessionId() or newly created. This is a
0173        convenience for transaction.session() """
0174        return self._transaction.session()
0175
0176    def isSessionExpired(self):
0177        """ Returns bool: whether or not this request originally
0178        contained an expired session ID.  Only works if the
0179        Application.config setting "IgnoreInvalidSession" is set to 1;
0180        otherwise you get a canned error page on an invalid session,
0181        so your servlet never gets processed.  """
0182        return self._sessionExpired
0183
0184    def setSessionExpired(self, sessionExpired):
0185        self._sessionExpired = sessionExpired
0186
0187    ## Authentication ##
0188
0189    def remoteUser(self):
0190        """ Always returns None since authentication is not yet
0191        supported. Take from CGI variable REMOTE_USER. """
0192        # @@ 2000-03-26 ce: maybe belongs in section below. clean up docs
0193        return self._environ['REMOTE_USER']
0194
0195    ## Remote info ##
0196
0197    def remoteAddress(self):
0198        """ Returns a string containing the Internet Protocol (IP)
0199        address of the client that sent the request. """
0200        return self._environ['REMOTE_ADDR']
0201
0202    def remoteName(self):
0203        """ Returns the fully qualified name of the client that sent
0204        the request, or the IP address of the client if the name
0205        cannot be determined. """
0206        env = self._environ
0207        return env.get('REMOTE_NAME', env['REMOTE_ADDR'])
0208
0209    ## Path ##
0210
0211    def urlPath(self):
0212        raise NotImplementedError
0213
0214    def originalURLPath(self):
0215        environ = self._environ.get('recursive.previous_environ', self._environ)
0216        url = environ.get("SCRIPT_NAME", '') + environ.get('PATH_INFO', '')
0217        #self._environ['wsgi.errors'].write('Original URL: %r (from %r)\n' % (url, environ))
0218        return url
0219
0220    def urlPathDir(self):
0221        raise NotImplementedError
0222
0223    def getstate(self):
0224        raise NotImplementedError
0225
0226    def setURLPath(self, path):
0227        raise NotImplementedError
0228
0229    def serverSidePath(self, path=None):
0230        raise NotImplementedError
0231
0232    def serverSideContextPath(self, path=None):
0233        here = sys.modules[self.transaction().servlet().__class__.__module__].__file__
0234        # @@: not correct for modules that are in subpackages of web
0235        base = os.path.dirname(here)
0236        if path:
0237            return os.path.join(base, path)
0238        else:
0239            return base
0240
0241    def contextName(self):
0242        return ''
0243
0244    def servletURI(self):
0245        """This is the URI of the servlet, without any query strings or extra path info"""
0246        # @@: should be implemented
0247        raise NotImplementedError
0248
0249    def uriWebKitRoot(self):
0250        raise NotImplementedError
0251
0252    def fsPath(self):
0253        raise NotImplementedError
0254
0255    def serverURL(self):
0256        raise NotImplementedError
0257
0258    def serverURLDir(self):
0259        raise NotImplementedError
0260
0261    def siteRoot(self):
0262        raise NotImplementedError
0263
0264    def siteRootFromCurrentServlet(self):
0265        raise NotImplementedError
0266
0267    def servletPathFromSiteRoot(self):
0268        raise NotImplementedError
0269
0270    ## Special ##
0271
0272    def adapterName(self):
0273        """
0274        Returns the name of the adapter as it appears in the URL.
0275        Example: '/WebKit.cgi'
0276        This is useful in special cases when you are constructing URLs. See Testing/Main.py for an example use.
0277        """
0278        deprecated()
0279        return '/'.join(self._environ['SCRIPT_NAME'].split('/')[:-1])
0280
0281    def rawRequest(self):
0282        raise NotImplementedError
0283
0284    def environ(self):
0285        return self._environ
0286
0287    def rawInput(self, rewind=0):
0288        """
0289        This gives you a file-like object for the data that was
0290        sent with the request (e.g., the body of a POST request,
0291        or the documented uploaded in a PUT request).
0292
0293        The file might not be rewound to the beginning if there
0294        was valid, form-encoded POST data.  Pass rewind=1 if
0295        you want to be sure you get the entire body of the request.
0296        """
0297        fs = self.fieldStorage()
0298        if rewind:
0299            fs.file.seek(0)
0300        return fs.file
0301
0302    ## Information ##
0303
0304    # @@ 2000-05-10: See FUTURE section of class doc string
0305
0306    def servletPath(self):
0307        raise NotImplementedError
0308
0309    def contextPath(self):
0310        raise NotImplementedError
0311
0312    def pathInfo(self):
0313        raise NotImplementedError
0314
0315    def pathTranslated(self):
0316        raise NotImplementedError
0317
0318    def queryString(self):
0319        """
0320        Returns the query string portion of the URL for this
0321        request. Taken from the CGI variable QUERY_STRING. """
0322        return self._environ.get('QUERY_STRING', '')
0323
0324    def uri(self):
0325        """
0326        Returns the request URI, which is the entire URL except
0327        for the query string. """
0328        return self._environ['REQUEST_URI']
0329
0330    def method(self):
0331        """
0332        Returns the HTTP request method (in all uppercase), typically
0333        from the set GET, POST, PUT, DELETE, OPTIONS and TRACE."""
0334        return self._environ['REQUEST_METHOD'].upper()
0335
0336    def sessionId(self):
0337        """ Returns a string with the session id specified by the
0338        client, or None if there isn't one. """
0339        sid = self.value('_SID_', None)
0340        return sid
0341
0342    def config(self):
0343        return self._environ.get('paste.config', {})
0344
0345    ## Inspection ##
0346
0347    def info(self):
0348        raise NotImplementedError
0349
0350    def htmlInfo(self):
0351        raise NotImplementedError