[web request] fix cookie 'expires' formating (closes #1953945)
This was because cookie.expires wasn't processed in cw.etwist.http,
though this code should had never existed, instead proper twisted
method should be called.
Also, move on the way to headers handling simplification and rewrite
cw request.set_cookie / remove_cookie to directly use the Cookie class
in cw.web.http_headers rather than going back and forth
simple cookie <-> raw string <-> http_headers.Cookie
IMO more on this should be done.
--- a/devtools/fake.py Fri Sep 23 12:17:12 2011 +0200
+++ b/devtools/fake.py Fri Sep 23 12:16:29 2011 +0200
@@ -63,8 +63,8 @@
self._session_data = {}
self._headers_in = Headers()
- def set_cookie(self, cookie, key, maxage=300, expires=None):
- super(FakeRequest, self).set_cookie(cookie, key, maxage=300, expires=None)
+ def set_cookie(self, name, value, maxage=300, expires=None, secure=False):
+ super(FakeRequest, self).set_cookie(name, value, maxage, expires, secure)
cookie = self.get_response_header('Set-Cookie')
self._headers_in.setHeader('Cookie', cookie)
--- a/etwist/http.py Fri Sep 23 12:17:12 2011 +0200
+++ b/etwist/http.py Fri Sep 23 12:16:29 2011 +0200
@@ -1,7 +1,7 @@
"""twisted server for CubicWeb web instances
:organization: Logilab
-:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:copyright: 2001-2011 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
"""
@@ -25,25 +25,14 @@
def _init_headers(self):
if self._headers_out is None:
return
-
- # initialize cookies
- cookies = self._headers_out.getHeader('set-cookie') or []
- for cookie in cookies:
- self._twreq.addCookie(cookie.name, cookie.value, cookie.expires,
- cookie.domain, cookie.path, #TODO max-age
- comment = cookie.comment, secure=cookie.secure)
- self._headers_out.removeHeader('set-cookie')
-
- # initialize other headers
- for k, v in self._headers_out.getAllRawHeaders():
- self._twreq.setHeader(k, v[0])
-
+ # initialize headers
+ for k, values in self._headers_out.getAllRawHeaders():
+ self._twreq.responseHeaders.setRawHeaders(k, values)
# add content-length if not present
if (self._headers_out.getHeader('content-length') is None
and self._stream is not None):
self._twreq.setHeader('content-length', len(self._stream))
-
def _finalize(self):
# we must set code before writing anything, else it's too late
if self._code is not None:
--- a/web/application.py Fri Sep 23 12:17:12 2011 +0200
+++ b/web/application.py Fri Sep 23 12:16:29 2011 +0200
@@ -236,12 +236,10 @@
def open_session(self, req, allow_no_cnx=True):
session = self.session_manager.open_session(req, allow_no_cnx=allow_no_cnx)
- cookie = req.get_cookie()
sessioncookie = self.session_cookie(req)
- cookie[sessioncookie] = session.sessionid
- if req.https and req.base_url().startswith('https://'):
- cookie[sessioncookie]['secure'] = True
- req.set_cookie(cookie, sessioncookie, maxage=None)
+ secure = req.https and req.base_url().startswith('https://')
+ req.set_cookie(sessioncookie, session.sessionid,
+ maxage=None, secure=secure)
if not session.anonymous_session:
self.session_manager.postlogin(req)
return session
@@ -251,8 +249,7 @@
`AuthenticationError`
"""
self.session_manager.close_session(req.session)
- sessioncookie = self.session_cookie(req)
- req.remove_cookie(req.get_cookie(), sessioncookie)
+ req.remove_cookie(self.session_cookie(req))
raise LogOut(url=goto_url)
# these are overridden by set_log_methods below
--- a/web/http_headers.py Fri Sep 23 12:17:12 2011 +0200
+++ b/web/http_headers.py Fri Sep 23 12:16:29 2011 +0200
@@ -1354,9 +1354,25 @@
raw_header.append(value)
self._headers[name] = _RecalcNeeded
+ def addHeader(self, name, value):
+ """
+ Add a parsed representatoin to a header that may or may not already exist.
+ If it exists, add it as a separate header to output; do not
+ replace anything.
+ """
+ name=name.lower()
+ header = self._headers.get(name)
+ if header is None:
+ # No header yet
+ header = []
+ self._headers[name] = header
+ elif header is _RecalcNeeded:
+ header = self._toParsed(name)
+ header.append(value)
+ self._raw_headers[name] = _RecalcNeeded
+
def removeHeader(self, name):
"""Removes the header named."""
-
name=name.lower()
if self._raw_headers.has_key(name):
del self._raw_headers[name]
--- a/web/request.py Fri Sep 23 12:17:12 2011 +0200
+++ b/web/request.py Fri Sep 23 12:16:29 2011 +0200
@@ -19,11 +19,12 @@
__docformat__ = "restructuredtext en"
-import Cookie
import hashlib
import time
import random
import base64
+from Cookie import SimpleCookie
+from calendar import timegm
from datetime import date
from urlparse import urlsplit
from itertools import count
@@ -42,7 +43,8 @@
from cubicweb.view import STRICT_DOCTYPE, TRANSITIONAL_DOCTYPE_NOEXT
from cubicweb.web import (INTERNAL_FIELD_VALUE, LOGGER, NothingToEdit,
RequestError, StatusResponse)
-from cubicweb.web.http_headers import Headers
+from cubicweb.web.httpcache import GMTOFFSET
+from cubicweb.web.http_headers import Headers, Cookie
_MARKER = object()
@@ -518,30 +520,44 @@
def get_cookie(self):
"""retrieve request cookies, returns an empty cookie if not found"""
+ # XXX use http_headers implementation
try:
- return Cookie.SimpleCookie(self.get_header('Cookie'))
+ return SimpleCookie(self.get_header('Cookie'))
except KeyError:
- return Cookie.SimpleCookie()
+ return SimpleCookie()
- def set_cookie(self, cookie, key, maxage=300, expires=None):
- """set / update a cookie key
+ def set_cookie(self, name, value, maxage=300, expires=None, secure=False):
+ """set / update a cookie
by default, cookie will be available for the next 5 minutes.
Give maxage = None to have a "session" cookie expiring when the
client close its browser
"""
- morsel = cookie[key]
- if maxage is not None:
- morsel['Max-Age'] = maxage
- if expires:
- morsel['expires'] = expires.strftime('%a, %d %b %Y %H:%M:%S %z')
+ if isinstance(name, SimpleCookie):
+ warn('[3.13] set_cookie now takes name and value as two first '
+ 'argument, not anymore cookie object and name',
+ DeprecationWarning, stacklevel=2)
+ secure = name[value]['secure']
+ name, value = value, name[value].value
+ if maxage: # don't check is None, 0 may be specified
+ expires = maxage + time.time()
+ assert expires is None, 'both max age and expires cant be specified'
+ elif expires:
+ expires = timegm((expires + GMTOFFSET).timetuple())
+ else:
+ expires = None
# make sure cookie is set on the correct path
- morsel['path'] = self.base_url_path()
- self.add_header('Set-Cookie', morsel.OutputString())
+ cookie = Cookie(name, value, self.base_url_path(), expires=expires,
+ secure=secure)
+ self.headers_out.addHeader('Set-cookie', cookie)
- def remove_cookie(self, cookie, key):
+ def remove_cookie(self, name, bwcompat=None):
"""remove a cookie by expiring it"""
- self.set_cookie(cookie, key, maxage=0, expires=date(1970, 1, 1))
+ if bwcompat is not None:
+ warn('[3.13] remove_cookie now take only a name as argument',
+ DeprecationWarning, stacklevel=2)
+ name = bwcompat
+ self.set_cookie(key, '', maxage=0, expires=date(1970, 1, 1))
def set_content_type(self, content_type, filename=None, encoding=None):
"""set output content type for this request. An optional filename
--- a/web/views/basecontrollers.py Fri Sep 23 12:17:12 2011 +0200
+++ b/web/views/basecontrollers.py Fri Sep 23 12:16:29 2011 +0200
@@ -535,24 +535,20 @@
statename = treecookiename(treeid)
treestate = cookies.get(statename)
if treestate is None:
- cookies[statename] = nodeeid
- self._cw.set_cookie(cookies, statename)
+ self._cw.set_cookie(statename, nodeeid)
else:
marked = set(filter(None, treestate.value.split(':')))
if nodeeid in marked:
marked.remove(nodeeid)
else:
marked.add(nodeeid)
- cookies[statename] = ':'.join(marked)
- self._cw.set_cookie(cookies, statename)
+ self._cw.set_cookie(statename, ':'.join(marked))
@jsonize
@deprecated("[3.13] use jQuery.cookie(cookiename, cookievalue, {path: '/'}) in js land instead")
def js_set_cookie(self, cookiename, cookievalue):
cookiename, cookievalue = str(cookiename), str(cookievalue)
- cookies = self._cw.get_cookie()
- cookies[cookiename] = cookievalue
- self._cw.set_cookie(cookies, cookiename)
+ self._cw.set_cookie(cookiename, cookievalue)
# relations edition stuff ##################################################
--- a/web/views/cwproperties.py Fri Sep 23 12:17:12 2011 +0200
+++ b/web/views/cwproperties.py Fri Sep 23 12:16:29 2011 +0200
@@ -102,8 +102,7 @@
cookiename = self._cookie_name(group)
cookie = cookies.get(cookiename)
if cookie is None:
- cookies[cookiename] = default
- self._cw.set_cookie(cookies, cookiename, maxage=None)
+ self._cw.set_cookie(cookiename, default, maxage=None)
status = default
else:
status = cookie.value
--- a/web/views/tabs.py Fri Sep 23 12:17:12 2011 +0200
+++ b/web/views/tabs.py Fri Sep 23 12:16:29 2011 +0200
@@ -93,8 +93,7 @@
activetab = cookies.get(cookiename)
if activetab is None:
domid = uilib.domid(default)
- cookies[cookiename] = domid
- self._cw.set_cookie(cookies, cookiename)
+ self._cw.set_cookie(cookiename, domid)
return domid
return activetab.value