diff --git a/src/quart/app.py b/src/quart/app.py index bb2e42c..2e2b883 100644 --- a/src/quart/app.py +++ b/src/quart/app.py @@ -1690,8 +1690,13 @@ async def process_response( ResponseTypes, await self.ensure_async(function)(response) ) - session_ = ctx.session - if not self.session_interface.is_null_session(session_): + # Use _session, not the session property: the property sets + # session.accessed = True, which would force Vary: Cookie on every + # response (including views that never touched the session). + session_ = ctx._session + if session_ is not None and not self.session_interface.is_null_session( + session_ + ): await self.ensure_async(self.session_interface.save_session)( self, session_, response ) @@ -1720,8 +1725,10 @@ async def postprocess_websocket( ResponseTypes, await self.ensure_async(function)(response) ) - session_ = ctx.session - if not self.session_interface.is_null_session(session_): + session_ = ctx._session + if session_ is not None and not self.session_interface.is_null_session( + session_ + ): await self.session_interface.save_session(self, session_, response) return response diff --git a/tests/test_sessions.py b/tests/test_sessions.py index 130b0e6..08ab3ad 100644 --- a/tests/test_sessions.py +++ b/tests/test_sessions.py @@ -5,6 +5,7 @@ from hypercorn.typing import HTTPScope from werkzeug.datastructures import Headers +from quart import session from quart.app import Quart from quart.sessions import SecureCookieSession from quart.sessions import SecureCookieSessionInterface @@ -84,3 +85,25 @@ async def test_secure_cookie_session_interface_save_session_no_access() -> None: response = await _save_session(session) assert response.headers.get("Set-Cookie") is None assert response.headers.get("Vary") is None + + +async def test_process_response_does_not_mark_session_accessed() -> None: + """Views that never touch the session must not get Vary: Cookie (#480).""" + app = Quart(__name__) + app.secret_key = "secret" + + @app.route("/") + async def index() -> str: + return "ok" + + @app.route("/touch") + async def touch() -> str: + session["x"] = 1 + return "ok" + + test_client = app.test_client() + response = await test_client.get("/") + assert response.headers.get("Vary") is None + + response = await test_client.get("/touch") + assert response.headers.get("Vary") == "Cookie"