From 0afffae4ecb660f2ecb94a756c2ab7729654ecf6 Mon Sep 17 00:00:00 2001 From: Alexey Date: Thu, 28 Dec 2017 00:49:46 +0300 Subject: [PATCH] Fixed #28965 -- Updated Set-Cookie's Expires date format to follow RFC 7231. --- django/contrib/sessions/middleware.py | 4 ++-- django/http/response.py | 7 +++---- docs/releases/2.1.txt | 6 ++++++ tests/messages_tests/test_cookie.py | 2 +- tests/requests/tests.py | 8 ++++---- tests/sessions_tests/tests.py | 8 ++++---- 6 files changed, 20 insertions(+), 15 deletions(-) diff --git a/django/contrib/sessions/middleware.py b/django/contrib/sessions/middleware.py index 60faa2b3287..7263b6ac2d3 100644 --- a/django/contrib/sessions/middleware.py +++ b/django/contrib/sessions/middleware.py @@ -6,7 +6,7 @@ from django.contrib.sessions.backends.base import UpdateError from django.core.exceptions import SuspiciousOperation from django.utils.cache import patch_vary_headers from django.utils.deprecation import MiddlewareMixin -from django.utils.http import cookie_date +from django.utils.http import http_date class SessionMiddleware(MiddlewareMixin): @@ -50,7 +50,7 @@ class SessionMiddleware(MiddlewareMixin): else: max_age = request.session.get_expiry_age() expires_time = time.time() + max_age - expires = cookie_date(expires_time) + expires = http_date(expires_time) # Save the session data and refresh the client cookie. # Skip session save for 500 responses, refs #3881. if response.status_code != 500: diff --git a/django/http/response.py b/django/http/response.py index c07dc5b6e07..76d731d53fd 100644 --- a/django/http/response.py +++ b/django/http/response.py @@ -14,7 +14,7 @@ from django.core.serializers.json import DjangoJSONEncoder from django.http.cookie import SimpleCookie from django.utils import timezone from django.utils.encoding import force_bytes, iri_to_uri -from django.utils.http import cookie_date +from django.utils.http import http_date _charset_from_content_type_re = re.compile(r';\s*charset=(?P[^\s;]+)', re.I) @@ -185,8 +185,7 @@ class HttpResponseBase: self.cookies[key]['max-age'] = max_age # IE requires expires, so set it if hasn't been already. if not expires: - self.cookies[key]['expires'] = cookie_date(time.time() + - max_age) + self.cookies[key]['expires'] = http_date(time.time() + max_age) if path is not None: self.cookies[key]['path'] = path if domain is not None: @@ -207,7 +206,7 @@ class HttpResponseBase: def delete_cookie(self, key, path='/', domain=None): self.set_cookie(key, max_age=0, path=path, domain=domain, - expires='Thu, 01-Jan-1970 00:00:00 GMT') + expires='Thu, 01 Jan 1970 00:00:00 GMT') # Common methods used by subclasses diff --git a/docs/releases/2.1.txt b/docs/releases/2.1.txt index b48804ab979..e1947728d90 100644 --- a/docs/releases/2.1.txt +++ b/docs/releases/2.1.txt @@ -232,6 +232,12 @@ Miscellaneous * The minimum supported version of ``mysqlclient`` is increased from 1.3.3 to 1.3.7. +* The date format of ``Set-Cookie``'s ``Expires`` directive is changed to + follow :rfc:`7231#section-7.1.1.1` instead of Netscape's cookie standard. + Hyphens present in dates like ``Tue, 25-Dec-2018 22:26:13 GMT`` are removed. + This change should be merely cosmetic except perhaps for antiquated browsers + that don't parse the new format. + .. _deprecated-features-2.1: Features deprecated in 2.1 diff --git a/tests/messages_tests/test_cookie.py b/tests/messages_tests/test_cookie.py index 68dea51bcf7..a5eff30fd4f 100644 --- a/tests/messages_tests/test_cookie.py +++ b/tests/messages_tests/test_cookie.py @@ -82,7 +82,7 @@ class CookieTests(BaseTests, SimpleTestCase): storage.update(response) self.assertEqual(response.cookies['messages'].value, '') self.assertEqual(response.cookies['messages']['domain'], '.example.com') - self.assertEqual(response.cookies['messages']['expires'], 'Thu, 01-Jan-1970 00:00:00 GMT') + self.assertEqual(response.cookies['messages']['expires'], 'Thu, 01 Jan 1970 00:00:00 GMT') def test_get_bad_cookie(self): request = self.get_request() diff --git a/tests/requests/tests.py b/tests/requests/tests.py index 4c26bccf4e2..fdc2e06e18a 100644 --- a/tests/requests/tests.py +++ b/tests/requests/tests.py @@ -14,7 +14,7 @@ from django.http.request import split_domain_port from django.test import RequestFactory, SimpleTestCase, override_settings from django.test.client import FakePayload from django.test.utils import freeze_time -from django.utils.http import cookie_date +from django.utils.http import http_date from django.utils.timezone import utc @@ -234,7 +234,7 @@ class RequestsTests(SimpleTestCase): response.set_cookie('c', 'old-value') self.assertEqual(response.cookies['c']['expires'], '') response.delete_cookie('c') - self.assertEqual(response.cookies['c']['expires'], 'Thu, 01-Jan-1970 00:00:00 GMT') + self.assertEqual(response.cookies['c']['expires'], 'Thu, 01 Jan 1970 00:00:00 GMT') response.set_cookie('c', 'new-value') self.assertEqual(response.cookies['c']['expires'], '') @@ -246,7 +246,7 @@ class RequestsTests(SimpleTestCase): self.assertIn( datetime_cookie['expires'], # assertIn accounts for slight time dependency (#23450) - ('Sat, 01-Jan-2028 04:05:06 GMT', 'Sat, 01-Jan-2028 04:05:07 GMT') + ('Sat, 01 Jan 2028 04:05:06 GMT', 'Sat, 01 Jan 2028 04:05:07 GMT') ) def test_max_age_expiration(self): @@ -257,7 +257,7 @@ class RequestsTests(SimpleTestCase): response.set_cookie('max_age', max_age=10) max_age_cookie = response.cookies['max_age'] self.assertEqual(max_age_cookie['max-age'], 10) - self.assertEqual(max_age_cookie['expires'], cookie_date(set_cookie_time + 10)) + self.assertEqual(max_age_cookie['expires'], http_date(set_cookie_time + 10)) def test_httponly_cookie(self): response = HttpResponse() diff --git a/tests/sessions_tests/tests.py b/tests/sessions_tests/tests.py index 3a3af1613ac..f70c8ca1e6f 100644 --- a/tests/sessions_tests/tests.py +++ b/tests/sessions_tests/tests.py @@ -730,9 +730,9 @@ class SessionMiddlewareTests(TestCase): # The cookie was deleted, not recreated. # A deleted cookie header looks like: - # Set-Cookie: sessionid=; expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/ + # Set-Cookie: sessionid=; expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; Path=/ self.assertEqual( - 'Set-Cookie: {}=""; expires=Thu, 01-Jan-1970 00:00:00 GMT; ' + 'Set-Cookie: {}=""; expires=Thu, 01 Jan 1970 00:00:00 GMT; ' 'Max-Age=0; Path=/'.format( settings.SESSION_COOKIE_NAME, ), @@ -758,11 +758,11 @@ class SessionMiddlewareTests(TestCase): # The cookie was deleted, not recreated. # A deleted cookie header with a custom domain and path looks like: # Set-Cookie: sessionid=; Domain=.example.local; - # expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; + # expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; # Path=/example/ self.assertEqual( 'Set-Cookie: {}=""; Domain=.example.local; expires=Thu, ' - '01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/example/'.format( + '01 Jan 1970 00:00:00 GMT; Max-Age=0; Path=/example/'.format( settings.SESSION_COOKIE_NAME, ), str(response.cookies[settings.SESSION_COOKIE_NAME])