From 191cf80d64c23936916265d6e79e9ea1c20ace0c Mon Sep 17 00:00:00 2001 From: Alex0AI <206435355+Alex0AI@users.noreply.github.com> Date: Thu, 8 Oct 2026 22:13:26 +0800 Subject: [PATCH] Fix Max-Age precedence when expiring session cookies --- CHANGELOG.md | 4 ++++ httpie/utils.py | 2 -- tests/test_sessions.py | 33 ++++++++++++++++++++++++++++++--- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0497ac3508..8a946b9124 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ This document records all notable changes to [HTTPie](https://httpie.io). This project adheres to [Semantic Versioning](https://semver.org/). +## Unreleased + +- Honor `Max-Age` over `Expires` when removing expired session cookies. + ## [3.2.4](https://github.com/httpie/cli/compare/3.2.3...3.2.4) (2024-11-01) - Fix default certs loading and unpin `requests`. ([#1596](https://github.com/httpie/cli/issues/1596)) diff --git a/httpie/utils.py b/httpie/utils.py index 4735b2be5d..5ad8e9cb83 100644 --- a/httpie/utils.py +++ b/httpie/utils.py @@ -193,8 +193,6 @@ def _max_age_to_expires(cookies, now): """ for cookie in cookies: - if 'expires' in cookie: - continue max_age = cookie.get('max-age') if max_age and max_age.isdigit(): cookie['expires'] = now + float(max_age) diff --git a/tests/test_sessions.py b/tests/test_sessions.py index aa5243487d..0fe20e7701 100644 --- a/tests/test_sessions.py +++ b/tests/test_sessions.py @@ -394,6 +394,19 @@ def test_expired_cookies(self, httpbin): assert 'cookie1' in updated_session['cookies'] assert 'cookie2' not in updated_session['cookies'] + def test_session_keeps_cookie_when_max_age_overrides_expired_date(self, localhost_http_server): + http( + '--session', str(self.session_path), + localhost_http_server + '/cookies/set', + 'cookie2==bar; Max-Age=3600; Expires=Thu, 01-Jan-1970 00:00:00 GMT; Path=/', + ) + r = http( + '--session', str(self.session_path), + '--print=H', + localhost_http_server + '/cookies', + ) + assert 'cookie2=bar' in r + def test_get_expired_cookies_using_max_age(self): cookies = 'one=two; Max-Age=0; path=/; domain=.tumblr.com; HttpOnly' expected_expired = [ @@ -401,6 +414,21 @@ def test_get_expired_cookies_using_max_age(self): ] assert get_expired_cookies(cookies, now=None) == expected_expired + @pytest.mark.parametrize( + 'expires, max_age, expired', + [ + ('Thu, 01-Jan-1970 00:00:00 GMT', '3600', False), + ('Fri, 12 Jun 2020 12:28:55 GMT', '0', True), + ('invalid', '0', True), + ('Thu, 01-Jan-1970 00:00:00 GMT', 'invalid', True), + ('Fri, 12 Jun 2020 12:28:55 GMT', 'invalid', False), + ] + ) + def test_max_age_takes_precedence_over_expires(self, expires, max_age, expired): + cookies = f'one=two; Expires={expires}; Max-Age={max_age}; Path=/' + expected = [{'name': 'one', 'path': '/'}] if expired else [] + assert get_expired_cookies(cookies, now=1000.0) == expected + @pytest.mark.parametrize( 'cookies, now, expected_expired', [ @@ -426,11 +454,10 @@ def test_get_expired_cookies_using_max_age(self): ] ), ( - # Checks we gracefully ignore expires date in invalid format. - # + # A valid Max-Age still applies when Expires is malformed. 'pfg=; Expires=Sat, 19-Sep-2020 06:58:14 GMT+0000; Max-Age=0; path=/; domain=.tumblr.com; secure; HttpOnly', None, - [] + [{'name': 'pfg', 'path': '/'}] ), ( 'hello=world; Path=/; Expires=Fri, 12 Jun 2020 12:28:55 GMT; HttpOnly',