diff -r 000000000000 -r b97547f5f1fa etwist/request.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/etwist/request.py Wed Nov 05 15:52:50 2008 +0100 @@ -0,0 +1,127 @@ +"""Twisted request handler for CubicWeb + +:organization: Logilab +:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr +""" +__docformat__ = "restructuredtext en" + +from twisted.web2 import http, http_headers + +from mx.DateTime import DateTimeFromTicks + +from cubicweb.web import DirectResponse +from cubicweb.web.request import CubicWebRequestBase +from cubicweb.web.httpcache import GMTOFFSET + +def cleanup_files(dct, encoding): + d = {} + for k, infos in dct.items(): + for (filename, mt, stream) in infos: + if filename: + # XXX: suppose that no file submitted <-> no filename + filename = unicode(filename, encoding) + mt = u'%s/%s' % (mt.mediaType, mt.mediaSubtype) + d[k] = (filename, mt, stream) + return d + + +class CubicWebTwistedRequestAdapter(CubicWebRequestBase): + def __init__(self, req, vreg, https, base_url): + self._twreq = req + self._base_url = base_url + super(CubicWebTwistedRequestAdapter, self).__init__(vreg, https, req.args) + self.form.update(cleanup_files(req.files, self.encoding)) + # prepare output headers + self.headers_out = http_headers.Headers() + self._headers = req.headers + + def base_url(self): + """return the root url of the application""" + return self._base_url + + def http_method(self): + """returns 'POST', 'GET', 'HEAD', etc.""" + return self._twreq.method + + def relative_path(self, includeparams=True): + """return the normalized path of the request (ie at least relative + to the application's root, but some other normalization may be needed + so that the returned path may be used to compare to generated urls + + :param includeparams: + boolean indicating if GET form parameters should be kept in the path + """ + path = self._twreq.uri[1:] # remove the root '/' + if not includeparams: + path = path.split('?', 1)[0] + return path + + def get_header(self, header, default=None, raw=True): + """return the value associated with the given input header, + raise KeyError if the header is not set + """ + if raw: + return self._twreq.headers.getRawHeaders(header, [default])[0] + return self._twreq.headers.getHeader(header, default) + + def set_header(self, header, value, raw=True): + """set an output HTTP header""" + if raw: + # adding encoded header is important, else page content + # will be reconverted back to unicode and apart unefficiency, this + # may cause decoding problem (e.g. when downloading a file) + self.headers_out.setRawHeaders(header, [str(value)]) + else: + self.headers_out.setHeader(header, value) + + def add_header(self, header, value): + """add an output HTTP header""" + # adding encoded header is important, else page content + # will be reconverted back to unicode and apart unefficiency, this + # may cause decoding problem (e.g. when downloading a file) + self.headers_out.addRawHeader(header, str(value)) + + def remove_header(self, header): + """remove an output HTTP header""" + self.headers_out.removeHeader(header) + + def _validate_cache(self): + """raise a `DirectResponse` exception if a cached page along the way + exists and is still usable + """ + if self.get_header('Cache-Control') in ('max-age=0', 'no-cache'): + # Expires header seems to be required by IE7 + self.add_header('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT') + return + try: + http.checkPreconditions(self._twreq, _PreResponse(self)) + except http.HTTPError, ex: + self.info('valid http cache, no actual rendering') + raise DirectResponse(ex.response) + # Expires header seems to be required by IE7 + self.add_header('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT') + + def header_accept_language(self): + """returns an ordered list of preferred languages""" + acceptedlangs = self.get_header('Accept-Language', raw=False) or {} + for lang, _ in sorted(acceptedlangs.iteritems(), key=lambda x: x[1], + reverse=True): + lang = lang.split('-')[0] + yield lang + + def header_if_modified_since(self): + """If the HTTP header If-modified-since is set, return the equivalent + mx date time value (GMT), else return None + """ + mtime = self.get_header('If-modified-since', raw=False) + if mtime: + # :/ twisted is returned a localized time stamp + return DateTimeFromTicks(mtime) + GMTOFFSET + return None + + +class _PreResponse(object): + def __init__(self, request): + self.headers = request.headers_out + self.code = 200