# HG changeset patch # User Julien Cristau # Date 1457531613 -3600 # Node ID a2091fa8cb2c5a95e171479c1182f4d9c71d4fd4 # Parent 1694e6e9ff941ebb10279b8dd1827d4e541ff33e [web/httpcache] fix Last-Modified generation time.mktime takes a time tuple in *local* time. We have an UTC datetime coming from the last_modified method, which we were interpreting as local time, leading to hilarity. Use calendar.timegm instead. Add tests for Last-Modified generation of the 'download' and 'manage' views. diff -r 1694e6e9ff94 -r a2091fa8cb2c cubicweb/web/httpcache.py --- a/cubicweb/web/httpcache.py Wed May 04 17:07:41 2016 +0200 +++ b/cubicweb/web/httpcache.py Wed Mar 09 14:53:33 2016 +0100 @@ -19,7 +19,7 @@ __docformat__ = "restructuredtext en" -from time import mktime +from calendar import timegm from datetime import datetime class NoHTTPCacheManager(object): @@ -78,7 +78,7 @@ # the front-end correctly generate it # ("%a, %d %b %Y %H:%M:%S GMT" return localized date that # twisted don't parse correctly) - req.set_header('Last-modified', mktime(mdate.timetuple()), raw=False) + req.set_header('Last-modified', timegm(mdate.timetuple()), raw=False) class EntityHTTPCacheManager(EtagHTTPCacheManager): diff -r 1694e6e9ff94 -r a2091fa8cb2c cubicweb/web/test/unittest_idownloadable.py --- a/cubicweb/web/test/unittest_idownloadable.py Wed May 04 17:07:41 2016 +0200 +++ b/cubicweb/web/test/unittest_idownloadable.py Wed Mar 09 14:53:33 2016 +0100 @@ -17,13 +17,16 @@ # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see . +from datetime import datetime from functools import partial -from logilab.common.testlib import unittest_main +from pytz import utc from cubicweb.devtools.testlib import CubicWebTC, real_error_handling from cubicweb import view from cubicweb.predicates import is_instance +from cubicweb.web import http_headers + class IDownloadableUser(view.EntityAdapter): __regid__ = 'IDownloadable' @@ -72,6 +75,9 @@ get('content-disposition')) self.assertEqual(['text/plain;charset=ascii'], get('content-type')) + last_mod = http_headers.parseDateTime(get('last-modified')[0]) + self.assertEqual(datetime.fromtimestamp(last_mod, tz=utc), + req.user.modification_date.replace(microsecond=0)) self.assertEqual(b'Babar is not dead!', data) def test_header_with_space(self): @@ -146,4 +152,5 @@ self.assertEqual(req.status_out, 500) if __name__ == '__main__': - unittest_main() + from unittest import main + main() diff -r 1694e6e9ff94 -r a2091fa8cb2c cubicweb/web/test/unittest_views_basecontrollers.py --- a/cubicweb/web/test/unittest_views_basecontrollers.py Wed May 04 17:07:41 2016 +0200 +++ b/cubicweb/web/test/unittest_views_basecontrollers.py Wed Mar 09 14:53:33 2016 +0100 @@ -17,6 +17,8 @@ # with CubicWeb. If not, see . """cubicweb.web.views.basecontrollers unit tests""" +import time + from six import text_type from six.moves.urllib.parse import urlsplit, urlunsplit, urljoin, parse_qs @@ -31,7 +33,7 @@ from cubicweb.devtools.webtest import CubicWebTestTC from cubicweb.utils import json_dumps from cubicweb.uilib import rql_for_eid -from cubicweb.web import Redirect, RemoteCallFailed +from cubicweb.web import Redirect, RemoteCallFailed, http_headers import cubicweb.server.session from cubicweb.server.session import Connection from cubicweb.web.views.autoform import get_pending_inserts, get_pending_deletes @@ -44,9 +46,11 @@ class ViewControllerTC(CubicWebTestTC): def test_view_ctrl_with_valid_cache_headers(self): + now = time.time() resp = self.webapp.get('/manage') self.assertEqual(resp.etag, 'manage/guests') self.assertEqual(resp.status_code, 200) + self.assertGreaterEqual(http_headers.parseDateTime(resp.headers['Last-Modified']), int(now)) cache_headers = {'if-modified-since': resp.headers['Last-Modified'], 'if-none-match': resp.etag} resp = self.webapp.get('/manage', headers=cache_headers)