From 915d149c9c6225c60185c3939c195fb18cd6e71a Mon Sep 17 00:00:00 2001
From: Mihir Singh <me@mihirsingh.com>
Date: Wed, 16 Apr 2014 17:38:41 -0400
Subject: [PATCH 4/6] Append a 'Vary: Cookie' header to the response when the
session has been accessed
---
flask/sessions.py | 58 ++++++++++++++++++++++++++++++++---------------
1 file changed, 40 insertions(+), 18 deletions(-)
diff --git a/flask/sessions.py b/flask/sessions.py
index 29857d23..359b3453 100644
--- a/flask/sessions.py
+++ b/flask/sessions.py
@@ -51,6 +51,13 @@ class SessionMixin(object):
#: The default mixin implementation just hardcodes `True` in.
modified = True
+ #: the accessed variable indicates whether or not the session object has
+ #: been accessed in that request. This allows flask to append a `Vary:
+ #: Cookie` header to the response if the session is being accessed. This
+ #: allows caching proxy servers, like Varnish, to use both the URL and the
+ #: session cookie as keys when caching pages, preventing multiple users
+ #: from being served the same cache.
+ accessed = True
class TaggedJSONSerializer(object):
"""A customized JSON serializer that supports a few extra types that
@@ -112,9 +119,18 @@ class SecureCookieSession(CallbackDict, SessionMixin):
def __init__(self, initial=None):
def on_update(self):
self.modified = True
+ self.accessed = True
CallbackDict.__init__(self, initial, on_update)
self.modified = False
+ self.accessed = False
+ def __getitem__(self, key):
+ self.accessed = True
+ return super(SecureCookieSession, self).__getitem__(key)
+
+ def get(self, key, default=None):
+ self.accessed = True
+ return super(SecureCookieSession, self).get(key, default)
def setdefault(self, key, default=None):
self.accessed = True
@@ -338,24 +354,30 @@ class SecureCookieSessionInterface(SessionInterface):
domain = self.get_cookie_domain(app)
path = self.get_cookie_path(app)
- # Delete case. If there is no session we bail early.
- # If the session was modified to be empty we remove the
- # whole cookie.
- if not session:
- if session.modified:
- response.delete_cookie(app.session_cookie_name,
- domain=domain, path=path)
- return
-
- # Modification case. There are upsides and downsides to
- # emitting a set-cookie header each request. The behavior
- # is controlled by the :meth:`should_set_cookie` method
- # which performs a quick check to figure out if the cookie
- # should be set or not. This is controlled by the
- # SESSION_REFRESH_EACH_REQUEST config flag as well as
- # the permanent flag on the session itself.
- if not self.should_set_cookie(app, session):
- return
+ if session.accessed:
+
+ response.headers.add('Vary', 'Cookie')
+
+ else:
+
+ # Delete case. If there is no session we bail early.
+ # If the session was modified to be empty we remove the
+ # whole cookie.
+ if not session:
+ if session.modified:
+ response.delete_cookie(app.session_cookie_name,
+ domain=domain, path=path)
+ return
+
+ # Modification case. There are upsides and downsides to
+ # emitting a set-cookie header each request. The behavior
+ # is controlled by the :meth:`should_set_cookie` method
+ # which performs a quick check to figure out if the cookie
+ # should be set or not. This is controlled by the
+ # SESSION_REFRESH_EACH_REQUEST config flag as well as
+ # the permanent flag on the session itself.
+ if not self.should_set_cookie(app, session):
+ return
httponly = self.get_cookie_httponly(app)
secure = self.get_cookie_secure(app)
--
2.31.1