Browse Source

Added SESSION_REFRESH_EACH_REQUEST config option.

This also changes how sessions are being refreshed.  With the new
behavior set-cookie is only emitted if the session is modified or if the
session is permanent.  Permanent sessions can be set to not refresh
automatically through the SESSION_REFRESH_EACH_REQUEST config key.

This fixes #798.
pull/814/head
Armin Ronacher 12 years ago
parent
commit
d1d835c023
  1. 7
      CHANGES
  2. 12
      docs/config.rst
  3. 1
      flask/app.py
  4. 33
      flask/sessions.py
  5. 43
      flask/testsuite/basic.py

7
CHANGES

@ -8,6 +8,13 @@ Version 1.0
(release date to be announced, codename to be selected)
- Added ``SESSION_REFRESH_EACH_REQUEST`` config key that controls the
set-cookie behavior. If set to `True` a permanent session will be
refreshed each request and get their lifetime extended, if set to
`False` it will only be modified if the session actually modifies.
Non permanent sessions are not affected by this and will always
expire if the browser window closes.
Version 0.10.2
--------------

12
docs/config.rst

@ -88,6 +88,15 @@ The following configuration values are used internally by Flask:
:class:`datetime.timedelta` object.
Starting with Flask 0.8 this can also be
an integer representing seconds.
``SESSION_REFRESH_EACH_REQUEST`` this flag controls how permanent
sessions are refresh. If set to `True`
(which is the default) then the cookie
is refreshed each request which
automatically bumps the lifetime. If
set to `False` a `set-cookie` header is
only sent if the session is modified.
Non permanent sessions are not affected
by this.
``USE_X_SENDFILE`` enable/disable x-sendfile
``LOGGER_NAME`` the name of the logger
``SERVER_NAME`` the name and port number of the server.
@ -210,6 +219,9 @@ The following configuration values are used internally by Flask:
.. versionadded:: 0.10
``JSON_AS_ASCII``, ``JSON_SORT_KEYS``, ``JSONIFY_PRETTYPRINT_REGULAR``
.. versionadded:: 1.0
``SESSION_REFRESH_EACH_REQUEST``
Configuring from Files
----------------------

1
flask/app.py

@ -285,6 +285,7 @@ class Flask(_PackageBoundObject):
'SESSION_COOKIE_PATH': None,
'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SECURE': False,
'SESSION_REFRESH_EACH_REQUEST': True,
'MAX_CONTENT_LENGTH': None,
'SEND_FILE_MAX_AGE_DEFAULT': 12 * 60 * 60, # 12 hours
'TRAP_BAD_REQUEST_ERRORS': False,

33
flask/sessions.py

@ -252,6 +252,24 @@ class SessionInterface(object):
if session.permanent:
return datetime.utcnow() + app.permanent_session_lifetime
def should_set_cookie(self, app, session):
"""Indicates weather a cookie should be set now or not. This is
used by session backends to figure out if they should emit a
set-cookie header or not. The default behavior is controlled by
the ``SESSION_REFRESH_EACH_REQUEST`` config variable. If
it's set to `False` then a cookie is only set if the session is
modified, if set to `True` it's always set if the session is
permanent.
This check is usually skipped if sessions get deleted.
.. versionadded:: 1.0
"""
if session.modified:
return True
save_each = app.config['SESSION_REFRESH_EACH_REQUEST']
return save_each and session.permanent
def open_session(self, app, request):
"""This method has to be implemented and must either return `None`
in case the loading failed because of a configuration error or an
@ -315,11 +333,26 @@ class SecureCookieSessionInterface(SessionInterface):
def save_session(self, app, session, response):
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
httponly = self.get_cookie_httponly(app)
secure = self.get_cookie_secure(app)
expires = self.get_expiration_time(app, session)

43
flask/testsuite/basic.py

@ -345,6 +345,49 @@ class BasicFunctionalityTestCase(FlaskTestCase):
self.assert_equal(type(rv['b']), bytes)
self.assert_equal(rv['t'], (1, 2, 3))
def test_session_cookie_setting(self):
app = flask.Flask(__name__)
app.testing = True
app.secret_key = 'dev key'
is_permanent = True
@app.route('/bump')
def bump():
rv = flask.session['foo'] = flask.session.get('foo', 0) + 1
flask.session.permanent = is_permanent
return str(rv)
@app.route('/read')
def read():
return str(flask.session.get('foo', 0))
def run_test(expect_header):
with app.test_client() as c:
self.assert_equal(c.get('/bump').data, '1')
self.assert_equal(c.get('/bump').data, '2')
self.assert_equal(c.get('/bump').data, '3')
rv = c.get('/read')
set_cookie = rv.headers.get('set-cookie')
self.assert_equal(set_cookie is not None, expect_header)
self.assert_equal(rv.data, '3')
is_permanent = True
app.config['SESSION_REFRESH_EACH_REQUEST'] = True
run_test(expect_header=True)
is_permanent = True
app.config['SESSION_REFRESH_EACH_REQUEST'] = False
run_test(expect_header=False)
is_permanent = False
app.config['SESSION_REFRESH_EACH_REQUEST'] = True
run_test(expect_header=False)
is_permanent = False
app.config['SESSION_REFRESH_EACH_REQUEST'] = False
run_test(expect_header=False)
def test_flashes(self):
app = flask.Flask(__name__)
app.secret_key = 'testkey'

Loading…
Cancel
Save