Blob Blame History Raw
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