diff --git a/aiohttp_client_cache/backends/base.py b/aiohttp_client_cache/backends/base.py index d93b465..e9facf2 100644 --- a/aiohttp_client_cache/backends/base.py +++ b/aiohttp_client_cache/backends/base.py @@ -47,6 +47,7 @@ def __init__( autoclose: bool = False, cache_control: bool = False, filter_fn: _FilterFn = lambda r: True, + restore_cookies: bool = True, **kwargs: Any, ): """ @@ -64,6 +65,8 @@ def __init__( filter_fn: function that takes a :py:class:`aiohttp.ClientResponse` object and returns a boolean indicating whether or not that response should be cached. Will be applied to both new and previously cached responses + restore_cookies: Copy cookies from cached responses (and their redirects) into the + session's cookie jar on a cache hit """ self.name = cache_name self.expire_after = expire_after @@ -73,6 +76,7 @@ def __init__( self.cache_control = cache_control self.filter_fn = filter_fn self.autoclose = autoclose + self.restore_cookies = restore_cookies self._disabled = contextvars.ContextVar('_disabled', default=False) # Allows multiple redirects or other aliased URLs to point to the same cached response diff --git a/aiohttp_client_cache/session.py b/aiohttp_client_cache/session.py index 46facf6..da4f866 100644 --- a/aiohttp_client_cache/session.py +++ b/aiohttp_client_cache/session.py @@ -124,6 +124,8 @@ async def _request( return set_response_defaults(new_response) def _restore_cookies(self, response: AnyResponse): + if not getattr(self.cache, 'restore_cookies', True): + return self.cookie_jar.update_cookies(response.cookies or {}, response.url) for redirect in response.history: self.cookie_jar.update_cookies(redirect.cookies or {}, redirect.url) diff --git a/docs/user_guide.md b/docs/user_guide.md index 388af79..b847ac5 100644 --- a/docs/user_guide.md +++ b/docs/user_guide.md @@ -323,6 +323,16 @@ responses (on read). Example: >>> cache = SQLiteCache(filter_fn=filter_by_size) ``` +### Cookies + +By default, cookies from a cached response (and any redirects in its history) are copied into the +session's cookie jar on a cache hit, as if the request had been sent. To keep cached responses from +modifying the active session's cookies, use `restore_cookies=False`: + +```python +>>> cache = SQLiteCache(restore_cookies=False) +``` + ### Library Compatibility This library works by extending `aiohttp.ClientSession`, and there are other libraries out there diff --git a/test/unit/test_session.py b/test/unit/test_session.py index 14e0b48..7d8bce9 100644 --- a/test/unit/test_session.py +++ b/test/unit/test_session.py @@ -164,6 +164,27 @@ async def test_session__cookies(mock_request): assert cookies['test_cookie'].value == 'value' +@patch.object(ClientSession, '_request', return_value=FakeCachedResponse) +async def test_session__cookies_not_restored(mock_request): + cache = MagicMock(spec=CacheBackend) + cache.restore_cookies = False + redirect = MagicMock( + url=URL('https://redirect.com'), cookies=SimpleCookie({'redirect_cookie': 'value'}) + ) + response = AsyncMock( + is_expired=False, + url=URL('https://test.com'), + cookies=SimpleCookie({'test_cookie': 'value'}), + history=[redirect], + ) + cache.request.return_value = response + + async with CachedSession(cache=cache) as session: + session.cookie_jar.clear() + await session.get('http://test.url') + assert len(session.cookie_jar) == 0 + + @patch.object(ClientSession, '_request', return_value=FakeCachedResponse) async def test_session__redirect_cookies(mock_request): cache = MagicMock(spec=CacheBackend)